summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2013-12-18 16:25:09 +0000
committerTorne (Richard Coles) <torne@google.com>2013-12-18 16:25:09 +0000
commita3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7 (patch)
treedafc1c6417406a7fbd422ad0bb890e96909ef564 /tools
parentd5f893c0bc79db3066bb5ae5d3d972ba1be7dd5f (diff)
downloadchromium_org-a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7.tar.gz
Merge from Chromium at DEPS revision 240154
This commit was generated by merge_to_master.py. Change-Id: I8f2ba858cf0e7f413dddedc2ae91dc37f7136c2e
Diffstat (limited to 'tools')
-rw-r--r--tools/android/checkstyle/checkstyle.py59
-rw-r--r--tools/android/checkstyle/chromium-style-5.0.xml16
-rw-r--r--tools/android/common/adb_connection.cc2
-rw-r--r--tools/android/common/daemon.cc4
-rw-r--r--tools/android/forwarder/forwarder.cc2
-rw-r--r--tools/android/forwarder2/common.cc2
-rw-r--r--tools/android/forwarder2/host_forwarder_main.cc1
-rw-r--r--tools/android/forwarder2/pipe_notifier.cc4
-rw-r--r--tools/android/memdump/memdump.cc47
-rwxr-xr-xtools/bisect-perf-regression.py135
-rw-r--r--tools/bisect_utils.py23
-rwxr-xr-xtools/check_ecs_deps/check_ecs_deps.py105
-rwxr-xr-xtools/checklicenses/checklicenses.py14
-rwxr-xr-xtools/clang/scripts/package.sh4
-rwxr-xr-xtools/cr/cr-bash-helpers.sh48
-rwxr-xr-xtools/cr/cr.sh50
-rw-r--r--tools/cr/cr/actions/adb.py144
-rw-r--r--tools/cr/cr/actions/builder.py72
-rw-r--r--tools/cr/cr/actions/debugger.py51
-rw-r--r--tools/cr/cr/actions/gdb.py40
-rw-r--r--tools/cr/cr/actions/gyp.py26
-rw-r--r--tools/cr/cr/actions/installer.py55
-rw-r--r--tools/cr/cr/actions/linux.py50
-rw-r--r--tools/cr/cr/actions/ninja.py86
-rw-r--r--tools/cr/cr/actions/runner.py87
-rw-r--r--tools/cr/cr/base/android.py15
-rw-r--r--tools/cr/cr/base/arch.py70
-rw-r--r--tools/cr/cr/base/client.py31
-rw-r--r--tools/cr/cr/commands/build.py81
-rw-r--r--tools/cr/cr/commands/debug.py40
-rw-r--r--tools/cr/cr/commands/init.py180
-rw-r--r--tools/cr/cr/commands/install.py36
-rw-r--r--tools/cr/cr/commands/run.py50
-rw-r--r--tools/cr/cr/commands/select.py61
-rw-r--r--tools/cr/cr/commands/shell.py53
-rw-r--r--tools/cr/cr/commands/sync.py55
-rw-r--r--tools/cr/cr/config.py4
-rw-r--r--tools/cr/cr/context.py7
-rw-r--r--tools/cr/cr/fixups/__init__.py9
-rw-r--r--tools/cr/cr/fixups/arch.py54
-rw-r--r--tools/cr/cr/visitor.py2
-rw-r--r--tools/deep_memory_profiler/lib/dump.py6
-rw-r--r--tools/deep_memory_profiler/lib/subcommand.py4
-rw-r--r--tools/deep_memory_profiler/lib/symbol.py6
-rwxr-xr-xtools/find_runtime_symbols/find_runtime_symbols.py17
-rwxr-xr-xtools/find_runtime_symbols/prepare_symbol_info.py11
-rw-r--r--tools/find_runtime_symbols/proc_maps.py125
-rw-r--r--tools/gdb/gdb_chrome.py1
-rw-r--r--tools/gn/bin/linux/gn.sha12
-rw-r--r--tools/gn/bin/linux/gn32.sha12
-rw-r--r--tools/gn/bin/mac/gn.sha12
-rw-r--r--tools/gn/bin/win/gn.exe.sha12
-rw-r--r--tools/gn/function_exec_script.cc2
-rw-r--r--tools/gn/function_write_file.cc2
-rw-r--r--tools/gn/generate_test_gn_data.cc2
-rw-r--r--tools/gn/gyp_target_writer.cc2
-rw-r--r--tools/gn/ninja_build_writer.cc2
-rw-r--r--tools/gn/ninja_script_target_writer.cc2
-rw-r--r--tools/gn/ninja_target_writer.cc2
-rw-r--r--tools/gn/ninja_toolchain_writer.cc2
-rw-r--r--tools/gn/scheduler.cc14
-rw-r--r--tools/gn/secondary/base/BUILD.gn2
-rw-r--r--tools/gn/secondary/skia/BUILD.gn1
-rw-r--r--tools/gn/setup.cc76
-rw-r--r--tools/gritsettings/resource_ids2
-rw-r--r--tools/imagediff/image_diff.cc4
-rw-r--r--tools/ipc_fuzzer/DEPS3
-rw-r--r--tools/ipc_fuzzer/OWNERS2
-rw-r--r--tools/ipc_fuzzer/ipc_fuzzer.gyp19
-rw-r--r--tools/ipc_fuzzer/message_lib/DEPS8
-rw-r--r--tools/ipc_fuzzer/message_lib/all_messages.h14
-rw-r--r--tools/ipc_fuzzer/message_lib/message_cracker.h32
-rw-r--r--tools/ipc_fuzzer/message_lib/message_file.h27
-rw-r--r--tools/ipc_fuzzer/message_lib/message_file_format.h63
-rw-r--r--tools/ipc_fuzzer/message_lib/message_file_reader.cc230
-rw-r--r--tools/ipc_fuzzer/message_lib/message_file_writer.cc176
-rw-r--r--tools/ipc_fuzzer/message_lib/message_lib.gyp34
-rw-r--r--tools/ipc_fuzzer/message_lib/message_names.cc37
-rw-r--r--tools/ipc_fuzzer/message_lib/message_names.h59
-rw-r--r--tools/ipc_fuzzer/mutate/mutate.cc10
-rw-r--r--tools/ipc_fuzzer/mutate/mutate.gyp23
-rwxr-xr-xtools/ipc_fuzzer/play_testcase.py86
-rw-r--r--tools/ipc_fuzzer/replay/DEPS3
-rw-r--r--tools/ipc_fuzzer/replay/replay.cc20
-rw-r--r--tools/ipc_fuzzer/replay/replay.gyp29
-rw-r--r--tools/ipc_fuzzer/replay/replay_process.cc106
-rw-r--r--tools/ipc_fuzzer/replay/replay_process.h59
-rw-r--r--tools/json_schema_compiler/idl_schema.py16
-rw-r--r--tools/linux/PRESUBMIT.py45
-rwxr-xr-xtools/linux/procfs.py729
-rwxr-xr-xtools/linux/tests/procfs_tests.py (renamed from tools/find_runtime_symbols/tests/proc_maps_test.py)12
-rw-r--r--tools/lsan/suppressions.txt11
-rw-r--r--tools/metrics/actions/chromeactions.txt66
-rwxr-xr-xtools/metrics/actions/extract_actions.py65
-rw-r--r--tools/metrics/histograms/histograms.xml914
-rwxr-xr-xtools/multi-process-rss.py143
-rw-r--r--tools/perf/benchmarks/dom_perf.py3
-rw-r--r--tools/perf/benchmarks/jsgamebench.py4
-rw-r--r--tools/perf/benchmarks/kraken.py2
-rw-r--r--tools/perf/benchmarks/media.py9
-rw-r--r--tools/perf/benchmarks/octane.py2
-rw-r--r--tools/perf/benchmarks/page_cycler.py4
-rw-r--r--tools/perf/benchmarks/robohornet_pro.py2
-rw-r--r--tools/perf/benchmarks/session_restore.py4
-rw-r--r--tools/perf/benchmarks/smoothness.py14
-rw-r--r--tools/perf/benchmarks/spaceport.py4
-rw-r--r--tools/perf/bootstrap_deps (renamed from tools/perf/perf_tools/bootstrap_deps)4
-rw-r--r--tools/perf/measurements/loading_profile.py2
-rw-r--r--tools/perf/measurements/loading_timeline.py2
-rw-r--r--tools/perf/measurements/loading_trace.py2
-rw-r--r--tools/perf/measurements/no_op.py21
-rw-r--r--tools/perf/measurements/page_cycler.py58
-rw-r--r--tools/perf/measurements/page_cycler_unittest.py100
-rw-r--r--tools/perf/measurements/rasterize_and_record.py9
-rw-r--r--tools/perf/measurements/rasterize_and_record_micro.py24
-rw-r--r--tools/perf/measurements/rasterize_and_record_micro_unittest.py47
-rw-r--r--tools/perf/measurements/rasterize_and_record_unittest.py47
-rw-r--r--tools/perf/measurements/smoothness.py14
-rw-r--r--tools/perf/measurements/smoothness_unittest.py57
-rw-r--r--tools/perf/metrics/media.py6
-rw-r--r--tools/perf/metrics/rendering_stats.py24
-rw-r--r--tools/perf/metrics/rendering_stats_unittest.py6
-rw-r--r--tools/perf/metrics/smoothness.py38
-rw-r--r--tools/perf/metrics/smoothness_unittest.py222
-rw-r--r--tools/perf/metrics/speedindex.py374
-rw-r--r--tools/perf/metrics/speedindex_unittest.py51
-rw-r--r--tools/perf/metrics/timeline.py86
-rw-r--r--tools/perf/page_sets/PRESUBMIT.py24
-rw-r--r--tools/perf/page_sets/data/2012Q3.json1
-rw-r--r--tools/perf/page_sets/data/browsermark.json3
-rw-r--r--tools/perf/page_sets/data/calendar_forward_backward.json1
-rw-r--r--tools/perf/page_sets/data/canvasmark.json1
-rw-r--r--tools/perf/page_sets/data/gmail_alt_threadlist_conversation.json1
-rw-r--r--tools/perf/page_sets/data/gmail_alt_two_labels.json1
-rw-r--r--tools/perf/page_sets/data/gmail_expand_collapse_conversation.json1
-rw-r--r--tools/perf/page_sets/data/html5gaming.json3
-rw-r--r--tools/perf/page_sets/data/intl_ar_fa_he.json1
-rw-r--r--tools/perf/page_sets/data/intl_es_fr_pt-BR.json1
-rw-r--r--tools/perf/page_sets/data/intl_hi_ru.json1
-rw-r--r--tools/perf/page_sets/data/intl_ja_zh.json1
-rw-r--r--tools/perf/page_sets/data/intl_ko_th_vi.json1
-rw-r--r--tools/perf/page_sets/data/jsgamebench.json1
-rw-r--r--tools/perf/page_sets/data/key_desktop_sites.json1
-rw-r--r--tools/perf/page_sets/data/key_mobile_sites.json25
-rw-r--r--tools/perf/page_sets/data/key_mobile_sites_005.wpr.sha11
-rw-r--r--tools/perf/page_sets/data/key_silk_cases.json3
-rw-r--r--tools/perf/page_sets/data/kraken.json1
-rw-r--r--tools/perf/page_sets/data/maps.json3
-rw-r--r--tools/perf/page_sets/data/mobile_memory.json9
-rw-r--r--tools/perf/page_sets/data/mobile_memory_006.wpr.sha11
-rw-r--r--tools/perf/page_sets/data/octane.json3
-rw-r--r--tools/perf/page_sets/data/peacekeeper_array.json3
-rw-r--r--tools/perf/page_sets/data/peacekeeper_dom.json3
-rw-r--r--tools/perf/page_sets/data/peacekeeper_experimental.json3
-rw-r--r--tools/perf/page_sets/data/peacekeeper_html5.json3
-rw-r--r--tools/perf/page_sets/data/peacekeeper_render.json3
-rw-r--r--tools/perf/page_sets/data/peacekeeper_string.json3
-rw-r--r--tools/perf/page_sets/data/pica.json3
-rw-r--r--tools/perf/page_sets/data/plus_alt_posts_photos.json1
-rw-r--r--tools/perf/page_sets/data/robohornet_pro.json (renamed from tools/perf/page_sets/data/robohornetpro.json)3
-rw-r--r--tools/perf/page_sets/data/robohornet_pro_000.wpr.sha1 (renamed from tools/perf/page_sets/data/robohornetpro_000.wpr.sha1)0
-rw-r--r--tools/perf/page_sets/data/scirra.json3
-rw-r--r--tools/perf/page_sets/data/sunspider.json3
-rw-r--r--tools/perf/page_sets/data/test.json1
-rw-r--r--tools/perf/page_sets/data/top_10.json1
-rw-r--r--tools/perf/page_sets/data/top_10_mobile.json3
-rw-r--r--tools/perf/page_sets/data/top_25.json3
-rw-r--r--tools/perf/page_sets/data/tough_canvas_cases.json1
-rw-r--r--tools/perf/page_sets/data/tough_layout_cases.json1
-rw-r--r--tools/perf/page_sets/data/typical_25.json1
-rw-r--r--tools/perf/page_sets/key_mobile_sites.json19
-rw-r--r--tools/perf/page_sets/media_cns_cases.json76
-rw-r--r--tools/perf/page_sets/mobile_memory.json28
-rw-r--r--tools/perf/page_sets/mse_cases/audio.mp4.sha11
-rw-r--r--tools/perf/page_sets/mse_cases/startup_test.js5
-rw-r--r--tools/perf/page_sets/mse_cases/video.mp4.sha11
-rw-r--r--tools/perf/page_sets/presubmit_unittest.py14
-rw-r--r--tools/perf/page_sets/tough_canvas_cases.json2
-rw-r--r--tools/perf/page_sets/tough_memory_multi_tab.json4
-rw-r--r--tools/perf/page_sets/tough_scheduling_cases.json266
-rw-r--r--tools/perf/page_sets/tough_scheduling_cases/div_touch_handler.html282
-rw-r--r--tools/perf/page_sets/tough_scheduling_cases/raf.html283
-rw-r--r--tools/perf/page_sets/tough_scheduling_cases/raf_canvas.html289
-rw-r--r--tools/perf/page_sets/tough_scheduling_cases/raf_touch_animation.html320
-rw-r--r--tools/perf/page_sets/tough_scheduling_cases/simple_text_page.html260
-rw-r--r--tools/perf/page_sets/tough_scheduling_cases/touch_handler_scrolling.html297
-rw-r--r--tools/perf/page_sets/tough_video_cases.json23
-rw-r--r--tools/perf/page_sets/tough_video_cases/video.html71
-rw-r--r--tools/perf/perf_tools/__init__.py25
-rwxr-xr-xtools/perf/run_measurement117
-rwxr-xr-xtools/perf_expectations/make_expectations.py4
-rw-r--r--tools/perf_expectations/perf_expectations.json4
-rwxr-xr-xtools/run-bisect-perf-regression.py23
-rw-r--r--tools/telemetry/bin/README.chromium3
-rw-r--r--tools/telemetry/bin/avconv.sha11
-rw-r--r--tools/telemetry/telemetry/__init__.py17
-rw-r--r--tools/telemetry/telemetry/core/backends/adb_commands.py23
-rw-r--r--tools/telemetry/telemetry/core/backends/android_rndis.py15
-rw-r--r--tools/telemetry/telemetry/core/backends/chrome/android_browser_backend.py22
-rw-r--r--tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py2
-rw-r--r--tools/telemetry/telemetry/core/backends/chrome/cros_browser_backend.py5
-rw-r--r--tools/telemetry/telemetry/core/backends/chrome/cros_interface.py3
-rw-r--r--tools/telemetry/telemetry/core/backends/chrome/inspector_backend.py17
-rw-r--r--tools/telemetry/telemetry/core/backends/chrome/inspector_timeline.py91
-rw-r--r--tools/telemetry/telemetry/core/backends/chrome/inspector_timeline_unittest.py37
-rw-r--r--tools/telemetry/telemetry/core/backends/png_bitmap.py168
-rw-r--r--tools/telemetry/telemetry/core/backends/png_bitmap_unittest.py82
-rw-r--r--tools/telemetry/telemetry/core/backends/webdriver/webdriver_tab_backend.py4
-rw-r--r--tools/telemetry/telemetry/core/bitmap.py191
-rw-r--r--tools/telemetry/telemetry/core/bitmap_unittest.py90
-rw-r--r--tools/telemetry/telemetry/core/browser_options.py2
-rw-r--r--tools/telemetry/telemetry/core/platform/__init__.py31
-rw-r--r--tools/telemetry/telemetry/core/platform/android_platform_backend.py86
-rw-r--r--tools/telemetry/telemetry/core/platform/android_platform_backend_unittest.py36
-rw-r--r--tools/telemetry/telemetry/core/platform/cros_platform_backend.py4
-rw-r--r--tools/telemetry/telemetry/core/platform/linux_platform_backend.py30
-rw-r--r--tools/telemetry/telemetry/core/platform/platform_backend.py9
-rw-r--r--tools/telemetry/telemetry/core/platform/posix_platform_backend.py2
-rw-r--r--tools/telemetry/telemetry/core/platform/posix_platform_backend_unittest.py39
-rw-r--r--tools/telemetry/telemetry/core/platform/proc_supporting_platform_backend.py14
-rw-r--r--tools/telemetry/telemetry/core/platform/proc_supporting_platform_backend_unittest.py64
-rw-r--r--tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py40
-rw-r--r--tools/telemetry/telemetry/core/platform/profiler/perf_profiler_unittest.py2
-rw-r--r--tools/telemetry/telemetry/core/platform/profiler/tcmalloc_heap_profiler.py10
-rw-r--r--tools/telemetry/telemetry/core/tab.py41
-rw-r--r--tools/telemetry/telemetry/core/timeline/counter.py12
-rw-r--r--tools/telemetry/telemetry/core/timeline/event.py38
-rw-r--r--tools/telemetry/telemetry/core/timeline/inspector_importer.py17
-rw-r--r--tools/telemetry/telemetry/core/timeline/inspector_importer_unittest.py26
-rw-r--r--tools/telemetry/telemetry/core/timeline/model.py20
-rw-r--r--tools/telemetry/telemetry/core/timeline/process.py4
-rw-r--r--tools/telemetry/telemetry/core/timeline/slice.py24
-rw-r--r--tools/telemetry/telemetry/core/timeline/slice_unittest.py18
-rw-r--r--tools/telemetry/telemetry/core/timeline/thread.py33
-rw-r--r--tools/telemetry/telemetry/core/timeline/trace_event_importer.py22
-rw-r--r--tools/telemetry/telemetry/core/timeline/trace_event_importer_unittest.py182
-rw-r--r--tools/telemetry/telemetry/core/util.py34
-rw-r--r--tools/telemetry/telemetry/exception_formatter.py16
-rw-r--r--tools/telemetry/telemetry/page/actions/click_element.py6
-rw-r--r--tools/telemetry/telemetry/page/actions/click_element_unittest.py19
-rw-r--r--tools/telemetry/telemetry/page/actions/scroll.js15
-rw-r--r--tools/telemetry/telemetry/page/gtest_test_results.py9
-rw-r--r--tools/telemetry/telemetry/page/page_filter.py31
-rw-r--r--tools/telemetry/telemetry/page/page_filter_unittest.py50
-rw-r--r--tools/telemetry/telemetry/page/page_measurement_unittest.py19
-rw-r--r--tools/telemetry/telemetry/page/page_runner.py5
-rw-r--r--tools/telemetry/telemetry/page/page_runner_unittest.py34
-rw-r--r--tools/telemetry/telemetry/page/page_set.py2
-rw-r--r--tools/telemetry/telemetry/page/page_set_archive_info.py28
-rw-r--r--tools/telemetry/telemetry/page/page_set_archive_info_unittest.py16
-rw-r--r--tools/telemetry/telemetry/page/page_test.py10
-rw-r--r--tools/telemetry/telemetry/unittest/system_stub.py10
-rw-r--r--tools/telemetry/telemetry/value/histogram_unittest.py4
-rw-r--r--tools/telemetry/telemetry/value/list_of_scalar_values_unittest.py23
-rw-r--r--tools/telemetry/telemetry/value/merge_values_unittest.py31
-rw-r--r--tools/telemetry/telemetry/value/scalar.py10
-rw-r--r--tools/telemetry/telemetry/value/scalar_unittest.py18
-rw-r--r--tools/telemetry/telemetry/value/value_backcompat.py30
-rw-r--r--tools/telemetry/unittest_data/frame0.pngbin0 -> 19957 bytes
-rw-r--r--tools/telemetry/unittest_data/frame1.pngbin0 -> 14952 bytes
-rw-r--r--tools/telemetry/unittest_data/frame2.pngbin0 -> 19653 bytes
-rw-r--r--tools/telemetry/unittest_data/frame3.pngbin0 -> 18208 bytes
-rw-r--r--tools/telemetry/unittest_data/frame4.pngbin0 -> 18290 bytes
-rw-r--r--tools/telemetry/unittest_data/frame5.pngbin0 -> 18741 bytes
-rw-r--r--tools/telemetry/unittest_data/frame6.pngbin0 -> 151596 bytes
-rw-r--r--tools/telemetry/unittest_data/frame7.pngbin0 -> 141033 bytes
-rw-r--r--tools/telemetry/unittest_data/stat1
-rw-r--r--tools/telemetry/unittest_data/status39
-rw-r--r--tools/telemetry/unittest_data/status_nohwm37
-rw-r--r--tools/telemetry/unittest_data/vid.mp4bin0 -> 111494 bytes
-rw-r--r--tools/telemetry_tools/telemetry_bootstrap.py94
-rwxr-xr-xtools/valgrind/asan/asan_symbolize.py25
-rwxr-xr-xtools/valgrind/chrome_tests.py3
-rw-r--r--tools/valgrind/drmemory/suppressions.txt17
-rw-r--r--tools/valgrind/drmemory/suppressions_full.txt3
-rw-r--r--tools/valgrind/gtest_exclude/content_unittests.gtest_mac.txt3
-rw-r--r--tools/valgrind/gtest_exclude/net_unittests.gtest-drmemory_win32.txt3
-rw-r--r--tools/valgrind/gtest_exclude/unit_tests.gtest_linux.txt3
-rw-r--r--tools/valgrind/memcheck/suppressions.txt44
-rw-r--r--tools/valgrind/tsan/ignores.txt1
-rw-r--r--tools/valgrind/tsan/suppressions_win32.txt7
-rw-r--r--tools/valgrind/tsan_v2/suppressions.txt3
-rw-r--r--tools/win/toolchain/get_toolchain_if_necessary.py103
283 files changed, 9324 insertions, 2055 deletions
diff --git a/tools/android/checkstyle/checkstyle.py b/tools/android/checkstyle/checkstyle.py
new file mode 100644
index 0000000000..c47fbee45c
--- /dev/null
+++ b/tools/android/checkstyle/checkstyle.py
@@ -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.
+
+"""Script that is used by PRESUBMIT.py to run style checks on Java files."""
+
+import os
+import subprocess
+
+
+def RunCheckstyle(input_api, output_api, style_file):
+ if not os.path.exists(style_file):
+ file_error = (' Java checkstyle configuration file is missing: '
+ + style_file)
+ return [output_api.PresubmitError(file_error)]
+
+ # Filter out non-Java files and files that were deleted.
+ java_files = [x.LocalPath() for x in input_api.AffectedFiles(False, False)
+ if os.path.splitext(x.LocalPath())[1] == '.java']
+ if not java_files:
+ return []
+
+ # Run checkstyle
+ checkstyle_env = os.environ.copy()
+ checkstyle_env['JAVA_CMD'] = 'java'
+ try:
+ check = subprocess.Popen(['checkstyle', '-c', style_file] + java_files,
+ stdout=subprocess.PIPE, env=checkstyle_env)
+ stdout, _ = check.communicate()
+ if check.returncode == 0:
+ return []
+ except OSError as e:
+ import errno
+ if e.errno == errno.ENOENT:
+ install_error = (' checkstyle is not installed. Please run '
+ 'build/install-build-deps-android.sh')
+ return [output_api.PresubmitPromptWarning(install_error)]
+
+ # Remove non-error values from stdout
+ errors = stdout.splitlines()
+
+ if errors and errors[0] == 'Starting audit...':
+ del errors[0]
+ if errors and errors[-1] == 'Audit done.':
+ del errors[-1]
+
+ # Filter out warnings
+ errors = [x for x in errors if 'warning: ' not in x]
+ if not errors:
+ return []
+
+ local_path = input_api.PresubmitLocalPath()
+ output = []
+ for error in errors:
+ # Change the full file path to relative path in the output lines
+ full_path, end = error.split(':', 1)
+ rel_path = os.path.relpath(full_path, local_path)
+ output.append(' %s:%s' % (rel_path, end))
+ return [output_api.PresubmitPromptWarning('\n'.join(output))] \ No newline at end of file
diff --git a/tools/android/checkstyle/chromium-style-5.0.xml b/tools/android/checkstyle/chromium-style-5.0.xml
index ce2f24f7ef..63c869e749 100644
--- a/tools/android/checkstyle/chromium-style-5.0.xml
+++ b/tools/android/checkstyle/chromium-style-5.0.xml
@@ -24,8 +24,10 @@
<message key="import.unused" value="Unused import: {0}. Use :JavaImportOrganize (ECLIM) or Ctrl+Shift+O (Eclipse) to sort imports"/>
</module>
<module name="JavadocType">
- <property name="severity" value="warning"/>
- <property name="scope" value="protected"/>
+ <property name="severity" value="error"/>
+ <property name="tokens" value="INTERFACE_DEF, CLASS_DEF"/>
+ <property name="scope" value="public"/>
+ <message key="javadoc.missing" value="Public classes and interfaces require JavaDoc comments."/>
</module>
<module name="JavadocMethod">
<property name="severity" value="warning"/>
@@ -95,7 +97,9 @@
<property name="severity" value="error"/>
<property name="reliefPattern" value=".*"/>
</module>
- <module name="ModifierOrder"/>
+ <module name="ModifierOrder">
+ <property name="severity" value="error"/>
+ </module>
<module name="WhitespaceAround">
<property name="severity" value="error"/>
<property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, EQUAL, GE, GT, LAND, LE, LITERAL_ASSERT, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, SL, SLIST, SL_ASSIGN, SR, SR_ASSIGN, STAR, STAR_ASSIGN, TYPE_EXTENSION_AND" />
@@ -121,9 +125,9 @@
<property name="severity" value="warning"/>
</module>
<module name="ImportOrder">
- <property name="severity" value="warning"/>
+ <property name="severity" value="error"/>
<message key="import.ordering" value="Wrong order for {0} import. Use :JavaImportOrganize (ECLIM) or Ctrl+Shift+O (Eclipse) to sort imports"/>
- <property name="groups" value="android, com, org, java"/>
+ <property name="groups" value="android, com, dalvik, gov, junit, libcore, net, org, java, javax"/>
<property name="ordered" value="true"/>
<property name="option" value="top"/>
<property name="separated" value="true"/>
@@ -142,10 +146,12 @@
<property name="message" value="All TODOs should be named. e.g. &quot;TODO(johndoe):"/>
</module>
<module name="RegexpSingleline">
+ <property name="severity" value="error"/>
<property name="format" value="[ \t]+$"/>
<property name="message" value="Trailing whitespace"/>
</module>
<module name="RegexpHeader">
+ <property name="severity" value="error"/>
<property name="header" value="^// Copyright 20\d\d The Chromium Authors. All rights reserved.$\n^// Use of this source code is governed by a BSD-style license that can be$\n^// found in the LICENSE file.$"/>
</module>
</module>
diff --git a/tools/android/common/adb_connection.cc b/tools/android/common/adb_connection.cc
index 91c25fed20..9985a3a711 100644
--- a/tools/android/common/adb_connection.cc
+++ b/tools/android/common/adb_connection.cc
@@ -22,7 +22,7 @@ namespace {
void CloseSocket(int fd) {
if (fd >= 0) {
int old_errno = errno;
- (void) HANDLE_EINTR(close(fd));
+ close(fd);
errno = old_errno;
}
}
diff --git a/tools/android/common/daemon.cc b/tools/android/common/daemon.cc
index 9eafdbce05..699c615676 100644
--- a/tools/android/common/daemon.cc
+++ b/tools/android/common/daemon.cc
@@ -4,6 +4,7 @@
#include "tools/android/common/daemon.h"
+#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
@@ -11,7 +12,6 @@
#include "base/command_line.h"
#include "base/logging.h"
-#include "base/posix/eintr_wrapper.h"
namespace {
@@ -25,7 +25,7 @@ void Exit(int unused) {
void CloseFileDescriptor(int fd) {
int old_errno = errno;
- (void) HANDLE_EINTR(close(fd));
+ close(fd);
errno = old_errno;
}
diff --git a/tools/android/forwarder/forwarder.cc b/tools/android/forwarder/forwarder.cc
index e77c8065ff..fe499033e5 100644
--- a/tools/android/forwarder/forwarder.cc
+++ b/tools/android/forwarder/forwarder.cc
@@ -31,7 +31,7 @@ volatile bool g_killed = false;
void CloseSocket(int fd) {
if (fd >= 0) {
int old_errno = errno;
- (void) HANDLE_EINTR(close(fd));
+ close(fd);
errno = old_errno;
}
}
diff --git a/tools/android/forwarder2/common.cc b/tools/android/forwarder2/common.cc
index c97ed8056b..3b7387d3a6 100644
--- a/tools/android/forwarder2/common.cc
+++ b/tools/android/forwarder2/common.cc
@@ -19,7 +19,7 @@ void PError(const char* msg) {
void CloseFD(int fd) {
const int errno_copy = errno;
- if (HANDLE_EINTR(close(fd)) < 0) {
+ if (IGNORE_EINTR(close(fd)) < 0) {
PError("close");
errno = errno_copy;
}
diff --git a/tools/android/forwarder2/host_forwarder_main.cc b/tools/android/forwarder2/host_forwarder_main.cc
index 9d539006df..185bcea4b5 100644
--- a/tools/android/forwarder2/host_forwarder_main.cc
+++ b/tools/android/forwarder2/host_forwarder_main.cc
@@ -28,7 +28,6 @@
#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "base/pickle.h"
-#include "base/posix/eintr_wrapper.h"
#include "base/safe_strerror_posix.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
diff --git a/tools/android/forwarder2/pipe_notifier.cc b/tools/android/forwarder2/pipe_notifier.cc
index 3aba18b0d0..9110fffcea 100644
--- a/tools/android/forwarder2/pipe_notifier.cc
+++ b/tools/android/forwarder2/pipe_notifier.cc
@@ -25,8 +25,8 @@ PipeNotifier::PipeNotifier() {
}
PipeNotifier::~PipeNotifier() {
- (void) HANDLE_EINTR(close(receiver_fd_));
- (void) HANDLE_EINTR(close(sender_fd_));
+ close(receiver_fd_);
+ close(sender_fd_);
}
bool PipeNotifier::Notify() {
diff --git a/tools/android/memdump/memdump.cc b/tools/android/memdump/memdump.cc
index 69a020285b..aa87829fdf 100644
--- a/tools/android/memdump/memdump.cc
+++ b/tools/android/memdump/memdump.cc
@@ -29,6 +29,8 @@
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
+const unsigned int kPageSize = getpagesize();
+
namespace {
class BitSet {
@@ -123,24 +125,17 @@ bool ParseMemoryMapLine(const std::string& line,
base::SplitString(line, ' ', tokens);
if (tokens->size() < 2)
return false;
- const int addr_len = 8;
const std::string& addr_range = tokens->at(0);
- if (addr_range.length() != addr_len + 1 + addr_len)
- return false;
+ std::vector<std::string> range_tokens;
+ base::SplitString(addr_range, '-', &range_tokens);
uint64 tmp = 0;
- if (!base::HexStringToUInt64(
- base::StringPiece(
- addr_range.begin(), addr_range.begin() + addr_len),
- &tmp)) {
+ const std::string& start_address_token = range_tokens.at(0);
+ if (!base::HexStringToUInt64(start_address_token, &tmp)) {
return false;
}
memory_map->start_address = static_cast<uint>(tmp);
- const int end_addr_start_pos = addr_len + 1;
- if (!base::HexStringToUInt64(
- base::StringPiece(
- addr_range.begin() + end_addr_start_pos,
- addr_range.begin() + end_addr_start_pos + addr_len),
- &tmp)) {
+ const std::string& end_address_token = range_tokens.at(1);
+ if (!base::HexStringToUInt64(end_address_token, &tmp)) {
return false;
}
memory_map->end_address = static_cast<uint>(tmp);
@@ -151,7 +146,7 @@ bool ParseMemoryMapLine(const std::string& line,
return false;
memory_map->offset = static_cast<uint>(tmp);
memory_map->committed_pages_bits.resize(
- (memory_map->end_address - memory_map->start_address) / PAGE_SIZE);
+ (memory_map->end_address - memory_map->start_address) / kPageSize);
const int map_name_index = 5;
if (tokens->size() >= map_name_index + 1) {
for (std::vector<std::string>::const_iterator it =
@@ -209,11 +204,11 @@ bool GetPagesForMemoryMap(int pagemap_fd,
BitSet* committed_pages_bits) {
for (uint addr = memory_map.start_address, page_index = 0;
addr < memory_map.end_address;
- addr += PAGE_SIZE, ++page_index) {
- DCHECK_EQ(0, addr % PAGE_SIZE);
+ addr += kPageSize, ++page_index) {
+ DCHECK_EQ(0, addr % kPageSize);
PageMapEntry page_map_entry = {};
COMPILE_ASSERT(sizeof(PageMapEntry) == sizeof(uint64), unexpected_size);
- const off64_t offset = addr / PAGE_SIZE;
+ const off64_t offset = addr / kPageSize;
if (!ReadFromFileAtOffset(pagemap_fd, offset, &page_map_entry))
return false;
if (page_map_entry.present) { // Ignore non-committed pages.
@@ -284,7 +279,7 @@ void ClassifyPages(std::vector<ProcessMemory>* processes_memory) {
FillPFNMaps(*processes_memory, &pfn_maps);
// Hash set keeping track of the physical pages mapped in a single process so
// that they can be counted only once.
- std::hash_set<uint64> physical_pages_mapped_in_process;
+ base::hash_set<uint64> physical_pages_mapped_in_process;
for (std::vector<ProcessMemory>::iterator it = processes_memory->begin();
it != processes_memory->end(); ++it) {
@@ -306,7 +301,7 @@ void ClassifyPages(std::vector<ProcessMemory>* processes_memory) {
continue;
}
const uint64 page_frame_number = page_info.page_frame_number;
- const std::pair<std::hash_set<uint64>::iterator, bool> result =
+ const std::pair<base::hash_set<uint64>::iterator, bool> result =
physical_pages_mapped_in_process.insert(page_frame_number);
const bool did_insert = result.second;
if (!did_insert) {
@@ -356,9 +351,9 @@ void AppendAppSharedField(const std::vector<PageCount>& app_shared_pages,
out->append("[");
for (std::vector<PageCount>::const_iterator it = app_shared_pages.begin();
it != app_shared_pages.end(); ++it) {
- out->append(base::IntToString(it->total_count * PAGE_SIZE));
+ out->append(base::IntToString(it->total_count * kPageSize));
out->append(":");
- out->append(base::IntToString(it->unevictable_count * PAGE_SIZE));
+ out->append(base::IntToString(it->unevictable_count * kPageSize));
if (it + 1 != app_shared_pages.end())
out->append(",");
}
@@ -367,7 +362,7 @@ void AppendAppSharedField(const std::vector<PageCount>& app_shared_pages,
void DumpProcessesMemoryMapsInShortFormat(
const std::vector<ProcessMemory>& processes_memory) {
- const int KB_PER_PAGE = PAGE_SIZE >> 10;
+ const int KB_PER_PAGE = kPageSize >> 10;
std::vector<int> totals_app_shared(processes_memory.size());
std::string buf;
std::cout << "pid\tprivate\t\tshared_app\tshared_other (KB)\n";
@@ -420,11 +415,11 @@ void DumpProcessesMemoryMapsInExtendedFormat(
memory_map.end_address,
memory_map.flags.c_str(),
memory_map.offset,
- memory_map.private_pages.unevictable_count * PAGE_SIZE,
- memory_map.private_pages.total_count * PAGE_SIZE,
+ memory_map.private_pages.unevictable_count * kPageSize,
+ memory_map.private_pages.total_count * kPageSize,
app_shared_buf.c_str(),
- memory_map.other_shared_pages.unevictable_count * PAGE_SIZE,
- memory_map.other_shared_pages.total_count * PAGE_SIZE,
+ memory_map.other_shared_pages.unevictable_count * kPageSize,
+ memory_map.other_shared_pages.total_count * kPageSize,
memory_map.name.c_str(),
memory_map.committed_pages_bits.AsB64String().c_str());
std::cout << buf;
diff --git a/tools/bisect-perf-regression.py b/tools/bisect-perf-regression.py
index 73a3113a87..f6b59c103f 100755
--- a/tools/bisect-perf-regression.py
+++ b/tools/bisect-perf-regression.py
@@ -66,17 +66,17 @@ import bisect_utils
# from: Parent depot that must be bisected before this is bisected.
DEPOT_DEPS_NAME = {
'chromium' : {
- "src" : "src/",
+ "src" : "src",
"recurse" : True,
"depends" : None,
- "from" : 'cros',
+ "from" : ['cros', 'android-chrome'],
'viewvc': 'http://src.chromium.org/viewvc/chrome?view=revision&revision='
},
'webkit' : {
"src" : "src/third_party/WebKit",
"recurse" : True,
"depends" : None,
- "from" : 'chromium',
+ "from" : ['chromium'],
'viewvc': 'http://src.chromium.org/viewvc/blink?view=revision&revision='
},
'angle' : {
@@ -84,14 +84,14 @@ DEPOT_DEPS_NAME = {
"src_old" : "src/third_party/angle",
"recurse" : True,
"depends" : None,
- "from" : 'chromium',
+ "from" : ['chromium'],
"platform": 'nt',
},
'v8' : {
"src" : "src/v8",
"recurse" : True,
"depends" : None,
- "from" : 'chromium',
+ "from" : ['chromium'],
"custom_deps": bisect_utils.GCLIENT_CUSTOM_DEPS_V8,
'viewvc': 'https://code.google.com/p/v8/source/detail?r=',
},
@@ -100,7 +100,7 @@ DEPOT_DEPS_NAME = {
"recurse" : True,
"depends" : None,
"svn": "https://v8.googlecode.com/svn/branches/bleeding_edge",
- "from" : 'v8',
+ "from" : ['v8'],
'viewvc': 'https://code.google.com/p/v8/source/detail?r=',
},
'skia/src' : {
@@ -108,7 +108,7 @@ DEPOT_DEPS_NAME = {
"recurse" : True,
"svn" : "http://skia.googlecode.com/svn/trunk/src",
"depends" : ['skia/include', 'skia/gyp'],
- "from" : 'chromium',
+ "from" : ['chromium'],
'viewvc': 'https://code.google.com/p/skia/source/detail?r=',
},
'skia/include' : {
@@ -116,7 +116,7 @@ DEPOT_DEPS_NAME = {
"recurse" : False,
"svn" : "http://skia.googlecode.com/svn/trunk/include",
"depends" : None,
- "from" : 'chromium',
+ "from" : ['chromium'],
'viewvc': 'https://code.google.com/p/skia/source/detail?r=',
},
'skia/gyp' : {
@@ -124,9 +124,9 @@ DEPOT_DEPS_NAME = {
"recurse" : False,
"svn" : "http://skia.googlecode.com/svn/trunk/gyp",
"depends" : None,
- "from" : 'chromium',
+ "from" : ['chromium'],
'viewvc': 'https://code.google.com/p/skia/source/detail?r=',
- }
+ },
}
DEPOT_NAMES = DEPOT_DEPS_NAME.keys()
@@ -143,6 +143,16 @@ BUILD_RESULT_SUCCEED = 0
BUILD_RESULT_FAIL = 1
BUILD_RESULT_SKIPPED = 2
+
+def _AddAdditionalDepotInfo(depot_info):
+ """Adds additional depot info to the global depot variables."""
+ global DEPOT_DEPS_NAME
+ global DEPOT_NAMES
+ DEPOT_DEPS_NAME = dict(DEPOT_DEPS_NAME.items() +
+ depot_info.items())
+ DEPOT_NAMES = DEPOT_DEPS_NAME.keys()
+
+
def CalculateTruncatedMean(data_set, truncate_percent):
"""Calculates the truncated mean of a set of values.
@@ -436,6 +446,8 @@ class Builder(object):
builder = CrosBuilder(opts)
elif opts.target_platform == 'android':
builder = AndroidBuilder(opts)
+ elif opts.target_platform == 'android-chrome':
+ builder = AndroidChromeBuilder(opts)
else:
builder = DesktopBuilder(opts)
return builder
@@ -484,21 +496,8 @@ class AndroidBuilder(Builder):
def __init__(self, opts):
super(AndroidBuilder, self).__init__(opts)
- def InstallAPK(self, opts):
- """Installs apk to device.
-
- Args:
- opts: The options parsed from the command line.
-
- Returns:
- True if successful.
- """
- path_to_tool = os.path.join('build', 'android', 'adb_install_apk.py')
- cmd = [path_to_tool, '--apk', 'ChromiumTestShell.apk', '--apk_package',
- 'org.chromium.chrome.testshell', '--release']
- return_code = RunProcess(cmd)
-
- return not return_code
+ def _GetTargets(self):
+ return ['chromium_testshell', 'cc_perftests_apk', 'android_tools']
def Build(self, depot, opts):
"""Builds the android content shell and other necessary tools using options
@@ -511,24 +510,28 @@ class AndroidBuilder(Builder):
Returns:
True if build was successful.
"""
- targets = ['chromium_testshell', 'cc_perftests_apk', 'android_tools']
-
threads = None
if opts.use_goma:
threads = 64
build_success = False
if opts.build_preference == 'ninja':
- build_success = BuildWithNinja(threads, targets)
+ build_success = BuildWithNinja(threads, self._GetTargets())
else:
assert False, 'No build system defined.'
- if build_success:
- build_success = self.InstallAPK(opts)
-
return build_success
+class AndroidChromeBuilder(AndroidBuilder):
+ """AndroidBuilder is used to build on android's chrome."""
+ def __init__(self, opts):
+ super(AndroidChromeBuilder, self).__init__(opts)
+
+ def _GetTargets(self):
+ return AndroidBuilder._GetTargets(self) + ['chrome_apk']
+
+
class CrosBuilder(Builder):
"""CrosBuilder is used to build and image ChromeOS/Chromium when cros is the
target platform."""
@@ -671,7 +674,7 @@ class GitSourceControl(SourceControl):
def IsGit(self):
return True
- def GetRevisionList(self, revision_range_end, revision_range_start):
+ def GetRevisionList(self, revision_range_end, revision_range_start, cwd=None):
"""Retrieves a list of revisions between |revision_range_start| and
|revision_range_end|.
@@ -685,7 +688,7 @@ class GitSourceControl(SourceControl):
"""
revision_range = '%s..%s' % (revision_range_start, revision_range_end)
cmd = ['log', '--format=%H', '-10000', '--first-parent', revision_range]
- log_output = CheckRunGit(cmd)
+ log_output = CheckRunGit(cmd, cwd=cwd)
revision_hash_list = log_output.split()
revision_hash_list.append(revision_range_start)
@@ -727,6 +730,10 @@ class GitSourceControl(SourceControl):
Returns:
A string containing a git SHA1 hash, otherwise None.
"""
+ # Android-chrome is git only, so no need to resolve this to anything else.
+ if depot == 'android-chrome':
+ return revision_to_check
+
if depot != 'cros':
if not IsStringInt(revision_to_check):
return revision_to_check
@@ -844,13 +851,13 @@ class GitSourceControl(SourceControl):
return commit_info
- def CheckoutFileAtRevision(self, file_name, revision):
+ def CheckoutFileAtRevision(self, file_name, revision, cwd=None):
"""Performs a checkout on a file at the given revision.
Returns:
True if successful.
"""
- return not RunGit(['checkout', revision, file_name])[1]
+ return not RunGit(['checkout', revision, file_name], cwd=cwd)[1]
def RevertFileToHead(self, file_name):
"""Unstages a file and returns it to HEAD.
@@ -947,8 +954,9 @@ class BisectPerformanceMetrics(object):
[int(o) for o in output.split('\n') if IsStringInt(o)]))
revision_work_list = sorted(revision_work_list, reverse=True)
else:
+ cwd = self._GetDepotDirectory(depot)
revision_work_list = self.source_control.GetRevisionList(bad_revision,
- good_revision)
+ good_revision, cwd=cwd)
return revision_work_list
@@ -1029,7 +1037,7 @@ class BisectPerformanceMetrics(object):
results = {}
- if depot == 'chromium':
+ if depot == 'chromium' or depot == 'android-chrome':
locals = {'Var': lambda _: locals["vars"][_],
'From': lambda *args: None}
execfile(bisect_utils.FILE_DEPS_GIT, {}, locals)
@@ -1043,8 +1051,8 @@ class BisectPerformanceMetrics(object):
if DEPOT_DEPS_NAME[d]['platform'] != os.name:
continue
- if DEPOT_DEPS_NAME[d]['recurse'] and\
- DEPOT_DEPS_NAME[d]['from'] == depot:
+ if (DEPOT_DEPS_NAME[d]['recurse'] and
+ depot in DEPOT_DEPS_NAME[d]['from']):
if (locals['deps'].has_key(DEPOT_DEPS_NAME[d]['src']) or
locals['deps'].has_key(DEPOT_DEPS_NAME[d]['src_old'])):
if locals['deps'].has_key(DEPOT_DEPS_NAME[d]['src']):
@@ -1137,7 +1145,7 @@ class BisectPerformanceMetrics(object):
if self.opts.debug_ignore_build:
return True
- return not bisect_utils.RunGClient(['runhooks'])
+ return not bisect_utils.RunGClient(['runhooks'], cwd=self.src_cwd)
def TryParseHistogramValuesFromOutput(self, metric, text):
"""Attempts to parse a metric in the format HISTOGRAM <graph: <trace>.
@@ -1394,7 +1402,8 @@ class BisectPerformanceMetrics(object):
"""
revisions_to_sync = [[depot, revision]]
- is_base = (depot == 'chromium') or (depot == 'cros')
+ is_base = ((depot == 'chromium') or (depot == 'cros') or
+ (depot == 'android-chrome'))
# Some SVN depots were split into multiple git depots, so we need to
# figure out for each mirror which git revision to grab. There's no
@@ -1445,7 +1454,7 @@ class BisectPerformanceMetrics(object):
True if successful.
"""
if not self.source_control.CheckoutFileAtRevision(
- bisect_utils.FILE_DEPS_GIT, revision):
+ bisect_utils.FILE_DEPS_GIT, revision, cwd=self.src_cwd):
return False
cwd = os.getcwd()
@@ -1559,7 +1568,7 @@ class BisectPerformanceMetrics(object):
Otherwise, a tuple with the error message.
"""
sync_client = None
- if depot == 'chromium':
+ if depot == 'chromium' or depot == 'android-chrome':
sync_client = 'gclient'
elif depot == 'cros':
sync_client = 'repo'
@@ -1581,7 +1590,15 @@ class BisectPerformanceMetrics(object):
if sync_client:
self.PerformPreBuildCleanup()
- if not self.source_control.SyncToRevision(r[1], sync_client):
+ # If you're using gclient to sync, you need to specify the depot you
+ # want so that all the dependencies sync properly as well.
+ # ie. gclient sync src@<SHA1>
+ current_revision = r[1]
+ if sync_client == 'gclient':
+ current_revision = '%s@%s' % (DEPOT_DEPS_NAME[depot]['src'],
+ current_revision)
+ if not self.source_control.SyncToRevision(current_revision,
+ sync_client):
success = False
break
@@ -1698,8 +1715,7 @@ class BisectPerformanceMetrics(object):
continue
if not (DEPOT_DEPS_NAME[next_depot]["recurse"] and
- DEPOT_DEPS_NAME[next_depot]['from'] ==
- min_revision_data['depot']):
+ min_revision_data['depot'] in DEPOT_DEPS_NAME[next_depot]['from']):
continue
if current_depot == 'v8':
@@ -1908,11 +1924,13 @@ class BisectPerformanceMetrics(object):
"""
if self.source_control.IsGit() and target_depot != 'cros':
cmd = ['log', '--format=%ct', '-1', good_revision]
- output = CheckRunGit(cmd)
+ cwd = self._GetDepotDirectory(target_depot)
+
+ output = CheckRunGit(cmd, cwd=cwd)
good_commit_time = int(output)
cmd = ['log', '--format=%ct', '-1', bad_revision]
- output = CheckRunGit(cmd)
+ output = CheckRunGit(cmd, cwd=cwd)
bad_commit_time = int(output)
return good_commit_time <= bad_commit_time
@@ -1971,6 +1989,8 @@ class BisectPerformanceMetrics(object):
target_depot = 'chromium'
if self.opts.target_platform == 'cros':
target_depot = 'cros'
+ elif self.opts.target_platform == 'android-chrome':
+ target_depot = 'android-chrome'
cwd = os.getcwd()
self.ChangeToDepotWorkingDirectory(target_depot)
@@ -2110,7 +2130,7 @@ class BisectPerformanceMetrics(object):
next_revision_index = min_revision
elif max_revision_data['passed'] == '?':
next_revision_index = max_revision
- elif current_depot in ['cros', 'chromium', 'v8']:
+ elif current_depot in ['android-chrome', 'cros', 'chromium', 'v8']:
previous_revision = revision_list[min_revision]
# If there were changes to any of the external libraries we track,
# should bisect the changes there as well.
@@ -2657,6 +2677,7 @@ class BisectOptions(object):
self.output_buildbot_annotations = None
self.no_custom_deps = False
self.working_directory = None
+ self.extra_src = None
self.debug_ignore_build = None
self.debug_ignore_sync = None
self.debug_ignore_perf_test = None
@@ -2733,7 +2754,7 @@ class BisectOptions(object):
'are msvs/ninja.')
group.add_option('--target_platform',
type='choice',
- choices=['chromium', 'cros', 'android'],
+ choices=['chromium', 'cros', 'android', 'android-chrome'],
default='chromium',
help='The target platform. Choices are "chromium" '
'(current platform), "cros", or "android". If you '
@@ -2744,6 +2765,10 @@ class BisectOptions(object):
action="store_true",
default=False,
help='Run the script with custom_deps or not.')
+ group.add_option('--extra_src',
+ type='str',
+ help='Path to a script which can be used to modify '
+ 'the bisect script\'s behavior.')
group.add_option('--cros_board',
type='str',
help='The cros board type to build.')
@@ -2863,6 +2888,12 @@ def main():
opts = BisectOptions()
parse_results = opts.ParseCommandLine()
+ if opts.extra_src:
+ extra_src = bisect_utils.LoadExtraSrc(opts.extra_src)
+ if not extra_src:
+ raise RuntimeError("Invalid or missing --extra_src.")
+ _AddAdditionalDepotInfo(extra_src.GetAdditionalDepotInfo())
+
if opts.working_directory:
custom_deps = bisect_utils.DEFAULT_GCLIENT_CUSTOM_DEPS
if opts.no_custom_deps:
@@ -2886,7 +2917,9 @@ def main():
"moment.")
# gClient sync seems to fail if you're not in master branch.
- if not source_control.IsInProperBranch() and not opts.debug_ignore_sync:
+ if (not source_control.IsInProperBranch() and
+ not opts.debug_ignore_sync and
+ not opts.working_directory):
raise RuntimeError("You must switch to master branch to run bisection.")
bisect_test = BisectPerformanceMetrics(source_control, opts)
diff --git a/tools/bisect_utils.py b/tools/bisect_utils.py
index a885c3d2a0..7bb33a31e7 100644
--- a/tools/bisect_utils.py
+++ b/tools/bisect_utils.py
@@ -7,6 +7,7 @@ outputting annotations on the buildbot waterfall. These are intended to be
used by the bisection scripts."""
import errno
+import imp
import os
import shutil
import stat
@@ -102,6 +103,23 @@ def OutputAnnotationStepLink(label, url):
sys.stdout.flush()
+def LoadExtraSrc(path_to_file):
+ """Attempts to load an extra source file. If this is successful, uses the
+ new module to override some global values, such as gclient spec data.
+
+ Returns:
+ The loaded src module, or None."""
+ try:
+ global GCLIENT_SPEC_DATA
+ global GCLIENT_SPEC_ANDROID
+ extra_src = imp.load_source('data', path_to_file)
+ GCLIENT_SPEC_DATA = extra_src.GetGClientSpec()
+ GCLIENT_SPEC_ANDROID = extra_src.GetGClientSpecExtraParams()
+ return extra_src
+ except ImportError, e:
+ return None
+
+
def IsTelemetryCommand(command):
"""Attempts to discern whether or not a given command is running telemetry."""
return ('tools/perf/run_' in command or 'tools\\perf\\run_' in command)
@@ -209,7 +227,7 @@ def RunGClientAndCreateConfig(opts, custom_deps=None, cwd=None):
spec = 'solutions =' + str(spec)
spec = ''.join([l for l in spec.splitlines()])
- if opts.target_platform == 'android':
+ if 'android' in opts.target_platform:
spec += GCLIENT_SPEC_ANDROID
return_code = RunGClient(
@@ -435,6 +453,7 @@ def SetupAndroidBuildEnvironment(opts, path_to_src=None):
for line in out.splitlines():
(k, _, v) = line.partition('=')
os.environ[k] = v
+
return not proc.returncode
@@ -447,7 +466,7 @@ def SetupPlatformBuildEnvironment(opts):
Returns:
True if successful.
"""
- if opts.target_platform == 'android':
+ if 'android' in opts.target_platform:
CopyAndSaveOriginalEnvironmentVars()
return SetupAndroidBuildEnvironment(opts)
elif opts.target_platform == 'cros':
diff --git a/tools/check_ecs_deps/check_ecs_deps.py b/tools/check_ecs_deps/check_ecs_deps.py
index d45e9cedfb..f6760d65b5 100755
--- a/tools/check_ecs_deps/check_ecs_deps.py
+++ b/tools/check_ecs_deps/check_ecs_deps.py
@@ -6,7 +6,6 @@
''' Verifies that builds of the embedded content_shell do not included
unnecessary dependencies.'''
-import getopt
import os
import re
import string
@@ -15,42 +14,70 @@ import sys
import optparse
kUndesiredLibraryList = [
- # 'libasound', # ALSA sound - needs to go eventually
+ 'libX11',
+ 'libXau',
+ 'libXcomposite',
+ 'libXcursor',
+ 'libXdamage',
+ 'libXdmcp',
+ 'libXext',
+ 'libXfixes',
+ 'libXi',
+ 'libXrandr',
+ 'libXrender',
+ 'libXtst',
+ 'libasound',
'libcairo',
+ 'libdbus',
+ 'libffi',
+ 'libgconf',
+ 'libgio',
'libglib',
+ 'libgmodule',
+ 'libgobject',
'libpango',
+ 'libpcre',
+ 'libpixman',
+ 'libpng',
+ 'libresolv',
+ 'libselinux',
'libudev',
+ 'libxcb',
]
kAllowedLibraryList = [
+ # Toolchain libraries (gcc/glibc)
+ 'ld-linux',
'libc',
- 'libcap',
'libdl',
- 'libdrm',
- 'libexpat',
- 'libffi',
- 'libfontconfig',
- 'libfreetype',
'libgcc_s',
- 'libgobject-2.0',
'libm',
- 'libnspr4',
+ 'libpthread',
+ 'librt',
+ 'libstdc++',
+ 'linux-vdso',
+
+ # Needed for default ozone platforms
+ 'libdrm',
+
+ # NSS & NSPR
'libnss3',
'libnssutil3',
- 'libpcre',
+ 'libnspr4',
'libplc4',
'libplds4',
- 'libpthread',
- 'librt',
'libsmime3',
- 'libstdc++',
+
+ # Miscellaneous
+ 'libcap',
+ 'libexpat',
+ 'libfontconfig',
'libz',
- 'linux-vdso',
]
binary_target = 'content_shell'
-def stdmsg(final, errors):
+def stdmsg(_final, errors):
if errors:
for message in errors:
print message
@@ -70,6 +97,7 @@ def _main():
'warn': lambda x: stdmsg('WARNING', x),
'abend': lambda x: stdmsg('FAILED', x),
'ok': lambda x: stdmsg('SUCCESS', x),
+ 'verbose': lambda x: None,
}
parser = optparse.OptionParser(
@@ -80,8 +108,13 @@ def _main():
parser.add_option("-b", "--build-dir",
help="the location of the compiler output")
parser.add_option("--target", help="Debug or Release")
+ parser.add_option('-v', '--verbose', default=False, action='store_true')
options, args = parser.parse_args()
+ if args:
+ parser.usage()
+ return -1
+
# Bake target into build_dir.
if options.target and options.build_dir:
assert (options.target !=
@@ -90,24 +123,31 @@ def _main():
options.target)
if options.build_dir != None:
- target = os.path.join(options.build_dir, binary_target)
+ build_dir = os.path.abspath(options.build_dir)
else:
- target = binary_target
+ build_dir = os.getcwd()
+
+ target = os.path.join(build_dir, binary_target)
if options.annotate:
- output = {
+ output.update({
'message': lambda x: bbmsg(None, x),
'fail': lambda x: bbmsg('FAILURE', x),
'warn': lambda x: bbmsg('WARNINGS', x),
'abend': lambda x: bbmsg('EXCEPTIONS', x),
'ok': lambda x: bbmsg(None, x),
- }
+ })
+
+ if options.verbose:
+ output['verbose'] = lambda x: stdmsg(None, x)
forbidden_regexp = re.compile(string.join(map(re.escape,
kUndesiredLibraryList), '|'))
- mapping_regexp = re.compile(r"\s*([^/]*) => ")
+ mapping_regexp = re.compile(r"\s*([^/]*) => (.*)")
blessed_regexp = re.compile(r"(%s)[-0-9.]*\.so" % string.join(map(re.escape,
kAllowedLibraryList), '|'))
+ built_regexp = re.compile(re.escape(build_dir + os.sep))
+
success = 0
warning = 0
@@ -131,15 +171,20 @@ def _main():
success = 1
deps = string.split(out, '\n')
for d in deps:
- libmatch = mapping_regexp.match(d)
- if libmatch:
- lib = libmatch.group(1)
- if forbidden_regexp.search(lib):
- success = 0
- output['message'](['Forbidden library: ' + lib])
- if not blessed_regexp.match(lib):
- warning = 1
- output['message'](['Unexpected library: ' + lib])
+ libmatch = mapping_regexp.match(d)
+ if libmatch:
+ lib = libmatch.group(1)
+ source = libmatch.group(2)
+ if forbidden_regexp.search(lib):
+ success = 0
+ output['message'](['Forbidden library: ' + lib])
+ elif built_regexp.match(source):
+ output['verbose'](['Built library: ' + lib])
+ elif blessed_regexp.match(lib):
+ output['verbose'](['Blessed library: ' + lib])
+ else:
+ warning = 1
+ output['message'](['Unexpected library: ' + lib])
if success == 1:
if warning == 1:
diff --git a/tools/checklicenses/checklicenses.py b/tools/checklicenses/checklicenses.py
index eedf5a3d3e..b1cc0a73af 100755
--- a/tools/checklicenses/checklicenses.py
+++ b/tools/checklicenses/checklicenses.py
@@ -66,6 +66,7 @@ WHITELISTED_LICENSES = [
'LGPL (v2.1 or later)',
'LGPL (v3 or later)',
'MIT/X11 (BSD like)',
+ 'MIT/X11 (BSD like) LGPL (v2.1 or later)',
'MPL (v1.0) LGPL (v2 or later)',
'MPL (v1.1)',
'MPL (v1.1) BSD (3 clause) GPL (v2) LGPL (v2.1 or later)',
@@ -156,6 +157,12 @@ PATH_SPECIFIC_WHITELISTED_LICENSES = {
'UNKNOWN',
],
+ # http://crbug.com/326117
+ # https://bitbucket.org/chrisatlee/poster/issue/21
+ 'third_party/chromite/third_party/poster': [
+ 'UNKNOWN',
+ ],
+
# Not used. http://crbug.com/156020
# Using third_party/cros_dbus_cplusplus/cros_dbus_cplusplus.gyp instead.
'third_party/cros_dbus_cplusplus/source/autogen.sh': [
@@ -330,6 +337,13 @@ PATH_SPECIFIC_WHITELISTED_LICENSES = {
'third_party/webdriver': [ # http://crbug.com/98590
'UNKNOWN',
],
+
+ # https://github.com/html5lib/html5lib-python/issues/125
+ # https://github.com/KhronosGroup/WebGL/issues/435
+ 'third_party/webgl/src': [
+ 'UNKNOWN',
+ ],
+
'third_party/webrtc': [ # http://crbug.com/98592
'UNKNOWN',
],
diff --git a/tools/clang/scripts/package.sh b/tools/clang/scripts/package.sh
index cd3b2b30b3..16b3c7ba50 100755
--- a/tools/clang/scripts/package.sh
+++ b/tools/clang/scripts/package.sh
@@ -76,9 +76,9 @@ if [ "$(uname -s)" = "Darwin" ]; then
install_name_tool -id @executable_path/${ASAN_DYLIB_NAME} "${ASAN_DYLIB}"
else
# Keep only
- # Release+Asserts/lib/clang/*/lib/linux/libclang_rt.{[atm]san,profile}-*.a
+ # Release+Asserts/lib/clang/*/lib/linux/libclang_rt.{[atm]san,profile,ubsan}-*.a
find "${LLVM_LIB_DIR}/clang" -type f -path '*lib/linux*' \
- ! -name '*[atm]san*' ! -name '*profile*' | xargs rm
+ ! -name '*[atm]san*' ! -name '*profile*' ! -name '*ubsan*' | xargs rm
fi
cp -R "${LLVM_LIB_DIR}/clang" $PDIR/lib
diff --git a/tools/cr/cr-bash-helpers.sh b/tools/cr/cr-bash-helpers.sh
new file mode 100755
index 0000000000..03f42ccbe6
--- /dev/null
+++ b/tools/cr/cr-bash-helpers.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# 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.
+
+# Source this file into your shell to gain the cr function and tab completion
+# for it
+
+# Make sure we're being sourced (possibly by another script). Check for bash
+# since zsh sets $0 when sourcing.
+if [[ -n "$BASH_VERSION" && "${BASH_SOURCE:-$0}" == "$0" ]]; then
+ echo "ERROR: cr-bash-helpers.sh must be sourced."
+ exit 1
+fi
+
+
+cr_base_dir=$(dirname $(realpath "${BASH_SOURCE:-$0}"))
+cr_main="${cr_base_dir}/main.py"
+cr_exec="PYTHONDONTWRITEBYTECODE=1 python ${cr_main}"
+
+# The main entry point to the cr tool.
+# Invokes the python script with pyc files turned off.
+function cr() {
+ env $cr_exec "$@"
+}
+
+# Attempts to cd to the root/src of the current client.
+function crcd() {
+ cd $(cr info -s CR_SRC)
+}
+
+# Add to your PS1 to have the current selected output directory in your prompt
+function _cr_ps1() {
+ cr info -s CR_OUT_FULL
+}
+
+# The tab completion handler, delegates into the python script.
+function _cr_complete() {
+ COMPREPLY=()
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local main="python -B "${cr_main}")"
+ local completions="$(env COMP_CWORD=${COMP_CWORD} COMP_WORD=${cur} $cr_exec)"
+ COMPREPLY=( $(compgen -W "${completions}" -- ${cur}) )
+}
+
+# Setup the bash auto complete
+complete -F _cr_complete cr
diff --git a/tools/cr/cr.sh b/tools/cr/cr.sh
index 2a67c0177b..5d1c13ade4 100755
--- a/tools/cr/cr.sh
+++ b/tools/cr/cr.sh
@@ -4,45 +4,11 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-# Source this file into your shell to gain the cr function and tab completion
-# for it
-
-# Make sure we're being sourced (possibly by another script). Check for bash
-# since zsh sets $0 when sourcing.
-if [[ -n "$BASH_VERSION" && "${BASH_SOURCE:-$0}" == "$0" ]]; then
- echo "ERROR: cr.sh must be sourced."
- exit 1
-fi
-
-
-cr_base_dir=$(dirname $(realpath "${BASH_SOURCE:-$0}"))
-cr_main="${cr_base_dir}/main.py"
-cr_exec="PYTHONDONTWRITEBYTECODE=1 python ${cr_main}"
-
-# The main entry point to the cr tool.
-# Invokes the python script with pyc files turned off.
-function cr() {
- env $cr_exec "$@"
-}
-
-# Attempts to cd to the root/src of the current client.
-function crcd() {
- cd $(cr info -s CR_SRC)
-}
-
-# Add to your PS1 to have the current selected output directory in your prompt
-function _cr_ps1() {
- cr info -s CR_OUT_FULL
-}
-
-# The tab completion handler, delegates into the python script.
-function _cr_complete() {
- COMPREPLY=()
- local cur="${COMP_WORDS[COMP_CWORD]}"
- local main="python -B "${cr_main}")"
- local completions="$(env COMP_CWORD=${COMP_CWORD} COMP_WORD=${cur} $cr_exec)"
- COMPREPLY=( $(compgen -W "${completions}" -- ${cur}) )
-}
-
-# Setup the bash auto complete
-complete -F _cr_complete cr
+echo "*************************************************"
+echo "* Do not source cr.sh ***************************"
+echo "*************************************************"
+echo "* You need to source cr-bash-helpers.sh instead *"
+echo "* This file will stop working and be removed *"
+echo "* soon. *"
+echo "*************************************************"
+source $(dirname $(realpath "${BASH_SOURCE:-$0}"))/cr-bash-helpers.sh
diff --git a/tools/cr/cr/actions/adb.py b/tools/cr/cr/actions/adb.py
new file mode 100644
index 0000000000..25117c74ec
--- /dev/null
+++ b/tools/cr/cr/actions/adb.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.
+
+"""A module to hold adb specific action implementations."""
+
+import re
+
+import cr
+
+
+class Adb(object):
+ """Exposes the functionality of the adb tool to the rest of cr.
+
+ This is intended as the only class in the cr that needs to understand the
+ adb command line, and expose it in neutral form to the rest of the code.
+ """
+
+ # Tracks the set of killed target names, so we don't keep issuing kill
+ # commands that are not going to have any effect.
+ _kills = {}
+
+ @classmethod
+ def GetPids(cls, target):
+ """Gets the set of running PIDs that match the specified target."""
+ pids = []
+ output = cr.Host.Capture(target, '{CR_ADB}', 'shell', 'ps')
+ pattern = re.compile(r'\S+\s+(\d+)\s+.*{CR_PROCESS}')
+ for line in output.split('\n'):
+ match = re.match(pattern, line)
+ if match:
+ pids.append(match.group(1))
+ return pids
+
+ @classmethod
+ def Run(cls, target, arguments):
+ """Invoke a target binary on the device."""
+ cr.Host.Execute(
+ target,
+ '{CR_ADB}', 'shell', 'am', 'start',
+ '-a', '{CR_ACTION}',
+ '-n', '{CR_INTENT}',
+ '{CR_RUN_ARGUMENTS}',
+ *arguments
+ )
+
+ @classmethod
+ def Kill(cls, target, _):
+ """Kill all running processes for a target."""
+ target_name = target.build_target
+ if target_name in cls._kills:
+ # already killed this target, do nothing
+ return
+ pids = cls.GetPids(target)
+ if pids:
+ cr.Host.Execute(target, '{CR_ADB}', 'shell', 'kill', *pids)
+ elif target.verbose:
+ print target.Substitute('{CR_TARGET_NAME} not running')
+ cls._kills[target_name] = True
+
+ @classmethod
+ def Uninstall(cls, target, arguments):
+ cr.Host.Execute(
+ target,
+ '{CR_ADB}', 'uninstall',
+ '{CR_PACKAGE}',
+ *arguments
+ )
+
+ @classmethod
+ def Install(cls, target, arguments):
+ cr.Host.Execute(
+ target,
+ '{CR_ADB}', 'install',
+ '{CR_BINARY}',
+ *arguments
+ )
+
+ @classmethod
+ def Reinstall(cls, target, arguments):
+ cr.Host.Execute(
+ target,
+ '{CR_ADB}', 'install',
+ '-r',
+ '-d',
+ '{CR_BINARY}',
+ *arguments
+ )
+
+ @classmethod
+ def AttachGdb(cls, target, arguments):
+ cr.Host.Execute(
+ target,
+ '{CR_ADB_GDB}',
+ '--adb={CR_ADB}',
+ '--symbol-dir=${CR_BUILD_DIR}/lib',
+ '--program-name={CR_TARGET_NAME}',
+ '--package-name={CR_PACKAGE}',
+ *arguments
+ )
+
+
+class AdbRunner(cr.Runner):
+ """An implementation of cr.Runner for the android platform."""
+
+ @property
+ def enabled(self):
+ return cr.AndroidPlatform.GetInstance().is_active
+
+ def Kill(self, context, targets, arguments):
+ for target in targets:
+ Adb.Kill(target, arguments)
+
+ def Run(self, context, target, arguments):
+ Adb.Run(target, arguments)
+
+ def Test(self, context, target, arguments):
+ cr.Host.Execute(
+ target,
+ '{CR_TEST_RUNNER}', '{CR_TEST_TYPE}',
+ '-s', '{CR_TARGET_NAME}',
+ '--{CR_TEST_MODE}',
+ *arguments
+ )
+
+
+class AdbInstaller(cr.Installer):
+ """An implementation of cr.Installer for the android platform."""
+
+ @property
+ def enabled(self):
+ return cr.AndroidPlatform.GetInstance().is_active
+
+ def Uninstall(self, context, targets, arguments):
+ for target in targets:
+ Adb.Uninstall(target, arguments)
+
+ def Install(self, context, targets, arguments):
+ for target in targets:
+ Adb.Install(target, arguments)
+
+ def Reinstall(self, context, targets, arguments):
+ for target in targets:
+ Adb.Reinstall(target, arguments)
diff --git a/tools/cr/cr/actions/builder.py b/tools/cr/cr/actions/builder.py
new file mode 100644
index 0000000000..74959afe62
--- /dev/null
+++ b/tools/cr/cr/actions/builder.py
@@ -0,0 +1,72 @@
+# 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 module for the Builder base class."""
+
+import cr
+
+
+class Builder(cr.Action, cr.Plugin.Type):
+ """Base class for implementing builders.
+
+ Builder implementations must override the Build and Clean methods at a
+ minimum to build a target and clean up back to a pristine state respectively.
+ They can also override Rebuild if they are able to handle it in a more
+ efficient way that a Clean Build sequence.
+ They should override the GetTargets method to return the set of valid targets
+ the build system knows about, and override IsTarget if they can implement it
+ more efficiently than checking from presents in the result of GetTargets.
+ """
+
+ SELECTOR_ARG = '--builder'
+ SELECTOR = 'CR_BUILDER'
+ SELECTOR_HELP = 'Sets the builder to use to update dependencies.'
+
+ @cr.Plugin.activemethod
+ def Build(self, context, targets, arguments):
+ raise NotImplementedError('Must be overridden.')
+
+ @cr.Plugin.activemethod
+ def Clean(self, context, targets, arguments):
+ """Clean temporary files built by a target."""
+ raise NotImplementedError('Must be overridden.')
+
+ @cr.Plugin.activemethod
+ def Rebuild(self, context, targets, arguments):
+ """Make a target build even if it is up to date.
+
+ Default implementation is to do a Clean and Build sequence.
+ Do not call the base version if you implement a more efficient one.
+ """
+ self.Clean(context, targets, [])
+ self.Build(context, targets, arguments)
+
+ @cr.Plugin.activemethod
+ def GetTargets(self, context):
+ """Gets the full set of targets supported by this builder.
+
+ Used in automatic target name transformations, and also in offering the
+ user choices.
+ """
+ return []
+
+ @cr.Plugin.activemethod
+ def IsTarget(self, context, target_name):
+ """Check if a target name is on the builder knows about."""
+ return target_name in self.GetTargets(context)
+
+
+class SkipBuilder(Builder):
+ """The "skip" version of a Builder, causes the build step to be skipped."""
+
+ @property
+ def priority(self):
+ return super(SkipBuilder, self).priority - 1
+
+ def Build(self, context, targets, arguments):
+ pass
+
+ def Clean(self, context, targets, arguments):
+ pass
+
diff --git a/tools/cr/cr/actions/debugger.py b/tools/cr/cr/actions/debugger.py
new file mode 100644
index 0000000000..a634ab2203
--- /dev/null
+++ b/tools/cr/cr/actions/debugger.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.
+
+"""A module for the Debugger base class."""
+
+import cr
+
+
+class Debugger(cr.Action, cr.Plugin.Type):
+ """Base class for implementing debuggers.
+
+ Implementations must override the Invoke and Attach methods.
+ """
+
+ SELECTOR_ARG = '--debugger'
+ SELECTOR = 'CR_DEBUGGER'
+ SELECTOR_HELP = 'Sets the debugger to use for debug commands.'
+
+ @classmethod
+ def AddArguments(cls, command, parser):
+ cr.Runner.AddSelectorArg(command, parser)
+
+ @classmethod
+ def ShouldInvoke(cls, context):
+ """Checks if the debugger is attaching or launching."""
+ return not cr.Runner.Skipping(context)
+
+ @cr.Plugin.activemethod
+ def Restart(self, context, targets, arguments):
+ """Ask the debugger to restart.
+
+ Defaults to a Kill Invoke sequence.
+ """
+ self.Kill(context, targets, [])
+ self.Invoke(context, targets, arguments)
+
+ @cr.Plugin.activemethod
+ def Kill(self, context, targets, arguments):
+ """Kill the running debugger."""
+ cr.Runner.Kill(context, targets, arguments)
+
+ @cr.Plugin.activemethod
+ def Invoke(self, context, targets, arguments):
+ """Invoke the program within a debugger."""
+ raise NotImplementedError('Must be overridden.')
+
+ @cr.Plugin.activemethod
+ def Attach(self, context, targets, arguments):
+ """Attach a debugger to a running program."""
+ raise NotImplementedError('Must be overridden.')
diff --git a/tools/cr/cr/actions/gdb.py b/tools/cr/cr/actions/gdb.py
new file mode 100644
index 0000000000..d93fe27eb9
--- /dev/null
+++ b/tools/cr/cr/actions/gdb.py
@@ -0,0 +1,40 @@
+# 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 cr
+
+
+class GdbDebugger(cr.Debugger):
+ """An implementation of cr.Debugger that launches gdb."""
+
+ DETECTED = cr.Config('DETECTED')
+
+ @property
+ def enabled(self):
+ return (cr.LinuxPlatform.GetInstance().is_active and
+ self.DETECTED.Find('CR_GDB'))
+
+ def Invoke(self, context, targets, arguments):
+ for target in targets:
+ cr.Host.Execute(
+ target,
+ '{CR_GDB}', '--eval-command=run', '--args',
+ '{CR_BINARY}',
+ '{CR_RUN_ARGUMENTS}',
+ *arguments
+ )
+
+ def Attach(self, context, targets, arguments):
+ raise NotImplementedError('Attach not currently supported for gdb.')
+
+
+def _DetectGdb():
+ """Attempts to find a valid gdb on the path."""
+ gdb_binaries = cr.Host.SearchPath('gdb')
+ if gdb_binaries:
+ GdbDebugger.DETECTED.Set(CR_GDB=gdb_binaries[0])
+
+_DetectGdb()
diff --git a/tools/cr/cr/actions/gyp.py b/tools/cr/cr/actions/gyp.py
new file mode 100644
index 0000000000..b3492bbefd
--- /dev/null
+++ b/tools/cr/cr/actions/gyp.py
@@ -0,0 +1,26 @@
+# 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 module to add gyp support to cr."""
+
+import cr
+
+
+class GypPrepareOut(cr.PrepareOut):
+ """A prepare action that runs gyp whenever you select an output directory."""
+
+ ENABLED = cr.Config.From(
+ GYP_GENERATORS='ninja',
+ GYP_GENERATOR_FLAGS='output_dir={CR_OUT_BASE} config={CR_BUILDTYPE}',
+ )
+
+ def Prepare(self, context):
+ if context.verbose >= 1:
+ print context.Substitute('Invoking gyp with {GYP_GENERATOR_FLAGS}')
+ cr.Host.Execute(
+ context,
+ '{CR_SRC}/build/gyp_chromium',
+ '--depth={CR_SRC}',
+ '--check'
+ )
diff --git a/tools/cr/cr/actions/installer.py b/tools/cr/cr/actions/installer.py
new file mode 100644
index 0000000000..0dd432df65
--- /dev/null
+++ b/tools/cr/cr/actions/installer.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 module for the Installer base class."""
+
+import cr
+
+
+class Installer(cr.Action, cr.Plugin.Type):
+ """Base class for implementing installers.
+
+ Installer implementations must implement the Uninstall and Install methods.
+ If the location into which targets are built is find for running them, then
+ they do not actually have to do anything.
+ """
+
+ SELECTOR_ARG = '--installer'
+ SELECTOR = 'CR_INSTALLER'
+ SELECTOR_HELP = 'Sets the installer to use.'
+
+ @cr.Plugin.activemethod
+ def Uninstall(self, context, targets, arguments):
+ """Removes a target from it's installed location."""
+
+ raise NotImplementedError('Must be overridden.')
+
+ @cr.Plugin.activemethod
+ def Install(self, context, targets, arguments):
+ """Installs a target somewhere so that it is ready to run."""
+ raise NotImplementedError('Must be overridden.')
+
+ @cr.Plugin.activemethod
+ def Reinstall(self, context, targets, arguments):
+ """Force a target to install even if already installed.
+
+ Default implementation is to do an Uninstall Install sequence.
+ Do not call the base version if you implement a more efficient one.
+ """
+ self.Uninstall(context, targets, [])
+ self.Install(context, targets, arguments)
+
+
+class SkipInstaller(Installer):
+ """An Installer the user chooses to bypass the install step of a command."""
+
+ @property
+ def priority(self):
+ return super(SkipInstaller, self).priority - 1
+
+ def Uninstall(self, context, targets, arguments):
+ pass
+
+ def Install(self, context, targets, arguments):
+ pass
diff --git a/tools/cr/cr/actions/linux.py b/tools/cr/cr/actions/linux.py
new file mode 100644
index 0000000000..1a47c84a8d
--- /dev/null
+++ b/tools/cr/cr/actions/linux.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 module to hold linux specific action implementations."""
+
+import cr
+
+
+class LinuxRunner(cr.Runner):
+ """An implementation of cr.Runner for the linux platform.
+
+ This supports directly executing the binaries from the output directory.
+ """
+
+ @property
+ def enabled(self):
+ return cr.LinuxPlatform.GetInstance().is_active
+
+ def Kill(self, context, targets, arguments):
+ # TODO(iancottrell): Think about how to implement this, or even if we should
+ print '**WARNING** Kill not yet implemented on linux'
+
+ def Run(self, context, target, arguments):
+ cr.Host.Execute(target, ['{CR_BINARY}', '{CR_RUN_ARGUMENTS}'] + arguments)
+
+ def Test(self, context, target, arguments):
+ self.Run(context, target, arguments)
+
+
+class LinuxInstaller(cr.Installer):
+ """An implementation of cr.Installer for the linux platform.
+
+ This does nothing, the linux runner works from the output directory, there
+ is no need to install anywhere.
+ """
+
+ @property
+ def enabled(self):
+ return cr.LinuxPlatform.GetInstance().is_active
+
+ def Uninstall(self, context, targets, arguments):
+ pass
+
+ def Install(self, context, targets, arguments):
+ pass
+
+ def Reinstall(self, context, targets, arguments):
+ pass
+
diff --git a/tools/cr/cr/actions/ninja.py b/tools/cr/cr/actions/ninja.py
new file mode 100644
index 0000000000..404e6caea2
--- /dev/null
+++ b/tools/cr/cr/actions/ninja.py
@@ -0,0 +1,86 @@
+# 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 module to add ninja support to cr."""
+
+import os
+
+import cr
+
+_PHONY_SUFFIX = ': phony'
+_LINK_SUFFIX = ': link'
+
+
+class NinjaBuilder(cr.Builder):
+ """An implementation of Builder that uses ninja to do the actual build."""
+
+ # Some basic configuration installed if we are enabled.
+ ENABLED = cr.Config.From(
+ NINJA_BINARY=os.path.join('{DEPOT_TOOLS}', 'ninja'),
+ NINJA_JOBS=200,
+ NINJA_PROCESSORS=12,
+ GOMA_DIR=os.path.join('{GOOGLE_CODE}', 'goma'),
+ )
+ # A placeholder for the system detected configuration
+ DETECTED = cr.Config('DETECTED')
+
+ def __init__(self):
+ super(NinjaBuilder, self).__init__()
+ self._targets = []
+
+ def Build(self, context, targets, arguments):
+ build_arguments = [target.build_target for target in targets]
+ build_arguments.extend(arguments)
+ cr.Host.Execute(
+ context,
+ '{NINJA_BINARY}',
+ '-C{CR_BUILD_DIR}',
+ '-j{NINJA_JOBS}',
+ '-l{NINJA_PROCESSORS}',
+ *build_arguments
+ )
+
+ def Clean(self, context, targets, arguments):
+ build_arguments = [target.build_target for target in targets]
+ build_arguments.extend(arguments)
+ cr.Host.Execute(
+ context,
+ '{NINJA_BINARY}',
+ '-C{CR_BUILD_DIR}',
+ '-tclean',
+ *build_arguments
+ )
+
+ def GetTargets(self, context):
+ """Overridden from Builder.GetTargets."""
+ if not self._targets:
+ try:
+ context.Get('CR_BUILD_DIR', raise_errors=True)
+ except KeyError:
+ return self._targets
+ output = cr.Host.Capture(
+ context,
+ '{NINJA_BINARY}',
+ '-C{CR_BUILD_DIR}',
+ '-ttargets',
+ 'all'
+ )
+ for line in output.split('\n'):
+ line = line.strip()
+ if line.endswith(_PHONY_SUFFIX):
+ target = line[:-len(_PHONY_SUFFIX)].strip()
+ self._targets.append(target)
+ elif line.endswith(_LINK_SUFFIX):
+ target = line[:-len(_LINK_SUFFIX)].strip()
+ self._targets.append(target)
+ return self._targets
+
+ @classmethod
+ def DetectNinja(cls):
+ # TODO(iancottrell): If we can't detect ninja, we should be disabled.
+ ninja_binaries = cr.Host.SearchPath('ninja')
+ if ninja_binaries:
+ cls.DETECTED.Set(NINJA_BINARY=ninja_binaries[0])
+
+NinjaBuilder.DetectNinja()
diff --git a/tools/cr/cr/actions/runner.py b/tools/cr/cr/actions/runner.py
new file mode 100644
index 0000000000..72590fc35e
--- /dev/null
+++ b/tools/cr/cr/actions/runner.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.
+
+"""A module for the Runner base class."""
+
+import cr
+
+
+class Runner(cr.Action, cr.Plugin.Type):
+ """Base class for implementing target runners.
+
+ Runner implementations must implement the Kill, Run and Test methods.
+
+ """
+
+ SELECTOR_ARG = '--runner'
+ SELECTOR = 'CR_RUNNER'
+ SELECTOR_HELP = 'Sets the runner to use to execute the target.'
+
+ @classmethod
+ def AddArguments(cls, command, parser):
+ parser.add_argument(
+ '--test', dest='CR_TEST_TYPE',
+ choices=cr.Target.TEST_TYPES,
+ default=None,
+ help="""
+ Sets the test type to use,
+ defaults to choosing based on the target.
+ Set to 'no' to force it to not be a test.
+ """
+ )
+
+ @cr.Plugin.activemethod
+ def Kill(self, context, targets, arguments):
+ """Stops all running processes that match a target."""
+ raise NotImplementedError('Must be overridden.')
+
+ @cr.Plugin.activemethod
+ def Run(self, context, target, arguments):
+ """Run a new copy of a runnable target."""
+ raise NotImplementedError('Must be overridden.')
+
+ @cr.Plugin.activemethod
+ def Test(self, context, target, arguments):
+ """Run a test target."""
+ raise NotImplementedError('Must be overridden.')
+
+ @cr.Plugin.activemethod
+ def Invoke(self, context, targets, arguments):
+ """Invoke a target.
+
+ This dispatches to either Test or Run depending on the target type.
+ """
+ for target in targets:
+ if target.is_test:
+ self.Test(context, target, arguments)
+ else:
+ self.Run(context, target, arguments)
+
+ @cr.Plugin.activemethod
+ def Restart(self, context, targets, arguments):
+ """Force a target to restart if it is already running.
+
+ Default implementation is to do a Kill Invoke sequence.
+ Do not call the base version if you implement a more efficient one.
+ """
+ self.Kill(context, targets, [])
+ self.Invoke(context, targets, arguments)
+
+
+class SkipRunner(Runner):
+ """A Runner the user chooses to bypass the run step of a command."""
+
+ @property
+ def priority(self):
+ return super(SkipRunner, self).priority - 1
+
+ def Kill(self, context, targets, arguments):
+ pass
+
+ def Run(self, context, target, arguments):
+ pass
+
+ def Test(self, context, target, arguments):
+ pass
+
diff --git a/tools/cr/cr/base/android.py b/tools/cr/cr/base/android.py
index 4fc56a578a..cf61577adc 100644
--- a/tools/cr/cr/base/android.py
+++ b/tools/cr/cr/base/android.py
@@ -19,10 +19,6 @@ _IGNORE_ENV = [
'GYP_DEFINES', # Because it gets a special merge handler
]
-# This is the name of the file we check for to see if this client is an
-# android capable one.
-# TODO(iancottrell): change this when we add handling to read the .gclient file
-_ANDROID_MARKER = '{CR_SRC}/third_party/android_tools/android_tools.gyp'
# The message to print when we detect use of an android output directory in a
# client that cannot build android.
_NOT_ANDROID_MESSAGE = """
@@ -66,9 +62,16 @@ class AndroidPlatform(cr.Platform):
return super(AndroidPlatform, self).priority + 1
def Prepare(self, context):
+ """Override Prepare from cr.Platform."""
super(AndroidPlatform, self).Prepare(context)
# Check we are an android capable client
- if not os.path.isfile(context.Substitute(_ANDROID_MARKER)):
+ is_android = 'android' in context.gclient.get('target_os', '')
+ if not is_android:
+ url = context.gclient.get('solutions', [{}])[0].get('url')
+ is_android = (url.startswith(
+ 'https://chrome-internal.googlesource.com/') and
+ url.endswith('/internal/apps.git'))
+ if not is_android:
print _NOT_ANDROID_MESSAGE
exit(1)
try:
@@ -77,7 +80,7 @@ class AndroidPlatform(cr.Platform):
# See what the env would be without env setup
before = context.exported
# Run env setup and capture/parse it's output
- envsetup = 'source {CR_ENVSETUP} --target-arch={CR_ARCH}'
+ envsetup = 'source {CR_ENVSETUP} --target-arch={CR_ENVSETUP_ARCH}'
output = cr.Host.CaptureShell(context, envsetup + ' > /dev/null && env')
env_setup = cr.Config('envsetup', literal=True, export=True)
for line in output.split('\n'):
diff --git a/tools/cr/cr/base/arch.py b/tools/cr/cr/base/arch.py
new file mode 100644
index 0000000000..743ac8efe4
--- /dev/null
+++ b/tools/cr/cr/base/arch.py
@@ -0,0 +1,70 @@
+# 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 module for the basic architectures supported by cr."""
+
+import cr
+
+DEFAULT = cr.Config.From(
+ CR_ENVSETUP_ARCH='{CR_ARCH}',
+)
+
+
+class Arch(cr.Plugin, cr.Plugin.Type):
+ """Base class for implementing cr architecture targets."""
+
+ SELECTOR = 'CR_ARCH'
+
+ @classmethod
+ def AddArguments(cls, parser):
+ parser.add_argument(
+ '--architecture', dest=cls.SELECTOR,
+ choices=cls.Choices(),
+ default=None,
+ help='Sets the target architecture to use. Overrides ' + cls.SELECTOR
+ )
+
+
+class IA32Arch(Arch):
+
+ ACTIVE = cr.Config.From(
+ CR_ENVSETUP_ARCH='x86',
+ )
+
+
+class Mips32Arch(Arch):
+
+ ACTIVE = cr.Config.From(
+ CR_ENVSETUP_ARCH='mips',
+ )
+
+ @property
+ def enabled(self):
+ return cr.AndroidPlatform.GetInstance().is_active
+
+
+class X64Arch(Arch):
+
+ ACTIVE = cr.Config.From(
+ CR_ENVSETUP_ARCH='x64',
+ )
+
+ @property
+ def priority(self):
+ return super(X64Arch, self).priority + 1
+
+
+class Arm32Arch(Arch):
+
+ ACTIVE = cr.Config.From(
+ CR_ENVSETUP_ARCH='arm',
+ )
+
+ @property
+ def priority(self):
+ return super(Arm32Arch, self).priority + 2
+
+ @property
+ def enabled(self):
+ return cr.AndroidPlatform.GetInstance().is_active
diff --git a/tools/cr/cr/base/client.py b/tools/cr/cr/base/client.py
index 6cdd83a9ec..d8eef12ec6 100644
--- a/tools/cr/cr/base/client.py
+++ b/tools/cr/cr/base/client.py
@@ -18,7 +18,7 @@ import cr.auto.build
import cr.auto.client
# The config version currently supported.
-VERSION = '0.3'
+VERSION = 0.5
# The default directory name to store config inside
CLIENT_CONFIG_PATH = '.cr'
# The partial filename to add to a directory to get it's config file.
@@ -36,6 +36,8 @@ OVERRIDES = cr.Config.From("""
CONFIG_VAR_LINE = '\n {0} = {1!r},'
# The format string for the tail of a config file.
CONFIG_FILE_SUFFIX = '\n)\n'
+# The name of the gclient config file
+GCLIENT_FILENAME = '.gclient'
# The default config values installed by this module.
DEFAULT = cr.Config.From(
@@ -64,7 +66,7 @@ def _DetectPath():
# See if we can detect the source tree root
_cached_path = os.getcwd()
while (_cached_path and
- not os.path.exists(os.path.join(_cached_path, '.gclient'))):
+ not os.path.exists(os.path.join(_cached_path, GCLIENT_FILENAME))):
old = _cached_path
_cached_path = os.path.dirname(_cached_path)
if _cached_path == old:
@@ -126,6 +128,31 @@ def ApplyOutArgument(context):
context.derived.Set(CR_OUT_FULL=out)
+def ReadGClient(context):
+ """Loads the .gclient configuration for the current client.
+
+ This will load from CR_CLIENT_PATH.
+
+ Args:
+ context: The active context to load configuratin for.
+ Returns:
+ The dict of values set in the .gclient file.
+
+ """
+ # Now attempt to load and parse the .gclient file
+ result = {}
+ try:
+ gclient_file = context.Substitute(
+ os.path.join('{CR_CLIENT_PATH}', GCLIENT_FILENAME))
+ with open(gclient_file, 'r') as spec_file:
+ # matching the behaviour of gclient, so pylint: disable=exec-used
+ exec(spec_file.read(), {}, result)
+ except IOError:
+ # no .gclient file, skip it
+ pass
+ return result
+
+
def LoadConfig(context):
"""Loads the client configuration for the given context.
diff --git a/tools/cr/cr/commands/build.py b/tools/cr/cr/commands/build.py
new file mode 100644
index 0000000000..2d2c27fd17
--- /dev/null
+++ b/tools/cr/cr/commands/build.py
@@ -0,0 +1,81 @@
+# 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 module for the build commands."""
+
+import cr
+
+
+class BuildCommand(cr.Command):
+ """The implementation of the build command.
+
+ This is a thin shell over the Builder.Build method of the selected builder.
+ """
+
+ def __init__(self):
+ super(BuildCommand, self).__init__()
+ self.help = 'Build a target'
+ self.description = ("""
+ Uses the specified builder for the platform to bring the target
+ up to date.
+ """)
+
+ def AddArguments(self, subparsers):
+ parser = super(BuildCommand, self).AddArguments(subparsers)
+ cr.Builder.AddArguments(self, parser)
+ cr.Target.AddArguments(self, parser, allow_multiple=True)
+ self.ConsumeArgs(parser, 'the builder')
+ return parser
+
+ def Run(self, context):
+ return cr.Builder.Build(
+ context, cr.Target.GetTargets(context), context.remains)
+
+
+class CleanCommand(cr.Command):
+ """The implementation of the clean command.
+
+ This is a thin shell over the Builder.Clean method of the selected builder.
+ """
+
+ def __init__(self):
+ super(CleanCommand, self).__init__()
+ self.help = 'Clean a target'
+ self.description = (
+ 'Uses the specified builder to clean out built files for the target.')
+
+ def AddArguments(self, subparsers):
+ parser = super(CleanCommand, self).AddArguments(subparsers)
+ cr.Builder.AddArguments(self, parser)
+ cr.Target.AddArguments(self, parser, allow_multiple=True)
+ self.ConsumeArgs(parser, 'the builder')
+ return parser
+
+ def Run(self, context):
+ return cr.Builder.Clean(
+ context, cr.Target.GetTargets(context), context.remains)
+
+
+class RebuildCommand(cr.Command):
+ """The implementation of the rebuild command.
+
+ This is a thin shell over the Builder.Rebuild method of the selected builder.
+ """
+
+ def __init__(self):
+ super(RebuildCommand, self).__init__()
+ self.help = 'Rebuild a target'
+ self.description = (
+ 'Uses the specified builder for the platform to rebuild a target.')
+
+ def AddArguments(self, subparsers):
+ parser = super(RebuildCommand, self).AddArguments(subparsers)
+ cr.Builder.AddArguments(self, parser)
+ cr.Target.AddArguments(self, parser, allow_multiple=True)
+ self.ConsumeArgs(parser, 'the builder')
+ return parser
+
+ def Run(self, context):
+ return cr.Builder.Rebuild(
+ context, cr.Target.GetTargets(context), context.remains)
diff --git a/tools/cr/cr/commands/debug.py b/tools/cr/cr/commands/debug.py
new file mode 100644
index 0000000000..48fb513b3c
--- /dev/null
+++ b/tools/cr/cr/commands/debug.py
@@ -0,0 +1,40 @@
+# 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 module for the run command."""
+
+import cr
+
+
+class DebugCommand(cr.Command):
+ """The implementation of the debug command.
+
+ This is much like the run command except it launches the program under
+ a debugger instead.
+ """
+
+ def __init__(self):
+ super(DebugCommand, self).__init__()
+ self.help = 'Debug a binary'
+
+ def AddArguments(self, subparsers):
+ parser = super(DebugCommand, self).AddArguments(subparsers)
+ cr.Builder.AddArguments(self, parser)
+ cr.Installer.AddArguments(self, parser)
+ cr.Debugger.AddArguments(self, parser)
+ cr.Target.AddArguments(self, parser)
+ self.ConsumeArgs(parser, 'the binary')
+ return parser
+
+ def Run(self, context):
+ targets = cr.Target.GetTargets(context)
+ if not cr.Debugger.ShouldInvoke(context):
+ cr.Debugger.Attach(context, targets, context.remains)
+ elif cr.Installer.Skipping(context):
+ cr.Debugger.Restart(context, targets, context.remains)
+ else:
+ cr.Builder.Build(context, targets, [])
+ cr.Debugger.Kill(context, targets, [])
+ cr.Installer.Reinstall(context, targets, [])
+ cr.Debugger.Invoke(context, targets, context.remains)
diff --git a/tools/cr/cr/commands/init.py b/tools/cr/cr/commands/init.py
new file mode 100644
index 0000000000..adcb4a180c
--- /dev/null
+++ b/tools/cr/cr/commands/init.py
@@ -0,0 +1,180 @@
+# 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 module for the init command."""
+
+import os
+
+import cr
+
+# The set of variables to store in the per output configuration.
+OUT_CONFIG_VARS = [
+ 'CR_VERSION',
+ cr.Platform.SELECTOR, cr.BuildType.SELECTOR, cr.Arch.SELECTOR,
+ 'CR_OUT_BASE', 'CR_OUT_FULL',
+]
+
+
+class InitCommand(cr.Command):
+ """The implementation of the init command.
+
+ The init command builds or updates an output directory.
+ It then uses the Prepare and Select commands to get that directory
+ ready to use.
+ """
+
+ def __init__(self):
+ super(InitCommand, self).__init__()
+ self.requires_build_dir = False
+ self.help = 'Create and configure an output directory'
+ self.description = ("""
+ If the .cr directory is not present, build it and add
+ the specified configuration.
+ If the file already exists, update the configuration with any
+ additional settings.
+ """)
+ self._settings = []
+
+ def AddArguments(self, subparsers):
+ """Overridden from cr.Command."""
+ parser = super(InitCommand, self).AddArguments(subparsers)
+ cr.Platform.AddArguments(parser)
+ cr.BuildType.AddArguments(parser)
+ cr.Arch.AddArguments(parser)
+ cr.SelectCommand.AddPrepareArguments(parser)
+ parser.add_argument(
+ '-s', '--set', dest='_settings', metavar='settings',
+ action='append',
+ help='Configuration overrides.'
+ )
+ return parser
+
+ def EarlyArgProcessing(self, context):
+ base_settings = getattr(context.args, '_settings', None)
+ if base_settings:
+ self._settings.extend(base_settings)
+ # Do not call super early processing, we do not want to apply
+ # the output arg...
+ out = cr.base.client.GetOutArgument(context)
+ if out:
+ # Output directory is fully specified
+ # We need to deduce other settings from it's name
+ base, buildtype = os.path.split(out)
+ if not (base and buildtype):
+ print 'Specified output directory must be two levels'
+ exit(1)
+ if not cr.BuildType.FindPlugin(buildtype):
+ print 'Specified build type', buildtype, 'is not valid'
+ print 'Must be one of', ','.join(p.name for p in cr.BuildType.Plugins())
+ exit(1)
+ if context.args.CR_BUILDTYPE and context.args.CR_BUILDTYPE != buildtype:
+ print 'If --type and --out are both specified, they must match'
+ print 'Got', context.args.CR_BUILDTYPE, 'and', buildtype
+ exit(1)
+ platform = context.args.CR_PLATFORM
+ if not platform:
+ # Try to guess platform based on output name
+ platforms = [p.name for p in cr.Platform.AllPlugins()]
+ matches = [p for p in platforms if p in base]
+ if len(matches) != 1:
+ print 'Platform is not set, and could not be guessed from', base
+ print 'Should be one of', ','.join(platforms)
+ if len(matches) > 1:
+ print 'Matched all of', ','.join(matches)
+ exit(1)
+ platform = matches[0]
+ context.derived.Set(
+ CR_OUT_FULL=out,
+ CR_OUT_BASE=base,
+ CR_PLATFORM=platform,
+ CR_BUILDTYPE=buildtype,
+ )
+ if not 'CR_OUT_BASE' in context:
+ context.derived['CR_OUT_BASE'] = 'out_{CR_PLATFORM}'
+ if not 'CR_OUT_FULL' in context:
+ context.derived['CR_OUT_FULL'] = os.path.join(
+ '{CR_OUT_BASE}', '{CR_BUILDTYPE}')
+
+ def Run(self, context):
+ """Overridden from cr.Command."""
+ src_path = context.Get('CR_SRC')
+ if not os.path.isdir(src_path):
+ print context.Substitute('Path {CR_SRC} is not a valid client')
+ exit(1)
+
+ # Ensure we have an output directory override ready to fill in
+ # This will only be missing if we are creating a brand new output
+ # directory
+ build_package = cr.auto.build
+
+ # Collect the old version (and float convert)
+ old_version = context.Find('CR_VERSION')
+ try:
+ old_version = float(old_version)
+ except (ValueError, TypeError):
+ old_version = 0.0
+ is_new = not hasattr(build_package, 'config')
+ if is_new:
+
+ class FakeModule(object):
+ OVERRIDES = cr.Config('OVERRIDES')
+
+ def __init__(self):
+ self.__name__ = 'config'
+
+ old_version = None
+ config = FakeModule()
+ setattr(build_package, 'config', config)
+ cr.plugin.ChainModuleConfigs(config)
+
+ # Force override the version
+ build_package.config.OVERRIDES.Set(CR_VERSION=cr.base.client.VERSION)
+ # Add all the variables that we always want to have
+ for name in OUT_CONFIG_VARS:
+ value = context.Find(name)
+ build_package.config.OVERRIDES[name] = value
+ # Apply the settings from the command line
+ for setting in self._settings:
+ name, separator, value = setting.partition('=')
+ name = name.strip()
+ if not separator:
+ value = True
+ else:
+ value = cr.Config.ParseValue(value.strip())
+ build_package.config.OVERRIDES[name] = value
+
+ # Run all the output directory init hooks
+ for hook in InitHook.Plugins():
+ hook.Run(context, old_version, build_package.config)
+ # Redo activations, they might have changed
+ cr.plugin.Activate(context)
+
+ # Write out the new configuration, and select it as the default
+ cr.base.client.WriteConfig(context, context.Get('CR_BUILD_DIR'),
+ build_package.config.OVERRIDES.exported)
+ # Prepare the platform in here, using the updated config
+ cr.Platform.Prepare(context)
+ cr.SelectCommand.Select(context)
+
+
+class InitHook(cr.Plugin, cr.Plugin.Type):
+ """Base class for output directory initialization hooks.
+
+ Implementations used to fix from old version to new ones live in the
+ cr.fixups package.
+ """
+
+ def Run(self, context, old_version, config):
+ """Run the initialization hook.
+
+ This is invoked once per init invocation.
+ Args:
+ context: The context of the init command.
+ old_version: The old version,
+ 0.0 if the old version was bad or missing,
+ None if building a new output direcory.
+ config: The mutable config that will be written.
+ """
+ raise NotImplementedError('Must be overridden.')
+
diff --git a/tools/cr/cr/commands/install.py b/tools/cr/cr/commands/install.py
new file mode 100644
index 0000000000..51a02b36ee
--- /dev/null
+++ b/tools/cr/cr/commands/install.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.
+
+"""A module for the install command."""
+
+import cr
+
+
+class InstallCommand(cr.Command):
+ """The implementation of the install command.
+
+ This first uses Builder.Build to bring the target up to date, and then
+ installs it using Installer.Reinstall.
+ The builder installs its command line arguments, and you can use those to
+ select which builder is used. Selecting the skip builder
+ (using --builder=skip) bypasses the build stage.
+ """
+
+ def __init__(self):
+ super(InstallCommand, self).__init__()
+ self.help = 'Install a binary'
+
+ def AddArguments(self, subparsers):
+ parser = super(InstallCommand, self).AddArguments(subparsers)
+ cr.Builder.AddArguments(self, parser)
+ cr.Installer.AddArguments(self, parser)
+ cr.Target.AddArguments(self, parser, allow_multiple=True)
+ self.ConsumeArgs(parser, 'the installer')
+ return parser
+
+ def Run(self, context):
+ targets = cr.Target.GetTargets(context)
+ if not cr.Installer.Skipping(context):
+ cr.Builder.Build(context, targets, [])
+ cr.Installer.Reinstall(context, targets, context.remains)
diff --git a/tools/cr/cr/commands/run.py b/tools/cr/cr/commands/run.py
new file mode 100644
index 0000000000..9b57d9439c
--- /dev/null
+++ b/tools/cr/cr/commands/run.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 module for the run command."""
+
+import cr
+
+
+class RunCommand(cr.Command):
+ """The implementation of the run command.
+
+ This first uses Builder to bring the target up to date.
+ It then uses Installer to install the target (if needed), and
+ finally it uses Runner to run the target.
+ You can use skip version to not perform any of these steps.
+ """
+
+ def __init__(self):
+ super(RunCommand, self).__init__()
+ self.help = 'Invoke a target'
+
+ def AddArguments(self, subparsers):
+ parser = super(RunCommand, self).AddArguments(subparsers)
+ cr.Builder.AddArguments(self, parser)
+ cr.Installer.AddArguments(self, parser)
+ cr.Runner.AddArguments(self, parser)
+ cr.Target.AddArguments(self, parser, allow_multiple=True)
+ self.ConsumeArgs(parser, 'the binary')
+ return parser
+
+ def Run(self, context):
+ targets = cr.Target.GetTargets(context)
+ test_targets = [target for target in targets if target.is_test]
+ run_targets = [target for target in targets if not target.is_test]
+ if cr.Installer.Skipping(context):
+ # No installer, only build test targets
+ build_targets = test_targets
+ else:
+ build_targets = targets
+ if build_targets:
+ cr.Builder.Build(context, build_targets, [])
+ # See if we can use restart when not installing
+ if cr.Installer.Skipping(context):
+ cr.Runner.Restart(context, targets, context.remains)
+ else:
+ cr.Runner.Kill(context, run_targets, [])
+ cr.Installer.Reinstall(context, run_targets, [])
+ cr.Runner.Invoke(context, targets, context.remains)
+
diff --git a/tools/cr/cr/commands/select.py b/tools/cr/cr/commands/select.py
new file mode 100644
index 0000000000..58cebdd384
--- /dev/null
+++ b/tools/cr/cr/commands/select.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.
+
+"""A module for the select command."""
+
+import cr
+
+# The set of variables SELECT writes into the client plugin to control the
+# active output directory.
+SELECT_OUT_VARS = ['CR_OUT_FULL']
+
+
+class SelectCommand(cr.Command):
+ """The implementation of the select command.
+
+ The select command is used to set the default output directory used by all
+ other commands. It does this by writing out a plugin into the client root
+ that sets the active output path.
+ """
+
+ def __init__(self):
+ super(SelectCommand, self).__init__()
+ self.help = 'Select an output directory'
+ self.description = ("""
+ This makes the specified output directory the default for all future
+ operations. It also invokes prepare on that directory.
+ """)
+
+ def AddArguments(self, subparsers):
+ parser = super(SelectCommand, self).AddArguments(subparsers)
+ self.AddPrepareArguments(parser)
+ return parser
+
+ @classmethod
+ def AddPrepareArguments(cls, parser):
+ parser.add_argument(
+ '--no-prepare', dest='_no_prepare',
+ action='store_true', default=False,
+ help='Don\'t prepare the output directory.'
+ )
+
+ def Run(self, context):
+ self.Select(context)
+
+ @classmethod
+ def Select(cls, context):
+ """Performs the select.
+
+ This is also called by the init command to auto select the new output
+ directory.
+ Args:
+ context: The cr Context to select in.
+ """
+ cr.base.client.WriteConfig(
+ context, context.Get('CR_CLIENT_PATH'), dict(
+ CR_OUT_FULL=context.Get('CR_OUT_FULL')))
+ cr.base.client.PrintInfo(context)
+ # Now we run the post select actions
+ if not getattr(context.args, '_no_prepare', None):
+ cr.PrepareCommand.Prepare(context)
diff --git a/tools/cr/cr/commands/shell.py b/tools/cr/cr/commands/shell.py
new file mode 100644
index 0000000000..452ebb7460
--- /dev/null
+++ b/tools/cr/cr/commands/shell.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.
+
+"""A module for the shell command."""
+
+import os
+import tempfile
+
+import cr
+
+
+class ShellCommand(cr.Command):
+ """The implementation of the shell command.
+
+ The shell command is the escape hatch that lets user run any program in the
+ same environment that cr would use if it were running it.
+ """
+
+ def __init__(self):
+ super(ShellCommand, self).__init__()
+ self.help = 'Launch a shell'
+ self.description = ("""
+ If no arguments are present, this launches an interactive system
+ shell (ie bash) with the environment modified to that used for the
+ build systems.
+ If any arguments are present, they are used as a command line to run
+ in that shell.
+ This allows you to run commands that are not yet available natively
+ in cr.
+ """)
+
+ def AddArguments(self, subparsers):
+ parser = super(ShellCommand, self).AddArguments(subparsers)
+ self.ConsumeArgs(parser, 'the shell')
+ return parser
+
+ def Run(self, context):
+ if context.remains:
+ cr.Host.Shell(context, *context.remains)
+ return
+ # If we get here, we are trying to launch an interactive shell
+ shell = os.environ.get('SHELL', None)
+ if shell is None:
+ print 'Don\'t know how to run a shell on this system'
+ elif shell.endswith('bash'):
+ ps1 = '[CR] ' + os.environ.get('PS1', '')
+ with tempfile.NamedTemporaryFile() as rcfile:
+ rcfile.write('source ~/.bashrc\nPS1="'+ps1+'"')
+ rcfile.flush()
+ cr.Host.Execute(context, shell, '--rcfile', rcfile.name)
+ else:
+ cr.Host.Execute(context, shell)
diff --git a/tools/cr/cr/commands/sync.py b/tools/cr/cr/commands/sync.py
new file mode 100644
index 0000000000..cef9b7a6f4
--- /dev/null
+++ b/tools/cr/cr/commands/sync.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 module for the sync command."""
+
+import os.path
+
+import cr
+
+
+class SyncCommand(cr.Command):
+ """The implementation of the sync command.
+
+ This command is a very thin shim over the gclient sync, and should remain so.
+ The only significant thing it adds is that the environment is set up so that
+ the run-hooks will do their work in the selected output directory.
+ """
+
+ # The configuration loaded to support this command.
+ DEFAULT = cr.Config.From(
+ GCLIENT_BINARY=os.path.join('{DEPOT_TOOLS}', 'gclient'),
+ )
+
+ # A placeholder for the detected gclient environment
+ DETECTED = cr.Config('DETECTED')
+
+ def __init__(self):
+ super(SyncCommand, self).__init__()
+ self.help = 'Sync the source tree'
+ self.description = 'Run gclient sync with the right environment.'
+
+ def AddArguments(self, subparsers):
+ parser = super(SyncCommand, self).AddArguments(subparsers)
+ self.ConsumeArgs(parser, 'gclient')
+ # TODO(iancottrell): clean no-hooks support would be nice.
+ return parser
+
+ def Run(self, context):
+ # TODO(iancottrell): we should probably run the python directly,
+ # rather than the shell wrapper
+ # TODO(iancottrell): try to help out when the local state is not a good
+ # one to do a sync in
+ cr.Host.Execute(context, '{GCLIENT_BINARY}', 'sync', *context.remains)
+
+
+def _AutoDetectGClient():
+ """Attempts to detect gclient and it's parent repository."""
+ gclient_binaries = cr.Host.SearchPath('gclient')
+ if gclient_binaries:
+ SyncCommand.DETECTED.Set(GCLIENT_BINARY=gclient_binaries[0])
+ SyncCommand.DETECTED.Set(DEPOT_TOOLS=os.path.dirname(gclient_binaries[0]))
+
+# Invoke the auto detection
+_AutoDetectGClient()
diff --git a/tools/cr/cr/config.py b/tools/cr/cr/config.py
index cbb4891afb..a19b837a44 100644
--- a/tools/cr/cr/config.py
+++ b/tools/cr/cr/config.py
@@ -234,3 +234,7 @@ class Config(cr.visitor.Node):
def __setitem__(self, key, value):
self._Set(key, value)
+
+ def __contains__(self, key):
+ return self.Find(key) is not None
+
diff --git a/tools/cr/cr/context.py b/tools/cr/cr/context.py
index e478514f56..3cc3217064 100644
--- a/tools/cr/cr/context.py
+++ b/tools/cr/cr/context.py
@@ -122,6 +122,7 @@ class Context(cr.config.Config, cr.loader.AutoExport):
self._subparsers = self.parser.add_subparsers()
# Add the global arguments
self.AddCommonArguments(self._parser)
+ self._gclient = {}
def AddSubParser(self, source):
parser = source.AddArguments(self._subparsers)
@@ -196,6 +197,12 @@ class Context(cr.config.Config, cr.loader.AutoExport):
def autocompleting(self):
return 'COMP_WORD' in os.environ
+ @property
+ def gclient(self):
+ if not self._gclient:
+ self._gclient = cr.base.client.ReadGClient(self)
+ return self._gclient
+
def ParseArgs(self, speculative=False):
cr.plugin.DynamicChoices.only_active = not speculative
self._speculative = speculative
diff --git a/tools/cr/cr/fixups/__init__.py b/tools/cr/cr/fixups/__init__.py
new file mode 100644
index 0000000000..86bfa2ad3c
--- /dev/null
+++ b/tools/cr/cr/fixups/__init__.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 package for all the version fixups.
+
+All the code in this package is there to fix up older output directories and
+clients to a form that works with the current version of cr.
+"""
diff --git a/tools/cr/cr/fixups/arch.py b/tools/cr/cr/fixups/arch.py
new file mode 100644
index 0000000000..e6ac81c508
--- /dev/null
+++ b/tools/cr/cr/fixups/arch.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.
+
+"""A module for architecture output directory fixups."""
+
+import cr
+
+
+class _ArchInitHookHelper(cr.InitHook):
+ """Base class helper for CR_ARCH value fixups."""
+
+ def _VersionTest(self, old_version):
+ _ = old_version
+ return True
+
+ def _ArchConvert(self, old_arch):
+ return old_arch
+
+ def Run(self, context, old_version, config):
+ if old_version is None or not self._VersionTest(old_version):
+ return
+ old_arch = config.OVERRIDES.Find(cr.Arch.SELECTOR)
+ new_arch = self._ArchConvert(old_arch)
+ if new_arch != old_arch:
+ print '** Fixing architecture from {0} to {1}'.format(old_arch, new_arch)
+ config.OVERRIDES[cr.Arch.SELECTOR] = new_arch
+
+
+class WrongArchDefaultInitHook(_ArchInitHookHelper):
+ """Fixes bad initial defaults.
+
+ In the initial versions of cr before output directories were versioned
+ it was writing invalid architecture defaults. This detects that case and sets
+ the architecture to the current default instead.
+ """
+
+ def _VersionTest(self, old_version):
+ return old_version <= 0.0
+
+ def _ArchConvert(self, _):
+ return cr.Arch.default.name
+
+
+class MipsAndArmRenameInitHook(_ArchInitHookHelper):
+ """Fixes rename of Mips and Arm to Mips32 and Arm32."""
+
+ def _ArchConvert(self, old_arch):
+ if old_arch == 'mips':
+ return cr.Mips32Arch.GetInstance().name
+ if old_arch == 'arm':
+ return cr.Arm32Arch.GetInstance().name
+ return old_arch
+
diff --git a/tools/cr/cr/visitor.py b/tools/cr/cr/visitor.py
index 3390597143..339040ca3c 100644
--- a/tools/cr/cr/visitor.py
+++ b/tools/cr/cr/visitor.py
@@ -30,6 +30,8 @@ class Visitor(object):
def VisitNode(self, node):
"""Called for every node in the tree."""
+ if not node.enabled:
+ return self
try:
try:
self.stack.append(node)
diff --git a/tools/deep_memory_profiler/lib/dump.py b/tools/deep_memory_profiler/lib/dump.py
index 115979e0f6..dc4b89840c 100644
--- a/tools/deep_memory_profiler/lib/dump.py
+++ b/tools/deep_memory_profiler/lib/dump.py
@@ -14,7 +14,7 @@ from lib.exceptions import EmptyDumpException, InvalidDumpException
from lib.exceptions import ObsoleteDumpVersionException, ParsingException
from lib.pageframe import PageFrame
from lib.range_dict import ExclusiveRangeDict
-from lib.symbol import proc_maps
+from lib.symbol import procfs
LOGGER = logging.getLogger('dmprof')
@@ -295,7 +295,7 @@ class Dump(object):
current_vma = {}
pageframe_list = []
while True:
- entry = proc_maps.ProcMaps.parse_line(self._lines[ln])
+ entry = procfs.ProcMaps.parse_line(self._lines[ln])
if entry:
current_vma = {}
for _, _, attr in self._procmaps.iter_range(entry.begin, entry.end):
@@ -433,7 +433,7 @@ class DumpList(object):
class ProcMapsEntryAttribute(ExclusiveRangeDict.RangeAttribute):
"""Represents an entry of /proc/maps in range_dict.ExclusiveRangeDict."""
- _DUMMY_ENTRY = proc_maps.ProcMapsEntry(
+ _DUMMY_ENTRY = procfs.ProcMapsEntry(
0, # begin
0, # end
'-', # readable
diff --git a/tools/deep_memory_profiler/lib/subcommand.py b/tools/deep_memory_profiler/lib/subcommand.py
index 25416f6ee3..39241092af 100644
--- a/tools/deep_memory_profiler/lib/subcommand.py
+++ b/tools/deep_memory_profiler/lib/subcommand.py
@@ -10,7 +10,7 @@ import re
from lib.bucket import BucketSet
from lib.dump import Dump, DumpList
from lib.symbol import SymbolDataSources, SymbolMappingCache, SymbolFinder
-from lib.symbol import proc_maps
+from lib.symbol import procfs
from lib.symbol import FUNCTION_SYMBOLS, SOURCEFILE_SYMBOLS, TYPEINFO_SYMBOLS
@@ -95,7 +95,7 @@ class SubCommand(object):
device_lib_path_candidates = set()
with open(prefix + '.maps') as maps_f:
- maps = proc_maps.ProcMaps.load(maps_f)
+ maps = procfs.ProcMaps.load_file(maps_f)
for entry in maps:
name = entry.as_dict()['name']
if any([base_dir in name for base_dir in SubCommand._DEVICE_BINDIRS]):
diff --git a/tools/deep_memory_profiler/lib/symbol.py b/tools/deep_memory_profiler/lib/symbol.py
index 897d4098d5..86dd44b28a 100644
--- a/tools/deep_memory_profiler/lib/symbol.py
+++ b/tools/deep_memory_profiler/lib/symbol.py
@@ -10,11 +10,15 @@ _BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
_FIND_RUNTIME_SYMBOLS_PATH = os.path.join(_BASE_PATH,
os.pardir,
'find_runtime_symbols')
+_TOOLS_LINUX_PATH = os.path.join(_BASE_PATH,
+ os.pardir,
+ 'linux')
sys.path.append(_FIND_RUNTIME_SYMBOLS_PATH)
+sys.path.append(_TOOLS_LINUX_PATH)
import find_runtime_symbols
import prepare_symbol_info
-import proc_maps # pylint: disable=W0611
+import procfs # pylint: disable=W0611,F0401
LOGGER = logging.getLogger('dmprof')
diff --git a/tools/find_runtime_symbols/find_runtime_symbols.py b/tools/find_runtime_symbols/find_runtime_symbols.py
index bed9e800b1..e96db4f95a 100755
--- a/tools/find_runtime_symbols/find_runtime_symbols.py
+++ b/tools/find_runtime_symbols/find_runtime_symbols.py
@@ -14,14 +14,21 @@ import os
import sys
from static_symbols import StaticSymbolsInFile
-from proc_maps import ProcMaps
+
+
+_BASE_PATH = os.path.dirname(os.path.abspath(__file__))
+_TOOLS_LINUX_PATH = os.path.join(_BASE_PATH, os.pardir, 'linux')
+sys.path.insert(0, _TOOLS_LINUX_PATH)
+
+
+from procfs import ProcMaps # pylint: disable=F0401
try:
from collections import OrderedDict # pylint: disable=E0611
except ImportError:
- BASE_PATH = os.path.dirname(os.path.abspath(__file__))
- SIMPLEJSON_PATH = os.path.join(BASE_PATH, os.pardir, os.pardir, 'third_party')
- sys.path.insert(0, SIMPLEJSON_PATH)
+ _SIMPLEJSON_PATH = os.path.join(_BASE_PATH, os.pardir, os.pardir,
+ 'third_party')
+ sys.path.insert(0, _SIMPLEJSON_PATH)
from simplejson import OrderedDict
@@ -76,7 +83,7 @@ class RuntimeSymbolsInProcess(object):
symbols_in_process = RuntimeSymbolsInProcess()
with open(os.path.join(prepared_data_dir, _MAPS_FILENAME), mode='r') as f:
- symbols_in_process._maps = ProcMaps.load(f)
+ symbols_in_process._maps = ProcMaps.load_file(f)
with open(os.path.join(prepared_data_dir, _FILES_FILENAME), mode='r') as f:
files = json.load(f)
diff --git a/tools/find_runtime_symbols/prepare_symbol_info.py b/tools/find_runtime_symbols/prepare_symbol_info.py
index d5503881a1..9bce5455c8 100755
--- a/tools/find_runtime_symbols/prepare_symbol_info.py
+++ b/tools/find_runtime_symbols/prepare_symbol_info.py
@@ -13,11 +13,16 @@ import subprocess
import sys
import tempfile
-from proc_maps import ProcMaps
-
BASE_PATH = os.path.dirname(os.path.abspath(__file__))
REDUCE_DEBUGLINE_PATH = os.path.join(BASE_PATH, 'reduce_debugline.py')
+_TOOLS_LINUX_PATH = os.path.join(BASE_PATH, os.pardir, 'linux')
+sys.path.insert(0, _TOOLS_LINUX_PATH)
+
+
+from procfs import ProcMaps # pylint: disable=F0401
+
+
LOGGER = logging.getLogger('prepare_symbol_info')
@@ -138,7 +143,7 @@ def prepare_symbol_info(maps_path,
shutil.copyfile(maps_path, os.path.join(output_dir_path, 'maps'))
with open(maps_path, mode='r') as f:
- maps = ProcMaps.load(f)
+ maps = ProcMaps.load_file(f)
LOGGER.debug('Listing up symbols.')
files = {}
diff --git a/tools/find_runtime_symbols/proc_maps.py b/tools/find_runtime_symbols/proc_maps.py
deleted file mode 100644
index 2d917b3212..0000000000
--- a/tools/find_runtime_symbols/proc_maps.py
+++ /dev/null
@@ -1,125 +0,0 @@
-# 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 re
-
-
-_MAPS_PATTERN = re.compile(
- r'^([a-f0-9]+)-([a-f0-9]+)\s+(.)(.)(.)(.)\s+([a-f0-9]+)\s+(\S+):(\S+)\s+'
- r'(\d+)\s*(.*)$', re.IGNORECASE)
-
-
-class ProcMapsEntry(object):
- """A class representing one line in /proc/.../maps."""
-
- def __init__(
- self, begin, end, readable, writable, executable, private, offset,
- major, minor, inode, name):
- self.begin = begin
- self.end = end
- self.readable = readable
- self.writable = writable
- self.executable = executable
- self.private = private
- self.offset = offset
- self.major = major
- self.minor = minor
- self.inode = inode
- self.name = name
-
- def as_dict(self):
- return {
- 'begin': self.begin,
- 'end': self.end,
- 'readable': self.readable,
- 'writable': self.writable,
- 'executable': self.executable,
- 'private': self.private,
- 'offset': self.offset,
- 'major': self.major,
- 'minor': self.minor,
- 'inode': self.inode,
- 'name': self.name,
- }
-
-
-class ProcMaps(object):
- """A class representing contents in /proc/.../maps."""
-
- def __init__(self):
- self._sorted_indexes = []
- self._dictionary = {}
- self._sorted = True
-
- def iter(self, condition):
- if not self._sorted:
- self._sorted_indexes.sort()
- self._sorted = True
- for index in self._sorted_indexes:
- if not condition or condition(self._dictionary[index]):
- yield self._dictionary[index]
-
- def __iter__(self):
- if not self._sorted:
- self._sorted_indexes.sort()
- self._sorted = True
- for index in self._sorted_indexes:
- yield self._dictionary[index]
-
- @staticmethod
- def load(f):
- table = ProcMaps()
- for line in f:
- table.append_line(line)
- return table
-
- def append_line(self, line):
- entry = self.parse_line(line)
- if entry:
- self._append_entry(entry)
-
- @staticmethod
- def parse_line(line):
- matched = _MAPS_PATTERN.match(line)
- if matched:
- return ProcMapsEntry( # pylint: disable=W0212
- int(matched.group(1), 16), # begin
- int(matched.group(2), 16), # end
- matched.group(3), # readable
- matched.group(4), # writable
- matched.group(5), # executable
- matched.group(6), # private
- int(matched.group(7), 16), # offset
- matched.group(8), # major
- matched.group(9), # minor
- int(matched.group(10), 10), # inode
- matched.group(11) # name
- )
- else:
- return None
-
- @staticmethod
- def constants(entry):
- return (entry.writable == '-' and entry.executable == '-' and re.match(
- '\S+(\.(so|dll|dylib|bundle)|chrome)((\.\d+)+\w*(\.\d+){0,3})?',
- entry.name))
-
- @staticmethod
- def executable(entry):
- return (entry.executable == 'x' and re.match(
- '\S+(\.(so|dll|dylib|bundle)|chrome)((\.\d+)+\w*(\.\d+){0,3})?',
- entry.name))
-
- @staticmethod
- def executable_and_constants(entry):
- return (((entry.writable == '-' and entry.executable == '-') or
- entry.executable == 'x') and re.match(
- '\S+(\.(so|dll|dylib|bundle)|chrome)((\.\d+)+\w*(\.\d+){0,3})?',
- entry.name))
-
- def _append_entry(self, entry):
- if self._sorted_indexes and self._sorted_indexes[-1] > entry.begin:
- self._sorted = False
- self._sorted_indexes.append(entry.begin)
- self._dictionary[entry.begin] = entry
diff --git a/tools/gdb/gdb_chrome.py b/tools/gdb/gdb_chrome.py
index 8173fa15a5..2c7c3717fb 100644
--- a/tools/gdb/gdb_chrome.py
+++ b/tools/gdb/gdb_chrome.py
@@ -21,6 +21,7 @@ to print |any_variable| without using any printers.
import datetime
import gdb
+import gdb.printing
import webkit
# When debugging this module, set the below variable to True, and then use
diff --git a/tools/gn/bin/linux/gn.sha1 b/tools/gn/bin/linux/gn.sha1
index f90995c663..aa19eb56f7 100644
--- a/tools/gn/bin/linux/gn.sha1
+++ b/tools/gn/bin/linux/gn.sha1
@@ -1 +1 @@
-3242cf9c82bd828e1f9e8dddba1e3a211c88a052 \ No newline at end of file
+9193c7af3614392d7d3cc2c0682d5f7a568ed5af \ No newline at end of file
diff --git a/tools/gn/bin/linux/gn32.sha1 b/tools/gn/bin/linux/gn32.sha1
index 4ccde46d8e..e5afab1be2 100644
--- a/tools/gn/bin/linux/gn32.sha1
+++ b/tools/gn/bin/linux/gn32.sha1
@@ -1 +1 @@
-6ef471f5d2ea9a74a39cf326e522a7ed21a97a0f \ No newline at end of file
+5cc3cd2636cfbf576314ba439c440ea87d3ec676 \ No newline at end of file
diff --git a/tools/gn/bin/mac/gn.sha1 b/tools/gn/bin/mac/gn.sha1
index 6a47cb0517..3ae90fdc2e 100644
--- a/tools/gn/bin/mac/gn.sha1
+++ b/tools/gn/bin/mac/gn.sha1
@@ -1 +1 @@
-a3744421ce9140f21341bda428b802538b6fe6fc
+af8d50d4f306b3f8e103bc34eb0706278714de06
diff --git a/tools/gn/bin/win/gn.exe.sha1 b/tools/gn/bin/win/gn.exe.sha1
index d392bfe3c1..42e0d08e33 100644
--- a/tools/gn/bin/win/gn.exe.sha1
+++ b/tools/gn/bin/win/gn.exe.sha1
@@ -1 +1 @@
-65e9bbc3fdcce14af2b2bf575782f27df4a403f3
+f8a3c09fbb79f69d531b507e9e236677ec9ded1f \ No newline at end of file
diff --git a/tools/gn/function_exec_script.cc b/tools/gn/function_exec_script.cc
index cca62a36f3..c4d226223c 100644
--- a/tools/gn/function_exec_script.cc
+++ b/tools/gn/function_exec_script.cc
@@ -356,7 +356,7 @@ Value RunExecScript(Scope* scope,
//
// If this shows up on benchmarks, we can cache whether we've done this
// or not and skip creating the directory.
- file_util::CreateDirectory(startup_dir);
+ base::CreateDirectory(startup_dir);
// Execute the process.
// TODO(brettw) set the environment block.
diff --git a/tools/gn/function_write_file.cc b/tools/gn/function_write_file.cc
index 0c0dd00098..9ac1221af4 100644
--- a/tools/gn/function_write_file.cc
+++ b/tools/gn/function_write_file.cc
@@ -71,7 +71,7 @@ Value RunWriteFile(Scope* scope,
base::FilePath file_path =
scope->settings()->build_settings()->GetFullPath(source_file);
const std::string& contents_string = contents.str();
- if (!file_util::CreateDirectory(file_path.DirName())) {
+ if (!base::CreateDirectory(file_path.DirName())) {
*err = Err(function->function(), "Unable to create directory.",
"I was using \"" + FilePathToUTF8(file_path.DirName()) + "\".");
return Value();
diff --git a/tools/gn/generate_test_gn_data.cc b/tools/gn/generate_test_gn_data.cc
index 017c4c4431..730ae990c1 100644
--- a/tools/gn/generate_test_gn_data.cc
+++ b/tools/gn/generate_test_gn_data.cc
@@ -82,7 +82,7 @@ void WriteLevel(const std::vector<int>& repo_path,
// Don't keep the file open while recursing.
{
- file_util::CreateDirectory(dirname);
+ base::CreateDirectory(dirname);
std::ofstream file;
file.open(FilePathToUTF8(filename).c_str(),
diff --git a/tools/gn/gyp_target_writer.cc b/tools/gn/gyp_target_writer.cc
index 725dbdc7cd..447b740b4a 100644
--- a/tools/gn/gyp_target_writer.cc
+++ b/tools/gn/gyp_target_writer.cc
@@ -42,7 +42,7 @@ void GypTargetWriter::WriteFile(const SourceFile& gyp_file,
const BuildSettings* debug_build_settings = debug_settings->build_settings();
base::FilePath gyp_file_path = debug_build_settings->GetFullPath(gyp_file);
- file_util::CreateDirectory(gyp_file_path.DirName());
+ base::CreateDirectory(gyp_file_path.DirName());
std::stringstream file;
file << "# Generated by GN. Do not edit.\n\n";
diff --git a/tools/gn/ninja_build_writer.cc b/tools/gn/ninja_build_writer.cc
index 79b2758e96..a8938b50ce 100644
--- a/tools/gn/ninja_build_writer.cc
+++ b/tools/gn/ninja_build_writer.cc
@@ -111,7 +111,7 @@ bool NinjaBuildWriter::RunAndWriteFile(
base::FilePath ninja_file(build_settings->GetFullPath(
SourceFile(build_settings->build_dir().value() + "build.ninja")));
- file_util::CreateDirectory(ninja_file.DirName());
+ base::CreateDirectory(ninja_file.DirName());
std::ofstream file;
file.open(FilePathToUTF8(ninja_file).c_str(),
diff --git a/tools/gn/ninja_script_target_writer.cc b/tools/gn/ninja_script_target_writer.cc
index 97f2552568..f2004e3303 100644
--- a/tools/gn/ninja_script_target_writer.cc
+++ b/tools/gn/ninja_script_target_writer.cc
@@ -72,7 +72,7 @@ std::string NinjaScriptTargetWriter::WriteRuleDefinition(
// there will be only one invocation so we can use a simple name.
std::string target_label = target_->label().GetUserVisibleName(true);
std::string custom_rule_name(target_label);
- ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name);
+ base::ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name);
custom_rule_name.append("_rule");
if (settings_->IsWin()) {
diff --git a/tools/gn/ninja_target_writer.cc b/tools/gn/ninja_target_writer.cc
index 2b866262a1..41523362af 100644
--- a/tools/gn/ninja_target_writer.cc
+++ b/tools/gn/ninja_target_writer.cc
@@ -51,7 +51,7 @@ void NinjaTargetWriter::RunAndWriteFile(const Target* target,
if (g_scheduler->verbose_logging())
g_scheduler->Log("Writing", FilePathToUTF8(ninja_file));
- file_util::CreateDirectory(ninja_file.DirName());
+ base::CreateDirectory(ninja_file.DirName());
// It's rediculously faster to write to a string and then write that to
// disk in one operation than to use an fstream here.
diff --git a/tools/gn/ninja_toolchain_writer.cc b/tools/gn/ninja_toolchain_writer.cc
index 0a6b3de7c5..c5330a03d8 100644
--- a/tools/gn/ninja_toolchain_writer.cc
+++ b/tools/gn/ninja_toolchain_writer.cc
@@ -47,7 +47,7 @@ bool NinjaToolchainWriter::RunAndWriteFile(
settings->build_settings())));
ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, FilePathToUTF8(ninja_file));
- file_util::CreateDirectory(ninja_file.DirName());
+ base::CreateDirectory(ninja_file.DirName());
std::ofstream file;
file.open(FilePathToUTF8(ninja_file).c_str(),
diff --git a/tools/gn/scheduler.cc b/tools/gn/scheduler.cc
index 254de0d5a0..81ca60e919 100644
--- a/tools/gn/scheduler.cc
+++ b/tools/gn/scheduler.cc
@@ -43,10 +43,16 @@ Scheduler::~Scheduler() {
bool Scheduler::Run() {
runner_.Run();
- base::AutoLock lock(lock_);
+ bool local_is_failed;
+ {
+ base::AutoLock lock(lock_);
+ local_is_failed = is_failed();
+ has_been_shutdown_ = true;
+ }
+ // Don't do this inside the lock since it will block on the workers, which
+ // may be in turn waiting on the lock.
pool_->Shutdown();
- has_been_shutdown_ = true;
- return !is_failed();
+ return !local_is_failed;
}
void Scheduler::Log(const std::string& verb, const std::string& msg) {
@@ -66,7 +72,7 @@ void Scheduler::FailWithError(const Err& err) {
{
base::AutoLock lock(lock_);
- if (is_failed_)
+ if (is_failed_ || has_been_shutdown_)
return; // Ignore errors once we see one.
is_failed_ = true;
}
diff --git a/tools/gn/secondary/base/BUILD.gn b/tools/gn/secondary/base/BUILD.gn
index 50f35593d5..1e1ae32ea6 100644
--- a/tools/gn/secondary/base/BUILD.gn
+++ b/tools/gn/secondary/base/BUILD.gn
@@ -787,7 +787,7 @@ component("base") {
"//build/config/linux:x11",
]
configs += linux_configs
- all_dependent_configs += linux_configs
+ all_dependent_configs = linux_configs
deps += [
"//base/third_party/symbolize",
diff --git a/tools/gn/secondary/skia/BUILD.gn b/tools/gn/secondary/skia/BUILD.gn
index 819e44888f..89b4911f02 100644
--- a/tools/gn/secondary/skia/BUILD.gn
+++ b/tools/gn/secondary/skia/BUILD.gn
@@ -65,7 +65,6 @@ skia_chrome_sources = [
"ext/bitmap_platform_device.h",
"ext/bitmap_platform_device_android.cc",
"ext/bitmap_platform_device_android.h",
- "ext/bitmap_platform_device_data.h",
"ext/bitmap_platform_device_linux.cc",
"ext/bitmap_platform_device_linux.h",
"ext/bitmap_platform_device_mac.cc",
diff --git a/tools/gn/setup.cc b/tools/gn/setup.cc
index feec71b12f..f49df85137 100644
--- a/tools/gn/setup.cc
+++ b/tools/gn/setup.cc
@@ -12,8 +12,10 @@
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
+#include "base/process/launch.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/input_file.h"
@@ -102,48 +104,6 @@ base::FilePath FindDotFile(const base::FilePath& current_dir) {
return FindDotFile(up_one_dir);
}
-#if defined(OS_WIN)
-// Searches the list of strings, and returns the FilePat corresponding to the
-// one ending in the given substring, or the empty path if none match.
-base::FilePath GetPathEndingIn(
- const std::vector<base::FilePath::StringType>& list,
- const base::FilePath::StringType ending_in) {
- for (size_t i = 0; i < list.size(); i++) {
- if (EndsWith(list[i], ending_in, true))
- return base::FilePath(list[i]);
- }
- return base::FilePath();
-}
-
-// Finds the depot tools directory in the path environment variable and returns
-// its value. Returns an empty file path if not found.
-//
-// We detect the depot_tools path by looking for a directory with depot_tools
-// at the end (optionally followed by a separator).
-base::FilePath ExtractDepotToolsFromPath() {
- static const wchar_t kPathVarName[] = L"Path";
- DWORD env_buf_size = GetEnvironmentVariable(kPathVarName, NULL, 0);
- if (env_buf_size == 0)
- return base::FilePath();
- base::string16 path;
- path.resize(env_buf_size);
- GetEnvironmentVariable(kPathVarName, &path[0],
- static_cast<DWORD>(path.size()));
- path.resize(path.size() - 1); // Trim off null.
-
- std::vector<base::string16> components;
- base::SplitString(path, ';', &components);
-
- base::string16 ending_in1 = L"depot_tools\\";
- base::FilePath::StringType ending_in2 = FILE_PATH_LITERAL("depot_tools");
-
- base::FilePath found = GetPathEndingIn(components, ending_in1);
- if (!found.empty())
- return found;
- return GetPathEndingIn(components, ending_in2);
-}
-#endif
-
// Called on any thread. Post the item to the builder on the main thread.
void ItemDefinedCallback(base::MessageLoop* main_loop,
scoped_refptr<Builder> builder,
@@ -343,29 +303,23 @@ bool Setup::FillSourceDir(const CommandLine& cmdline) {
void Setup::FillPythonPath() {
#if defined(OS_WIN)
- // We use python from the depot tools which should be on the path. If we
- // converted the python_path to a python_command_line then we could
- // potentially use "cmd.exe /c python.exe" and remove this.
- static const wchar_t kPythonName[] = L"python.exe";
- base::FilePath depot_tools = ExtractDepotToolsFromPath();
- if (!depot_tools.empty()) {
- base::FilePath python =
- depot_tools.Append(L"python_bin").Append(kPythonName);
+ // Find Python on the path so we can use the absolute path in the build.
+ const base::char16 kGetPython[] =
+ L"cmd.exe /c python -c \"import sys; print sys.executable\"";
+ std::string python_path;
+ if (base::GetAppOutput(kGetPython, &python_path)) {
+ TrimWhitespaceASCII(python_path, TRIM_ALL, &python_path);
if (scheduler_.verbose_logging())
- scheduler_.Log("Using python", FilePathToUTF8(python));
- build_settings_.set_python_path(python);
- return;
- }
-
- if (scheduler_.verbose_logging()) {
- scheduler_.Log("WARNING", "Could not find depot_tools on path, using "
- "just " + FilePathToUTF8(kPythonName));
+ scheduler_.Log("Found python", python_path);
+ } else {
+ scheduler_.Log("WARNING", "Could not find python on path, using "
+ "just \"python.exe\"");
+ python_path = "python.exe";
}
+ build_settings_.set_python_path(base::FilePath(UTF8ToUTF16(python_path)));
#else
- static const char kPythonName[] = "python";
+ build_settings_.set_python_path(base::FilePath("python"));
#endif
-
- build_settings_.set_python_path(base::FilePath(kPythonName));
}
bool Setup::RunConfigFile() {
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids
index 1fce884f54..f520ed96b6 100644
--- a/tools/gritsettings/resource_ids
+++ b/tools/gritsettings/resource_ids
@@ -200,7 +200,7 @@
"chrome/browser/resources/sync_file_system_internals_resources.grd": {
"includes": [29000],
},
- "third_party/libaddressinput/src/cpp/res/messages.grd": {
+ "third_party/libaddressinput/chromium/cpp/res/messages.grd": {
"messages": [29050],
},
"components/component_strings.grd": {
diff --git a/tools/imagediff/image_diff.cc b/tools/imagediff/image_diff.cc
index 27acf767ca..604f27e88d 100644
--- a/tools/imagediff/image_diff.cc
+++ b/tools/imagediff/image_diff.cc
@@ -96,7 +96,7 @@ class Image {
// Creates the image from the given filename on disk, and returns true on
// success.
bool CreateFromFilename(const base::FilePath& path) {
- FILE* f = file_util::OpenFile(path, "rb");
+ FILE* f = base::OpenFile(path, "rb");
if (!f)
return false;
@@ -108,7 +108,7 @@ class Image {
compressed.insert(compressed.end(), buf, buf + num_read);
}
- file_util::CloseFile(f);
+ base::CloseFile(f);
if (!image_diff_png::DecodePNG(&compressed[0], compressed.size(),
&data_, &w_, &h_)) {
diff --git a/tools/ipc_fuzzer/DEPS b/tools/ipc_fuzzer/DEPS
new file mode 100644
index 0000000000..f3d83f86c0
--- /dev/null
+++ b/tools/ipc_fuzzer/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+tools/ipc_fuzzer/message_lib",
+]
diff --git a/tools/ipc_fuzzer/OWNERS b/tools/ipc_fuzzer/OWNERS
new file mode 100644
index 0000000000..729e2fa553
--- /dev/null
+++ b/tools/ipc_fuzzer/OWNERS
@@ -0,0 +1,2 @@
+aedla@chromium.org
+tsepez@chromium.org
diff --git a/tools/ipc_fuzzer/ipc_fuzzer.gyp b/tools/ipc_fuzzer/ipc_fuzzer.gyp
new file mode 100644
index 0000000000..46e1bdb53d
--- /dev/null
+++ b/tools/ipc_fuzzer/ipc_fuzzer.gyp
@@ -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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'ipc_fuzzer',
+ 'type': 'none',
+ 'dependencies': [
+ 'mutate/mutate.gyp:ipc_fuzzer_mutate',
+ 'replay/replay.gyp:ipc_fuzzer_replay',
+ ],
+ },
+ ],
+}
diff --git a/tools/ipc_fuzzer/message_lib/DEPS b/tools/ipc_fuzzer/message_lib/DEPS
new file mode 100644
index 0000000000..5b62065526
--- /dev/null
+++ b/tools/ipc_fuzzer/message_lib/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+ "+chrome/common",
+ "+components/autofill/core/common",
+ "+components/nacl/common",
+ "+components/tracing",
+ "+components/visitedlink/common",
+ "+content/common",
+]
diff --git a/tools/ipc_fuzzer/message_lib/all_messages.h b/tools/ipc_fuzzer/message_lib/all_messages.h
new file mode 100644
index 0000000000..fa5795bd9b
--- /dev/null
+++ b/tools/ipc_fuzzer/message_lib/all_messages.h
@@ -0,0 +1,14 @@
+// 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.
+
+// Multiply-included file, hence no include guard.
+// Inclusion of all message files recognized by message_lib. All messages
+// received by RenderProcessHost should be included here for the IPC fuzzer.
+
+#include "chrome/common/all_messages.h"
+#include "components/autofill/core/common/autofill_messages.h"
+#include "components/nacl/common/nacl_host_messages.h"
+#include "components/tracing/tracing_messages.h"
+#include "components/visitedlink/common/visitedlink_messages.h"
+#include "content/common/all_messages.h"
diff --git a/tools/ipc_fuzzer/message_lib/message_cracker.h b/tools/ipc_fuzzer/message_lib/message_cracker.h
new file mode 100644
index 0000000000..37acf96183
--- /dev/null
+++ b/tools/ipc_fuzzer/message_lib/message_cracker.h
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef TOOLS_IPC_FUZZER_MESSAGE_LIB_MESSAGE_CRACKER_H_
+#define TOOLS_IPC_FUZZER_MESSAGE_LIB_MESSAGE_CRACKER_H_
+
+#include <string.h>
+#include "ipc/ipc_message.h"
+
+// Means for updating protected message fields.
+class MessageCracker : public IPC::Message {
+ public:
+ static void CopyMessageID(IPC::Message* dst, IPC::Message* src) {
+ memcpy(ToCracker(dst)->mutable_payload(),
+ ToCracker(src)->payload(),
+ sizeof(int));
+ }
+
+ static void SetMessageType(IPC::Message* message, uint32 type) {
+ ToCracker(message)->header()->type = type;
+ }
+
+ private:
+ static MessageCracker* ToCracker(IPC::Message* message) {
+ return reinterpret_cast<MessageCracker*>(message);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(MessageCracker);
+};
+
+#endif // TOOLS_IPC_FUZZER_MESSAGE_LIB_MESSAGE_CRACKER_H_
diff --git a/tools/ipc_fuzzer/message_lib/message_file.h b/tools/ipc_fuzzer/message_lib/message_file.h
new file mode 100644
index 0000000000..726571ebb9
--- /dev/null
+++ b/tools/ipc_fuzzer/message_lib/message_file.h
@@ -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.
+
+#ifndef TOOLS_IPC_FUZZER_MESSAGE_LIB_MESSAGE_FILE_H_
+#define TOOLS_IPC_FUZZER_MESSAGE_LIB_MESSAGE_FILE_H_
+
+#include "base/files/file_path.h"
+#include "base/memory/scoped_vector.h"
+#include "ipc/ipc_message.h"
+
+namespace ipc_fuzzer {
+
+typedef ScopedVector<IPC::Message> MessageVector;
+
+class MessageFile {
+ public:
+ static bool Read(const base::FilePath& path, MessageVector* messages);
+ static bool Write(const base::FilePath& path, const MessageVector& messages);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MessageFile);
+};
+
+} // namespace ipc_fuzzer
+
+#endif // TOOLS_IPC_FUZZER_MESSAGE_LIB_MESSAGE_FILE_H_
diff --git a/tools/ipc_fuzzer/message_lib/message_file_format.h b/tools/ipc_fuzzer/message_lib/message_file_format.h
new file mode 100644
index 0000000000..005f9364db
--- /dev/null
+++ b/tools/ipc_fuzzer/message_lib/message_file_format.h
@@ -0,0 +1,63 @@
+// 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.
+
+#ifndef TOOLS_IPC_FUZZER_MESSAGE_LIB_MESSAGE_FILE_FORMAT_H_
+#define TOOLS_IPC_FUZZER_MESSAGE_LIB_MESSAGE_FILE_FORMAT_H_
+
+#include "base/basictypes.h"
+
+// Message file contains IPC messages and message names. Each message type
+// has a NameTableEntry mapping the type to a name.
+//
+// |========================|
+// | FileHeader |
+// |========================|
+// | Message |
+// |------------------------|
+// | Message |
+// |------------------------|
+// | ... |
+// |========================|
+// | NameTableEntry |
+// |------------------------|
+// | NameTableEntry |
+// |------------------------|
+// | ... |
+// |------------------------|
+// | type = 0x0002070f |
+// | string_table_offset = ---.
+// |------------------------| \
+// | ... | |
+// |========================| |
+// | message name | |
+// |------------------------| |
+// | message name | |
+// |------------------------| |
+// | ... | |
+// |------------------------| /
+// | "ViewHostMsg_OpenURL" <--*
+// |------------------------|
+// | ... |
+// |========================|
+
+namespace ipc_fuzzer {
+
+struct FileHeader {
+ static const uint32 kMagicValue = 0x1bcf11ee;
+ static const uint32 kCurrentVersion = 1;
+
+ uint32 magic;
+ uint32 version;
+ uint32 message_count;
+ uint32 name_count;
+};
+
+struct NameTableEntry {
+ uint32 type;
+ uint32 string_table_offset;
+};
+
+} // namespace ipc_fuzzer
+
+#endif // TOOLS_IPC_FUZZER_MESSAGE_LIB_MESSAGE_FILE_FORMAT_H_
diff --git a/tools/ipc_fuzzer/message_lib/message_file_reader.cc b/tools/ipc_fuzzer/message_lib/message_file_reader.cc
new file mode 100644
index 0000000000..e93c460b7e
--- /dev/null
+++ b/tools/ipc_fuzzer/message_lib/message_file_reader.cc
@@ -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.
+
+#include <limits.h>
+
+#include "base/files/file_path.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "ipc/ipc_message.h"
+#include "tools/ipc_fuzzer/message_lib/message_cracker.h"
+#include "tools/ipc_fuzzer/message_lib/message_file.h"
+#include "tools/ipc_fuzzer/message_lib/message_file_format.h"
+#include "tools/ipc_fuzzer/message_lib/message_names.h"
+
+namespace ipc_fuzzer {
+
+namespace {
+
+// Helper class to read IPC message file into a MessageVector and
+// fix message types.
+class Reader {
+ public:
+ Reader(const base::FilePath& path);
+ bool Read(MessageVector* messages);
+
+ private:
+ template <typename T>
+ bool CutObject(const T** object);
+
+ // Reads the header, checks magic and version.
+ bool ReadHeader();
+
+ bool MapFile();
+ bool ReadMessages();
+
+ // Last part of the file is a string table for message names.
+ bool ReadStringTable();
+
+ // Reads type <-> name mapping into name_map_. References string table.
+ bool ReadNameTable();
+
+ // Removes obsolete messages from the vector.
+ bool RemoveUnknownMessages();
+
+ // Does type -> name -> correct_type fixup.
+ void FixMessageTypes();
+
+ // Raw data.
+ base::FilePath path_;
+ base::MemoryMappedFile mapped_file_;
+ base::StringPiece file_data_;
+ base::StringPiece string_table_;
+
+ // Parsed data.
+ const FileHeader* header_;
+ MessageVector* messages_;
+ MessageNames name_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(Reader);
+};
+
+Reader::Reader(const base::FilePath& path)
+ : path_(path),
+ header_(NULL),
+ messages_(NULL) {
+}
+
+template <typename T>
+bool Reader::CutObject(const T** object) {
+ if (file_data_.size() < sizeof(T)) {
+ LOG(ERROR) << "Unexpected EOF.";
+ return false;
+ }
+ *object = reinterpret_cast<const T*>(file_data_.data());
+ file_data_.remove_prefix(sizeof(T));
+ return true;
+}
+
+bool Reader::ReadHeader() {
+ if (!CutObject<FileHeader>(&header_))
+ return false;
+ if (header_->magic != FileHeader::kMagicValue) {
+ LOG(ERROR) << path_.value() << " is not an IPC message file.";
+ return false;
+ }
+ if (header_->version != FileHeader::kCurrentVersion) {
+ LOG(ERROR) << "Wrong version for message file " << path_.value() << ". "
+ << "File version is " << header_->version << ", "
+ << "current version is " << FileHeader::kCurrentVersion << ".";
+ return false;
+ }
+ return true;
+}
+
+bool Reader::MapFile() {
+ if (!mapped_file_.Initialize(path_)) {
+ LOG(ERROR) << "Failed to map testcase: " << path_.value();
+ return false;
+ }
+ const char* data = reinterpret_cast<const char*>(mapped_file_.data());
+ file_data_.set(data, mapped_file_.length());
+ return true;
+}
+
+bool Reader::ReadMessages() {
+ for (size_t i = 0; i < header_->message_count; ++i) {
+ const char* begin = file_data_.begin();
+ const char* end = file_data_.end();
+ const char* message_tail = IPC::Message::FindNext(begin, end);
+ if (!message_tail) {
+ LOG(ERROR) << "Failed to parse message.";
+ return false;
+ }
+
+ size_t msglen = message_tail - begin;
+ if (msglen > INT_MAX) {
+ LOG(ERROR) << "Message too large.";
+ return false;
+ }
+
+ // Copy is necessary to fix message type later.
+ IPC::Message const_message(begin, msglen);
+ IPC::Message* message = new IPC::Message(const_message);
+ messages_->push_back(message);
+ file_data_.remove_prefix(msglen);
+ }
+ return true;
+}
+
+bool Reader::ReadStringTable() {
+ size_t name_count = header_->name_count;
+ if (!name_count)
+ return true;
+ if (name_count > file_data_.size() / sizeof(NameTableEntry)) {
+ LOG(ERROR) << "Invalid name table size: " << name_count;
+ return false;
+ }
+
+ size_t string_table_offset = name_count * sizeof(NameTableEntry);
+ string_table_ = file_data_.substr(string_table_offset);
+ if (string_table_.empty()) {
+ LOG(ERROR) << "Missing string table.";
+ return false;
+ }
+ if (string_table_.end()[-1] != '\0') {
+ LOG(ERROR) << "String table doesn't end with NUL.";
+ return false;
+ }
+ return true;
+}
+
+bool Reader::ReadNameTable() {
+ for (size_t i = 0; i < header_->name_count; ++i) {
+ const NameTableEntry* entry;
+ if (!CutObject<NameTableEntry>(&entry))
+ return false;
+ size_t offset = entry->string_table_offset;
+ if (offset >= string_table_.size()) {
+ LOG(ERROR) << "Invalid string table offset: " << offset;
+ return false;
+ }
+ name_map_.Add(entry->type, std::string(string_table_.data() + offset));
+ }
+ return true;
+}
+
+bool Reader::RemoveUnknownMessages() {
+ MessageVector::iterator it = messages_->begin();
+ while (it != messages_->end()) {
+ uint32 type = (*it)->type();
+ if (!name_map_.TypeExists(type)) {
+ LOG(ERROR) << "Missing name table entry for type " << type;
+ return false;
+ }
+ const std::string& name = name_map_.TypeToName(type);
+ if (!MessageNames::GetInstance()->NameExists(name)) {
+ LOG(WARNING) << "Unknown message " << name;
+ it = messages_->erase(it);
+ } else {
+ ++it;
+ }
+ }
+ return true;
+}
+
+// Message types are based on line numbers, so a minor edit of *_messages.h
+// changes the types of messages in that file. The types are fixed here to
+// increase the lifetime of message files. This is only a partial fix because
+// message arguments and structure layouts can change as well.
+void Reader::FixMessageTypes() {
+ for (MessageVector::iterator it = messages_->begin();
+ it != messages_->end(); ++it) {
+ uint32 type = (*it)->type();
+ const std::string& name = name_map_.TypeToName(type);
+ uint32 correct_type = MessageNames::GetInstance()->NameToType(name);
+ if (type != correct_type)
+ MessageCracker::SetMessageType(*it, correct_type);
+ }
+}
+
+bool Reader::Read(MessageVector* messages) {
+ messages_ = messages;
+
+ if (!MapFile())
+ return false;
+ if (!ReadHeader())
+ return false;
+ if (!ReadMessages())
+ return false;
+ if (!ReadStringTable())
+ return false;
+ if (!ReadNameTable())
+ return false;
+ if (!RemoveUnknownMessages())
+ return false;
+ FixMessageTypes();
+
+ return true;
+}
+
+} // namespace
+
+bool MessageFile::Read(const base::FilePath& path, MessageVector* messages) {
+ Reader reader(path);
+ return reader.Read(messages);
+}
+
+} // namespace ipc_fuzzer
diff --git a/tools/ipc_fuzzer/message_lib/message_file_writer.cc b/tools/ipc_fuzzer/message_lib/message_file_writer.cc
new file mode 100644
index 0000000000..46953d8f50
--- /dev/null
+++ b/tools/ipc_fuzzer/message_lib/message_file_writer.cc
@@ -0,0 +1,176 @@
+// 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.
+
+#include <limits.h>
+#include <set>
+
+#include "base/logging.h"
+#include "base/platform_file.h"
+#include "tools/ipc_fuzzer/message_lib/message_file.h"
+#include "tools/ipc_fuzzer/message_lib/message_file_format.h"
+#include "tools/ipc_fuzzer/message_lib/message_names.h"
+
+namespace ipc_fuzzer {
+
+namespace {
+
+// Helper class to write a MessageVector + message names to a file.
+class Writer {
+ public:
+ Writer(const base::FilePath& path);
+ ~Writer();
+ bool Write(const MessageVector& messages);
+
+ private:
+ bool OpenFile();
+
+ // Helper to append data to file_.
+ bool WriteBlob(const void *buffer, size_t size);
+
+ // Collects a set of MessageVector message types. Corresponding message
+ // names need to be included in the file.
+ bool CollectMessageTypes();
+
+ bool WriteHeader();
+ bool WriteMessages();
+
+ // Each name table entry is a message type + string table offset.
+ bool WriteNameTable();
+
+ // String table contains the actual message names.
+ bool WriteStringTable();
+
+ typedef std::set<uint32> TypesSet;
+ base::FilePath path_;
+ base::PlatformFile file_;
+ const MessageVector* messages_;
+ TypesSet types_;
+
+ DISALLOW_COPY_AND_ASSIGN(Writer);
+};
+
+Writer::Writer(const base::FilePath& path)
+ : path_(path),
+ file_(base::kInvalidPlatformFileValue),
+ messages_(NULL) {
+}
+
+Writer::~Writer() {
+ if (file_ != base::kInvalidPlatformFileValue)
+ base::ClosePlatformFile(file_);
+}
+
+bool Writer::OpenFile() {
+ file_ = base::CreatePlatformFile(
+ path_,
+ base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE,
+ NULL,
+ NULL);
+ if (file_ == base::kInvalidPlatformFileValue) {
+ LOG(ERROR) << "Failed to create IPC message file: " << path_.value();
+ return false;
+ }
+ return true;
+}
+
+bool Writer::WriteBlob(const void *buffer, size_t size) {
+ if (size > INT_MAX)
+ return false;
+ const char* char_buffer = static_cast<const char*>(buffer);
+ int ret = base::WritePlatformFileAtCurrentPos(file_, char_buffer, size);
+ if (ret != size) {
+ LOG(ERROR) << "Failed to write " << size << " bytes.";
+ return false;
+ }
+ return true;
+}
+
+bool Writer::CollectMessageTypes() {
+ for (size_t i = 0; i < messages_->size(); ++i) {
+ uint32_t type = (*messages_)[i]->type();
+ if (!MessageNames::GetInstance()->TypeExists(type)) {
+ LOG(ERROR) << "Unknown message type: " << type;
+ return false;
+ }
+ types_.insert(type);
+ }
+ return true;
+}
+
+bool Writer::WriteHeader() {
+ FileHeader header;
+ if (messages_->size() > UINT_MAX)
+ return false;
+ header.magic = FileHeader::kMagicValue;
+ header.version = FileHeader::kCurrentVersion;
+ header.message_count = messages_->size();
+ header.name_count = types_.size();
+ if (!WriteBlob(&header, sizeof(FileHeader)))
+ return false;
+ return true;
+}
+
+bool Writer::WriteMessages() {
+ for (size_t i = 0; i < messages_->size(); ++i) {
+ IPC::Message* message = (*messages_)[i];
+ if (!WriteBlob(message->data(), message->size()))
+ return false;
+ }
+ return true;
+}
+
+bool Writer::WriteNameTable() {
+ size_t string_table_offset = 0;
+ NameTableEntry entry;
+
+ for (TypesSet::iterator it = types_.begin(); it != types_.end(); ++it) {
+ if (string_table_offset > UINT_MAX)
+ return false;
+ entry.type = *it;
+ entry.string_table_offset = string_table_offset;
+ if (!WriteBlob(&entry, sizeof(NameTableEntry)))
+ return false;
+ const std::string& name = MessageNames::GetInstance()->TypeToName(*it);
+ string_table_offset += name.length() + 1;
+ }
+ return true;
+}
+
+bool Writer::WriteStringTable() {
+ for (TypesSet::iterator it = types_.begin(); it != types_.end(); ++it) {
+ const std::string& name = MessageNames::GetInstance()->TypeToName(*it);
+ if (!WriteBlob(name.c_str(), name.length() + 1))
+ return false;
+ }
+ return true;
+}
+
+bool Writer::Write(const MessageVector& messages) {
+ messages_ = &messages;
+
+ if (!OpenFile())
+ return false;
+ if (!CollectMessageTypes())
+ return false;
+ if (!WriteHeader())
+ return false;
+ if (!WriteMessages())
+ return false;
+ if (!WriteNameTable())
+ return false;
+ if (!WriteStringTable())
+ return false;
+
+ return true;
+}
+
+} // namespace
+
+bool MessageFile::Write(const base::FilePath& path,
+ const MessageVector& messages) {
+ Writer writer(path);
+ return writer.Write(messages);
+}
+
+} // namespace ipc_fuzzer
diff --git a/tools/ipc_fuzzer/message_lib/message_lib.gyp b/tools/ipc_fuzzer/message_lib/message_lib.gyp
new file mode 100644
index 0000000000..013fb6d336
--- /dev/null
+++ b/tools/ipc_fuzzer/message_lib/message_lib.gyp
@@ -0,0 +1,34 @@
+# 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'ipc_message_lib',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../../../base/base.gyp:base',
+ '../../../chrome/chrome.gyp:common',
+ '../../../ipc/ipc.gyp:ipc',
+ '../../../skia/skia.gyp:skia',
+ ],
+ 'sources': [
+ 'all_messages.h',
+ 'message_cracker.h',
+ 'message_file.h',
+ 'message_file_format.h',
+ 'message_file_reader.cc',
+ 'message_file_writer.cc',
+ 'message_names.cc',
+ 'message_names.h',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ },
+ ],
+}
diff --git a/tools/ipc_fuzzer/message_lib/message_names.cc b/tools/ipc_fuzzer/message_lib/message_names.cc
new file mode 100644
index 0000000000..b80b3881c0
--- /dev/null
+++ b/tools/ipc_fuzzer/message_lib/message_names.cc
@@ -0,0 +1,37 @@
+// 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.
+
+#include "tools/ipc_fuzzer/message_lib/message_names.h"
+#include "tools/ipc_fuzzer/message_lib/all_messages.h"
+
+#include "ipc/ipc_message_null_macros.h"
+#undef IPC_MESSAGE_DECL
+#define IPC_MESSAGE_DECL(kind, type, name, in, out, ilist, olist) \
+ names.Add(static_cast<uint32>(name::ID), #name);
+
+void PopulateIpcMessageNames(ipc_fuzzer::MessageNames& names) {
+#include "tools/ipc_fuzzer/message_lib/all_messages.h"
+}
+
+namespace ipc_fuzzer {
+
+// static
+MessageNames* MessageNames::all_names_ = NULL;
+
+MessageNames::MessageNames() {
+}
+
+MessageNames::~MessageNames() {
+}
+
+// static
+MessageNames* MessageNames::GetInstance() {
+ if (!all_names_) {
+ all_names_ = new MessageNames();
+ PopulateIpcMessageNames(*all_names_);
+ }
+ return all_names_;
+}
+
+} // namespace ipc_fuzzer
diff --git a/tools/ipc_fuzzer/message_lib/message_names.h b/tools/ipc_fuzzer/message_lib/message_names.h
new file mode 100644
index 0000000000..7ad47cc6b5
--- /dev/null
+++ b/tools/ipc_fuzzer/message_lib/message_names.h
@@ -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.
+
+#ifndef TOOLS_IPC_FUZZER_MESSAGE_LIB_MESSAGE_NAMES_H_
+#define TOOLS_IPC_FUZZER_MESSAGE_LIB_MESSAGE_NAMES_H_
+
+#include <map>
+#include <string>
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace ipc_fuzzer {
+
+class MessageNames {
+ public:
+ MessageNames();
+ ~MessageNames();
+ static MessageNames* GetInstance();
+
+ void Add(uint32 type, const std::string& name) {
+ name_map_[type] = name;
+ type_map_[name] = type;
+ }
+
+ bool TypeExists(uint32 type) {
+ return name_map_.find(type) != name_map_.end();
+ }
+
+ bool NameExists(const std::string& name) {
+ return type_map_.find(name) != type_map_.end();
+ }
+
+ const std::string& TypeToName(uint32 type) {
+ TypeToNameMap::iterator it = name_map_.find(type);
+ CHECK(it != name_map_.end());
+ return it->second;
+ }
+
+ uint32 NameToType(const std::string& name) {
+ NameToTypeMap::iterator it = type_map_.find(name);
+ CHECK(it != type_map_.end());
+ return it->second;
+ }
+
+ private:
+ typedef std::map<uint32, std::string> TypeToNameMap;
+ typedef std::map<std::string, uint32> NameToTypeMap;
+ TypeToNameMap name_map_;
+ NameToTypeMap type_map_;
+
+ static MessageNames* all_names_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageNames);
+};
+
+} // namespace ipc_fuzzer
+
+#endif // TOOLS_IPC_FUZZER_MESSAGE_LIB_MESSAGE_NAMES_H_
diff --git a/tools/ipc_fuzzer/mutate/mutate.cc b/tools/ipc_fuzzer/mutate/mutate.cc
new file mode 100644
index 0000000000..4264879fce
--- /dev/null
+++ b/tools/ipc_fuzzer/mutate/mutate.cc
@@ -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.
+
+#include <stdlib.h>
+
+int main(int argc, const char** argv) {
+ // TODO(aedla): Implement mutator.
+ return EXIT_SUCCESS;
+}
diff --git a/tools/ipc_fuzzer/mutate/mutate.gyp b/tools/ipc_fuzzer/mutate/mutate.gyp
new file mode 100644
index 0000000000..0ba896726c
--- /dev/null
+++ b/tools/ipc_fuzzer/mutate/mutate.gyp
@@ -0,0 +1,23 @@
+# 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'ipc_fuzzer_mutate',
+ 'type': 'executable',
+ 'dependencies': [
+ ],
+ 'sources': [
+ 'mutate.cc',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ },
+ ],
+}
diff --git a/tools/ipc_fuzzer/play_testcase.py b/tools/ipc_fuzzer/play_testcase.py
new file mode 100755
index 0000000000..32a7662195
--- /dev/null
+++ b/tools/ipc_fuzzer/play_testcase.py
@@ -0,0 +1,86 @@
+#!/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.
+
+"""Wrapper around chrome.
+
+Replaces all the child processes (renderer, GPU, plugins and utility) with the
+IPC fuzzer. The fuzzer will then play back a specified testcase.
+
+Depends on ipc_fuzzer being available on the same directory as chrome.
+"""
+
+import os
+import platform
+import subprocess
+import sys
+
+def main():
+ if len(sys.argv) <= 1:
+ print 'Usage: play_testcase.py [chrome_flag...] testcase'
+ return 1
+
+ script_path = os.path.realpath(__file__)
+ ipc_fuzzer_dir = os.path.dirname(script_path)
+ out_dir = os.path.abspath(os.path.join(ipc_fuzzer_dir, os.pardir,
+ os.pardir, 'out'));
+ build_dir = ''
+ chrome_path = ''
+ chrome_binary = 'chrome'
+
+ for build in ['Debug', 'Release']:
+ try_build = os.path.join(out_dir, build)
+ try_chrome = os.path.join(try_build, chrome_binary)
+ if os.path.exists(try_chrome):
+ build_dir = try_build
+ chrome_path = try_chrome
+
+ if not chrome_path:
+ print 'chrome executable not found.'
+ return 1
+
+ fuzzer_path = os.path.join(build_dir, 'ipc_fuzzer_replay')
+ if not os.path.exists(fuzzer_path):
+ print fuzzer_path + ' not found.'
+ print ('Please use enable_ipc_fuzzer=1 GYP define and '
+ 'build ipc_fuzzer target.')
+ return 1
+
+ prefixes = {
+ '--renderer-cmd-prefix',
+ '--gpu-launcher',
+ '--plugin-launcher',
+ '--ppapi-plugin-launcher',
+ '--utility-cmd-prefix',
+ }
+
+ args = [
+ chrome_path,
+ '--ipc-fuzzer-testcase=' + sys.argv[-1],
+ '--no-sandbox',
+ '--disable-kill-after-bad-ipc',
+ ]
+
+ launchers = {}
+ for prefix in prefixes:
+ launchers[prefix] = fuzzer_path
+
+ for arg in sys.argv[1:-1]:
+ if arg.find('=') != -1:
+ switch, value = arg.split('=', 1)
+ if switch in prefixes:
+ launchers[switch] = value + ' ' + launchers[switch]
+ continue
+ args.append(arg)
+
+ for switch, value in launchers.items():
+ args.append(switch + '=' + value)
+
+ print args
+
+ return subprocess.call(args)
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/tools/ipc_fuzzer/replay/DEPS b/tools/ipc_fuzzer/replay/DEPS
new file mode 100644
index 0000000000..3a889e705e
--- /dev/null
+++ b/tools/ipc_fuzzer/replay/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+chrome/common",
+]
diff --git a/tools/ipc_fuzzer/replay/replay.cc b/tools/ipc_fuzzer/replay/replay.cc
new file mode 100644
index 0000000000..73bf55ae58
--- /dev/null
+++ b/tools/ipc_fuzzer/replay/replay.cc
@@ -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.
+
+#include <stdlib.h>
+#include "tools/ipc_fuzzer/replay/replay_process.h"
+
+int main(int argc, const char** argv) {
+ ipc_fuzzer::ReplayProcess replay;
+ if (!replay.Initialize(argc, argv))
+ return EXIT_FAILURE;
+
+ replay.OpenChannel();
+
+ if (!replay.OpenTestcase())
+ return EXIT_FAILURE;
+
+ replay.Run();
+ return EXIT_SUCCESS;
+}
diff --git a/tools/ipc_fuzzer/replay/replay.gyp b/tools/ipc_fuzzer/replay/replay.gyp
new file mode 100644
index 0000000000..57273f8591
--- /dev/null
+++ b/tools/ipc_fuzzer/replay/replay.gyp
@@ -0,0 +1,29 @@
+# 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'ipc_fuzzer_replay',
+ 'type': 'executable',
+ 'dependencies': [
+ '../message_lib/message_lib.gyp:ipc_message_lib',
+ '../../../base/base.gyp:base',
+ '../../../chrome/chrome.gyp:common',
+ '../../../ipc/ipc.gyp:ipc',
+ ],
+ 'sources': [
+ 'replay.cc',
+ 'replay_process.cc',
+ 'replay_process.h',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ },
+ ],
+}
diff --git a/tools/ipc_fuzzer/replay/replay_process.cc b/tools/ipc_fuzzer/replay/replay_process.cc
new file mode 100644
index 0000000000..f5a53671c9
--- /dev/null
+++ b/tools/ipc_fuzzer/replay/replay_process.cc
@@ -0,0 +1,106 @@
+// 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.
+
+#include "tools/ipc_fuzzer/replay/replay_process.h"
+
+#include <limits.h>
+#include <string>
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/posix/global_descriptors.h"
+#include "chrome/common/chrome_switches.h"
+#include "ipc/ipc_descriptors.h"
+#include "ipc/ipc_switches.h"
+
+namespace ipc_fuzzer {
+
+ReplayProcess::ReplayProcess()
+ : main_loop_(base::MessageLoop::TYPE_DEFAULT),
+ io_thread_("Chrome_ChildIOThread"),
+ shutdown_event_(true, false),
+ message_index_(0) {
+}
+
+ReplayProcess::~ReplayProcess() {
+ channel_.reset();
+}
+
+bool ReplayProcess::Initialize(int argc, const char** argv) {
+ CommandLine::Init(argc, argv);
+
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kIpcFuzzerTestcase)) {
+ LOG(ERROR) << "This binary shouldn't be executed directly, "
+ << "please use tools/ipc_fuzzer/play_testcase.py";
+ return false;
+ }
+
+ // Log to default destination.
+ logging::SetMinLogLevel(logging::LOG_ERROR);
+ logging::InitLogging(logging::LoggingSettings());
+
+ io_thread_.StartWithOptions(
+ base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
+
+ base::GlobalDescriptors* g_fds = base::GlobalDescriptors::GetInstance();
+ g_fds->Set(kPrimaryIPCChannel,
+ kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor);
+ return true;
+}
+
+void ReplayProcess::OpenChannel() {
+ std::string channel_name =
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kProcessChannelID);
+
+ channel_.reset(
+ new IPC::ChannelProxy(channel_name,
+ IPC::Channel::MODE_CLIENT,
+ this,
+ io_thread_.message_loop_proxy()));
+}
+
+bool ReplayProcess::OpenTestcase() {
+ base::FilePath path = CommandLine::ForCurrentProcess()->GetSwitchValuePath(
+ switches::kIpcFuzzerTestcase);
+ return MessageFile::Read(path, &messages_);
+}
+
+void ReplayProcess::SendNextMessage() {
+ if (message_index_ >= messages_.size()) {
+ base::MessageLoop::current()->Quit();
+ return;
+ }
+
+ // Take next message and release it from vector.
+ IPC::Message* message = messages_[message_index_];
+ messages_[message_index_++] = NULL;
+
+ if (!channel_->Send(message)) {
+ LOG(ERROR) << "ChannelProxy::Send() failed";
+ base::MessageLoop::current()->Quit();
+ }
+}
+
+void ReplayProcess::Run() {
+ timer_.reset(new base::Timer(false, true));
+ timer_->Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(1),
+ base::Bind(&ReplayProcess::SendNextMessage,
+ base::Unretained(this)));
+ base::MessageLoop::current()->Run();
+}
+
+bool ReplayProcess::OnMessageReceived(const IPC::Message& msg) {
+ return true;
+}
+
+void ReplayProcess::OnChannelError() {
+ LOG(ERROR) << "Channel error, quitting";
+ base::MessageLoop::current()->Quit();
+}
+
+} // namespace ipc_fuzzer
diff --git a/tools/ipc_fuzzer/replay/replay_process.h b/tools/ipc_fuzzer/replay/replay_process.h
new file mode 100644
index 0000000000..8a396a8560
--- /dev/null
+++ b/tools/ipc_fuzzer/replay/replay_process.h
@@ -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.
+
+#ifndef TOOLS_IPC_FUZZER_REPLAY_REPLAY_PROCESS_H_
+#define TOOLS_IPC_FUZZER_REPLAY_REPLAY_PROCESS_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/timer/timer.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_listener.h"
+#include "ipc/ipc_message.h"
+#include "tools/ipc_fuzzer/message_lib/message_file.h"
+
+namespace ipc_fuzzer {
+
+class ReplayProcess : public IPC::Listener {
+ public:
+ ReplayProcess();
+ virtual ~ReplayProcess();
+
+ // Set up command line, logging, IO thread. Returns true on success, false
+ // otherwise.
+ bool Initialize(int argc, const char** argv);
+
+ // Open a channel to the browser process. It will think we are a renderer.
+ void OpenChannel();
+
+ // Extract messages from a file specified by --ipc-fuzzer-testcase=
+ // Returns true on success, false otherwise.
+ bool OpenTestcase();
+
+ // Send messages to the browser.
+ void Run();
+
+ // IPC::Listener implementation.
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual void OnChannelError() OVERRIDE;
+
+ private:
+ void SendNextMessage();
+
+ scoped_ptr<IPC::ChannelProxy> channel_;
+ base::MessageLoop main_loop_;
+ base::Thread io_thread_;
+ base::WaitableEvent shutdown_event_;
+ scoped_ptr<base::Timer> timer_;
+ MessageVector messages_;
+ size_t message_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReplayProcess);
+};
+
+} // namespace ipc_fuzzer
+
+#endif // TOOLS_IPC_FUZZER_REPLAY_REPLAY_PROCESS_H_
diff --git a/tools/json_schema_compiler/idl_schema.py b/tools/json_schema_compiler/idl_schema.py
index b0566032b2..c6e49bf507 100644
--- a/tools/json_schema_compiler/idl_schema.py
+++ b/tools/json_schema_compiler/idl_schema.py
@@ -107,7 +107,7 @@ class Callspec(object):
# TODO(asargent): fix the IDL parser to support optional return types.
if return_type.get('type') == 'object' or '$ref' in return_type:
return_type['optional'] = True
- for node in self.node.children:
+ for node in self.node.GetChildren():
parameter = Param(node).process(callbacks)
if parameter['name'] in self.comment:
parameter['description'] = self.comment[parameter['name']]
@@ -139,7 +139,7 @@ class Dictionary(object):
def process(self, callbacks):
properties = OrderedDict()
- for node in self.node.children:
+ for node in self.node.GetChildren():
if node.cls == 'Member':
k, v = Member(node).process(callbacks)
properties[k] = v
@@ -181,7 +181,7 @@ class Member(object):
option_name))
is_function = False
parameter_comments = OrderedDict()
- for node in self.node.children:
+ for node in self.node.GetChildren():
if node.cls == 'Comment':
(parent_comment, parameter_comments) = ProcessComment(node.GetName())
properties['description'] = parent_comment
@@ -223,7 +223,7 @@ class Typeref(object):
properties = self.additional_properties
result = properties
- if self.parent.GetProperty('OPTIONAL', False):
+ if self.parent.GetProperty('OPTIONAL'):
properties['optional'] = True
# The IDL parser denotes array types by adding a child 'Array' node onto
@@ -290,10 +290,10 @@ class Enum(object):
def process(self, callbacks):
enum = []
- for node in self.node.children:
+ for node in self.node.GetChildren():
if node.cls == 'EnumItem':
enum_value = {'name': node.GetName()}
- for child in node.children:
+ for child in node.GetChildren():
if child.cls == 'Comment':
enum_value['description'] = ProcessComment(child.GetName())[0]
else:
@@ -338,7 +338,7 @@ class Namespace(object):
self.description = description
def process(self):
- for node in self.namespace.children:
+ for node in self.namespace.GetChildren():
if node.cls == 'Dictionary':
self.types.append(Dictionary(node).process(self.callbacks))
elif node.cls == 'Callback':
@@ -368,7 +368,7 @@ class Namespace(object):
def process_interface(self, node):
members = []
- for member in node.children:
+ for member in node.GetChildren():
if member.cls == 'Member':
name, properties = Member(member).process(self.callbacks)
members.append(properties)
diff --git a/tools/linux/PRESUBMIT.py b/tools/linux/PRESUBMIT.py
new file mode 100644
index 0000000000..d4d8601f9e
--- /dev/null
+++ b/tools/linux/PRESUBMIT.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.
+
+"""Top-level presubmit script for linux.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for
+details on the presubmit API built into gcl.
+"""
+
+
+def CommonChecks(input_api, output_api):
+ import sys
+ def join(*args):
+ return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
+
+ output = []
+ sys_path_backup = sys.path
+ try:
+ sys.path = [
+ join('..', 'linux'),
+ ] + sys.path
+ output.extend(input_api.canned_checks.RunPylint(input_api, output_api))
+ finally:
+ sys.path = sys_path_backup
+
+ output.extend(
+ input_api.canned_checks.RunUnitTestsInDirectory(
+ input_api, output_api,
+ input_api.os_path.join(input_api.PresubmitLocalPath(), 'tests'),
+ whitelist=[r'.+_tests\.py$']))
+
+ if input_api.is_committing:
+ output.extend(input_api.canned_checks.PanProjectChecks(input_api,
+ output_api,
+ owners_check=False))
+ return output
+
+
+def CheckChangeOnUpload(input_api, output_api):
+ return CommonChecks(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+ return CommonChecks(input_api, output_api)
diff --git a/tools/linux/procfs.py b/tools/linux/procfs.py
new file mode 100755
index 0000000000..6308fdd93d
--- /dev/null
+++ b/tools/linux/procfs.py
@@ -0,0 +1,729 @@
+#!/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.
+
+# A Python library to read and store procfs (/proc) information on Linux.
+#
+# Each information storage class in this file stores original data as original
+# as reasonablly possible. Translation is done when requested. It is to make it
+# always possible to probe the original data.
+
+
+import collections
+import logging
+import os
+import re
+import struct
+import sys
+
+
+class _NullHandler(logging.Handler):
+ def emit(self, record):
+ pass
+
+
+_LOGGER = logging.getLogger('procfs')
+_LOGGER.addHandler(_NullHandler())
+
+
+class ProcStat(object):
+ """Reads and stores information in /proc/pid/stat."""
+ _PATTERN = re.compile(r'^'
+ '(?P<PID>-?[0-9]+) '
+ '\((?P<COMM>.+)\) '
+ '(?P<STATE>[RSDZTW]) '
+ '(?P<PPID>-?[0-9]+) '
+ '(?P<PGRP>-?[0-9]+) '
+ '(?P<SESSION>-?[0-9]+) '
+ '(?P<TTY_NR>-?[0-9]+) '
+ '(?P<TPGID>-?[0-9]+) '
+ '(?P<FLAGS>[0-9]+) '
+ '(?P<MINFIT>[0-9]+) '
+ '(?P<CMINFIT>[0-9]+) '
+ '(?P<MAJFIT>[0-9]+) '
+ '(?P<CMAJFIT>[0-9]+) '
+ '(?P<UTIME>[0-9]+) '
+ '(?P<STIME>[0-9]+) '
+ '(?P<CUTIME>[0-9]+) '
+ '(?P<CSTIME>[0-9]+) '
+ '(?P<PRIORITY>[0-9]+) '
+ '(?P<NICE>[0-9]+) '
+ '(?P<NUM_THREADS>[0-9]+) '
+ '(?P<ITREALVALUE>[0-9]+) '
+ '(?P<STARTTIME>[0-9]+) '
+ '(?P<VSIZE>[0-9]+) '
+ '(?P<RSS>[0-9]+) '
+ '(?P<RSSLIM>[0-9]+) '
+ '(?P<STARTCODE>[0-9]+) '
+ '(?P<ENDCODE>[0-9]+) '
+ '(?P<STARTSTACK>[0-9]+) '
+ '(?P<KSTKESP>[0-9]+) '
+ '(?P<KSTKEIP>[0-9]+) '
+ '(?P<SIGNAL>[0-9]+) '
+ '(?P<BLOCKED>[0-9]+) '
+ '(?P<SIGIGNORE>[0-9]+) '
+ '(?P<SIGCATCH>[0-9]+) '
+ '(?P<WCHAN>[0-9]+) '
+ '(?P<NSWAP>[0-9]+) '
+ '(?P<CNSWAP>[0-9]+) '
+ '(?P<EXIT_SIGNAL>[0-9]+) '
+ '(?P<PROCESSOR>[0-9]+) '
+ '(?P<RT_PRIORITY>[0-9]+) '
+ '(?P<POLICY>[0-9]+) '
+ '(?P<DELAYACCT_BLKIO_TICKS>[0-9]+) '
+ '(?P<GUEST_TIME>[0-9]+) '
+ '(?P<CGUEST_TIME>[0-9]+)', re.IGNORECASE)
+
+ def __init__(self, raw, pid, vsize, rss):
+ self._raw = raw
+ self._pid = pid
+ self._vsize = vsize
+ self._rss = rss
+
+ @staticmethod
+ def load_file(stat_f):
+ raw = stat_f.readlines()
+ stat = ProcStat._PATTERN.match(raw[0])
+ return ProcStat(raw,
+ stat.groupdict().get('PID'),
+ stat.groupdict().get('VSIZE'),
+ stat.groupdict().get('RSS'))
+
+ @staticmethod
+ def load(pid):
+ with open(os.path.join('/proc', str(pid), 'stat'), 'r') as stat_f:
+ return ProcStat.load_file(stat_f)
+
+ @property
+ def raw(self):
+ return self._raw
+
+ @property
+ def pid(self):
+ return int(self._pid)
+
+ @property
+ def vsize(self):
+ return int(self._vsize)
+
+ @property
+ def rss(self):
+ return int(self._rss)
+
+
+class ProcStatm(object):
+ """Reads and stores information in /proc/pid/statm."""
+ _PATTERN = re.compile(r'^'
+ '(?P<SIZE>[0-9]+) '
+ '(?P<RESIDENT>[0-9]+) '
+ '(?P<SHARE>[0-9]+) '
+ '(?P<TEXT>[0-9]+) '
+ '(?P<LIB>[0-9]+) '
+ '(?P<DATA>[0-9]+) '
+ '(?P<DT>[0-9]+)', re.IGNORECASE)
+
+ def __init__(self, raw, size, resident, share, text, lib, data, dt):
+ self._raw = raw
+ self._size = size
+ self._resident = resident
+ self._share = share
+ self._text = text
+ self._lib = lib
+ self._data = data
+ self._dt = dt
+
+ @staticmethod
+ def load_file(statm_f):
+ raw = statm_f.readlines()
+ statm = ProcStatm._PATTERN.match(raw[0])
+ return ProcStatm(raw,
+ statm.groupdict().get('SIZE'),
+ statm.groupdict().get('RESIDENT'),
+ statm.groupdict().get('SHARE'),
+ statm.groupdict().get('TEXT'),
+ statm.groupdict().get('LIB'),
+ statm.groupdict().get('DATA'),
+ statm.groupdict().get('DT'))
+
+ @staticmethod
+ def load(pid):
+ with open(os.path.join('/proc', str(pid), 'statm'), 'r') as statm_f:
+ return ProcStatm.load_file(statm_f)
+
+ @property
+ def raw(self):
+ return self._raw
+
+ @property
+ def size(self):
+ return int(self._size)
+
+ @property
+ def resident(self):
+ return int(self._resident)
+
+ @property
+ def share(self):
+ return int(self._share)
+
+ @property
+ def text(self):
+ return int(self._text)
+
+ @property
+ def lib(self):
+ return int(self._lib)
+
+ @property
+ def data(self):
+ return int(self._data)
+
+ @property
+ def dt(self):
+ return int(self._dt)
+
+
+class ProcStatus(object):
+ """Reads and stores information in /proc/pid/status."""
+ _PATTERN = re.compile(r'^(?P<NAME>[A-Za-z0-9_]+):\s+(?P<VALUE>.*)')
+
+ def __init__(self, raw, dct):
+ self._raw = raw
+ self._pid = dct.get('Pid')
+ self._name = dct.get('Name')
+ self._vm_peak = dct.get('VmPeak')
+ self._vm_size = dct.get('VmSize')
+ self._vm_lck = dct.get('VmLck')
+ self._vm_pin = dct.get('VmPin')
+ self._vm_hwm = dct.get('VmHWM')
+ self._vm_rss = dct.get('VmRSS')
+ self._vm_data = dct.get('VmData')
+ self._vm_stack = dct.get('VmStk')
+ self._vm_exe = dct.get('VmExe')
+ self._vm_lib = dct.get('VmLib')
+ self._vm_pte = dct.get('VmPTE')
+ self._vm_swap = dct.get('VmSwap')
+
+ @staticmethod
+ def load_file(status_f):
+ raw = status_f.readlines()
+ dct = {}
+ for line in raw:
+ status_match = ProcStatus._PATTERN.match(line)
+ if status_match:
+ match_dict = status_match.groupdict()
+ dct[match_dict['NAME']] = match_dict['VALUE']
+ else:
+ raise SyntaxError('Unknown /proc/pid/status format.')
+ return ProcStatus(raw, dct)
+
+ @staticmethod
+ def load(pid):
+ with open(os.path.join('/proc', str(pid), 'status'), 'r') as status_f:
+ return ProcStatus.load_file(status_f)
+
+ @property
+ def raw(self):
+ return self._raw
+
+ @property
+ def pid(self):
+ return int(self._pid)
+
+ @property
+ def vm_peak(self):
+ """Returns a high-water (peak) virtual memory size in kilo-bytes."""
+ if self._vm_peak.endswith('kB'):
+ return int(self._vm_peak.split()[0])
+ raise ValueError('VmPeak is not in kB.')
+
+ @property
+ def vm_size(self):
+ """Returns a virtual memory size in kilo-bytes."""
+ if self._vm_size.endswith('kB'):
+ return int(self._vm_size.split()[0])
+ raise ValueError('VmSize is not in kB.')
+
+ @property
+ def vm_hwm(self):
+ """Returns a high-water (peak) resident set size (RSS) in kilo-bytes."""
+ if self._vm_hwm.endswith('kB'):
+ return int(self._vm_hwm.split()[0])
+ raise ValueError('VmHWM is not in kB.')
+
+ @property
+ def vm_rss(self):
+ """Returns a resident set size (RSS) in kilo-bytes."""
+ if self._vm_rss.endswith('kB'):
+ return int(self._vm_rss.split()[0])
+ raise ValueError('VmRSS is not in kB.')
+
+
+class ProcMapsEntry(object):
+ """A class representing one line in /proc/pid/maps."""
+
+ def __init__(
+ self, begin, end, readable, writable, executable, private, offset,
+ major, minor, inode, name):
+ self.begin = begin
+ self.end = end
+ self.readable = readable
+ self.writable = writable
+ self.executable = executable
+ self.private = private
+ self.offset = offset
+ self.major = major
+ self.minor = minor
+ self.inode = inode
+ self.name = name
+
+ def as_dict(self):
+ return {
+ 'begin': self.begin,
+ 'end': self.end,
+ 'readable': self.readable,
+ 'writable': self.writable,
+ 'executable': self.executable,
+ 'private': self.private,
+ 'offset': self.offset,
+ 'major': self.major,
+ 'minor': self.minor,
+ 'inode': self.inode,
+ 'name': self.name,
+ }
+
+
+class ProcMaps(object):
+ """Reads and stores information in /proc/pid/maps."""
+
+ MAPS_PATTERN = re.compile(
+ r'^([a-f0-9]+)-([a-f0-9]+)\s+(.)(.)(.)(.)\s+([a-f0-9]+)\s+(\S+):(\S+)\s+'
+ r'(\d+)\s*(.*)$', re.IGNORECASE)
+
+ def __init__(self):
+ self._sorted_indexes = []
+ self._dictionary = {}
+ self._sorted = True
+
+ def iter(self, condition):
+ if not self._sorted:
+ self._sorted_indexes.sort()
+ self._sorted = True
+ for index in self._sorted_indexes:
+ if not condition or condition(self._dictionary[index]):
+ yield self._dictionary[index]
+
+ def __iter__(self):
+ if not self._sorted:
+ self._sorted_indexes.sort()
+ self._sorted = True
+ for index in self._sorted_indexes:
+ yield self._dictionary[index]
+
+ @staticmethod
+ def load_file(maps_f):
+ table = ProcMaps()
+ for line in maps_f:
+ table.append_line(line)
+ return table
+
+ @staticmethod
+ def load(pid):
+ with open(os.path.join('/proc', str(pid), 'maps'), 'r') as maps_f:
+ return ProcMaps.load_file(maps_f)
+
+ def append_line(self, line):
+ entry = self.parse_line(line)
+ if entry:
+ self._append_entry(entry)
+ return entry
+
+ @staticmethod
+ def parse_line(line):
+ matched = ProcMaps.MAPS_PATTERN.match(line)
+ if matched:
+ return ProcMapsEntry( # pylint: disable=W0212
+ int(matched.group(1), 16), # begin
+ int(matched.group(2), 16), # end
+ matched.group(3), # readable
+ matched.group(4), # writable
+ matched.group(5), # executable
+ matched.group(6), # private
+ int(matched.group(7), 16), # offset
+ matched.group(8), # major
+ matched.group(9), # minor
+ int(matched.group(10), 10), # inode
+ matched.group(11) # name
+ )
+ else:
+ return None
+
+ @staticmethod
+ def constants(entry):
+ return (entry.writable == '-' and entry.executable == '-' and re.match(
+ '\S+(\.(so|dll|dylib|bundle)|chrome)((\.\d+)+\w*(\.\d+){0,3})?',
+ entry.name))
+
+ @staticmethod
+ def executable(entry):
+ return (entry.executable == 'x' and re.match(
+ '\S+(\.(so|dll|dylib|bundle)|chrome)((\.\d+)+\w*(\.\d+){0,3})?',
+ entry.name))
+
+ @staticmethod
+ def executable_and_constants(entry):
+ return (((entry.writable == '-' and entry.executable == '-') or
+ entry.executable == 'x') and re.match(
+ '\S+(\.(so|dll|dylib|bundle)|chrome)((\.\d+)+\w*(\.\d+){0,3})?',
+ entry.name))
+
+ def _append_entry(self, entry):
+ if self._sorted_indexes and self._sorted_indexes[-1] > entry.begin:
+ self._sorted = False
+ self._sorted_indexes.append(entry.begin)
+ self._dictionary[entry.begin] = entry
+
+
+class ProcSmaps(object):
+ """Reads and stores information in /proc/pid/smaps."""
+ _SMAPS_PATTERN = re.compile(r'^(?P<NAME>[A-Za-z0-9_]+):\s+(?P<VALUE>.*)')
+
+ class VMA(object):
+ def __init__(self):
+ self._size = 0
+ self._rss = 0
+ self._pss = 0
+
+ def append(self, name, value):
+ dct = {
+ 'Size': '_size',
+ 'Rss': '_rss',
+ 'Pss': '_pss',
+ 'Referenced': '_referenced',
+ 'Private_Clean': '_private_clean',
+ 'Shared_Clean': '_shared_clean',
+ 'KernelPageSize': '_kernel_page_size',
+ 'MMUPageSize': '_mmu_page_size',
+ }
+ if name in dct:
+ self.__setattr__(dct[name], value)
+
+ @property
+ def size(self):
+ if self._size.endswith('kB'):
+ return int(self._size.split()[0])
+ return int(self._size)
+
+ @property
+ def rss(self):
+ if self._rss.endswith('kB'):
+ return int(self._rss.split()[0])
+ return int(self._rss)
+
+ @property
+ def pss(self):
+ if self._pss.endswith('kB'):
+ return int(self._pss.split()[0])
+ return int(self._pss)
+
+ def __init__(self, raw, total_dct, maps, vma_internals):
+ self._raw = raw
+ self._size = total_dct['Size']
+ self._rss = total_dct['Rss']
+ self._pss = total_dct['Pss']
+ self._referenced = total_dct['Referenced']
+ self._shared_clean = total_dct['Shared_Clean']
+ self._private_clean = total_dct['Private_Clean']
+ self._kernel_page_size = total_dct['KernelPageSize']
+ self._mmu_page_size = total_dct['MMUPageSize']
+ self._maps = maps
+ self._vma_internals = vma_internals
+
+ @staticmethod
+ def load(pid):
+ with open(os.path.join('/proc', str(pid), 'smaps'), 'r') as smaps_f:
+ raw = smaps_f.readlines()
+
+ vma = None
+ vma_internals = collections.OrderedDict()
+ total_dct = collections.defaultdict(int)
+ maps = ProcMaps()
+ for line in raw:
+ maps_match = ProcMaps.MAPS_PATTERN.match(line)
+ if maps_match:
+ vma = maps.append_line(line.strip())
+ vma_internals[vma] = ProcSmaps.VMA()
+ else:
+ smaps_match = ProcSmaps._SMAPS_PATTERN.match(line)
+ if smaps_match:
+ match_dict = smaps_match.groupdict()
+ vma_internals[vma].append(match_dict['NAME'], match_dict['VALUE'])
+ total_dct[match_dict['NAME']] += int(match_dict['VALUE'].split()[0])
+
+ return ProcSmaps(raw, total_dct, maps, vma_internals)
+
+ @property
+ def size(self):
+ return self._size
+
+ @property
+ def rss(self):
+ return self._rss
+
+ @property
+ def referenced(self):
+ return self._referenced
+
+ @property
+ def pss(self):
+ return self._pss
+
+ @property
+ def private_clean(self):
+ return self._private_clean
+
+ @property
+ def shared_clean(self):
+ return self._shared_clean
+
+ @property
+ def kernel_page_size(self):
+ return self._kernel_page_size
+
+ @property
+ def mmu_page_size(self):
+ return self._mmu_page_size
+
+ @property
+ def vma_internals(self):
+ return self._vma_internals
+
+
+class ProcPagemap(object):
+ """Reads and stores partial information in /proc/pid/pagemap.
+
+ It picks up virtual addresses to read based on ProcMaps (/proc/pid/maps).
+ See https://www.kernel.org/doc/Documentation/vm/pagemap.txt for details.
+ """
+ _BYTES_PER_PAGEMAP_VALUE = 8
+ _BYTES_PER_OS_PAGE = 4096
+ _VIRTUAL_TO_PAGEMAP_OFFSET = _BYTES_PER_OS_PAGE / _BYTES_PER_PAGEMAP_VALUE
+
+ _MASK_PRESENT = 1 << 63
+ _MASK_SWAPPED = 1 << 62
+ _MASK_FILEPAGE_OR_SHAREDANON = 1 << 61
+ _MASK_SOFTDIRTY = 1 << 55
+ _MASK_PFN = (1 << 55) - 1
+
+ class VMA(object):
+ def __init__(self, vsize, present, swapped, pageframes):
+ self._vsize = vsize
+ self._present = present
+ self._swapped = swapped
+ self._pageframes = pageframes
+
+ @property
+ def vsize(self):
+ return int(self._vsize)
+
+ @property
+ def present(self):
+ return int(self._present)
+
+ @property
+ def swapped(self):
+ return int(self._swapped)
+
+ @property
+ def pageframes(self):
+ return self._pageframes
+
+ def __init__(self, vsize, present, swapped, vma_internals, in_process_dup):
+ self._vsize = vsize
+ self._present = present
+ self._swapped = swapped
+ self._vma_internals = vma_internals
+ self._in_process_dup = in_process_dup
+
+ @staticmethod
+ def load(pid, maps):
+ total_present = 0
+ total_swapped = 0
+ total_vsize = 0
+ in_process_dup = 0
+ vma_internals = collections.OrderedDict()
+ process_pageframe_set = set()
+
+ pagemap_fd = os.open(
+ os.path.join('/proc', str(pid), 'pagemap'), os.O_RDONLY)
+ for vma in maps:
+ present = 0
+ swapped = 0
+ vsize = 0
+ pageframes = collections.defaultdict(int)
+ begin_offset = ProcPagemap._offset(vma.begin)
+ chunk_size = ProcPagemap._offset(vma.end) - begin_offset
+ os.lseek(pagemap_fd, begin_offset, os.SEEK_SET)
+ buf = os.read(pagemap_fd, chunk_size)
+ if len(buf) < chunk_size:
+ _LOGGER.warn('Failed to read pagemap at 0x%x in %d.' % (vma.begin, pid))
+ pagemap_values = struct.unpack(
+ '=%dQ' % (len(buf) / ProcPagemap._BYTES_PER_PAGEMAP_VALUE), buf)
+ for pagemap_value in pagemap_values:
+ vsize += ProcPagemap._BYTES_PER_OS_PAGE
+ if pagemap_value & ProcPagemap._MASK_PRESENT:
+ if (pagemap_value & ProcPagemap._MASK_PFN) in process_pageframe_set:
+ in_process_dup += ProcPagemap._BYTES_PER_OS_PAGE
+ else:
+ process_pageframe_set.add(pagemap_value & ProcPagemap._MASK_PFN)
+ if (pagemap_value & ProcPagemap._MASK_PFN) not in pageframes:
+ present += ProcPagemap._BYTES_PER_OS_PAGE
+ pageframes[pagemap_value & ProcPagemap._MASK_PFN] += 1
+ if pagemap_value & ProcPagemap._MASK_SWAPPED:
+ swapped += ProcPagemap._BYTES_PER_OS_PAGE
+ vma_internals[vma] = ProcPagemap.VMA(vsize, present, swapped, pageframes)
+ total_present += present
+ total_swapped += swapped
+ total_vsize += vsize
+ os.close(pagemap_fd)
+
+ return ProcPagemap(total_vsize, total_present, total_swapped,
+ vma_internals, in_process_dup)
+
+ @staticmethod
+ def _offset(virtual_address):
+ return virtual_address / ProcPagemap._VIRTUAL_TO_PAGEMAP_OFFSET
+
+ @property
+ def vsize(self):
+ return int(self._vsize)
+
+ @property
+ def present(self):
+ return int(self._present)
+
+ @property
+ def swapped(self):
+ return int(self._swapped)
+
+ @property
+ def vma_internals(self):
+ return self._vma_internals
+
+
+class _ProcessMemory(object):
+ """Aggregates process memory information from /proc for manual testing."""
+ def __init__(self, pid):
+ self._pid = pid
+ self._maps = None
+ self._pagemap = None
+ self._stat = None
+ self._status = None
+ self._statm = None
+ self._smaps = []
+
+ def _read(self, proc_file):
+ lines = []
+ with open(os.path.join('/proc', str(self._pid), proc_file), 'r') as proc_f:
+ lines = proc_f.readlines()
+ return lines
+
+ def read_all(self):
+ self.read_stat()
+ self.read_statm()
+ self.read_status()
+ self.read_smaps()
+ self.read_maps()
+ self.read_pagemap(self._maps)
+
+ def read_maps(self):
+ self._maps = ProcMaps.load(self._pid)
+
+ def read_pagemap(self, maps):
+ self._pagemap = ProcPagemap.load(self._pid, maps)
+
+ def read_smaps(self):
+ self._smaps = ProcSmaps.load(self._pid)
+
+ def read_stat(self):
+ self._stat = ProcStat.load(self._pid)
+
+ def read_statm(self):
+ self._statm = ProcStatm.load(self._pid)
+
+ def read_status(self):
+ self._status = ProcStatus.load(self._pid)
+
+ @property
+ def pid(self):
+ return self._pid
+
+ @property
+ def maps(self):
+ return self._maps
+
+ @property
+ def pagemap(self):
+ return self._pagemap
+
+ @property
+ def smaps(self):
+ return self._smaps
+
+ @property
+ def stat(self):
+ return self._stat
+
+ @property
+ def statm(self):
+ return self._statm
+
+ @property
+ def status(self):
+ return self._status
+
+
+def main(argv):
+ """The main function for manual testing."""
+ _LOGGER.setLevel(logging.WARNING)
+ handler = logging.StreamHandler()
+ handler.setLevel(logging.WARNING)
+ handler.setFormatter(logging.Formatter(
+ '%(asctime)s:%(name)s:%(levelname)s:%(message)s'))
+ _LOGGER.addHandler(handler)
+
+ pids = []
+ for arg in argv[1:]:
+ try:
+ pid = int(arg)
+ except ValueError:
+ raise SyntaxError("%s is not an integer." % arg)
+ else:
+ pids.append(pid)
+
+ procs = {}
+ for pid in pids:
+ procs[pid] = _ProcessMemory(pid)
+ procs[pid].read_all()
+
+ print '=== PID: %d ===' % pid
+
+ print ' stat: %d' % procs[pid].stat.vsize
+ print ' statm: %d' % (procs[pid].statm.size * 4096)
+ print ' status: %d (Peak:%d)' % (procs[pid].status.vm_size * 1024,
+ procs[pid].status.vm_peak * 1024)
+ print ' smaps: %d' % (procs[pid].smaps.size * 1024)
+ print 'pagemap: %d' % procs[pid].pagemap.vsize
+ print ' stat: %d' % (procs[pid].stat.rss * 4096)
+ print ' statm: %d' % (procs[pid].statm.resident * 4096)
+ print ' status: %d (Peak:%d)' % (procs[pid].status.vm_rss * 1024,
+ procs[pid].status.vm_hwm * 1024)
+ print ' smaps: %d' % (procs[pid].smaps.rss * 1024)
+ print 'pagemap: %d' % procs[pid].pagemap.present
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/tools/find_runtime_symbols/tests/proc_maps_test.py b/tools/linux/tests/procfs_tests.py
index 502f252c55..e1e837a591 100755
--- a/tools/find_runtime_symbols/tests/proc_maps_test.py
+++ b/tools/linux/tests/procfs_tests.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# 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.
@@ -12,7 +12,7 @@ import unittest
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, ROOT_DIR)
-from proc_maps import ProcMaps
+from procfs import ProcMaps
class ProcMapsTest(unittest.TestCase):
@@ -77,26 +77,26 @@ class ProcMapsTest(unittest.TestCase):
}
def test_load(self):
- maps = ProcMaps.load(cStringIO.StringIO(self._TEST_PROCMAPS))
+ maps = ProcMaps.load_file(cStringIO.StringIO(self._TEST_PROCMAPS))
for index, entry in enumerate(maps):
self.assertEqual(entry.as_dict(), self._expected_as_dict(index))
def test_constants(self):
- maps = ProcMaps.load(cStringIO.StringIO(self._TEST_PROCMAPS))
+ maps = ProcMaps.load_file(cStringIO.StringIO(self._TEST_PROCMAPS))
selected = [4, 7]
for index, entry in enumerate(maps.iter(ProcMaps.constants)):
self.assertEqual(entry.as_dict(),
self._expected_as_dict(selected[index]))
def test_executable(self):
- maps = ProcMaps.load(cStringIO.StringIO(self._TEST_PROCMAPS))
+ maps = ProcMaps.load_file(cStringIO.StringIO(self._TEST_PROCMAPS))
selected = [3, 6]
for index, entry in enumerate(maps.iter(ProcMaps.executable)):
self.assertEqual(entry.as_dict(),
self._expected_as_dict(selected[index]))
def test_executable_and_constants(self):
- maps = ProcMaps.load(cStringIO.StringIO(self._TEST_PROCMAPS))
+ maps = ProcMaps.load_file(cStringIO.StringIO(self._TEST_PROCMAPS))
selected = [3, 4, 6, 7]
for index, entry in enumerate(maps.iter(ProcMaps.executable_and_constants)):
self.assertEqual(entry.as_dict(),
diff --git a/tools/lsan/suppressions.txt b/tools/lsan/suppressions.txt
index 2e8b1c221e..da0a7bd3f7 100644
--- a/tools/lsan/suppressions.txt
+++ b/tools/lsan/suppressions.txt
@@ -75,8 +75,8 @@ leak:gpu::gles2::GLES2DecoderImpl::DoRenderbufferStorage
# Leak in cc_unittests. http://crbug.com/317965
leak:cc::ThreadProxy::ReadyToFinalizeTextureUpdates
-# Leak in unit_tests. http://crbug.com/309468
-leak:TranslateBubbleViewTest::SetUp
+# Leak in unit_tests and browser_tests. http://crbug.com/309468
+leak:TranslateBubbleView::CreateViewBeforeTranslate()
# GTK leaks. GTK is deprecated, so we won't be fixing these.
# Most of them should apply to the full browser process only.
@@ -88,13 +88,6 @@ leak:GtkNativeViewManager
leak:_gdk_x11_window_get_toplevel
leak:gtk_util::*AppModal
-# Infobar-related leaks in browser tests.
-# TODO(earthdok): Retriage them once http://crbug.com/62154 is fixed.
-leak:chrome::ShowBadFlagsPrompt
-leak:TranslateInfoBarDelegate::Create
-leak:TranslateLanguageList::TranslateLanguageList
-leak:SimpleAlertInfoBarDelegate::Create
-
# Small shutdown-only leak in browser tests (24 bytes). http://crbug.com/317116
# TODO(earthdok): Either annotate or narrow down the suppression.
leak:content::RenderViewTest::TearDown
diff --git a/tools/metrics/actions/chromeactions.txt b/tools/metrics/actions/chromeactions.txt
index b87b5ad655..a927c53dab 100644
--- a/tools/metrics/actions/chromeactions.txt
+++ b/tools/metrics/actions/chromeactions.txt
@@ -11,6 +11,7 @@
0xe9d2339d6b9225c7 AboutFlags_disable-outdated-plugins
0x40c0bddfe31a67c2 AboutFlags_disable-pnacl
0xf73b5212b48e2a75 AboutFlags_disable-print-preview
+0x973035dcbd7da57d AboutFlags_disable-views-rect-based-targeting
0x088990bbea59a185 AboutFlags_disable-website-settings
0xdddbf34cee2c636c AboutFlags_dns-server
0x3add7fc42803849e AboutFlags_downloads-new-ui
@@ -45,42 +46,91 @@
0x6e65ea1edca0f441 AboutFlags_vertical-tabs
0xb5726d311462960f AboutFlags_views-use-rect-based-targeting
0xb0140558fbb7c0bf AboutFlags_xss-auditor
+0x56d1715aaf01650e Accel_Accessible_Focus_Next
+0xbbf8552fa6bc4ec8 Accel_Accessible_Focus_Previous
+0x6897e3c3045c2049 Accel_Add_Remove_Display
+0x323b3db9c0dc8a29 Accel_Advanced_Print
0x46299b11fb76c5d3 Accel_Back_Backspace
0x942956b3c704a9a2 Accel_Back_F1
0x651680cc43f0574d Accel_Back_Left
0x9d53a3047a707543 Accel_BrightnessDown_F6
0xb05e783429d6cfac Accel_BrightnessUp_F7
+0xbd73010eb2b54e3e Accel_Disable_Caps_Lock
0x2fa7197377421869 Accel_Exit_First_Q
0x75c842188f8d6864 Accel_Exit_Second_Q
0x7f2c75700d286c16 Accel_FocusLocation_D
0xe566241731cecfbb Accel_FocusLocation_L
0xb4f736d60280d914 Accel_FocusSearch_E
0xbe8f2bb497e569c3 Accel_FocusSearch_K
+0xd1a08c0ad9020bcb Accel_Focus_Bookmarks
+0x49eb338835815c65 Accel_Focus_Launcher
+0xf14b8cacdf69810d Accel_Focus_Location
+0xf5d5437926066992 Accel_Focus_Next_Pane
+0x34c39dd57d09c168 Accel_Focus_Previous_Pane
+0x4ee80099ff1e7faf Accel_Focus_Search
+0x8d13cf705975a471 Accel_Focus_Toolbar
0x87886216ce3f147d Accel_Forward_Backspace
0x5aa4a40eb4d92d0a Accel_Forward_F2
0xad7e6131ff6f2528 Accel_Forward_Right
0x36adab865c373745 Accel_Fullscreen_F4
0x125f05329e660307 Accel_KeyboardBrightnessDown_F6
0xcdd268156ab80bb1 Accel_KeyboardBrightnessUp_F7
+0xe0211ccf9161bf90 Accel_Launch_App
+0x18bced3edb68ea2e Accel_Launch_Last_App
0xb3ca99d79240c92f Accel_LockScreen_L
0x8fadf02749171923 Accel_LockScreen_LockButton
0xaf0a974743d27634 Accel_LockScreen_PowerButton
0x290302f64fd4dfe1 Accel_Maximize_Restore_F4
0x5e6bce93ecde1df8 Accel_NewTab_T
+0xde2191f2bea26a79 Accel_New_Incognito_Window
+0x757d4b57a1ec0fc5 Accel_New_Window
0x5a27559e6846a75e Accel_NextWindow_F5
0xc217e3bb30e9a004 Accel_NextWindow_Tab
+0x0dbf80bca1f5aa55 Accel_Next_Ime
+0xf7e71746dcb2dcb6 Accel_Open_Crosh
+0x568322b9a748838c Accel_Open_Feedback_Page
+0x73d6c20888399463 Accel_Open_File_Manager
0x450e0851cdca9dba Accel_Overview_F5
0x2e92845dc8edee91 Accel_PrevWindow_F5
0x49b33857117c4c43 Accel_PrevWindow_Tab
+0x1287b6b8d53be265 Accel_Previous_Ime
0xa2654c422408df28 Accel_Reload_F3
0x6db94baf96eb6cfd Accel_Reload_R
+0x6c2a5c8adfe66d24 Accel_Restore_Tab
+0x7a90b3b33e942604 Accel_Rotate_Window
+0x9898c5b87b6b9800 Accel_Scale_Ui_Down
+0xb1b4ccdf5b8a40d9 Accel_Scale_Ui_Reset
+0x51abf32e73b8d48b Accel_Scale_Ui_Up
0xe315330404ef14a8 Accel_Search_LWin
0x01b760c64064556f Accel_SelectNextTab
0x86e5b771da4dc62f Accel_SelectPreviousTab
+0x9792f1f961c20c23 Accel_Show_App_Menu
+0xd87cd8d5a98aa3c3 Accel_Show_Keyboard_Overlay
+0x0fd23c275cddd272 Accel_Show_Message_Center_Bubble
+0x629c5c45c8c5d04d Accel_Show_System_Tray_Bubble
+0x718c0cc58e95908d Accel_Show_Task_Manager
0x178d1faf1167b4ca Accel_ShutDown_PowerButton
+0x0c31c5b8cba6153c Accel_Silence_Spoken_Feedback
+0xd22a9c9640925fae Accel_Swap_Primary_Display
+0x66699817f069d43e Accel_Switch_Ime
+0x744101dfa25c0f2a Accel_Switch_To_Next_User
+0x9186389d613efe32 Accel_Switch_To_Previous_User
+0xf6a1706f2c8aaa07 Accel_Take_Partial_Screenshot
+0xc9658518e5333399 Accel_Take_Screenshot
+0x6e94b035f45335c8 Accel_Toggle_Caps_Lock
+0x5430faa376fbc325 Accel_Toggle_Maximized
+0x3336c54f07ac2500 Accel_Toggle_Minimized
+0x4a523ddfebc25031 Accel_Toggle_Minimized_M
+0x943a938771a11aaa Accel_Toggle_Minimized_Minus
+0xc90f960d50b63469 Accel_Toggle_Mirror_Mode
+0xa1a07dd227e5332e Accel_Toggle_Spoken_Feedback
+0x5523297f977df4f6 Accel_Touch_Hud_Clear
0x7974ffb673dd5475 Accel_VolumeDown_F9
0xbe6aee0aaab8ed8a Accel_VolumeMute_F8
0x1cbd5b055dc9a3cc Accel_VolumeUp_F10
+0x62ff3afb978ab1bd Accel_Window_Position_Center
+0xbf7e1632990c4967 Accel_Window_Snap_Left
+0x361de755acf47c9b Accel_Window_Snap_Right
0x649461e26c8e5796 AcceptedGeneratedKeyword
0x2b6ed5e3f78e1295 AcceptedKeyword
0xaa16d48a61361aa6 AcceptedKeywordHint
@@ -153,6 +203,7 @@
0xd7e4d61883121c76 BadMessageTerminate_NC
0x448f44d226b839b7 BadMessageTerminate_NC17
0xd267646495d87640 BadMessageTerminate_RDH
+0x90d4be9ff70af550 BadMessageTerminate_RFH
0xce2d3186346ab161 BadMessageTerminate_RVD
0xf845124429e7aa80 BadMessageTerminate_RVH
0xcb59a352ad13dc91 BadMessageTerminate_RWH
@@ -408,6 +459,7 @@
0xb63daeca06ecea16 EnterFullScreenWithWrenchMenu
0xfef46e5063ce3dc7 Exit
0xcf0c759bda6783fd ExtensionNotFound_ED
+0x29c354a2e25e4bc9 ExtensionWipeoutNotificationShown
0xdf56dffe242f621a Extensions.ExtensionInstalled
0x4f07c158c8047ab9 Extensions.ExtensionUninstalled
0x08566bf746b7f665 Extensions.IDChangedError
@@ -625,6 +677,10 @@
0x5569092107ae9fb0 JustifyCenter
0xb2e54b8c7671e92e JustifyLeft
0xcd362d0d3c2a301a JustifyRight
+0xbcdd33480e8d115d KeySystemSupport.Widevine.Queried
+0x9768048447968d10 KeySystemSupport.Widevine.Supported
+0x2bde1e6310d1cd7d KeySystemSupport.WidevineWithType.Queried
+0xc15bca86fafab1d5 KeySystemSupport.WidevineWithType.Supported
0x54eeedf1d26e7698 KeywordEditor_AddKeyword
0x204aad0f4a1f0f22 KeywordEditor_AddKeywordJS
0x37ac83353fd9be1f KeywordEditor_ModifiedKeyword
@@ -1124,6 +1180,9 @@
0xd51394e7d79ed777 MobileContextMenuCopyImageLinkAddress
0xa7b47844dbca122c MobileContextMenuCopyLinkAddress
0x5ff99655278e08bf MobileContextMenuCopyLinkText
+0x329e7c190c46883a MobileContextMenuDownloadImage
+0x24fcd970b3be9539 MobileContextMenuDownloadLink
+0x19f18ed23304f451 MobileContextMenuDownloadVideo
0x6607f1a62c7086b7 MobileContextMenuImage
0x5ac057123a1aa102 MobileContextMenuLink
0xaea6ec9898f5dca7 MobileContextMenuOpenImageInNewTab
@@ -1134,6 +1193,7 @@
0xf123d71a7f1df18a MobileContextMenuSearchByImage
0x470fb8fde85c094b MobileContextMenuShareLink
0xb177bc454b6a2105 MobileContextMenuText
+0x68b96cc2620ba615 MobileContextMenuVideo
0xac284cf4338f8a66 MobileContextMenuViewImage
0x145c1748b4db8fb5 MobileFocusedFakeboxOnNtp
0x92ade4e05606fe2d MobileFocusedOmniboxNotOnNtp
@@ -1264,6 +1324,8 @@
0xa86d43452b05bf72 Notifications.ShowMessageCenter
0x9a102e132f96503f Notifications.ShowSettings
0xd2175e238a75b56a Notifications.Unmute
+0xbace688dcdf180fe Omnibox.ServerSuggestDelete.Failure
+0x65a8c968a93ddb00 Omnibox.ServerSuggestDelete.Success
0xa72c673a1f44d11f OmniboxDestinationURLIsSearchOnDSP
0x6048dbd4f2f2ca50 OmniboxDestinationURLMatchesDefaultSearchProvider
0x268376698078c71b OmniboxInputInProgress
@@ -1629,6 +1691,7 @@
0x2fbe99005588ef01 StartupTick
0x11a755d598c0c417 Stop
0x926a51baad949d12 Strikethrough
+0x055a197053882ccd SuspiciousExtensionBubbleDismissed
0x197787e3bdf50f03 SyncedNotifications.SendingServiceDisabled
0xb789d5822bf411fe SyncedNotifications.SendingServiceEnabled
0xc3100ff8c8a00184 SystemBack
@@ -1659,6 +1722,8 @@
0x552a7bf28446f0aa Tab_DropURLBetweenTabs
0xc508d4d1583f51d6 Tab_DropURLOnTab
0xdad4668bc0b8e79c TaskManager
+0xbe0a9ab559ef544d Terminate_ProcessMismatch_CreateNewWidget
+0xa908a8af2378a060 Terminate_ProcessMismatch_CreateNewWindow
0x23afdebb497957f4 Themes.Gone
0x07c710bf40f92990 Themes.Loaded
0x77ae5b6a7529d8b5 Themes.Migrated
@@ -1708,6 +1773,7 @@
0x91586aefc4a53dc7 VirtualKeyboardLoaded
0x6932875212d36f57 WP_EditImage
0x34a770eb3bbf5632 WP_Gallery
+0xe56fc587145f9d39 WebView.CaptureVisibleRegion
0x462194a5096cab5e WebView.ClearData
0x1058b3af68275a1d WebView.ExecuteScript
0xbe313bfe6325dad0 WebView.Go
diff --git a/tools/metrics/actions/extract_actions.py b/tools/metrics/actions/extract_actions.py
index 05f8096500..91f3254bf0 100755
--- a/tools/metrics/actions/extract_actions.py
+++ b/tools/metrics/actions/extract_actions.py
@@ -47,7 +47,14 @@ KNOWN_COMPUTED_USERS = (
'about_flags.cc', # do not generate a warning; see AddAboutFlagsActions()
'external_metrics.cc', # see AddChromeOSActions()
'core_options_handler.cc', # see AddWebUIActions()
- 'browser_render_process_host.cc' # see AddRendererActions()
+ 'browser_render_process_host.cc', # see AddRendererActions()
+ 'render_thread_impl.cc', # impl of RenderThread::RecordComputedAction()
+ 'render_process_host_impl.cc', # browser side impl for
+ # RenderThread::RecordComputedAction()
+ 'mock_render_thread.cc', # mock of RenderThread::RecordComputedAction()
+ 'ppb_pdf_impl.cc', # see AddClosedSourceActions()
+ 'pepper_pdf_host.cc', # see AddClosedSourceActions()
+ 'key_systems_support_uma.cc', # See AddKeySystemSupportActions()
)
# Language codes used in Chrome. The list should be updated when a new
@@ -217,6 +224,9 @@ def AddAndroidActions(actions):
actions.add('MobileContextMenuCopyImageLinkAddress')
actions.add('MobileContextMenuCopyLinkAddress')
actions.add('MobileContextMenuCopyLinkText')
+ actions.add('MobileContextMenuDownloadImage')
+ actions.add('MobileContextMenuDownloadLink')
+ actions.add('MobileContextMenuDownloadVideo')
actions.add('MobileContextMenuImage')
actions.add('MobileContextMenuLink')
actions.add('MobileContextMenuOpenImageInNewTab')
@@ -227,6 +237,7 @@ def AddAndroidActions(actions):
actions.add('MobileContextMenuSearchByImage')
actions.add('MobileContextMenuShareLink')
actions.add('MobileContextMenuText')
+ actions.add('MobileContextMenuVideo')
actions.add('MobileContextMenuViewImage')
actions.add('MobileFocusedFakeboxOnNtp')
actions.add('MobileFocusedOmniboxNotOnNtp')
@@ -486,28 +497,6 @@ def WalkDirectory(root_path, actions, extensions, callback):
if ext in extensions:
callback(os.path.join(path, file), actions)
-def GrepForRendererActions(path, actions):
- """Grep a source file for calls to RenderThread::RecordUserMetrics.
-
- Arguments:
- path: path to the file
- actions: set of actions to add to
- """
- # We look for the ViewHostMsg_UserMetricsRecordAction constructor.
- # This should be on one line.
- action_re = re.compile(
- r'[^a-zA-Z]RenderThread::Get\(\)->RecordUserMetrics\("([^"]*)')
- action_re2 = re.compile(
- r'[^a-zA-Z]RenderThreadImpl::current\(\)->RecordUserMetrics\("([^"]*)')
- for line in open(path):
- match = action_re.search(line)
- if match: # Call to RecordUserMetrics through Content API
- actions.add(match.group(1))
- continue
- match = action_re2.search(line)
- if match: # Call to RecordUserMetrics inside Content
- actions.add(match.group(1))
-
def AddLiteralActions(actions):
"""Add literal actions specified via calls to UserMetrics functions.
@@ -517,6 +506,8 @@ def AddLiteralActions(actions):
EXTENSIONS = ('.cc', '.mm', '.c', '.m')
# Walk the source tree to process all .cc files.
+ ash_root = os.path.normpath(os.path.join(REPOSITORY_ROOT, 'ash'))
+ WalkDirectory(ash_root, actions, EXTENSIONS, GrepForActions)
chrome_root = os.path.normpath(os.path.join(REPOSITORY_ROOT, 'chrome'))
WalkDirectory(chrome_root, actions, EXTENSIONS, GrepForActions)
content_root = os.path.normpath(os.path.join(REPOSITORY_ROOT, 'content'))
@@ -537,21 +528,6 @@ def AddWebUIActions(actions):
'resources')
WalkDirectory(resources_root, actions, ('.html'), GrepForWebUIActions)
-def AddRendererActions(actions):
- """Add user actions sent via calls to RenderThread::RecordUserMetrics.
-
- Arguments:
- actions: set of actions to add to.
- """
- EXTENSIONS = ('.cc', '.mm', '.c', '.m')
-
- chrome_renderer_root = os.path.join(REPOSITORY_ROOT, 'chrome', 'renderer')
- content_renderer_root = os.path.join(REPOSITORY_ROOT, 'content', 'renderer')
- WalkDirectory(chrome_renderer_root, actions, EXTENSIONS,
- GrepForRendererActions)
- WalkDirectory(content_renderer_root, actions, EXTENSIONS,
- GrepForRendererActions)
-
def AddHistoryPageActions(actions):
"""Add actions that are used in History page.
@@ -574,6 +550,17 @@ def AddHistoryPageActions(actions):
actions.add('HistoryPage_ConfirmRemoveSelected')
actions.add('HistoryPage_CancelRemoveSelected')
+def AddKeySystemSupportActions(actions):
+ """Add actions that are used for key system support metrics.
+
+ Arguments
+ actions: set of actions to add to.
+ """
+ actions.add('KeySystemSupport.Widevine.Queried')
+ actions.add('KeySystemSupport.WidevineWithType.Queried')
+ actions.add('KeySystemSupport.Widevine.Supported')
+ actions.add('KeySystemSupport.WidevineWithType.Supported')
+
def main(argv):
if '--hash' in argv:
hash_output = True
@@ -601,7 +588,6 @@ def main(argv):
# AddWebKitEditorActions(actions)
AddAboutFlagsActions(actions)
AddWebUIActions(actions)
- AddRendererActions(actions)
AddLiteralActions(actions)
@@ -614,6 +600,7 @@ def main(argv):
AddClosedSourceActions(actions)
AddExtensionActions(actions)
AddHistoryPageActions(actions)
+ AddKeySystemSupportActions(actions)
if hash_output:
f = open(chromeactions_path, "wb")
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 316635bae8..3fc01a636c 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -343,6 +343,13 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="Ash.WindowSelector.CycleTime" units="milliseconds">
+ <summary>
+ The amount of time the Alt key is held after pressing Alt+Tab to begin
+ cycling through windows.
+ </summary>
+</histogram>
+
<histogram name="Ash.WindowSelector.Items">
<summary>
The number of items (single windows or groups of windows such as panels) in
@@ -3271,16 +3278,33 @@ other types of suffix sets.
</histogram>
<histogram name="Drive.CacheDBOpenStatus" enum="DriveCacheDBOpenStatus">
+ <obsolete>
+ Deperecated 8/2013.
+ </obsolete>
<summary>Status of drive cache metadata database open.</summary>
</histogram>
+<histogram name="Drive.DirectoryFeedLoadTime" units="milliseconds">
+ <summary>
+ Time spent to load the list of files in a single directory from Google Drive
+ server.
+ </summary>
+</histogram>
+
<histogram name="Drive.EntireFeedLoadTime" units="microseconds">
+ <obsolete>
+ Deprecated 12/2013 due to the UMA stat bucket layout change. We'll use
+ Drive.FullFeedLoadTime instead.
+ </obsolete>
<summary>
Time spent to load the entire file system information from the server
</summary>
</histogram>
<histogram name="Drive.EntryKind" enum="DriveEntryKind">
+ <obsolete>
+ Deprecated 10/2012.
+ </obsolete>
<summary>
Provides breakdown of specific formats for hosted documents. Recorded when
feed is loaded from the server.
@@ -3288,13 +3312,27 @@ other types of suffix sets.
</histogram>
<histogram name="Drive.FileFormat" enum="DriveFileFormat">
+ <obsolete>
+ Deprecated 10/2012.
+ </obsolete>
<summary>
Provides breakdown of specific file formats for regular files. Recorded when
feed is loaded from the server.
</summary>
</histogram>
+<histogram name="Drive.FullFeedLoadTime" units="milliseconds">
+ <summary>
+ Time spent to load the entire file system information from the server
+ </summary>
+</histogram>
+
<histogram name="Drive.InitialFeedLoadTime" units="microseconds">
+ <obsolete>
+ Deperecated 12/2013 since it did not record meaningful information.
+ Drive.DirectoryFeedLoadTime should be checked for measuring the time until
+ the user sees the first response of file lists.
+ </obsolete>
<summary>
Time spent to load the initial part of the file system information from the
server
@@ -4247,7 +4285,7 @@ other types of suffix sets.
<histogram name="Extensions.AppTabLaunchType" enum="ExtensionLaunchType">
<summary>
- The number of apps launched grouped by ExtensionPrefs::LaunchType.
+ The number of apps launched grouped by extensions::LaunchType.
</summary>
</histogram>
@@ -5324,6 +5362,28 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="GPU.CollectContextGraphicsInfo" units="microseconds">
+ <summary>
+ The time that the GPU process spends collecting driver information during
+ startup.
+ </summary>
+</histogram>
+
+<histogram name="GPU.CreateBrowserCompositor" units="microseconds">
+ <summary>
+ The time that the browser process takes to create the compositor from its
+ point of view. One of these is created for each top-level window (browser
+ frame, menus, etc.).
+ </summary>
+</histogram>
+
+<histogram name="GPU.InitializeOneOffTime" units="microseconds">
+ <summary>
+ The time that the GPU process spends in initializing the GL surface, and
+ collecting graphics information.
+ </summary>
+</histogram>
+
<histogram name="History.DeleteFTSIndexDatabases">
<summary>
Count of &quot;History Index *&quot; databases deleted. These databases
@@ -5364,6 +5424,31 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="History.TopSitesRecoveredPercentage" units="%">
+ <summary>
+ Size of the recovered TopSites database relative to the original corrupt
+ database. Recovery is VACUUM-like, so the resulting database should always
+ be smaller. Substantial 100% results would indicate empty databases being
+ recovered, substantial low% results would indicate very little data being
+ recovered.
+ </summary>
+</histogram>
+
+<histogram name="History.TopSitesRecoveredRowsThumbnails">
+ <summary>
+ Rows recovered from [thumbnails] table in TopSites recovery.
+ </summary>
+</histogram>
+
+<histogram name="History.TopSitesRecovery" enum="HistoryTopSitesRecoveryEnum">
+ <summary>
+ The TopSites recovery code is written conservatively, with successful
+ recovery committed and any failure leading to rollback. This tracks the
+ outcomes to determine which cases are high-frequency enough to warrant
+ adding additional code to handle them (versus simply deleting the data).
+ </summary>
+</histogram>
+
<histogram name="History.TopSitesVisitsByRank" units="rank">
<summary>
Page visits to each of a user's top 50 sites. Visits to all other sites go
@@ -5484,6 +5569,38 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="InertialSensor.AccelerometerAndroidAvailable"
+ enum="BooleanAvailable">
+ <summary>
+ Whether the Sensor.TYPE_LINEAR_ACCELERATION was available at the start of
+ Device Motion.
+ </summary>
+</histogram>
+
+<histogram name="InertialSensor.AccelerometerIncGravityAndroidAvailable"
+ enum="BooleanAvailable">
+ <summary>
+ Whether the Sensor.TYPE_ACCELEROMETER was available at the start of Device
+ Motion.
+ </summary>
+</histogram>
+
+<histogram name="InertialSensor.GyroscopeAndroidAvailable"
+ enum="BooleanAvailable">
+ <summary>
+ Whether the Sensor.TYPE_GYROSCOPE was available at the start of Device
+ Motion.
+ </summary>
+</histogram>
+
+<histogram name="InertialSensor.RotationVectorAndroidAvailable"
+ enum="BooleanAvailable">
+ <summary>
+ Whether the Sensor.TYPE_ROTATION_VECTOR was available at the start of Device
+ Orientation.
+ </summary>
+</histogram>
+
<histogram name="Installer.AttemptsCount.Total" units="count">
<summary>
The number of update attempts until the update has been applied. This is
@@ -7169,6 +7286,16 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="MultiProfile.DiscardedTabsPerUser">
+ <summary>
+ The relation of discarded tabs vs. the amount of simultaneous users. The
+ counts are the number of discards and the buckets are the number of users.
+ Since the count values are absolute numbers, they need to be normalized
+ before use - so divide the counts by the percentage of users found under
+ 'MultiProfile.UsersPerSession'.
+ </summary>
+</histogram>
+
<histogram name="MultiProfile.SessionMode" enum="MultiProfileSessionMode">
<summary>
The session counter for different multi profile modes which gets stored once
@@ -7202,7 +7329,14 @@ other types of suffix sets.
</summary>
</histogram>
-<histogram name="Multiprofile.UserCount">
+<histogram name="MultiProfile.TeleportWindowType"
+ enum="MultiProfileTeleportWindowType">
+ <summary>
+ Counts the number of teleported windows by types in separated desktop mode.
+ </summary>
+</histogram>
+
+<histogram name="MultiProfile.UsersPerSession">
<summary>
The number of users simultaneously signed into a multiprofile session on
ChromeOS. This is recorded whenever a new user logs in.
@@ -7621,6 +7755,46 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="Net.DailyContentLength_DataReductionProxyEnabled_Https"
+ units="KB">
+ <summary>
+ The total content size in KB of all HTTPS response bodies in the previous
+ calendar day while the data reduction proxy setting was enabled. The metric
+ is reported when the first response in the current day is received.
+ </summary>
+</histogram>
+
+<histogram name="Net.DailyContentLength_DataReductionProxyEnabled_LongBypass"
+ units="KB">
+ <summary>
+ The total content size in KB of all long-bypassed HTTP response bodies in
+ the previous calendar day while the data reduction proxy setting was
+ enabled. The metric is reported when the first response in the current day
+ is received.
+ </summary>
+</histogram>
+
+<histogram name="Net.DailyContentLength_DataReductionProxyEnabled_ShortBypass"
+ units="KB">
+ <summary>
+ The total content size in KB of all short-bypassed HTTP response bodies in
+ the previous calendar day while the data reduction proxy setting was
+ enabled. The metric is reported when the first response in the current day
+ is received.
+ </summary>
+</histogram>
+
+<histogram name="Net.DailyContentLength_DataReductionProxyEnabled_Unknown"
+ units="KB">
+ <summary>
+ The total content size in KB of all HTTP response bodies for requests that
+ were not served by the enabled data reduction proxy for unknown reasons in
+ the previous calendar day while the data reduction proxy setting was
+ enabled. The metric is reported when the first response in the current day
+ is received.
+ </summary>
+</histogram>
+
<histogram name="Net.DailyContentLength_ViaDataReductionProxy" units="KB">
<summary>
The total content size in KB of all HTTP/HTTPS response bodies in the
@@ -7639,6 +7813,47 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="Net.DailyContentPercent_DataReductionProxyEnabled_Https"
+ units="Percent">
+ <summary>
+ The percentage of total HTTPS response body size while the data reduction
+ proxy is enabled to total HTTP/HTTPS response body size in the previous
+ calendar day. The metric is reported when the first response in the current
+ day is received.
+ </summary>
+</histogram>
+
+<histogram name="Net.DailyContentPercent_DataReductionProxyEnabled_LongBypass"
+ units="Percent">
+ <summary>
+ The percentage of total long-bypassed response body size while the data
+ reduction proxy is enabled to total HTTP/HTTPS response body size in the
+ previous calendar day. The metric is reported when the first response in the
+ current day is received.
+ </summary>
+</histogram>
+
+<histogram name="Net.DailyContentPercent_DataReductionProxyEnabled_ShortBypass"
+ units="Percent">
+ <summary>
+ The percentage of total short-bypassed response body size while the data
+ reduction proxy is enabled to total HTTP/HTTPS response body size in the
+ previous calendar day. The metric is reported when the first response in the
+ current day is received.
+ </summary>
+</histogram>
+
+<histogram name="Net.DailyContentPercent_DataReductionProxyEnabled_Unknown"
+ units="Percent">
+ <summary>
+ The percentage of total body size of responses that were not served by the
+ data reduction proxy for unknown reason while the data reduction proxy is
+ enabled to total HTTP/HTTPS response body size in the previous calendar day.
+ The metric is reported when the first response in the current day is
+ received.
+ </summary>
+</histogram>
+
<histogram name="Net.DailyContentPercent_ViaDataReductionProxy" units="Percent">
<summary>
The percentage of total HTTP/HTTPS response body size via the data reduction
@@ -10239,6 +10454,21 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="NetConnectivity5">
+ <summary>
+ In this experiment, a few packets were sent from Google to clients via UDP
+ on port 443 or 80 to perform net connectivity test.
+ </summary>
+</histogram>
+
+<histogram name="NetConnectivity5.TestFailed.WritePending"
+ enum="BooleanSuccess">
+ <summary>
+ Next NetConnectivity5 experiment weren't started because there is an
+ outstading pending write.
+ </summary>
+</histogram>
+
<histogram name="Network.3G.Gobi.Activation" units="milliseconds">
<summary>The time the Gobi modem takes to complete activation.</summary>
</histogram>
@@ -10337,6 +10567,17 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="Network.MigrationNssToPem"
+ enum="MigrationNssToPemNetworkTypes">
+ <summary>
+ Chrome OS metric counting the number of network configurations that
+ contained a NSS nickname identifying a CA certificate, which triggered the
+ migration to PEM encoding. This metric doesn't consider whether the
+ migration was successful but once a migration was successful the nickname is
+ removed.
+ </summary>
+</histogram>
+
<histogram name="Network.ServiceErrors" enum="NetworkServiceError">
<summary>Chrome OS connection manager service errors seen.</summary>
</histogram>
@@ -11267,6 +11508,16 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="NewTabPage.NumberOfExternalTiles">
+ <summary>
+ The number of external tiles that are displayed on the NTP. External tiles
+ are those for which the visuals are handled by the page itself, not by the
+ iframe. Recorded before changing focus away from the NTP, be it by
+ navigating to a URL, switching tabs, changing the active window or closing
+ the tab/shutting down Chrome.
+ </summary>
+</histogram>
+
<histogram name="NewTabPage.NumberOfMouseOvers">
<summary>
The total number of times the user hovered the mouse over Most Visited tile
@@ -11342,7 +11593,9 @@ other types of suffix sets.
<histogram name="NewTabPage.SuggestionsType" enum="NtpSuggestionsType">
<summary>
Indicate, for each impression of the New Tab Page, whether the suggestions
- were obtained from the client or server.
+ were obtained from the client or server. Recorded before changing focus away
+ from the NTP, be it by navigating to a URL, switching tabs, changing the
+ active window or closing the tab/shutting down Chrome.
</summary>
</histogram>
@@ -11436,6 +11689,18 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="OAuth2Login.ListAccountsFailure" enum="GoogleServiceAuthError">
+ <summary>
+ Failure reason of final ListAccounts call failure during ChromeOS login.
+ </summary>
+</histogram>
+
+<histogram name="OAuth2Login.ListAccountsRetry" enum="GoogleServiceAuthError">
+ <summary>
+ Retry reason of failed ListAccounts call during ChromeOS login.
+ </summary>
+</histogram>
+
<histogram name="OAuth2Login.MergeSessionFailure" enum="GoogleServiceAuthError">
<summary>
Failure reason of final MergeSession call during ChromeOS login.
@@ -11480,6 +11745,15 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="OAuth2Login.PostMergeVerification"
+ enum="PostMergeVerificationOutcome">
+ <summary>
+ Outcome of ChromeOS GAIA cookie post merge session verification process. It
+ measures how often /MergeSession request collided with browser session
+ restore process resulting in partially authenticated primary GAIA session.
+ </summary>
+</histogram>
+
<histogram name="OAuth2Login.SessionRestore" enum="GaiaSessionRestoreOutcome">
<summary>Outcome of ChromeOS GAIA cookie session restore process.</summary>
</histogram>
@@ -13462,6 +13736,22 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="PLT.UserTiming_Mark" units="milliseconds">
+ <summary>
+ This time is based on the User Timing spec and measures the time from
+ Navigation Timing navigationStart until the point where the page called
+ performance.mark().
+ </summary>
+</histogram>
+
+<histogram name="PLT.UserTiming_MeasureDuration" units="milliseconds">
+ <summary>
+ This time is based on the User Timing spec and reports the time between two
+ arbitrary points defined by the page being loaded and directly matches the
+ measurement exposed by performance.measure().
+ </summary>
+</histogram>
+
<histogram name="Plugin.PpapiBrokerLoadResult" enum="PluginLoadResult">
<summary>The result from an attempt to load a PPAPI broker.</summary>
</histogram>
@@ -13827,6 +14117,28 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="Precache.DownloadedNonPrecache" units="bytes">
+ <summary>
+ The number of bytes that were downloaded over the network for HTTP/HTTPS
+ fetches that were not motivated by precaching. Logged per-request.
+ </summary>
+</histogram>
+
+<histogram name="Precache.DownloadedPrecacheMotivated" units="bytes">
+ <summary>
+ The number of bytes that were downloaded because of precaching. Logged
+ per-request.
+ </summary>
+</histogram>
+
+<histogram name="Precache.Saved" units="bytes">
+ <summary>
+ The number of bytes during user browsing that were served from the cache,
+ but would have been downloaded over a network if precaching was disabled.
+ Logged per-request.
+ </summary>
+</histogram>
+
<histogram name="Prerender.Event" enum="PrerenderEvent">
<summary>
Enumeration of what events related to prerendering have occurred.
@@ -14485,6 +14797,14 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="Profile.ProfileError" enum="ProfileErrorType">
+ <summary>
+ The error with the current user profile that caused an error dialog to be
+ shown. This dialog is shown usually when there is some sort of corruption in
+ the user's profile data.
+ </summary>
+</histogram>
+
<histogram name="Profile.SupervisedProfileCreateError"
enum="GoogleServiceAuthError">
<summary>
@@ -19849,6 +20169,16 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="UMA.InitSequence" enum="UmaInitSequence">
+ <summary>
+ Logged during MetricsService initialization whether the init task or the
+ initial log timer completed first. The expectation is the vast majority of
+ the time, the init task should complete first. If metrics show otherwise,
+ then it may indicate there's a bug in the MetricsService init sequence and
+ that it should be investigated.
+ </summary>
+</histogram>
+
<histogram name="UMA.Large Accumulated Log Not Persisted" units="bytes">
<summary>
Number of bytes in an excessively large log that was discarded at shutdown
@@ -20126,7 +20456,7 @@ other types of suffix sets.
</histogram>
<histogram name="Variations.HeaderConstructionTime" units="microseconds">
- <summary>How long it took to create the X-Chrome-Variations header.</summary>
+ <summary>How long it took to create the X-Client-Data header.</summary>
</histogram>
<histogram name="Variations.NetworkAvailability" enum="BooleanSuccess">
@@ -21095,10 +21425,10 @@ other types of suffix sets.
</enum>
<enum name="AppLaunchContainer" type="int">
- <int value="0" label="LAUNCH_WINDOW"/>
- <int value="1" label="LAUNCH_PANEL"/>
- <int value="2" label="LAUNCH_TAB"/>
- <int value="3" label="LAUNCH_NONE (v2 packaged apps)"/>
+ <int value="0" label="LAUNCH_CONTAINER_WINDOW"/>
+ <int value="1" label="LAUNCH_CONTAINER_PANEL"/>
+ <int value="2" label="LAUNCH_CONTAINER_TAB"/>
+ <int value="3" label="LAUNCH_CONTAINER_NONE (v2 packaged apps)"/>
</enum>
<enum name="AppLauncherPromo" type="int">
@@ -21510,6 +21840,11 @@ other types of suffix sets.
<int value="1" label="Attempted"/>
</enum>
+<enum name="BooleanAvailable" type="int">
+ <int value="0" label="Not Available"/>
+ <int value="1" label="Available"/>
+</enum>
+
<enum name="BooleanCloseTimeout" type="int">
<int value="0" label="Closed normally"/>
<int value="1" label="Timed out"/>
@@ -22898,6 +23233,8 @@ other types of suffix sets.
limit should only start running after initial user activity in a
session"/>
<int value="248" label="Report device users"/>
+ <int value="249" label="User avatar image"/>
+ <int value="250" label="Enable network configuration prompt when offline"/>
</enum>
<enum name="EnterprisePolicyInvalidations" type="int">
@@ -23163,7 +23500,7 @@ other types of suffix sets.
<int value="34" label="AUTOTESTPRIVATE_RESTART"/>
<int value="35" label="USB_CLAIMINTERFACE"/>
<int value="36" label="MEDIAPLAYERPRIVATE_SETWINDOWHEIGHT"/>
- <int value="37" label="PROCESSES_GETPROCESSINFO"/>
+ <int value="37" label="DELETED_EXPERIMENTAL_PROCESSES_GETPROCESSINFO"/>
<int value="38" label="HISTORY_GETVISITS"/>
<int value="39" label="SOCKET_BIND"/>
<int value="40" label="TABS_MOVE"/>
@@ -23179,7 +23516,7 @@ other types of suffix sets.
<int value="50" label="APP_CURRENTWINDOWINTERNAL_SETBOUNDS"/>
<int value="51" label="CLOUDPRINTPRIVATE_SETUPCONNECTOR"/>
<int value="52" label="SERIAL_SETCONTROLSIGNALS"/>
- <int value="53" label="FILEBROWSERPRIVATE_SETLASTMODIFIED"/>
+ <int value="53" label="DELETED_FILEBROWSERPRIVATE_SETLASTMODIFIED"/>
<int value="54" label="IDLE_SETDETECTIONINTERVAL"/>
<int value="55" label="FILEBROWSERPRIVATE_GETFILETASKS"/>
<int value="56" label="WEBSTOREPRIVATE_GETSTORELOGIN"/>
@@ -23189,7 +23526,7 @@ other types of suffix sets.
<int value="60" label="APP_CURRENTWINDOWINTERNAL_SHOW"/>
<int value="61" label="WEBSTOREPRIVATE_GETBROWSERLOGIN"/>
<int value="62" label="EXPERIMENTAL_IDENTITY_GETAUTHTOKEN"/>
- <int value="63" label="SYSTEMINFO_DISPLAY_GETDISPLAYINFO"/>
+ <int value="63" label="DELETED_SYSTEMINFO_DISPLAY_GETDISPLAYINFO"/>
<int value="64" label="BROWSINGDATA_REMOVEPLUGINDATA"/>
<int value="65" label="SOCKET_LISTEN"/>
<int value="66" label="MEDIAGALLERIES_GETMEDIAFILESYSTEMS"/>
@@ -23199,7 +23536,7 @@ other types of suffix sets.
<int value="70" label="INPUTMETHODPRIVATE_GET"/>
<int value="71" label="USB_CLOSEDEVICE"/>
<int value="72" label="TTS_STOP"/>
- <int value="73" label="SERIAL_GETPORTS"/>
+ <int value="73" label="DELETED_SERIAL_GETPORTS"/>
<int value="74" label="DELETED_FILEBROWSERPRIVATE_CLEARDRIVECACHE"/>
<int value="75" label="SERIAL_GETCONTROLSIGNALS"/>
<int value="76" label="DEVELOPERPRIVATE_ENABLE"/>
@@ -23220,7 +23557,7 @@ other types of suffix sets.
<int value="91" label="APP_CURRENTWINDOWINTERNAL_MINIMIZE"/>
<int value="92" label="DEVELOPERPRIVATE_AUTOUPDATE"/>
<int value="93" label="DNS_RESOLVE"/>
- <int value="94" label="EXPERIMENTAL_SYSTEMINFO_MEMORY_GET"/>
+ <int value="94" label="DELETED_EXPERIMENTAL_SYSTEMINFO_MEMORY_GET"/>
<int value="95" label="HISTORY_ADDURL"/>
<int value="96" label="TABS_GET"/>
<int value="97" label="BROWSERACTION_SETBADGETEXT"/>
@@ -23228,11 +23565,11 @@ other types of suffix sets.
<int value="99" label="WINDOWS_CREATE"/>
<int value="100" label="DEVELOPERPRIVATE_LOADUNPACKED"/>
<int value="101" label="DELETED_DOWNLOADS_SETDESTINATION"/>
- <int value="102" label="PROCESSES_GETPROCESSIDFORTAB"/>
+ <int value="102" label="DELETED_EXPERIMENTAL_PROCESSES_GETPROCESSIDFORTAB"/>
<int value="103" label="BOOKMARKS_GETCHILDREN"/>
<int value="104" label="BROWSERACTION_GETTITLE"/>
<int value="105" label="TERMINALPRIVATE_OPENTERMINALPROCESS"/>
- <int value="106" label="SERIAL_CLOSE"/>
+ <int value="106" label="DELETED_SERIAL_CLOSE"/>
<int value="107" label="CONTEXTMENUS_REMOVE"/>
<int value="108" label="FILEBROWSERPRIVATE_REQUESTFILESYSTEM"/>
<int value="109" label="ECHOPRIVATE_GETREGISTRATIONCODE"/>
@@ -23244,7 +23581,7 @@ other types of suffix sets.
<int value="115" label="STORAGE_SET"/>
<int value="116" label="FONTSETTINGS_GETDEFAULTFONTSIZE"/>
<int value="117" label="EXTENSION_SETUPDATEURLDATA"/>
- <int value="118" label="SERIAL_WRITE"/>
+ <int value="118" label="DELETED_SERIAL_WRITE"/>
<int value="119" label="IDLE_QUERYSTATE"/>
<int value="120" label="DELETED_EXPERIMENTAL_RLZ_GETACCESSPOINTRLZ"/>
<int value="121" label="WEBSTOREPRIVATE_SETSTORELOGIN"/>
@@ -23315,9 +23652,9 @@ other types of suffix sets.
<int value="185" label="DOWNLOADS_GETFILEICON"/>
<int value="186" label="PAGEACTION_GETTITLE"/>
<int value="187" label="BROWSINGDATA_REMOVE"/>
- <int value="188" label="SERIAL_OPEN"/>
+ <int value="188" label="DELETED_SERIAL_OPEN"/>
<int value="189" label="FILESYSTEM_GETDISPLAYPATH"/>
- <int value="190" label="FILEBROWSERPRIVATE_FORMATDEVICE"/>
+ <int value="190" label="FILEBROWSERPRIVATE_FORMATVOLUME"/>
<int value="191" label="BOOKMARKS_GET"/>
<int value="192" label="DELETED_MANAGEDMODEPRIVATE_GET"/>
<int value="193" label="ALARMS_CLEAR"/>
@@ -23331,7 +23668,7 @@ other types of suffix sets.
<int value="201" label="CONTENTSETTINGS_GETRESOURCEIDENTIFIERS"/>
<int value="202" label="SOCKET_CREATE"/>
<int value="203" label="DEVELOPERPRIVATE_RELOAD"/>
- <int value="204" label="FILEBROWSERPRIVATE_GETMOUNTPOINTS"/>
+ <int value="204" label="FILEBROWSERPRIVATE_GETVOLUMEMETADATALIST"/>
<int value="205" label="APP_RUNTIME_POSTINTENTRESPONSE"/>
<int value="206" label="DELETED_MANAGEDMODEPRIVATE_SETPOLICY"/>
<int value="207" label="WEBSTOREPRIVATE_BEGININSTALLWITHMANIFEST3"/>
@@ -23339,7 +23676,7 @@ other types of suffix sets.
<int value="209" label="USB_CONTROLTRANSFER"/>
<int value="210" label="DELETED_EXPERIMENTAL_SPEECHINPUT_STOP"/>
<int value="211" label="USB_BULKTRANSFER"/>
- <int value="212" label="FILEBROWSERPRIVATE_GETVOLUMEMETADATA"/>
+ <int value="212" label="DELETED_FILEBROWSERPRIVATE_GETVOLUMEMETADATA"/>
<int value="213" label="PAGECAPTURE_SAVEASMHTML"/>
<int value="214" label="EXTENSION_ISALLOWEDINCOGNITOACCESS"/>
<int value="215" label="BROWSINGDATA_REMOVEAPPCACHE"/>
@@ -23422,7 +23759,7 @@ other types of suffix sets.
<int value="292" label="EXPERIMENTAL_APP_NOTIFY"/>
<int value="293" label="METRICSPRIVATE_RECORDLONGTIME"/>
<int value="294" label="SOCKET_READ"/>
- <int value="295" label="PROCESSES_TERMINATE"/>
+ <int value="295" label="DELETED_EXPERIMENTAL_PROCESSES_TERMINATE"/>
<int value="296" label="METRICSPRIVATE_RECORDTIME"/>
<int value="297" label="BOOKMARKMANAGERPRIVATE_GETSTRINGS"/>
<int value="298" label="USB_ISOCHRONOUSTRANSFER"/>
@@ -23464,7 +23801,7 @@ other types of suffix sets.
<int value="334" label="TABS_DETECTLANGUAGE"/>
<int value="335" label="METRICSPRIVATE_RECORDVALUE"/>
<int value="336" label="BOOKMARKMANAGERPRIVATE_SORTCHILDREN"/>
- <int value="337" label="SERIAL_READ"/>
+ <int value="337" label="DELETED_SERIAL_READ"/>
<int value="338" label="APP_CURRENTWINDOWINTERNAL_MAXIMIZE"/>
<int value="339" label="EXPERIMENTAL_DISCOVERY_CLEARALLSUGGESTIONS"/>
<int value="340" label="DELETED_MANAGEDMODEPRIVATE_ENTER"/>
@@ -23482,7 +23819,7 @@ other types of suffix sets.
<int value="352" label="TTS_ISSPEAKING"/>
<int value="353" label="BOOKMARKS_REMOVETREE"/>
<int value="354" label="FILEBROWSERPRIVATE_SEARCHDRIVE"/>
- <int value="355" label="EXPERIMENTAL_SYSTEMINFO_CPU_GET"/>
+ <int value="355" label="DELETED_EXPERIMENTAL_SYSTEMINFO_CPU_GET"/>
<int value="356" label="FILEBROWSERPRIVATE_SETPREFERENCES"/>
<int value="357" label="FONTSETTINGS_SETFONT"/>
<int value="358" label="SOCKET_GETNETWORKLIST"/>
@@ -23514,11 +23851,11 @@ other types of suffix sets.
<int value="384" label="WINDOWS_GETALL"/>
<int value="385" label="DELETED_FILEBROWSERPRIVATE_TOGGLEFULLSCREEN"/>
<int value="386" label="APP_CURRENTWINDOWINTERNAL_RESTORE"/>
- <int value="387" label="WEBSOCKETPROXYPRIVATE_GETPASSPORTFORTCP"/>
+ <int value="387" label="DELETED_WEBSOCKETPROXYPRIVATE_GETPASSPORTFORTCP"/>
<int value="388" label="PAGEACTION_HIDE"/>
- <int value="389" label="EXPERIMENTAL_SYSTEMINFO_STORAGE_GET"/>
+ <int value="389" label="DELETED_EXPERIMENTAL_SYSTEMINFO_STORAGE_GET"/>
<int value="390" label="DOWNLOADS_ACCEPTDANGER"/>
- <int value="391" label="WEBSOCKETPROXYPRIVATE_GETURLFORTCP"/>
+ <int value="391" label="DELETED_WEBSOCKETPROXYPRIVATE_GETURLFORTCP"/>
<int value="392" label="FILEBROWSERPRIVATE_GETSIZESTATS"/>
<int value="393" label="DOWNLOADS_RESUME"/>
<int value="394" label="COOKIES_GETALLCOOKIESTORES"/>
@@ -23527,8 +23864,8 @@ other types of suffix sets.
<int value="397" label="WEBVIEW_EXECUTESCRIPT"/>
<int value="398" label="NOTIFICATIONS_UPDATE"/>
<int value="399" label="NOTIFICATIONS_CLEAR"/>
- <int value="400" label="SESSIONRESTORE_GETRECENTLYCLOSED"/>
- <int value="401" label="SESSIONRESTORE_RESTORE"/>
+ <int value="400" label="DELETED_SESSIONRESTORE_GETRECENTLYCLOSED"/>
+ <int value="401" label="DELETED_SESSIONRESTORE_RESTORE"/>
<int value="402" label="MANAGEMENT_UNINSTALLSELF"/>
<int value="403" label="ECHOPRIVATE_GETOOBETIMESTAMP"/>
<int value="404" label="FILEBROWSERPRIVATE_VALIDATEPATHNAMELENGTH"/>
@@ -23541,8 +23878,8 @@ other types of suffix sets.
<int value="411" label="MEDIAGALLERIESPRIVATE_GETALLGALLERYWATCH"/>
<int value="412" label="MEDIAGALLERIESPRIVATE_REMOVEALLGALLERYWATCH"/>
<int value="413" label="FILEBROWSERPRIVATE_SEARCHDRIVEMETADATA"/>
- <int value="414" label="ECHOPRIVATE_CHECKALLOWREDEEMOFFERS"/>
- <int value="415" label="MEDIAGALLERIESPRIVATE_EJECTDEVICE"/>
+ <int value="414" label="DELETED_ECHOPRIVATE_CHECKALLOWREDEEMOFFERS"/>
+ <int value="415" label="DELETED_MEDIAGALLERIESPRIVATE_EJECTDEVICE"/>
<int value="416" label="FILEBROWSERPRIVATE_LOGOUTUSERFORREAUTHENTICATION"/>
<int value="417" label="DEVELOPERPRIVATE_CHOOSEPATH"/>
<int value="418" label="DEVELOPERPRIVATE_PACKDIRECTORY"/>
@@ -23609,15 +23946,16 @@ other types of suffix sets.
<int value="478" label="MUSICMANAGERPRIVATE_GETDEVICEID"/>
<int value="479" label="TTS_PAUSE"/>
<int value="480" label="TTS_RESUME"/>
- <int value="481" label="SYSTEMINFO_CPU_GET"/>
- <int value="482" label="EXPERIMENTAL_SYSTEMINFO_STORAGE_ADDWATCH"/>
- <int value="483" label="EXPERIMENTAL_SYSTEMINFO_STORAGE_REMOVEWATCH"/>
- <int value="484" label="EXPERIMENTAL_SYSTEMINFO_STORAGE_GETALLWATCH"/>
- <int value="485" label="EXPERIMENTAL_SYSTEMINFO_STORAGE_REMOVEALLWATCH"/>
- <int value="486" label="SYSTEMINFO_MEMORY_GET"/>
+ <int value="481" label="DELETED_SYSTEMINFO_CPU_GET"/>
+ <int value="482" label="DELETED_EXPERIMENTAL_SYSTEMINFO_STORAGE_ADDWATCH"/>
+ <int value="483" label="DELETED_EXPERIMENTAL_SYSTEMINFO_STORAGE_REMOVEWATCH"/>
+ <int value="484" label="DELETED_EXPERIMENTAL_SYSTEMINFO_STORAGE_GETALLWATCH"/>
+ <int value="485"
+ label="DELETED_EXPERIMENTAL_SYSTEMINFO_STORAGE_REMOVEALLWATCH"/>
+ <int value="486" label="DELETED_SYSTEMINFO_MEMORY_GET"/>
<int value="487" label="ACTIVITYLOGPRIVATE_GETEXTENSIONACTIVITIES"/>
<int value="488" label="RUNTIME_GETPACKAGEDIRECTORYENTRY"/>
- <int value="489" label="SYSTEMINFO_DISPLAY_SETDISPLAYPROPERTIES"/>
+ <int value="489" label="DELETED_SYSTEMINFO_DISPLAY_SETDISPLAYPROPERTIES"/>
<int value="490" label="FEEDBACKPRIVATE_GETUSEREMAIL"/>
<int value="491" label="FEEDBACKPRIVATE_GETSYSTEMINFORMATION"/>
<int value="492" label="FEEDBACKPRIVATE_SENDFEEDBACK"/>
@@ -23634,24 +23972,152 @@ other types of suffix sets.
<int value="503" label="TYPES_PRIVATE_CHROMEDIRECTSETTING_GET"/>
<int value="504" label="TYPES_PRIVATE_CHROMEDIRECTSETTING_SET"/>
<int value="505" label="TYPES_PRIVATE_CHROMEDIRECTSETTING_CLEAR"/>
- <int value="506" label="SYSTEM_CPU_GETINFO"/>
- <int value="507" label="SYSTEM_DISPLAY_GETINFO"/>
- <int value="508" label="SYSTEM_DISPLAY_SETDISPLAYPROPERTIES"/>
- <int value="509" label="SYSTEM_MEMORY_GETINFO"/>
- <int value="510" label="SYSTEM_STORAGE_GETINFO"/>
- <int value="511" label="SYSTEM_STORAGE_EJECTDEVICE"/>
- <int value="512" label="SYSTEM_STORAGE_ADDAVAILABLECAPACITYWATCH"/>
- <int value="513" label="SYSTEM_STORAGE_REMOVEAVAILABLECAPACITYWATCH"/>
- <int value="514" label="SYSTEM_STORAGE_GETALLAVAILABLECAPACITYWATCHES"/>
- <int value="515" label="SYSTEM_STORAGE_REMOVEALLAVAILABLECAPACITYWATCHES"/>
- <int value="516" label="INFOBARS_SHOW"/>
- <int value="517" label="VIRTUALKEYBOARDPRIVATE_MOVECURSOR"/>
- <int value="518" label="AUTOTESTPRIVATE_SIMULATEASANMEMORYBUG"/>
- <int value="519" label="WALLPAPER_SETWALLPAPER"/>
- <int value="520" label="VIRTUALKEYBOARDPRIVATE_HIDEKEYBOARD"/>
- <int value="521" label="SYSTEM_STORAGE_GETAVAILABLECAPACITY"/>
- <int value="522" label="VIRTUALKEYBOARDPRIVATE_KEYBOARDLOADED"/>
- <int value="523" label="MEDIAGALLERIES_GETALLMEDIAFILESYSTEMMETADATA"/>
+ <int value="506" label="DELETED_EXPERIMENTAL_SYSTEMINFO_STORAGE_EJECTDEVICE"/>
+ <int value="507" label="SYSTEM_CPU_GETINFO"/>
+ <int value="508" label="BOOKMARKMANAGERPRIVATE_REMOVETREES"/>
+ <int value="509" label="SYSTEM_DISPLAY_GETINFO"/>
+ <int value="510" label="SYSTEM_DISPLAY_SETDISPLAYPROPERTIES"/>
+ <int value="511" label="SYSTEM_MEMORY_GETINFO"/>
+ <int value="512" label="FILEBROWSERPRIVATE_GETSHAREURL"/>
+ <int value="513" label="SYSTEM_STORAGE_GETINFO"/>
+ <int value="514" label="SYSTEM_STORAGE_EJECTDEVICE"/>
+ <int value="515" label="DELETED_SYSTEM_STORAGE_ADDAVAILABLECAPACITYWATCH"/>
+ <int value="516" label="DELETED_SYSTEM_STORAGE_REMOVEAVAILABLECAPACITYWATCH"/>
+ <int value="517"
+ label="DELETED_SYSTEM_STORAGE_GETALLAVAILABLECAPACITYWATCHES"/>
+ <int value="518"
+ label="DELETED_SYSTEM_STORAGE_REMOVEALLAVAILABLECAPACITYWATCHES"/>
+ <int value="519" label="DOWNLOADS_REMOVEFILE"/>
+ <int value="520" label="DOWNLOADS_SHOWDEFAULTFOLDER"/>
+ <int value="521" label="INFOBARS_SHOW"/>
+ <int value="522" label="DOWNLOADS_SETSHELFENABLED"/>
+ <int value="523" label="IMAGEWRITER_WRITEFROMURL"/>
+ <int value="524" label="IMAGEWRITER_WRITEFROMFILE"/>
+ <int value="525" label="IMAGEWRITER_CANCELWRITE"/>
+ <int value="526" label="IMAGEWRITER_DESTROYPARTITIONS"/>
+ <int value="527" label="FEEDBACKPRIVATE_GETSTRINGS"/>
+ <int value="528" label="LOGPRIVATE_GETHISTORICAL"/>
+ <int value="529" label="VIRTUALKEYBOARDPRIVATE_MOVECURSOR"/>
+ <int value="530" label="METRICSPRIVATE_GETVARIATIONPARAMS"/>
+ <int value="531" label="WEBVIEW_SETPERMISSION"/>
+ <int value="532" label="DESKTOPCAPTURE_CHOOSEDESKTOPMEDIA"/>
+ <int value="533" label="APP_CURRENTWINDOWINTERNAL_SETSHAPE"/>
+ <int value="534" label="PROCESSES_GETPROCESSINFO"/>
+ <int value="535" label="PROCESSES_GETPROCESSIDFORTAB"/>
+ <int value="536" label="PROCESSES_TERMINATE"/>
+ <int value="537" label="SOCKETS_UDP_CREATE"/>
+ <int value="538" label="SOCKETS_UDP_UPDATE"/>
+ <int value="539" label="SOCKETS_UDP_BIND"/>
+ <int value="540" label="SOCKETS_UDP_SEND"/>
+ <int value="541" label="SOCKETS_UDP_CLOSE"/>
+ <int value="542" label="SOCKETS_UDP_GETINFO"/>
+ <int value="543" label="SOCKETS_UDP_GETSOCKETS"/>
+ <int value="544" label="SOCKETS_UDP_JOINGROUP"/>
+ <int value="545" label="SOCKETS_UDP_LEAVEGROUP"/>
+ <int value="546" label="SOCKETS_UDP_SETMULTICASTTIMETOLIVE"/>
+ <int value="547" label="SOCKETS_UDP_SETMULTICASTLOOPBACKMODE"/>
+ <int value="548" label="SOCKETS_UDP_GETJOINEDGROUPS"/>
+ <int value="549" label="SIGNED_IN_DEVICES_GET"/>
+ <int value="550" label="AUTOTESTPRIVATE_SIMULATEASANMEMORYBUG"/>
+ <int value="551" label="WEBVIEW_CLEARDATA"/>
+ <int value="552" label="SESSIONS_GETRECENTLYCLOSED"/>
+ <int value="553" label="SESSIONS_GETDEVICES"/>
+ <int value="554" label="SESSIONS_RESTORE"/>
+ <int value="555" label="SYNCFILESYSTEM_GETSERVICESTATUS"/>
+ <int value="556" label="ECHOPRIVATE_SETOFFERINFO"/>
+ <int value="557" label="ECHOPRIVATE_GETOFFERINFO"/>
+ <int value="558" label="DEVELOPERPRIVATE_ISPROFILEMANAGED"/>
+ <int value="559" label="FILEBROWSERPRIVATE_INSTALLWEBSTOREITEM"/>
+ <int value="560" label="FILEBROWSERPRIVATE_STARTCOPY"/>
+ <int value="561" label="FILEBROWSERPRIVATE_CANCELCOPY"/>
+ <int value="562" label="NETWORKINGPRIVATE_CREATENETWORK"/>
+ <int value="563" label="BRAILLEDISPLAYPRIVATE_GETDISPLAYSTATE"/>
+ <int value="564" label="BRAILLEDISPLAYPRIVATE_WRITEDOTS"/>
+ <int value="565" label="USB_GETDEVICES"/>
+ <int value="566" label="USB_REQUESTACCESS"/>
+ <int value="567" label="USB_OPENDEVICE"/>
+ <int value="568" label="ACTIVITYLOGPRIVATE_DELETEDATABASE"/>
+ <int value="569" label="ACTIVITYLOGPRIVATE_DELETEURLS"/>
+ <int value="570" label="FILEBROWSERPRIVATE_REQUESTWEBSTOREACCESSTOKEN"/>
+ <int value="571" label="IMAGEWRITER_LISTREMOVABLESTORAGEDEVICES"/>
+ <int value="572" label="WALLPAPER_SETWALLPAPER"/>
+ <int value="573" label="VIRTUALKEYBOARDPRIVATE_HIDEKEYBOARD"/>
+ <int value="574" label="AUTOTESTPRIVATE_LOCKSCREEN"/>
+ <int value="575" label="WEBRTCLOGGINGPRIVATE_SETMETADATA"/>
+ <int value="576" label="WEBRTCLOGGINGPRIVATE_START"/>
+ <int value="577" label="WEBRTCLOGGINGPRIVATE_SETUPLOADONRENDERCLOSE"/>
+ <int value="578" label="WEBRTCLOGGINGPRIVATE_STOP"/>
+ <int value="579" label="WEBRTCLOGGINGPRIVATE_UPLOAD"/>
+ <int value="580" label="WEBRTCLOGGINGPRIVATE_DISCARD"/>
+ <int value="581" label="WEBVIEW_OVERRIDEUSERAGENT"/>
+ <int value="582" label="PRINCIPALSPRIVATE_SHOWAVATARBUBBLE"/>
+ <int value="583" label="PRINCIPALSPRIVATE_SIGNOUT"/>
+ <int value="584" label="CAST_CHANNEL_OPEN"/>
+ <int value="585" label="CAST_CHANNEL_SEND"/>
+ <int value="586" label="CAST_CHANNEL_CLOSE"/>
+ <int value="587" label="RUNTIME_RESTART"/>
+ <int value="588" label="DESKTOPCAPTURE_CANCELCHOOSEDESKTOPMEDIA"/>
+ <int value="589" label="APP_CURRENTWINDOWINTERNAL_SETALWAYSONTOP"/>
+ <int value="590" label="SOCKETS_TCP_CREATE"/>
+ <int value="591" label="SOCKETS_TCP_UPDATE"/>
+ <int value="592" label="SOCKETS_TCP_SETPAUSED"/>
+ <int value="593" label="SOCKETS_TCP_SETKEEPALIVE"/>
+ <int value="594" label="SOCKETS_TCP_SETNODELAY"/>
+ <int value="595" label="SOCKETS_TCP_CONNECT"/>
+ <int value="596" label="SOCKETS_TCP_DISCONNECT"/>
+ <int value="597" label="SOCKETS_TCP_SEND"/>
+ <int value="598" label="SOCKETS_TCP_CLOSE"/>
+ <int value="599" label="SOCKETS_TCP_GETINFO"/>
+ <int value="600" label="SOCKETS_TCP_GETSOCKETS"/>
+ <int value="601" label="NETWORKINGPRIVATE_GETENABLEDNETWORKTYPES"/>
+ <int value="602" label="NETWORKINGPRIVATE_ENABLENETWORKTYPE"/>
+ <int value="603" label="NETWORKINGPRIVATE_DISABLENETWORKTYPE"/>
+ <int value="604" label="SOCKETS_TCP_SERVER_CREATE"/>
+ <int value="605" label="SOCKETS_TCP_SERVER_UPDATE"/>
+ <int value="606" label="SOCKETS_TCP_SERVER_SETPAUSED"/>
+ <int value="607" label="SOCKETS_TCP_SERVER_LISTEN"/>
+ <int value="608" label="SOCKETS_TCP_SERVER_DISCONNECT"/>
+ <int value="609" label="SOCKETS_TCP_SERVER_CLOSE"/>
+ <int value="610" label="SOCKETS_TCP_SERVER_GETINFO"/>
+ <int value="611" label="SOCKETS_TCP_SERVER_GETSOCKETS"/>
+ <int value="612" label="SYSTEM_STORAGE_GETAVAILABLECAPACITY"/>
+ <int value="613" label="BROWSERACTION_OPEN_POPUP"/>
+ <int value="614" label="WEBRTC_AUDIO_PRIVATE_GET_SINKS"/>
+ <int value="615" label="WEBRTC_AUDIO_PRIVATE_GET_ACTIVE_SINK"/>
+ <int value="616" label="WEBRTC_AUDIO_PRIVATE_SET_ACTIVE_SINK"/>
+ <int value="617" label="WEBRTC_AUDIO_PRIVATE_GET_ASSOCIATED_SINK"/>
+ <int value="618" label="VIRTUALKEYBOARDPRIVATE_KEYBOARDLOADED"/>
+ <int value="619" label="APP_CURRENTWINDOWINTERNAL_SETMINWIDTH"/>
+ <int value="620" label="APP_CURRENTWINDOWINTERNAL_SETMINHEIGHT"/>
+ <int value="621" label="APP_CURRENTWINDOWINTERNAL_SETMAXWIDTH"/>
+ <int value="622" label="APP_CURRENTWINDOWINTERNAL_SETMAXHEIGHT"/>
+ <int value="623" label="SYSTEMPRIVATE_GETAPIKEY"/>
+ <int value="624" label="CHROMEOSINFOPRIVATE_SET"/>
+ <int value="625" label="BOOKMARKMANAGERPRIVATE_GETMETAINFO"/>
+ <int value="626" label="BOOKMARKMANAGERPRIVATE_SETMETAINFO"/>
+ <int value="627" label="FILESYSTEMPROVIDER_MOUNT"/>
+ <int value="628" label="AUTOTESTPRIVATE_GETEXTENSIONSINFO"/>
+ <int value="629" label="SCREENLOCKPRIVATE_GETLOCKED"/>
+ <int value="630" label="SCREENLOCKPRIVATE_SETLOCKED"/>
+ <int value="631" label="SCREENLOCKPRIVATE_SHOWMESSAGE"/>
+ <int value="632" label="FEEDBACKPRIVATE_GETHISTOGRAMS"/>
+ <int value="633" label="SYSTEM_NETWORK_GETNETWORKINTERFACES"/>
+ <int value="634" label="SERIAL_GETDEVICES"/>
+ <int value="635" label="SERIAL_UPDATE"/>
+ <int value="636" label="SERIAL_SETPAUSED"/>
+ <int value="637" label="SERIAL_GETINFO"/>
+ <int value="638" label="SERIAL_GETCONNECTIONS"/>
+ <int value="639" label="SERIAL_SEND"/>
+ <int value="640" label="GCM_REGISTER"/>
+ <int value="641" label="GCM_SEND"/>
+ <int value="642" label="SERIAL_CONNECT"/>
+ <int value="643" label="SERIAL_DISCONNECT"/>
+ <int value="644" label="MEDIAGALLERIES_GETALLMEDIAFILESYSTEMMETADATA"/>
+ <int value="645" label="FIRSTRUNPRIVATE_GETLOCALIZEDSTRINGS"/>
+ <int value="646" label="FIRSTRUNPRIVATE_LAUNCHTUTORIAL"/>
+ <int value="647" label="SOCKETS_UDP_SETPAUSED"/>
+ <int value="648" label="WEBVIEW_CAPTUREVISIBLEREGION"/>
+ <int value="649" label="MEDIAGALLERIES_GETMETADATA"/>
</enum>
<enum name="ExtensionInstallCause" type="int">
@@ -24682,6 +25148,46 @@ other types of suffix sets.
</int>
</enum>
+<enum name="HistoryTopSitesRecoveryEnum" type="int">
+ <summary>Error states noted in top_sites_database.cc recovery code.</summary>
+ <int value="0" label="RECOVERY_EVENT_RECOVERED">Successful recovery.</int>
+ <int value="1" label="RECOVERY_EVENT_DEPRECATED">
+ Recovery found deprecated version and razed.
+ </int>
+ <int value="2" label="RECOVERY_EVENT_FAILED_SCOPER">
+ sql::Recovery failed init.
+ </int>
+ <int value="3" label="RECOVERY_EVENT_FAILED_META_VERSION">
+ Failed sql::Recovery::SetupMeta() or GetMetaVersionNumber().
+ </int>
+ <int value="4" label="RECOVERY_EVENT_FAILED_META_WRONG_VERSION">
+ Recovery meta table has an unexpected version.
+ </int>
+ <int value="5" label="RECOVERY_EVENT_FAILED_META_INIT">
+ Failed sql::MetaTable::Init().
+ </int>
+ <int value="6" label="RECOVERY_EVENT_FAILED_SCHEMA_INIT">
+ Failed to init target schema.
+ </int>
+ <int value="7" label="RECOVERY_EVENT_FAILED_AUTORECOVER_THUMBNAILS">
+ Failed recovery on thumbnails table.
+ </int>
+ <int value="8" label="RECOVERY_EVENT_FAILED_COMMIT">
+ Failure from sql::Recovery::Recovered().
+ </int>
+ <int value="9" label="RECOVERY_EVENT_INVARIANT_RANK">
+ Rows were deleted because |url_rank| and |last_forced| didn't agree. Does
+ not prevent recovery.
+ </int>
+ <int value="10" label="RECOVERY_EVENT_INVARIANT_REDIRECT">
+ Rows were deleted because |redirects| did not contain |url|. Does not
+ prevent recovery.
+ </int>
+ <int value="11" label="RECOVERY_EVENT_INVARIANT_CONTIGUOUS">
+ |url_rank| was renumbered due to missing rows. Does not prevent recovery.
+ </int>
+</enum>
+
<enum name="HttpAuthCount" type="int">
<int value="0" label="Basic Start"/>
<int value="1" label="Basic Reject"/>
@@ -26263,6 +26769,12 @@ other types of suffix sets.
<int value="3" label="kInvalidPlayerState"/>
</enum>
+<enum name="MigrationNssToPemNetworkTypes" type="int">
+ <int value="0" label="EAP"/>
+ <int value="1" label="OpenVPN"/>
+ <int value="2" label="IPsec"/>
+</enum>
+
<enum name="MistSwitchResult" type="int">
<int value="0" label="Success"/>
<int value="1" label="Failure"/>
@@ -26332,6 +26844,16 @@ other types of suffix sets.
<int value="3" label="Return by launcher"/>
</enum>
+<enum name="MultiProfileTeleportWindowType" type="int">
+ <int value="0" label="Tabbed browser"/>
+ <int value="1" label="Tabbed incognito browser"/>
+ <int value="2" label="V1 app"/>
+ <int value="3" label="V2 app"/>
+ <int value="4" label="Panel"/>
+ <int value="5" label="Popup"/>
+ <int value="6" label="Unknown"/>
+</enum>
+
<enum name="NavigationScheme" type="int">
<int value="0" label="(Unknown)"/>
<int value="1" label="http"/>
@@ -27635,6 +28157,17 @@ other types of suffix sets.
<int value="5" label="5"/>
</enum>
+<enum name="PostMergeVerificationOutcome" type="int">
+ <int value="0" label="Undefined"/>
+ <int value="1" label="Succeeded"/>
+ <int value="2" label="No accounts found"/>
+ <int value="3" label="Missing primary account"/>
+ <int value="4" label="Primary account is not the first"/>
+ <int value="5" label="Verification failed"/>
+ <int value="6" label="Connection failed"/>
+ <int value="7" label="Overflow"/>
+</enum>
+
<enum name="PowerBrightnessAdjust" type="int">
<int value="0" label="Brightness Down"/>
<int value="1" label="Brightness Up"/>
@@ -27853,6 +28386,11 @@ other types of suffix sets.
<int value="83" label="Continue prerender check next URL not skipped"/>
<int value="84" label="Prerender Service returned hinting candidates"/>
<int value="85" label="Namespace mismatch: merge result namespace not alias"/>
+ <int value="86" label="Tab Helper URL seen entry"/>
+ <int value="87" label="Tab Helper URL seen match browser navigation"/>
+ <int value="88" label="Tab Helper URL seen namespace match entry"/>
+ <int value="89"
+ label="Tab Helper URL seen namespace match browser navigation"/>
</enum>
<enum name="PrerenderLocalVisitCoreTransition" type="int">
@@ -27950,6 +28488,14 @@ other types of suffix sets.
<int value="4" label="Canceled"/>
</enum>
+<enum name="ProfileErrorType" type="int">
+ <int value="0" label="History error"/>
+ <int value="1" label="Preferences error"/>
+ <int value="2" label="Webdata autofill DB error"/>
+ <int value="3" label="Webdata token DB error"/>
+ <int value="4" label="Webdata DB error"/>
+</enum>
+
<enum name="ProfileImageDownloadResult" type="int">
<int value="0" label="DownloadSuccessChanged">
<summary>
@@ -29687,6 +30233,7 @@ other types of suffix sets.
<int value="10" label="prefs::kDefaultSearchProviderName"/>
<int value="11" label="prefs::kPinnedTabs"/>
<int value="12" label="prefs::kExtensionKnownDisabled"/>
+ <int value="13" label="prefs::kProfileResetPromptMemento"/>
</enum>
<enum name="TranslateError" type="int">
@@ -29750,22 +30297,31 @@ other types of suffix sets.
<int value="11" label="Gesture finger down"/>
<int value="12" label="Gesture finger up"/>
<int value="13" label="Gesture double tap"/>
- <int value="14" label="Gesture two-finger tap"/>
- <int value="15" label="Gesture pinch begin"/>
- <int value="16" label="Gesture pinch end"/>
- <int value="17" label="Gesture pinch update (2 fingers)"/>
- <int value="18" label="Long press"/>
- <int value="19" label="Multi-finger swipe (2 fingers)"/>
- <int value="20" label="Scroll"/>
- <int value="21" label="Scroll fling start"/>
- <int value="22" label="Scroll fling cancel"/>
- <int value="23" label="Multi-finger swipe (3 fingers)"/>
- <int value="24" label="Multi-finger swipe (4+ fingers)"/>
- <int value="25" label="Gesture scroll update (2 fingers)"/>
- <int value="26" label="Gesture scroll update (3 fingers)"/>
- <int value="27" label="Gesture scroll update (4+ fingers)"/>
- <int value="28" label="Gesture pinch update (3 fingers)"/>
- <int value="29" label="Gesture pinch update (4+ fingers)"/>
+ <int value="14" label="Gesture triple tap"/>
+ <int value="15" label="Gesture two-finger tap"/>
+ <int value="16" label="Gesture pinch begin"/>
+ <int value="17" label="Gesture pinch end"/>
+ <int value="18" label="Gesture pinch update (2 fingers)"/>
+ <int value="19" label="Long press"/>
+ <int value="20" label="Multi-finger swipe (2 fingers)"/>
+ <int value="21" label="Scroll"/>
+ <int value="22" label="Scroll fling start"/>
+ <int value="23" label="Scroll fling cancel"/>
+ <int value="24" label="Multi-finger swipe (3 fingers)"/>
+ <int value="25" label="Multi-finger swipe (4+ fingers)"/>
+ <int value="26" label="Gesture scroll update (2 fingers)"/>
+ <int value="27" label="Gesture scroll update (3 fingers)"/>
+ <int value="28" label="Gesture scroll update (4+ fingers)"/>
+ <int value="29" label="Gesture pinch update (3 fingers)"/>
+ <int value="30" label="Gesture pinch update (4+ fingers)"/>
+ <int value="31" label="Long tap"/>
+ <int value="32" label="Show Press"/>
+ <int value="33" label="Tap Cancel"/>
+</enum>
+
+<enum name="UmaInitSequence" type="int">
+ <int value="0" label="Timer fired first"/>
+ <int value="1" label="Init task completed first"/>
</enum>
<enum name="UmaUploadResponseStatus" type="int">
@@ -31437,6 +31993,7 @@ other types of suffix sets.
client successfully sent a UMA upload. Each packet was numbered
when it was sent by Google."/>
<affected-histogram name="NetConnectivity4"/>
+ <affected-histogram name="NetConnectivity5"/>
</fieldtrial>
<fieldtrial name="NetConnectivity4NATBindPacketReceives" separator=".">
@@ -31465,6 +32022,7 @@ other types of suffix sets.
between sendings of the first two packets. Results are only
shown if the first two packets are both received."/>
<affected-histogram name="NetConnectivity4.NATBind.Sent2"/>
+ <affected-histogram name="NetConnectivity5.NATBind.Sent2"/>
</fieldtrial>
<fieldtrial name="NetConnectivity4PacketFirst6" separator=".">
@@ -31480,6 +32038,9 @@ other types of suffix sets.
<affected-histogram name="NetConnectivity4.NonPacedPacket"/>
<affected-histogram name="NetConnectivity4.PacedPacket"/>
<affected-histogram name="NetConnectivity4.StartPacket"/>
+ <affected-histogram name="NetConnectivity5.NonPacedPacket"/>
+ <affected-histogram name="NetConnectivity5.PacedPacket"/>
+ <affected-histogram name="NetConnectivity5.StartPacket"/>
</fieldtrial>
<fieldtrial name="NetConnectivity4PacketReceives" separator=".">
@@ -31507,6 +32068,9 @@ other types of suffix sets.
<affected-histogram name="NetConnectivity4.NonPacedPacket.Sent21"/>
<affected-histogram name="NetConnectivity4.PacedPacket.Sent21"/>
<affected-histogram name="NetConnectivity4.StartPacket.Sent21"/>
+ <affected-histogram name="NetConnectivity5.NonPacedPacket.Sent21"/>
+ <affected-histogram name="NetConnectivity5.PacedPacket.Sent21"/>
+ <affected-histogram name="NetConnectivity5.StartPacket.Sent21"/>
</fieldtrial>
<fieldtrial name="NetConnectivity4PacketRTT" separator=".">
@@ -31535,6 +32099,9 @@ other types of suffix sets.
<affected-histogram name="NetConnectivity4.NonPacedPacket"/>
<affected-histogram name="NetConnectivity4.PacedPacket"/>
<affected-histogram name="NetConnectivity4.StartPacket"/>
+ <affected-histogram name="NetConnectivity5.NonPacedPacket"/>
+ <affected-histogram name="NetConnectivity5.PacedPacket"/>
+ <affected-histogram name="NetConnectivity5.StartPacket"/>
</fieldtrial>
<fieldtrial name="NetConnectivity4PacketRTTSeries" separator=".">
@@ -31547,6 +32114,10 @@ other types of suffix sets.
name="NetConnectivity4.NonPacedPacket.Sent21.Success.RTT"/>
<affected-histogram name="NetConnectivity4.PacedPacket.Sent21.Success.RTT"/>
<affected-histogram name="NetConnectivity4.StartPacket.Sent21.Success.RTT"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.Success.RTT"/>
+ <affected-histogram name="NetConnectivity5.PacedPacket.Sent21.Success.RTT"/>
+ <affected-histogram name="NetConnectivity5.StartPacket.Sent21.Success.RTT"/>
</fieldtrial>
<fieldtrial name="NetConnectivity4PacketsAll" separator=".">
@@ -31741,6 +32312,191 @@ other types of suffix sets.
name="NetConnectivity4.StartPacket.Sent21.Success.RTT.Packet10"/>
<affected-histogram
name="NetConnectivity4.StartPacket.Sent21.Success.RTT.Packet20"/>
+ <affected-histogram name="NetConnectivity5.NATBind.Sent2.Bind.Failure"/>
+ <affected-histogram name="NetConnectivity5.NATBind.Sent2.Bind.Success"/>
+ <affected-histogram
+ name="NetConnectivity5.NATBind.Sent2.Connectivity.Failure"/>
+ <affected-histogram
+ name="NetConnectivity5.NATBind.Sent2.Connectivity.Success"/>
+ <affected-histogram
+ name="NetConnectivity5.NATBind.Sent2.SendToLastRecvDelay"/>
+ <affected-histogram name="NetConnectivity5.NonPacedPacket.Sent21.GotAPacket"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst01Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst02Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst03Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst04Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst05Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst06Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst07Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst08Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst09Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst10Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst11Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst12Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst13Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst14Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst15Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst16Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst17Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst18Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst19Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst20Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.NumRecvFromFirst21Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.PacketDelay"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.PacketsRecv"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.RecvNthPacket"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.SendToLastRecvDelay"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.Success.RTT.Packet01"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.Success.RTT.Packet02"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.Success.RTT.Packet03"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.Success.RTT.Packet10"/>
+ <affected-histogram
+ name="NetConnectivity5.NonPacedPacket.Sent21.Success.RTT.Packet20"/>
+ <affected-histogram name="NetConnectivity5.PacedPacket.Sent21.GotAPacket"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst01Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst02Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst03Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst04Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst05Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst06Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst07Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst08Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst09Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst10Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst11Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst12Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst13Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst14Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst15Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst16Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst17Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst18Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst19Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst20Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.NumRecvFromFirst21Packets"/>
+ <affected-histogram name="NetConnectivity5.PacedPacket.Sent21.PacketDelay"/>
+ <affected-histogram name="NetConnectivity5.PacedPacket.Sent21.PacketsRecv"/>
+ <affected-histogram name="NetConnectivity5.PacedPacket.Sent21.RecvNthPacket"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.SendToLastRecvDelay"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.Success.RTT.Packet01"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.Success.RTT.Packet02"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.Success.RTT.Packet03"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.Success.RTT.Packet10"/>
+ <affected-histogram
+ name="NetConnectivity5.PacedPacket.Sent21.Success.RTT.Packet20"/>
+ <affected-histogram name="NetConnectivity5.StartPacket.Sent21.GotAPacket"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst01Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst02Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst03Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst04Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst05Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst06Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst07Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst08Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst09Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst10Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst11Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst12Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst13Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst14Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst15Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst16Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst17Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst18Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst19Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst20Packets"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.NumRecvFromFirst21Packets"/>
+ <affected-histogram name="NetConnectivity5.StartPacket.Sent21.PacketDelay"/>
+ <affected-histogram name="NetConnectivity5.StartPacket.Sent21.PacketsRecv"/>
+ <affected-histogram name="NetConnectivity5.StartPacket.Sent21.RecvNthPacket"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.SendToLastRecvDelay"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.Success.RTT.Packet01"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.Success.RTT.Packet02"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.Success.RTT.Packet03"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.Success.RTT.Packet10"/>
+ <affected-histogram
+ name="NetConnectivity5.StartPacket.Sent21.Success.RTT.Packet20"/>
</fieldtrial>
<fieldtrial name="NetConnectivity4PacketSizeTest" separator=".">
@@ -31751,6 +32507,7 @@ other types of suffix sets.
label="This histogram records the size of the packet size that was
received from the server."/>
<affected-histogram name="NetConnectivity4"/>
+ <affected-histogram name="NetConnectivity5"/>
</fieldtrial>
<fieldtrial name="NetConnectivity4PacketSizeTestPort" separator=".">
@@ -31760,6 +32517,10 @@ other types of suffix sets.
name="NetConnectivity4.PacketSizeTest.Connectivity.Failure"/>
<affected-histogram
name="NetConnectivity4.PacketSizeTest.Connectivity.Success"/>
+ <affected-histogram
+ name="NetConnectivity5.PacketSizeTest.Connectivity.Failure"/>
+ <affected-histogram
+ name="NetConnectivity5.PacketSizeTest.Connectivity.Success"/>
</fieldtrial>
<fieldtrial name="NetConnectivity4SeriesRecv" separator=".">
@@ -31784,6 +32545,9 @@ other types of suffix sets.
<affected-histogram name="NetConnectivity4.NonPacedPacket.First6.SeriesRecv"/>
<affected-histogram name="NetConnectivity4.PacedPacket.First6.SeriesRecv"/>
<affected-histogram name="NetConnectivity4.StartPacket.First6.SeriesRecv"/>
+ <affected-histogram name="NetConnectivity5.NonPacedPacket.First6.SeriesRecv"/>
+ <affected-histogram name="NetConnectivity5.PacedPacket.First6.SeriesRecv"/>
+ <affected-histogram name="NetConnectivity5.StartPacket.First6.SeriesRecv"/>
</fieldtrial>
<fieldtrial name="NetProxyResolverExecutionTime">
@@ -31900,6 +32664,13 @@ other types of suffix sets.
<affected-histogram name="Plugin.PpapiPluginLoadResult"/>
</fieldtrial>
+<fieldtrial name="PrecacheCellular" separator=".">
+ <group name="Cellular"
+ label="covers fetches when connected to cellular networks"/>
+ <affected-histogram name="Precache.DownloadedNonPrecache"/>
+ <affected-histogram name="Precache.Saved"/>
+</fieldtrial>
+
<fieldtrial name="Prefetch">
<group name="ContentPrefetchPrefetchOff"
label="Prefetch is completely disabled."/>
@@ -32006,6 +32777,7 @@ other types of suffix sets.
<group name="web" label="Link triggered prerender."/>
<group name="webcross" label="Link triggered prerender, cross domain."/>
<group name="websame" label="Link triggered prerender, same domain."/>
+ <affected-histogram name="Prerender.Event"/>
<affected-histogram name="Prerender.FinalStatus"/>
<affected-histogram name="Prerender.FinalStatus_Prerender5minTTL"/>
<affected-histogram name="Prerender.FinalStatus_PrerenderControl"/>
diff --git a/tools/multi-process-rss.py b/tools/multi-process-rss.py
new file mode 100755
index 0000000000..100d0f759b
--- /dev/null
+++ b/tools/multi-process-rss.py
@@ -0,0 +1,143 @@
+#!/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.
+
+# Counts a resident set size (RSS) of multiple processes without double-counts.
+# If they share the same page frame, the page frame is counted only once.
+#
+# Usage:
+# ./multi-process-rss.py <pid>|<pid>r [...]
+#
+# If <pid> has 'r' at the end, all descendants of the process are accounted.
+#
+# Example:
+# ./multi-process-rss.py 12345 23456r
+#
+# The command line above counts the RSS of 1) process 12345, 2) process 23456
+# and 3) all descendant processes of process 23456.
+
+
+import collections
+import logging
+import os
+import psutil
+import sys
+
+
+if sys.platform.startswith('linux'):
+ _TOOLS_PATH = os.path.dirname(os.path.abspath(__file__))
+ _TOOLS_LINUX_PATH = os.path.join(_TOOLS_PATH, 'linux')
+ sys.path.append(_TOOLS_LINUX_PATH)
+ import procfs # pylint: disable=F0401
+
+
+class _NullHandler(logging.Handler):
+ def emit(self, record):
+ pass
+
+
+_LOGGER = logging.getLogger('multi-process-rss')
+_LOGGER.addHandler(_NullHandler())
+
+
+def _recursive_get_children(pid):
+ try:
+ children = psutil.Process(pid).get_children()
+ except psutil.error.NoSuchProcess:
+ return []
+ descendant = []
+ for child in children:
+ descendant.append(child.pid)
+ descendant.extend(_recursive_get_children(child.pid))
+ return descendant
+
+
+def list_pids(argv):
+ pids = []
+ for arg in argv[1:]:
+ try:
+ if arg.endswith('r'):
+ recursive = True
+ pid = int(arg[:-1])
+ else:
+ recursive = False
+ pid = int(arg)
+ except ValueError:
+ raise SyntaxError("%s is not an integer." % arg)
+ else:
+ pids.append(pid)
+ if recursive:
+ children = _recursive_get_children(pid)
+ pids.extend(children)
+
+ pids = sorted(set(pids), key=pids.index) # uniq: maybe slow, but simple.
+
+ return pids
+
+
+def count_pageframes(pids):
+ pageframes = collections.defaultdict(int)
+ pagemap_dct = {}
+ for pid in pids:
+ maps = procfs.ProcMaps.load(pid)
+ if not maps:
+ _LOGGER.warning('/proc/%d/maps not found.' % pid)
+ continue
+ pagemap = procfs.ProcPagemap.load(pid, maps)
+ if not pagemap:
+ _LOGGER.warning('/proc/%d/pagemap not found.' % pid)
+ continue
+ pagemap_dct[pid] = pagemap
+
+ for pid, pagemap in pagemap_dct.iteritems():
+ for vma in pagemap.vma_internals.itervalues():
+ for pageframe, number in vma.pageframes.iteritems():
+ pageframes[pageframe] += number
+
+ return pageframes
+
+
+def count_statm(pids):
+ resident = 0
+ shared = 0
+ private = 0
+
+ for pid in pids:
+ statm = procfs.ProcStatm.load(pid)
+ if not statm:
+ _LOGGER.warning('/proc/%d/statm not found.' % pid)
+ continue
+ resident += statm.resident
+ shared += statm.share
+ private += (statm.resident - statm.share)
+
+ return (resident, shared, private)
+
+
+def main(argv):
+ logging_handler = logging.StreamHandler()
+ logging_handler.setLevel(logging.WARNING)
+ logging_handler.setFormatter(logging.Formatter(
+ '%(asctime)s:%(name)s:%(levelname)s:%(message)s'))
+
+ _LOGGER.setLevel(logging.WARNING)
+ _LOGGER.addHandler(logging_handler)
+
+ if sys.platform.startswith('linux'):
+ logging.getLogger('procfs').setLevel(logging.WARNING)
+ logging.getLogger('procfs').addHandler(logging_handler)
+ pids = list_pids(argv)
+ pageframes = count_pageframes(pids)
+ else:
+ _LOGGER.error('%s is not supported.' % sys.platform)
+ return 1
+
+ # TODO(dmikurube): Classify this total RSS.
+ print len(pageframes) * 4096
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/tools/perf/benchmarks/dom_perf.py b/tools/perf/benchmarks/dom_perf.py
index 1a466947fc..f24e5e3046 100644
--- a/tools/perf/benchmarks/dom_perf.py
+++ b/tools/perf/benchmarks/dom_perf.py
@@ -5,6 +5,7 @@
import json
import math
import os
+import sys
from telemetry import test
from telemetry.core import util
@@ -74,6 +75,8 @@ class DomPerf(test.Test):
means better performance: Bigger is better!"""
test = _DomPerfMeasurement
+ enabled = not sys.platform.startswith('linux')
+
def CreatePageSet(self, options):
dom_perf_dir = os.path.join(util.GetChromiumSrcDir(), 'data', 'dom_perf')
base_page = 'file://run.html?reportInJS=1&run='
diff --git a/tools/perf/benchmarks/jsgamebench.py b/tools/perf/benchmarks/jsgamebench.py
index e872b55c9d..4074b24230 100644
--- a/tools/perf/benchmarks/jsgamebench.py
+++ b/tools/perf/benchmarks/jsgamebench.py
@@ -15,7 +15,7 @@ class _JsgamebenchMeasurement(page_measurement.PageMeasurement):
def MeasurePage(self, _, tab, results):
tab.ExecuteJavaScript('UI.call({}, "perftest")')
tab.WaitForJavaScriptExpression(
- 'document.getElementById("perfscore0") != null', 1200)
+ 'document.getElementById("perfscore0") != null', 1800)
js_get_results = 'document.getElementById("perfscore0").innerHTML'
result = int(tab.EvaluateJavaScript(js_get_results))
results.Add('Score', 'score (bigger is better)', result)
@@ -27,7 +27,7 @@ class Jsgamebench(test.Test):
def CreatePageSet(self, options):
return page_set.PageSet.FromDict({
- 'archive_data_file': '../data/jsgamebench.json',
+ 'archive_data_file': '../page_sets/data/jsgamebench.json',
'pages': [
{ 'url': 'http://localhost/' }
]
diff --git a/tools/perf/benchmarks/kraken.py b/tools/perf/benchmarks/kraken.py
index 0fd4291dd2..d77b03482d 100644
--- a/tools/perf/benchmarks/kraken.py
+++ b/tools/perf/benchmarks/kraken.py
@@ -41,7 +41,7 @@ class Kraken(test.Test):
def CreatePageSet(self, options):
return page_set.PageSet.FromDict({
- 'archive_data_file': '../data/kraken.json',
+ 'archive_data_file': '../page_sets/data/kraken.json',
'pages': [
{ 'url': 'http://krakenbenchmark.mozilla.org/kraken-1.1/driver.html' }
]
diff --git a/tools/perf/benchmarks/media.py b/tools/perf/benchmarks/media.py
index 7fcf610085..7c4d165ddb 100644
--- a/tools/perf/benchmarks/media.py
+++ b/tools/perf/benchmarks/media.py
@@ -1,6 +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.
+
+import sys
+
from measurements import media
from telemetry import test
@@ -9,6 +12,12 @@ class Media(test.Test):
test = media.Media
page_set = 'page_sets/tough_video_cases.json'
+class MediaNetworkSimulation(test.Test):
+ """Obtains media metrics under different network simulations."""
+ test = media.Media
+ enabled = not sys.platform.startswith('linux')
+ page_set = 'page_sets/media_cns_cases.json'
+
class MediaAndroid(test.Test):
"""Obtains media metrics for key user scenarios on Android."""
test = media.Media
diff --git a/tools/perf/benchmarks/octane.py b/tools/perf/benchmarks/octane.py
index 2e5d7911c6..3097196de6 100644
--- a/tools/perf/benchmarks/octane.py
+++ b/tools/perf/benchmarks/octane.py
@@ -33,7 +33,7 @@ class _OctaneMeasurement(page_measurement.PageMeasurement):
def MeasurePage(self, _, tab, results):
tab.WaitForJavaScriptExpression(
- 'completed && !document.getElementById("progress-bar-container")', 600)
+ 'completed && !document.getElementById("progress-bar-container")', 1200)
results_log = tab.EvaluateJavaScript('__results')
all_scores = []
diff --git a/tools/perf/benchmarks/page_cycler.py b/tools/perf/benchmarks/page_cycler.py
index b9a0b8c971..54093d7c9a 100644
--- a/tools/perf/benchmarks/page_cycler.py
+++ b/tools/perf/benchmarks/page_cycler.py
@@ -1,6 +1,8 @@
# Copyright (c) 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 sys
+
from telemetry import test
from measurements import page_cycler
@@ -103,6 +105,8 @@ class PageCyclerToughLayoutCases(test.Test):
class PageCyclerTypical25(test.Test):
+ # crbug.com/273986: This test is really flakey on xp.
+ enabled = not sys.platform.startswith('win')
test = page_cycler.PageCycler
page_set = 'page_sets/typical_25.json'
options = {'pageset_repeat_iters': 10}
diff --git a/tools/perf/benchmarks/robohornet_pro.py b/tools/perf/benchmarks/robohornet_pro.py
index 1809b7bf76..a25004eec0 100644
--- a/tools/perf/benchmarks/robohornet_pro.py
+++ b/tools/perf/benchmarks/robohornet_pro.py
@@ -26,7 +26,7 @@ class RobohornetPro(test.Test):
def CreatePageSet(self, options):
return page_set.PageSet.FromDict({
- 'archive_data_file': '../data/robohornetpro.json',
+ 'archive_data_file': '../page_sets/data/robohornet_pro.json',
# Measurement require use of real Date.now() for measurement.
'make_javascript_deterministic': False,
'pages': [
diff --git a/tools/perf/benchmarks/session_restore.py b/tools/perf/benchmarks/session_restore.py
index 016d482694..d82dd04ace 100644
--- a/tools/perf/benchmarks/session_restore.py
+++ b/tools/perf/benchmarks/session_restore.py
@@ -1,12 +1,16 @@
# 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 sys
+
from telemetry import test
from measurements import session_restore
class SessionRestoreColdTypical25(test.Test):
+ # crbug.com/325479: Disabling this test for now since it never ran before.
+ enabled = not sys.platform.startswith('linux')
tag = 'cold'
test = session_restore.SessionRestore
page_set = 'page_sets/typical_25.json'
diff --git a/tools/perf/benchmarks/smoothness.py b/tools/perf/benchmarks/smoothness.py
index 0d3a8364f3..a407c2b6cc 100644
--- a/tools/perf/benchmarks/smoothness.py
+++ b/tools/perf/benchmarks/smoothness.py
@@ -1,6 +1,9 @@
# Copyright (c) 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 sys
+
from telemetry import test
from measurements import smoothness
@@ -16,6 +19,7 @@ class SmoothnessTop25(test.Test):
class SmoothnessToughCanvasCases(test.Test):
test = smoothness.Smoothness
+ enabled = sys.platform != 'darwin'
page_set = 'page_sets/tough_canvas_cases.json'
@@ -25,3 +29,13 @@ class SmoothnessKeyMobileSites(test.Test):
http://www.chromium.org/developers/design-documents/rendering-benchmarks"""
test = smoothness.Smoothness
page_set = 'page_sets/key_mobile_sites.json'
+
+
+class SmoothnessToughSchedulingCases(test.Test):
+ """Measures rendering statistics while interacting with pages that have
+ challenging scheduling properties.
+
+ https://docs.google.com/a/chromium.org/document/d/
+ 17yhE5Po9By0sCdM1yZT3LiUECaUr_94rQt9j-4tOQIM/view"""
+ test = smoothness.Smoothness
+ page_set = 'page_sets/tough_scheduling_cases.json'
diff --git a/tools/perf/benchmarks/spaceport.py b/tools/perf/benchmarks/spaceport.py
index 34baaac5a8..7eff74bc35 100644
--- a/tools/perf/benchmarks/spaceport.py
+++ b/tools/perf/benchmarks/spaceport.py
@@ -6,6 +6,7 @@
import logging
import os
+import sys
from telemetry import test
from telemetry.core import util
@@ -56,6 +57,9 @@ class Spaceport(test.Test):
"""spaceport.io's PerfMarks benchmark."""
test = _SpaceportMeasurement
+ # crbug.com/166703: This test frequently times out on Windows.
+ enabled = sys.platform != 'darwin' and not sys.platform.startswith('win')
+
def CreatePageSet(self, options):
spaceport_dir = os.path.join(util.GetChromiumSrcDir(), 'chrome', 'test',
'data', 'third_party', 'spaceport')
diff --git a/tools/perf/perf_tools/bootstrap_deps b/tools/perf/bootstrap_deps
index 1823408376..2d82147c4b 100644
--- a/tools/perf/perf_tools/bootstrap_deps
+++ b/tools/perf/bootstrap_deps
@@ -1,4 +1,4 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# 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.
@@ -16,8 +16,6 @@ deps = {
"https://src.chromium.org/chrome/trunk/src/tools/perf/page_sets",
"src/tools/perf/metrics":
"https://src.chromium.org/chrome/trunk/src/tools/perf/metrics",
- "src/tools/perf/perf_tools":
- "https://src.chromium.org/chrome/trunk/src/tools/perf/perf_tools",
"src/tools/perf/profile_creators":
"https://src.chromium.org/chrome/trunk/src/tools/perf/profile_creators",
}
diff --git a/tools/perf/measurements/loading_profile.py b/tools/perf/measurements/loading_profile.py
index 994f0969d1..1e9db77809 100644
--- a/tools/perf/measurements/loading_profile.py
+++ b/tools/perf/measurements/loading_profile.py
@@ -51,5 +51,5 @@ class LoadingProfile(page_measurement.PageMeasurement):
break
for function, period in perf_profiler.PerfProfiler.GetTopSamples(
- tab.browser.platform.GetOSName(), profile_file, 10).iteritems():
+ profile_file, 10).iteritems():
results.Add(function.replace('.', '_'), 'period', period)
diff --git a/tools/perf/measurements/loading_timeline.py b/tools/perf/measurements/loading_timeline.py
index b90748d74d..95ff6d2fb6 100644
--- a/tools/perf/measurements/loading_timeline.py
+++ b/tools/perf/measurements/loading_timeline.py
@@ -8,7 +8,7 @@ from telemetry.page import page_measurement
class LoadingTimeline(page_measurement.PageMeasurement):
def __init__(self, *args, **kwargs):
super(LoadingTimeline, self).__init__(*args, **kwargs)
- self._timeline_metric = timeline.TimelineMetric(
+ self._timeline_metric = timeline.LoadTimesTimelineMetric(
timeline.TIMELINE_MODE, 'thread 0')
@property
diff --git a/tools/perf/measurements/loading_trace.py b/tools/perf/measurements/loading_trace.py
index 04f7db128d..111e549999 100644
--- a/tools/perf/measurements/loading_trace.py
+++ b/tools/perf/measurements/loading_trace.py
@@ -8,7 +8,7 @@ from telemetry.page import page_measurement
class LoadingTrace(page_measurement.PageMeasurement):
def __init__(self, *args, **kwargs):
super(LoadingTrace, self).__init__(*args, **kwargs)
- self._timeline_metric = timeline.TimelineMetric(
+ self._timeline_metric = timeline.LoadTimesTimelineMetric(
timeline.TRACING_MODE, 'CrRendererMain')
@property
diff --git a/tools/perf/measurements/no_op.py b/tools/perf/measurements/no_op.py
new file mode 100644
index 0000000000..58660262f9
--- /dev/null
+++ b/tools/perf/measurements/no_op.py
@@ -0,0 +1,21 @@
+# 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.page import page_measurement
+
+class NoOp(page_measurement.PageMeasurement):
+ def __init__(self):
+ super(NoOp, self).__init__('no_op')
+
+ def CanRunForPage(self, page):
+ return hasattr(page, 'no_op')
+
+ def WillRunAction(self, page, tab, action):
+ pass
+
+ def DidRunAction(self, page, tab, action):
+ pass
+
+ def MeasurePage(self, page, tab, results):
+ pass
diff --git a/tools/perf/measurements/page_cycler.py b/tools/perf/measurements/page_cycler.py
index a0fdc470b7..2daccb38c2 100644
--- a/tools/perf/measurements/page_cycler.py
+++ b/tools/perf/measurements/page_cycler.py
@@ -17,7 +17,6 @@ cycling all pages.
import collections
import os
-import sys
from metrics import cpu
from metrics import io
@@ -41,8 +40,6 @@ class PageCycler(page_measurement.PageMeasurement):
self._memory_metric = None
self._cpu_metric = None
self._v8_object_stats_metric = None
- self._number_warm_runs = None
- self._cold_runs_requested = False
self._cold_run_start_index = None
self._has_loaded_page = collections.defaultdict(int)
@@ -107,48 +104,39 @@ class PageCycler(page_measurement.PageMeasurement):
if options.report_speed_index:
self._report_speed_index = True
- # A disk cache bug causes some page cyclers to hang on mac.
- # TODO(tonyg): Re-enable these tests when crbug.com/268646 is fixed.
- if (sys.platform == 'darwin' and
- (sys.argv[-1].endswith('/intl_hi_ru.json') or
- sys.argv[-1].endswith('/tough_layout_cases.json') or
- sys.argv[-1].endswith('/typical_25.json'))):
- print '%s is currently disabled on mac. Skipping test.' % sys.argv[-1]
- sys.exit(0)
-
- self._cold_runs_requested = (options.cold_load_percent != None)
+ cold_runs_percent_set = (options.cold_load_percent != None)
# Handle requests for cold cache runs
- if (self._cold_runs_requested and
+ if (cold_runs_percent_set and
(options.repeat_options.page_repeat_secs or
options.repeat_options.pageset_repeat_secs)):
raise Exception('--cold-load-percent is incompatible with timed repeat')
- if (self._cold_runs_requested and
+ if (cold_runs_percent_set and
(options.cold_load_percent < 0 or options.cold_load_percent > 100)):
raise Exception('--cold-load-percent must be in the range [0-100]')
- # TODO(rdsmith): Properly handle interaction of page_repeat with
- # dropping the first run.
- number_warm_pageset_runs = int(
- (int(options.repeat_options.pageset_repeat_iters) - 1) *
- (100 - int(options.cold_load_percent or 0)) / 100)
-
- # Make sure _number_cold_runs is an integer multiple of page_repeat.
+ # Make sure _cold_run_start_index is an integer multiple of page_repeat.
# Without this, --pageset_shuffle + --page_repeat could lead to
# assertion failures on _started_warm in WillNavigateToPage.
- self._number_warm_runs = (number_warm_pageset_runs *
- options.repeat_options.page_repeat_iters)
- self._cold_run_start_index = (self._number_warm_runs +
- options.repeat_options.page_repeat_iters)
- self.discard_first_result = ((self._cold_runs_requested and
- not options.cold_load_percent) or
- self.discard_first_result)
+ if cold_runs_percent_set:
+ number_warm_pageset_runs = int(
+ (int(options.repeat_options.pageset_repeat_iters) - 1) *
+ (100 - options.cold_load_percent) / 100)
+ number_warm_runs = (number_warm_pageset_runs *
+ options.repeat_options.page_repeat_iters)
+ self._cold_run_start_index = (number_warm_runs +
+ options.repeat_options.page_repeat_iters)
+ self.discard_first_result = (not options.cold_load_percent or
+ self.discard_first_result)
+ else:
+ self._cold_run_start_index = (
+ options.repeat_options.pageset_repeat_iters *
+ options.repeat_options.page_repeat_iters)
def MeasurePage(self, page, tab, results):
tab.WaitForJavaScriptExpression('__pc_load_time', 60)
- chart_name_prefix = ('' if not self._cold_runs_requested else
- 'cold_' if self.IsRunCold(page.url) else
+ chart_name_prefix = ('cold_' if self.IsRunCold(page.url) else
'warm_')
results.Add('page_load_time', 'ms',
@@ -181,8 +169,7 @@ class PageCycler(page_measurement.PageMeasurement):
def IsRunCold(self, url):
return (self.ShouldRunCold(url) or
- (self._cold_runs_requested and
- self._has_loaded_page[url] == 0))
+ self._has_loaded_page[url] == 0)
def ShouldRunCold(self, url):
# We do the warm runs first for two reasons. The first is so we can
@@ -192,8 +179,7 @@ class PageCycler(page_measurement.PageMeasurement):
# contribute to the cold data and warm the catch for the following
# warm run, and clearing the cache before the load of the following
# URL would eliminate the intended warmup for the previous URL.
- return (self._cold_runs_requested and
- self._has_loaded_page[url] >= self._cold_run_start_index)
+ return (self._has_loaded_page[url] >= self._cold_run_start_index)
def results_are_the_same_on_every_page(self):
- return not self._cold_runs_requested
+ return False
diff --git a/tools/perf/measurements/page_cycler_unittest.py b/tools/perf/measurements/page_cycler_unittest.py
index 39356ad14d..13f4cd2921 100644
--- a/tools/perf/measurements/page_cycler_unittest.py
+++ b/tools/perf/measurements/page_cycler_unittest.py
@@ -29,6 +29,22 @@ class MockMemoryMetric(object):
def AddSummaryResults(self, tab, results):
pass
+# Used to mock loading a page.
+class FakePage(object):
+ def __init__(self, url):
+ self.url = url
+
+# Used to mock a browser tab.
+class FakeTab(object):
+ def __init__(self):
+ self.clear_cache_calls = 0
+ def ClearCache(self):
+ self.clear_cache_calls += 1
+ def EvaluateJavaScript(self, _):
+ return 1
+ def WaitForJavaScriptExpression(self, _, __):
+ pass
+
class PageCyclerUnitTest(unittest.TestCase):
# TODO(tonyg): Remove this backfill when we can assume python 2.7 everywhere.
@@ -36,7 +52,7 @@ class PageCyclerUnitTest(unittest.TestCase):
self.assertTrue(first in second,
msg="'%s' not found in '%s'" % (first, second))
- def setupCycler(self, args):
+ def setupCycler(self, args, setup_memory_module=False):
cycler = page_cycler.PageCycler()
options = browser_options.BrowserFinderOptions()
parser = options.CreateParser()
@@ -44,26 +60,39 @@ class PageCyclerUnitTest(unittest.TestCase):
parser.parse_args(args)
cycler.CustomizeBrowserOptions(options)
+ if setup_memory_module:
+ # Mock out memory metrics; the real ones require a real browser.
+ mock_memory_metric = MockMemoryMetric()
+
+ mock_memory_module = simple_mock.MockObject()
+ mock_memory_module.ExpectCall(
+ 'MemoryMetric').WithArgs(simple_mock.DONT_CARE).WillReturn(
+ mock_memory_metric)
+
+ real_memory_module = page_cycler.memory
+ try:
+ page_cycler.memory = mock_memory_module
+ cycler.DidStartBrowser(None)
+ finally:
+ page_cycler.memory = real_memory_module
+
return cycler
def testOptionsColdLoadNoArgs(self):
cycler = self.setupCycler([])
- self.assertFalse(cycler._cold_runs_requested)
- self.assertEqual(cycler._number_warm_runs, 9)
+ self.assertEquals(cycler._cold_run_start_index, 10)
def testOptionsColdLoadPagesetRepeat(self):
cycler = self.setupCycler(['--pageset-repeat=20', '--page-repeat=2'])
- self.assertFalse(cycler._cold_runs_requested)
- self.assertEqual(cycler._number_warm_runs, 38)
+ self.assertEquals(cycler._cold_run_start_index, 40)
def testOptionsColdLoadRequested(self):
cycler = self.setupCycler(['--pageset-repeat=21', '--page-repeat=2',
'--cold-load-percent=40'])
- self.assertTrue(cycler._cold_runs_requested)
- self.assertEqual(cycler._number_warm_runs, 24)
+ self.assertEquals(cycler._cold_run_start_index, 26)
def testIncompatibleOptions(self):
exception_seen = False
@@ -79,36 +108,8 @@ class PageCyclerUnitTest(unittest.TestCase):
def testCacheHandled(self):
cycler = self.setupCycler(['--pageset-repeat=5',
- '--cold-load-percent=50'])
-
- # Mock out memory metrics; the real ones require a real browser.
- mock_memory_metric = MockMemoryMetric()
-
- mock_memory_module = simple_mock.MockObject()
- mock_memory_module.ExpectCall(
- 'MemoryMetric').WithArgs(simple_mock.DONT_CARE).WillReturn(
- mock_memory_metric)
-
- real_memory_module = page_cycler.memory
- try:
- page_cycler.memory = mock_memory_module
- cycler.DidStartBrowser(None)
- finally:
- page_cycler.memory = real_memory_module
-
- class FakePage(object):
- def __init__(self, url):
- self.url = url
-
- class FakeTab(object):
- def __init__(self):
- self.clear_cache_calls = 0
- def ClearCache(self):
- self.clear_cache_calls += 1
- def EvaluateJavaScript(self, _):
- return 1
- def WaitForJavaScriptExpression(self, _, __):
- pass
+ '--cold-load-percent=50'],
+ True)
url_name = "http://fakepage.com"
page = FakePage(url_name)
@@ -129,9 +130,30 @@ class PageCyclerUnitTest(unittest.TestCase):
self.assertEqual(1, len(values))
self.assertEqual(values[0].page, page)
- chart_name = 'warm_times' if i > 3 else 'cold_times'
- print values[0].name
+ chart_name = 'cold_times' if i == 0 or i > 2 else 'warm_times'
self.assertEqual(values[0].name, '%s.page_load_time' % chart_name)
self.assertEqual(values[0].units, 'ms')
cycler.DidNavigateToPage(page, tab)
+
+ def testColdWarm(self):
+ cycler = self.setupCycler(['--pageset-repeat=3'], True)
+ pages = [FakePage("http://fakepage1.com"), FakePage("http://fakepage2.com")]
+ tab = FakeTab()
+ results = page_measurement_results.PageMeasurementResults()
+ for i in range(3):
+ for page in pages:
+ cycler.WillNavigateToPage(page, tab)
+ results.WillMeasurePage(page)
+ cycler.MeasurePage(page, tab, results)
+
+ values = results.page_specific_values_for_current_page
+ results.DidMeasurePage()
+
+ self.assertEqual(1, len(values))
+ self.assertEqual(values[0].page, page)
+
+ chart_name = 'cold_times' if i == 0 else 'warm_times'
+ self.assertEqual(values[0].name, '%s.page_load_time' % chart_name)
+
+ cycler.DidNavigateToPage(page, tab)
diff --git a/tools/perf/measurements/rasterize_and_record.py b/tools/perf/measurements/rasterize_and_record.py
index a69e96e7e0..a359d25bbe 100644
--- a/tools/perf/measurements/rasterize_and_record.py
+++ b/tools/perf/measurements/rasterize_and_record.py
@@ -65,15 +65,6 @@ class RasterizeAndRecord(page_measurement.PageMeasurement):
'forced and threaded. Skipping measurement.')
sys.exit(0)
- # TODO(ernstm): Remove this temporary workaround when reference build has
- # been updated to branch 1671 or later.
- backend = tab.browser._browser_backend # pylint: disable=W0212
- if (not hasattr(backend, 'chrome_branch_number') or
- (sys.platform != 'android' and backend.chrome_branch_number < 1671)):
- print ('Warning: rasterize_and_record requires Chrome branch 1671 or '
- 'later. Skipping measurement.')
- sys.exit(0)
-
# Rasterize only what's visible.
tab.ExecuteJavaScript(
'chrome.gpuBenchmarking.setRasterizeOnlyVisibleContent();')
diff --git a/tools/perf/measurements/rasterize_and_record_micro.py b/tools/perf/measurements/rasterize_and_record_micro.py
index 2c8f52f625..0928d3330e 100644
--- a/tools/perf/measurements/rasterize_and_record_micro.py
+++ b/tools/perf/measurements/rasterize_and_record_micro.py
@@ -60,17 +60,23 @@ class RasterizeAndRecordMicro(page_measurement.PageMeasurement):
tab.ExecuteJavaScript("""
window.benchmark_results = {};
window.benchmark_results.done = false;
- chrome.gpuBenchmarking.runMicroBenchmark(
- "rasterize_and_record_benchmark",
- function(value) {
- window.benchmark_results.done = true;
- window.benchmark_results.results = value;
- }, {
- "record_repeat_count": """ + str(record_repeat) + """,
- "rasterize_repeat_count": """ + str(rasterize_repeat) + """
- });
+ window.benchmark_results.scheduled =
+ chrome.gpuBenchmarking.runMicroBenchmark(
+ "rasterize_and_record_benchmark",
+ function(value) {
+ window.benchmark_results.done = true;
+ window.benchmark_results.results = value;
+ }, {
+ "record_repeat_count": """ + str(record_repeat) + """,
+ "rasterize_repeat_count": """ + str(rasterize_repeat) + """
+ });
""")
+ scheduled = tab.EvaluateJavaScript('window.benchmark_results.scheduled')
+ if (not scheduled):
+ raise page_measurement.MeasurementFailure(
+ 'Failed to schedule rasterize_and_record_micro')
+
tab.WaitForJavaScriptExpression(
'window.benchmark_results.done', self.options.timeout)
diff --git a/tools/perf/measurements/rasterize_and_record_micro_unittest.py b/tools/perf/measurements/rasterize_and_record_micro_unittest.py
new file mode 100644
index 0000000000..8940a3d67b
--- /dev/null
+++ b/tools/perf/measurements/rasterize_and_record_micro_unittest.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.
+from measurements import rasterize_and_record_micro
+from telemetry.core import wpr_modes
+from telemetry.page import page_measurement_unittest_base
+from telemetry.unittest import options_for_unittests
+
+
+class RasterizeAndRecordMicroUnitTest(
+ page_measurement_unittest_base.PageMeasurementUnitTestBase):
+ """Smoke test for rasterize_and_record_micro measurement
+
+ Runs rasterize_and_record_micro measurement on a simple page and verifies
+ that all metrics were added to the results. The test is purely functional,
+ i.e. it only checks if the metrics are present and non-zero.
+ """
+
+ def setUp(self):
+ self._options = options_for_unittests.GetCopy()
+ self._options.browser_options.wpr_mode = wpr_modes.WPR_OFF
+ self._options.rasterize_repeat = 1
+ self._options.record_repeat = 1
+ self._options.start_wait_time = 0.0
+
+ def testRasterizeAndRecordMicro(self):
+ ps = self.CreatePageSetFromFileInUnittestDataDir('blank.html')
+ measurement = rasterize_and_record_micro.RasterizeAndRecordMicro()
+ results = self.RunMeasurement(measurement, ps, options=self._options)
+ self.assertEquals(0, len(results.failures))
+
+ rasterize_time = results.FindAllPageSpecificValuesNamed('rasterize_time')
+ self.assertEquals(len(rasterize_time), 1)
+ self.assertGreater(rasterize_time[0].GetRepresentativeNumber(), 0)
+
+ record_time = results.FindAllPageSpecificValuesNamed('record_time')
+ self.assertEquals(len(record_time), 1)
+ self.assertGreater(record_time[0].GetRepresentativeNumber(), 0)
+
+ rasterized_pixels = results.FindAllPageSpecificValuesNamed(
+ 'pixels_rasterized')
+ self.assertEquals(len(rasterized_pixels), 1)
+ self.assertGreater(rasterized_pixels[0].GetRepresentativeNumber(), 0)
+
+ recorded_pixels = results.FindAllPageSpecificValuesNamed('pixels_recorded')
+ self.assertEquals(len(recorded_pixels), 1)
+ self.assertGreater(recorded_pixels[0].GetRepresentativeNumber(), 0)
diff --git a/tools/perf/measurements/rasterize_and_record_unittest.py b/tools/perf/measurements/rasterize_and_record_unittest.py
new file mode 100644
index 0000000000..7040730338
--- /dev/null
+++ b/tools/perf/measurements/rasterize_and_record_unittest.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.
+from measurements import rasterize_and_record
+from telemetry.core import wpr_modes
+from telemetry.page import page_measurement_unittest_base
+from telemetry.unittest import options_for_unittests
+
+
+class RasterizeAndRecordUnitTest(
+ page_measurement_unittest_base.PageMeasurementUnitTestBase):
+ """Smoke test for rasterize_and_record measurement
+
+ Runs rasterize_and_record measurement on a simple page and verifies that
+ all metrics were added to the results. The test is purely functional,
+ i.e. it only checks if the metrics are present and non-zero.
+ """
+
+ def setUp(self):
+ self._options = options_for_unittests.GetCopy()
+ self._options.browser_options.wpr_mode = wpr_modes.WPR_OFF
+ self._options.raster_record_repeat = 1
+ self._options.start_wait_time = 0.0
+ self._options.stop_wait_time = 2.0
+
+ def testRasterizeAndRecord(self):
+ ps = self.CreatePageSetFromFileInUnittestDataDir('blank.html')
+ measurement = rasterize_and_record.RasterizeAndRecord()
+ results = self.RunMeasurement(measurement, ps, options=self._options)
+ self.assertEquals(0, len(results.failures))
+
+ rasterize_time = results.FindAllPageSpecificValuesNamed('rasterize_time')
+ self.assertEquals(len(rasterize_time), 1)
+ self.assertGreater(rasterize_time[0].GetRepresentativeNumber(), 0)
+
+ record_time = results.FindAllPageSpecificValuesNamed('record_time')
+ self.assertEquals(len(record_time), 1)
+ self.assertGreater(record_time[0].GetRepresentativeNumber(), 0)
+
+ rasterized_pixels = results.FindAllPageSpecificValuesNamed(
+ 'rasterized_pixels')
+ self.assertEquals(len(rasterized_pixels), 1)
+ self.assertGreater(rasterized_pixels[0].GetRepresentativeNumber(), 0)
+
+ recorded_pixels = results.FindAllPageSpecificValuesNamed('recorded_pixels')
+ self.assertEquals(len(recorded_pixels), 1)
+ self.assertGreater(recorded_pixels[0].GetRepresentativeNumber(), 0)
diff --git a/tools/perf/measurements/smoothness.py b/tools/perf/measurements/smoothness.py
index cc14a67f99..5032383384 100644
--- a/tools/perf/measurements/smoothness.py
+++ b/tools/perf/measurements/smoothness.py
@@ -4,7 +4,6 @@
from metrics import smoothness
from metrics import timeline
-from telemetry.page import page_test
from telemetry.page import page_measurement
@@ -33,13 +32,11 @@ class Smoothness(page_measurement.PageMeasurement):
def CanRunForPage(self, page):
return hasattr(page, 'smoothness')
- def WillRunAction(self, page, tab, action):
+ def WillRunActions(self, page, tab):
if self.options.metric == 'smoothness':
- compound_action = page_test.GetCompoundActionFromPage(
- page, self._action_name_to_run)
- self._metric = smoothness.SmoothnessMetric(compound_action)
+ self._metric = smoothness.SmoothnessMetric()
elif self.options.metric == 'timeline':
- self._metric = timeline.TimelineMetric(timeline.TRACING_MODE)
+ self._metric = timeline.ThreadTimesTimelineMetric()
self._metric.Start(page, tab)
@@ -47,6 +44,11 @@ class Smoothness(page_measurement.PageMeasurement):
tab.browser.platform.StartRawDisplayFrameRateMeasurement()
def DidRunAction(self, page, tab, action):
+ timeline_marker_name = action.GetTimelineMarkerName()
+ if self.options.metric == 'smoothness' and timeline_marker_name:
+ self._metric.AddTimelineMarkerNameToIncludeInMetric(timeline_marker_name)
+
+ def DidRunActions(self, page, tab):
if tab.browser.platform.IsRawDisplayFrameRateSupported():
tab.browser.platform.StopRawDisplayFrameRateMeasurement()
self._metric.Stop(page, tab)
diff --git a/tools/perf/measurements/smoothness_unittest.py b/tools/perf/measurements/smoothness_unittest.py
new file mode 100644
index 0000000000..5d2ba9a85f
--- /dev/null
+++ b/tools/perf/measurements/smoothness_unittest.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.
+from measurements import smoothness
+from telemetry.core import wpr_modes
+from telemetry.page import page_measurement_unittest_base
+from telemetry.unittest import options_for_unittests
+from metrics import timeline
+
+class SmoothnessUnitTest(
+ page_measurement_unittest_base.PageMeasurementUnitTestBase):
+ """Smoke test for smoothness measurement
+
+ Runs smoothness measurement on a simple page and verifies
+ that all metrics were added to the results. The test is purely functional,
+ i.e. it only checks if the metrics are present and non-zero.
+ """
+
+ def setUp(self):
+ self._options = options_for_unittests.GetCopy()
+ self._options.browser_options.wpr_mode = wpr_modes.WPR_OFF
+
+ def testSmoothnessWithSmoothnessMetric(self):
+ ps = self.CreatePageSetFromFileInUnittestDataDir('scrollable_page.html')
+ measurement = smoothness.Smoothness()
+ results = self.RunMeasurement(measurement, ps, options=self._options)
+ self.assertEquals(0, len(results.failures))
+
+ frame_times = results.FindAllPageSpecificValuesNamed('frame_times')
+ self.assertEquals(len(frame_times), 1)
+ self.assertGreater(frame_times[0].GetRepresentativeNumber(), 0)
+
+ mean_frame_time = results.FindAllPageSpecificValuesNamed('mean_frame_time')
+ self.assertEquals(len(mean_frame_time), 1)
+ self.assertGreater(mean_frame_time[0].GetRepresentativeNumber(), 0)
+
+ jank = results.FindAllPageSpecificValuesNamed('jank')
+ self.assertEquals(len(jank), 1)
+ self.assertGreater(jank[0].GetRepresentativeNumber(), 0)
+
+ mostly_smooth = results.FindAllPageSpecificValuesNamed('mostly_smooth')
+ self.assertEquals(len(mostly_smooth), 1)
+ self.assertGreaterEqual(mostly_smooth[0].GetRepresentativeNumber(), 0)
+
+
+ def testSmoothnessWithTimelineMetric(self):
+ ps = self.CreatePageSetFromFileInUnittestDataDir('scrollable_page.html')
+ measurement = smoothness.Smoothness()
+ timeline_options = self._options
+ timeline_options.metric = 'timeline'
+ results = self.RunMeasurement(measurement, ps, options = timeline_options)
+ self.assertEquals(0, len(results.failures))
+
+ for category in timeline.TimelineThreadCategories.values():
+ value_name = "thread_time_" + category + "_running_percentage"
+ thread_time = results.FindAllPageSpecificValuesNamed(value_name)
+ self.assertEquals(len(thread_time), 1)
diff --git a/tools/perf/metrics/media.py b/tools/perf/metrics/media.py
index 14e8920f35..57d0c3e7df 100644
--- a/tools/perf/metrics/media.py
+++ b/tools/perf/metrics/media.py
@@ -58,7 +58,11 @@ class MediaMetric(Metric):
for m in metrics:
if m.startswith(metric):
special_label = m[len(metric):]
- results.Add(trace + special_label, unit, metrics[m],
+ if isinstance(metrics[m], list):
+ values = [float(v) for v in metrics[m]]
+ else:
+ values = float(metrics[m])
+ results.Add(trace + special_label, unit, values,
chart_name=metric, data_type='default')
trace = media_metric['id']
diff --git a/tools/perf/metrics/rendering_stats.py b/tools/perf/metrics/rendering_stats.py
index 1114a2f032..30220de73d 100644
--- a/tools/perf/metrics/rendering_stats.py
+++ b/tools/perf/metrics/rendering_stats.py
@@ -37,12 +37,7 @@ class RenderingStats(object):
marker.start+marker.duration)
def initMainThreadStatsFromTimeline(self, start, end):
- # TODO(ernstm): Remove when CL with new event names was rolled into
- # reference build.
event_name = 'BenchmarkInstrumentation::MainThreadRenderingStats'
- for event in self.renderer_process.IterAllSlicesOfName(
- 'MainThreadRenderingStats::IssueTraceEvent'):
- event_name = 'MainThreadRenderingStats::IssueTraceEvent'
events = []
for event in self.renderer_process.IterAllSlicesOfName(event_name):
if event.start >= start and event.end <= end:
@@ -53,12 +48,7 @@ class RenderingStats(object):
first_frame = True
for event in events:
- # TODO(ernstm): remove screen_frame_count when RenderingStats
- # cleanup CL was picked up by the reference build.
- if 'frame_count' in event.args['data']:
- frame_count = event.args['data']['frame_count']
- else:
- frame_count = event.args['data']['screen_frame_count']
+ frame_count = event.args['data']['frame_count']
if frame_count > 1:
raise ValueError, 'trace contains multi-frame render stats'
if frame_count == 1:
@@ -78,12 +68,7 @@ class RenderingStats(object):
event.args['data']['recorded_pixel_count'])
def initImplThreadStatsFromTimeline(self, start, end):
- # TODO(ernstm): Remove when CL with new event names was rolled into
- # reference build.
event_name = 'BenchmarkInstrumentation::ImplThreadRenderingStats'
- for event in self.renderer_process.IterAllSlicesOfName(
- 'ImplThreadRenderingStats::IssueTraceEvent'):
- event_name = 'ImplThreadRenderingStats::IssueTraceEvent'
events = []
for event in self.renderer_process.IterAllSlicesOfName(event_name):
if event.start >= start and event.end <= end:
@@ -94,12 +79,7 @@ class RenderingStats(object):
first_frame = True
for event in events:
- # TODO(ernstm): remove screen_frame_count when RenderingStats
- # cleanup CL was picked up by the reference build.
- if 'frame_count' in event.args['data']:
- frame_count = event.args['data']['frame_count']
- else:
- frame_count = event.args['data']['screen_frame_count']
+ frame_count = event.args['data']['frame_count']
if frame_count > 1:
raise ValueError, 'trace contains multi-frame render stats'
if frame_count == 1:
diff --git a/tools/perf/metrics/rendering_stats_unittest.py b/tools/perf/metrics/rendering_stats_unittest.py
index ec18597b8c..42eec9ff8c 100644
--- a/tools/perf/metrics/rendering_stats_unittest.py
+++ b/tools/perf/metrics/rendering_stats_unittest.py
@@ -60,7 +60,8 @@ def AddMainThreadRenderingStats(mock_timer, thread, first_frame,
# Add a slice with the event data to the given thread.
thread.PushCompleteSlice(
'benchmark', 'BenchmarkInstrumentation::MainThreadRenderingStats',
- timestamp, 0.0, {'data': data})
+ timestamp, duration=0.0, thread_timestamp=None, thread_duration=None,
+ args={'data': data})
if not ref_stats:
return
@@ -97,7 +98,8 @@ def AddImplThreadRenderingStats(mock_timer, thread, first_frame,
# Add a slice with the event data to the given thread.
thread.PushCompleteSlice(
'benchmark', 'BenchmarkInstrumentation::ImplThreadRenderingStats',
- timestamp, 0.0, {'data': data})
+ timestamp, duration=0.0, thread_timestamp=None, thread_duration=None,
+ args={'data': data})
if not ref_stats:
return
diff --git a/tools/perf/metrics/smoothness.py b/tools/perf/metrics/smoothness.py
index ff20c441b8..2016631e60 100644
--- a/tools/perf/metrics/smoothness.py
+++ b/tools/perf/metrics/smoothness.py
@@ -24,43 +24,27 @@ class NoSupportedActionError(page_measurement.MeasurementFailure):
'None of the actions is supported by smoothness measurement')
-def GetTimelineMarkerNamesFromAction(compound_action):
- timeline_marker_names = []
- if not isinstance(compound_action, list):
- compound_action = [compound_action]
- for action in compound_action:
- if action.GetTimelineMarkerName():
- timeline_marker_names.append(action.GetTimelineMarkerName())
- if not timeline_marker_names:
- raise NoSupportedActionError()
- return timeline_marker_names
-
-
class SmoothnessMetric(Metric):
- def __init__(self, compound_action):
+ def __init__(self):
super(SmoothnessMetric, self).__init__()
self._stats = None
- self._compound_action = compound_action
+ self._timeline_marker_names = []
+
+ def AddTimelineMarkerNameToIncludeInMetric(self, timeline_marker_name):
+ self._timeline_marker_names.append(timeline_marker_name)
def Start(self, page, tab):
- # TODO(ermst): Remove "webkit" category after Blink r157377 is picked up by
- # the reference builds.
- tab.browser.StartTracing('webkit,webkit.console,benchmark', 60)
+ tab.browser.StartTracing('webkit.console,benchmark', 60)
tab.ExecuteJavaScript('console.time("' + TIMELINE_MARKER + '")')
def Stop(self, page, tab):
tab.ExecuteJavaScript('console.timeEnd("' + TIMELINE_MARKER + '")')
timeline_model = tab.browser.StopTracing().AsTimelineModel()
- smoothness_marker = timeline_model.FindTimelineMarkers(TIMELINE_MARKER)
- timeline_marker_names = GetTimelineMarkerNamesFromAction(
- self._compound_action)
try:
timeline_markers = timeline_model.FindTimelineMarkers(
- timeline_marker_names)
- except MarkerMismatchError:
- # TODO(ernstm): re-raise exception as MeasurementFailure when the
- # reference build was updated.
- timeline_markers = smoothness_marker
+ self._timeline_marker_names)
+ except MarkerMismatchError as e:
+ raise page_measurement.MeasurementFailure(str(e))
except MarkerOverlapError as e:
raise page_measurement.MeasurementFailure(str(e))
@@ -91,6 +75,6 @@ class SmoothnessMetric(Metric):
results.Add('jank', '', round(jank, 4))
# Are we hitting 60 fps for 95 percent of all frames?
- # We use 17ms as a slightly looser threshold, instead of 1000.0/60.0.
+ # We use 19ms as a somewhat looser threshold, instead of 1000.0/60.0.
percentile_95 = statistics.Percentile(self._stats.frame_times, 95.0)
- results.Add('mostly_smooth', '', 1.0 if percentile_95 < 17.0 else 0.0)
+ results.Add('mostly_smooth', '', 1.0 if percentile_95 < 19.0 else 0.0)
diff --git a/tools/perf/metrics/smoothness_unittest.py b/tools/perf/metrics/smoothness_unittest.py
deleted file mode 100644
index f1b1677deb..0000000000
--- a/tools/perf/metrics/smoothness_unittest.py
+++ /dev/null
@@ -1,222 +0,0 @@
-# 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 random
-import unittest
-
-from metrics import smoothness
-from metrics import statistics
-from metrics import rendering_stats
-from telemetry.core.trace_result import TraceResult
-from telemetry.core.backends.chrome.tracing_backend import ChromeRawTraceResult
-from telemetry.page import page
-from telemetry.page.page_measurement_results import PageMeasurementResults
-
-SYNTHETIC_GESTURE_MARKER = 'SyntheticGestureController::running'
-RENDERER_PROCESS_MARKER = 'RendererProcessMarker'
-
-
-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.
- """
- def __init__(self):
- self.microseconds = 0
-
- def Advance(self, low=0, high=100000):
- duration = random.randint(low, high)
- self.microseconds += duration
- return duration
-
-
-class MockFrame(object):
- """Mocks rendering, texture and latency stats for a single frame."""
- def __init__(self, mock_timer):
- """ Initialize the stats to random values """
- self.start = mock_timer.microseconds
- self.main_stats = {}
- self.impl_stats = {}
- self.texture_stats = {}
- self.latency_stats = {}
- self.main_stats['frame_count'] = 0
- self.main_stats['paint_time'] = mock_timer.Advance()
- self.main_stats['painted_pixel_count'] = random.randint(0, 2000000)
- self.main_stats['record_time'] = mock_timer.Advance()
- self.main_stats['recorded_pixel_count'] = random.randint(0, 2000000)
- self.impl_stats['frame_count'] = 1
- self.impl_stats['rasterize_time'] = mock_timer.Advance()
- self.impl_stats['rasterized_pixel_count'] = random.randint(0, 2000000)
- self.end = mock_timer.microseconds
- self.duration = self.end - self.start
-
- def AppendTraceEventForMainThreadStats(self, trace_events):
- """Appends a trace event with the main thread stats.
-
- The trace event is a dict with the following keys:
- 'name',
- 'tts' (thread timestamp),
- 'pid' (process id),
- 'ts' (timestamp),
- 'cat' (category),
- 'tid' (thread id),
- 'ph' (phase),
- 'args' (a dict with the key 'data').
- This is related to src/base/debug/trace_event.h.
- """
- event = {'name': 'MainThreadRenderingStats::IssueTraceEvent',
- 'tts': self.end,
- 'pid': 20978,
- 'ts': self.end,
- 'cat': 'benchmark',
- 's': 't',
- 'tid': 11,
- 'ph': 'i',
- 'args': {'data': self.main_stats}}
- trace_events.append(event)
-
- def AppendTraceEventForImplThreadStats(self, trace_events):
- """Appends a trace event with the impl thread stat."""
- event = {'name': 'ImplThreadRenderingStats::IssueTraceEvent',
- 'tts': self.end,
- 'pid': 20978,
- 'ts': self.end,
- 'cat': 'benchmark',
- 's': 't',
- 'tid': 11,
- 'ph': 'i',
- 'args': {'data': self.impl_stats}}
- trace_events.append(event)
-
-
-class SmoothnessMetricUnitTest(unittest.TestCase):
- def testCalcResultsTraceEvents(self):
- # Make the test repeatable by seeding the random number generator
- # (which is used by the mock timer) with a constant number.
- random.seed(1234567)
- mock_timer = MockTimer()
- trace_events = []
- total_time_seconds = 0.0
- num_frames_sent = 0.0
- first_frame = True
- previous_frame_time = None
- # This list represents time differences between frames in milliseconds.
- expected_frame_times = []
-
- # Append start trace events for the timeline marker and gesture marker,
- # with some amount of time in between them.
- trace_events.append({'name': RENDERER_PROCESS_MARKER,
- 'tts': mock_timer.microseconds,
- 'args': {},
- 'pid': 20978,
- 'ts': mock_timer.microseconds,
- 'cat': 'webkit',
- 'tid': 11,
- 'ph': 'S', # Phase: start.
- 'id': '0x12345'})
- mock_timer.Advance()
- trace_events.append({'name': SYNTHETIC_GESTURE_MARKER,
- 'tts': mock_timer.microseconds,
- 'args': {},
- 'pid': 20978,
- 'ts': mock_timer.microseconds,
- 'cat': 'webkit',
- 'tid': 11,
- 'ph': 'S',
- 'id': '0xabcde'})
-
- # Generate 100 random mock frames and append their trace events.
- for _ in xrange(0, 100):
- mock_frame = MockFrame(mock_timer)
- mock_frame.AppendTraceEventForMainThreadStats(trace_events)
- mock_frame.AppendTraceEventForImplThreadStats(trace_events)
- # Exclude the first frame, because it may have started before the
- # benchmark run.
- if not first_frame:
- total_time_seconds += mock_frame.duration / 1e6
- num_frames_sent += mock_frame.main_stats['frame_count']
- num_frames_sent += mock_frame.impl_stats['frame_count']
- first_frame = False
- current_frame_time = mock_timer.microseconds / 1000.0
- if previous_frame_time:
- difference = current_frame_time - previous_frame_time
- difference = round(difference, 2)
- expected_frame_times.append(difference)
- previous_frame_time = current_frame_time
-
- # Append finish trace events for the timeline and gesture markers, in the
- # reverse order from how they were added, with some time in between.
- trace_events.append({'name': SYNTHETIC_GESTURE_MARKER,
- 'tts': mock_timer.microseconds,
- 'args': {},
- 'pid': 20978,
- 'ts': mock_timer.microseconds,
- 'cat': 'webkit',
- 'tid': 11,
- 'ph': 'F', # Phase: finish.
- 'id': '0xabcde'})
- mock_timer.Advance()
- trace_events.append({'name': RENDERER_PROCESS_MARKER,
- 'tts': mock_timer.microseconds,
- 'args': {},
- 'pid': 20978,
- 'ts': mock_timer.microseconds,
- 'cat': 'webkit',
- 'tid': 11,
- 'ph': 'F',
- 'id': '0x12345'})
-
- # Create a timeline object from the trace.
- trace_result = TraceResult(ChromeRawTraceResult(trace_events))
- timeline = trace_result.AsTimelineModel()
-
- # Find the timeline marker and gesture marker in the timeline,
- # and create a RenderingStats object.
- renderer_process_markers = timeline.FindTimelineMarkers(
- RENDERER_PROCESS_MARKER)
- self.assertEquals(len(renderer_process_markers), 1)
- renderer_process = renderer_process_markers[0].start_thread.parent
- timeline_markers = timeline.FindTimelineMarkers(
- SYNTHETIC_GESTURE_MARKER)
- stats = rendering_stats.RenderingStats(renderer_process, timeline_markers)
-
- # Make a results object and add results to it from the smoothness metric.
- results = PageMeasurementResults()
- p0 = page.Page('http://foo.com/', None)
- results.WillMeasurePage(p0)
- smoothness_metric = smoothness.SmoothnessMetric(None)
- smoothness_metric.SetStats(stats)
- smoothness_metric.AddResults(None, results)
- results.DidMeasurePage()
-
- frame_times = results.FindPageSpecificValuesForPage(p0, 'frame_times')[0]
- self.assertEquals(
- expected_frame_times,
- frame_times.values)
-
- mean_frame_time = results.FindPageSpecificValuesForPage(
- p0, 'mean_frame_time')[0]
- self.assertAlmostEquals(
- 1000.0 * (total_time_seconds / num_frames_sent),
- mean_frame_time.value,
- places=2)
-
- # We don't verify the correctness of the discrepancy computation itself,
- # because we have a separate unit test for that purpose.
- jank = results.FindPageSpecificValuesForPage(p0, 'jank')[0]
- self.assertAlmostEquals(
- statistics.FrameDiscrepancy(stats.frame_timestamps, True),
- jank.value,
- places=4)
-
- # We do not verify the correctness of Percentile here; Percentile should
- # have its own test.
- # The 17 here represents a threshold of 17 ms; this should match the value
- # in the smoothness metric.
- mostly_smooth = results.FindPageSpecificValuesForPage(
- p0, 'mostly_smooth')[0]
- self.assertEquals(
- statistics.Percentile(expected_frame_times, 95.0) < 17.0,
- mostly_smooth.value)
diff --git a/tools/perf/metrics/speedindex.py b/tools/perf/metrics/speedindex.py
index 9a45341340..0dd013865e 100644
--- a/tools/perf/metrics/speedindex.py
+++ b/tools/perf/metrics/speedindex.py
@@ -7,6 +7,7 @@ import os
from metrics import Metric
+
class SpeedIndexMetric(Metric):
"""The speed index metric is one way of measuring page load speed.
@@ -15,11 +16,12 @@ class SpeedIndexMetric(Metric):
portion of the screen. It includes paint events that occur after the
onload event, and it doesn't include time loading things off-screen.
- This speed index metric is based on the devtools speed index at
- WebPageTest.org (WPT). For more info see: http://goo.gl/e7AH5l
+ This speed index metric is based on WebPageTest.org (WPT).
+ For more info see: http://goo.gl/e7AH5l
"""
def __init__(self):
super(SpeedIndexMetric, self).__init__()
+ self._impl = None
self._script_is_loaded = False
self._is_finished = False
with open(os.path.join(os.path.dirname(__file__), 'speedindex.js')) as f:
@@ -32,21 +34,23 @@ class SpeedIndexMetric(Metric):
a PageMeasurement, so that all the events can be captured. If it's called
in DidNavigateToPage, that will be too late.
"""
- tab.StartTimelineRecording()
+ self._impl = (VideoSpeedIndexImpl(tab) if tab.video_capture_supported else
+ PaintRectSpeedIndexImpl(tab))
+ self._impl.Start()
self._script_is_loaded = False
self._is_finished = False
def Stop(self, _, tab):
"""Stop timeline recording."""
- assert self.IsFinished(tab)
- tab.StopTimelineRecording()
+ assert self._impl, 'Must call Start() before Stop()'
+ assert self.IsFinished(tab), 'Must wait for IsFinished() before Stop()'
+ self._impl.Stop()
# Optional argument chart_name is not in base class Metric.
# pylint: disable=W0221
def AddResults(self, tab, results, chart_name=None):
"""Calculate the speed index and add it to the results."""
- events = tab.timeline_model.GetAllEvents()
- index = _SpeedIndex(events, _GetViewportSize(tab))
+ index = self._impl.CalculateSpeedIndex()
results.Add('speed_index', 'ms', index, chart_name=chart_name)
def IsFinished(self, tab):
@@ -86,166 +90,218 @@ class SpeedIndexMetric(Metric):
return self._is_finished
-def _GetViewportSize(tab):
- """Returns dimensions of the viewport."""
- return tab.EvaluateJavaScript('[ window.innerWidth, window.innerHeight ]')
-
+class SpeedIndexImpl(object):
-def _SpeedIndex(events, viewport):
- """Calculate the speed index of a page load from a list of events.
+ def __init__(self, tab):
+ """Constructor.
- The speed index number conceptually represents the number of milliseconds
- that the page was "visually incomplete". If the page were 0% complete for
- 1000 ms, then the score would be 1000; if it were 0% complete for 100 ms
- then 90% complete (ie 10% incomplete) for 900 ms, then the score would be
- 1.0*100 + 0.1*900 = 190.
+ Args:
+ tab: The telemetry.core.Tab object for which to calculate SpeedIndex.
+ """
+ self.tab = tab
- Args:
- events: A list of telemetry.core.timeline.slice.Slice objects
- viewport: A tuple (width, height) of the window.
+ def Start(self):
+ raise NotImplementedError()
- Returns:
- A single number, milliseconds of visual incompleteness.
- """
- paint_events = _IncludedPaintEvents(events)
- time_area_dict = _TimeAreaDict(paint_events, viewport)
- time_completeness_dict = _TimeCompletenessDict(time_area_dict)
- # The first time interval starts from the start of the first event.
- prev_time = events[0].start
- prev_completeness = 0.0
- speed_index = 0.0
- for time, completeness in sorted(time_completeness_dict.items()):
- # Add the incemental value for the interval just before this event.
- elapsed_time = time - prev_time
- incompleteness = (1.0 - prev_completeness)
- speed_index += elapsed_time * incompleteness
-
- # Update variables for next iteration.
- prev_completeness = completeness
- prev_time = time
-
- return speed_index
-
-
-def _TimeCompletenessDict(time_area_dict):
- """Make a dictionary of time to visual completeness.
-
- In the WPT PHP implementation, this is also called 'visual progress'.
- """
- total_area = sum(time_area_dict.values())
- assert total_area > 0.0, 'Total paint event area must be greater than 0.'
- completeness = 0.0
- time_completeness_dict = {}
- for time, area in sorted(time_area_dict.items()):
- completeness += float(area) / total_area
- # Visual progress is rounded to the nearest percentage point as in WPT.
- time_completeness_dict[time] = round(completeness, 2)
- return time_completeness_dict
+ def Stop(self):
+ raise NotImplementedError()
+ def GetTimeCompletenessList(self):
+ """Returns a list of time to visual completeness tuples.
-def _IncludedPaintEvents(events):
- """Get all events that are counted in the calculation of the speed index.
+ In the WPT PHP implementation, this is also called 'visual progress'.
+ """
+ raise NotImplementedError()
- There's one category of paint event that's filtered out: paint events
- that occur before the first 'ResourceReceiveResponse' and 'Layout' events.
+ def CalculateSpeedIndex(self):
+ """Calculate the speed index.
- Previously in the WPT speed index, paint events that contain children paint
- events were also filtered out.
- """
- def FirstLayoutTime(events):
- """Get the start time of the first layout after a resource received."""
- has_received_response = False
- for event in events:
- if event.name == 'ResourceReceiveResponse':
- has_received_response = True
- elif has_received_response and event.name == 'Layout':
- return event.start
- assert False, 'There were no layout events after resource receive events.'
-
- first_layout_time = FirstLayoutTime(events)
- paint_events = [e for e in events
- if e.start >= first_layout_time and e.name == 'Paint']
- return paint_events
-
-
-def _TimeAreaDict(paint_events, viewport):
- """Make a dict from time to adjusted area value for events at that time.
-
- The adjusted area value of each paint event is determined by how many paint
- events cover the same rectangle, and whether it's a full-window paint event.
- "Adjusted area" can also be thought of as "points" of visual completeness --
- each rectangle has a certain number of points and these points are
- distributed amongst the paint events that paint that rectangle.
-
- Args:
- paint_events: A list of paint events
- viewport: A tuple (width, height) of the window.
-
- Returns:
- A dictionary of times of each paint event (in milliseconds) to the
- adjusted area that the paint event is worth.
- """
- width, height = viewport
- fullscreen_area = width * height
-
- def ClippedArea(rectangle):
- """Returns rectangle area clipped to viewport size."""
- _, x0, y0, x1, y1 = rectangle
- x0 = max(0, x0)
- y0 = max(0, y0)
- x1 = min(width, x1)
- y1 = min(height, y1)
- return max(0, x1 - x0) * max(0, y1 - y0)
-
- grouped = _GroupEventByRectangle(paint_events)
- event_area_dict = collections.defaultdict(int)
-
- for rectangle, events in grouped.items():
- # The area points for each rectangle are divided up among the paint
- # events in that rectangle.
- area = ClippedArea(rectangle)
- update_count = len(events)
- adjusted_area = float(area) / update_count
-
- # Paint events for the largest-area rectangle are counted as 50%.
- if area == fullscreen_area:
- adjusted_area /= 2
-
- for event in events:
- # The end time for an event is used for that event's time.
- event_time = event.end
- event_area_dict[event_time] += adjusted_area
-
- return event_area_dict
-
-
-def _GetRectangle(paint_event):
- """Get the specific rectangle on the screen for a paint event.
-
- Each paint event belongs to a frame (as in html <frame> or <iframe>).
- This, together with location and dimensions, comprises a rectangle.
- In the WPT source, this 'rectangle' is also called a 'region'.
- """
- def GetBox(quad):
- """Gets top-left and bottom-right coordinates from paint event.
+ The speed index number conceptually represents the number of milliseconds
+ that the page was "visually incomplete". If the page were 0% complete for
+ 1000 ms, then the score would be 1000; if it were 0% complete for 100 ms
+ then 90% complete (ie 10% incomplete) for 900 ms, then the score would be
+ 1.0*100 + 0.1*900 = 190.
- In the timeline data from devtools, paint rectangle dimensions are
- represented x-y coordinates of four corners, clockwise from the top-left.
- See: function WebInspector.TimelinePresentationModel.quadFromRectData
- in file src/out/Debug/obj/gen/devtools/TimelinePanel.js.
+ Returns:
+ A single number, milliseconds of visual incompleteness.
"""
- x0, y0, _, _, x1, y1, _, _ = quad
- return (x0, y0, x1, y1)
-
- assert paint_event.name == 'Paint'
- frame = paint_event.args['frameId']
- return (frame,) + GetBox(paint_event.args['data']['clip'])
-
+ time_completeness_list = self.GetTimeCompletenessList()
+ prev_completeness = 0.0
+ speed_index = 0.0
+ prev_time = time_completeness_list[0][0]
+ for time, completeness in time_completeness_list:
+ # Add the incemental value for the interval just before this event.
+ elapsed_time = time - prev_time
+ incompleteness = (1.0 - prev_completeness)
+ speed_index += elapsed_time * incompleteness
+
+ # Update variables for next iteration.
+ prev_completeness = completeness
+ prev_time = time
+ return speed_index
+
+
+class VideoSpeedIndexImpl(SpeedIndexImpl):
+
+ def __init__(self, tab):
+ super(VideoSpeedIndexImpl, self).__init__(tab)
+ assert self.tab.video_capture_supported
+ self._time_completeness_list = None
+
+ def Start(self):
+ # TODO(tonyg): Bitrate is arbitrary here. Experiment with screen capture
+ # overhead vs. speed index accuracy and set the bitrate appropriately.
+ self.tab.StartVideoCapture(min_bitrate_mbps=4)
+
+ def Stop(self):
+ self._time_completeness_list = []
+ self.tab.StopVideoCapture()
+ # TODO(tonyg/szym): Implement this.
+ raise NotImplementedError('SpeedIndex video calculation not implemented.')
+
+ def GetTimeCompletenessList(self):
+ assert self._time_completeness_list, 'Must call Stop() first.'
+ return self._time_completeness_list
+
+
+class PaintRectSpeedIndexImpl(SpeedIndexImpl):
+
+ def __init__(self, tab):
+ super(PaintRectSpeedIndexImpl, self).__init__(tab)
+
+ def Start(self):
+ self.tab.StartTimelineRecording()
+
+ def Stop(self):
+ self.tab.StopTimelineRecording()
+
+ def GetTimeCompletenessList(self):
+ events = self.tab.timeline_model.GetAllEvents()
+ viewport = self._GetViewportSize()
+ paint_events = self._IncludedPaintEvents(events)
+ time_area_dict = self._TimeAreaDict(paint_events, viewport)
+ total_area = sum(time_area_dict.values())
+ assert total_area > 0.0, 'Total paint event area must be greater than 0.'
+ completeness = 0.0
+ time_completeness_list = []
+
+ # TODO(tonyg): This sets the start time to the start of the first paint
+ # event. That can't be correct. The start time should be navigationStart.
+ # Since the previous screen is not cleared at navigationStart, we should
+ # probably assume the completeness is 0 until the first paint and add the
+ # time of navigationStart as the start. We need to confirm what WPT does.
+ time_completeness_list.append(
+ (self.tab.timeline_model.GetAllEvents()[0].start, completeness))
+
+ for time, area in sorted(time_area_dict.items()):
+ completeness += float(area) / total_area
+ # Visual progress is rounded to the nearest percentage point as in WPT.
+ time_completeness_list.append((time, round(completeness, 2)))
+ return time_completeness_list
+
+ def _GetViewportSize(self):
+ """Returns dimensions of the viewport."""
+ return self.tab.EvaluateJavaScript(
+ '[ window.innerWidth, window.innerHeight ]')
+
+ def _IncludedPaintEvents(self, events):
+ """Get all events that are counted in the calculation of the speed index.
+
+ There's one category of paint event that's filtered out: paint events
+ that occur before the first 'ResourceReceiveResponse' and 'Layout' events.
+
+ Previously in the WPT speed index, paint events that contain children paint
+ events were also filtered out.
+ """
+ def FirstLayoutTime(events):
+ """Get the start time of the first layout after a resource received."""
+ has_received_response = False
+ for event in events:
+ if event.name == 'ResourceReceiveResponse':
+ has_received_response = True
+ elif has_received_response and event.name == 'Layout':
+ return event.start
+ assert False, 'There were no layout events after resource receive events.'
+
+ first_layout_time = FirstLayoutTime(events)
+ paint_events = [e for e in events
+ if e.start >= first_layout_time and e.name == 'Paint']
+ return paint_events
+
+ def _TimeAreaDict(self, paint_events, viewport):
+ """Make a dict from time to adjusted area value for events at that time.
+
+ The adjusted area value of each paint event is determined by how many paint
+ events cover the same rectangle, and whether it's a full-window paint event.
+ "Adjusted area" can also be thought of as "points" of visual completeness --
+ each rectangle has a certain number of points and these points are
+ distributed amongst the paint events that paint that rectangle.
+
+ Args:
+ paint_events: A list of paint events
+ viewport: A tuple (width, height) of the window.
-def _GroupEventByRectangle(paint_events):
- """Group all paint events according to the rectangle that they update."""
- result = collections.defaultdict(list)
- for event in paint_events:
- assert event.name == 'Paint'
- result[_GetRectangle(event)].append(event)
- return result
+ Returns:
+ A dictionary of times of each paint event (in milliseconds) to the
+ adjusted area that the paint event is worth.
+ """
+ width, height = viewport
+ fullscreen_area = width * height
+
+ def ClippedArea(rectangle):
+ """Returns rectangle area clipped to viewport size."""
+ _, x0, y0, x1, y1 = rectangle
+ clipped_width = max(0, min(width, x1) - max(0, x0))
+ clipped_height = max(0, min(height, y1) - max(0, y0))
+ return clipped_width * clipped_height
+
+ grouped = self._GroupEventByRectangle(paint_events)
+ event_area_dict = collections.defaultdict(int)
+
+ for rectangle, events in grouped.items():
+ # The area points for each rectangle are divided up among the paint
+ # events in that rectangle.
+ area = ClippedArea(rectangle)
+ update_count = len(events)
+ adjusted_area = float(area) / update_count
+
+ # Paint events for the largest-area rectangle are counted as 50%.
+ if area == fullscreen_area:
+ adjusted_area /= 2
+
+ for event in events:
+ # The end time for an event is used for that event's time.
+ event_time = event.end
+ event_area_dict[event_time] += adjusted_area
+
+ return event_area_dict
+
+ def _GetRectangle(self, paint_event):
+ """Get the specific rectangle on the screen for a paint event.
+
+ Each paint event belongs to a frame (as in html <frame> or <iframe>).
+ This, together with location and dimensions, comprises a rectangle.
+ In the WPT source, this 'rectangle' is also called a 'region'.
+ """
+ def GetBox(quad):
+ """Gets top-left and bottom-right coordinates from paint event.
+
+ In the timeline data from devtools, paint rectangle dimensions are
+ represented x-y coordinates of four corners, clockwise from the top-left.
+ See: function WebInspector.TimelinePresentationModel.quadFromRectData
+ in file src/out/Debug/obj/gen/devtools/TimelinePanel.js.
+ """
+ x0, y0, _, _, x1, y1, _, _ = quad
+ return (x0, y0, x1, y1)
+
+ assert paint_event.name == 'Paint'
+ frame = paint_event.args['frameId']
+ return (frame,) + GetBox(paint_event.args['data']['clip'])
+
+ def _GroupEventByRectangle(self, paint_events):
+ """Group all paint events according to the rectangle that they update."""
+ result = collections.defaultdict(list)
+ for event in paint_events:
+ assert event.name == 'Paint'
+ result[self._GetRectangle(event)].append(event)
+ return result
diff --git a/tools/perf/metrics/speedindex_unittest.py b/tools/perf/metrics/speedindex_unittest.py
index 856127f71e..d92f43f1b4 100644
--- a/tools/perf/metrics/speedindex_unittest.py
+++ b/tools/perf/metrics/speedindex_unittest.py
@@ -19,19 +19,50 @@ _SAMPLE_DATA = json.load(open(os.path.join(_TEST_DIR, 'sample_timeline.json')))
_SAMPLE_EVENTS = model.TimelineModel(event_data=_SAMPLE_DATA).GetAllEvents()
+class FakeTimelineModel(object):
+
+ def __init__(self):
+ self._events = []
+
+ def SetAllEvents(self, events):
+ self._events = events
+
+ def GetAllEvents(self):
+ return self._events
+
+
+class FakeTab(object):
+
+ def __init__(self):
+ self._timeline_model = FakeTimelineModel()
+ self._javascript_result = None
+
+ @property
+ def timeline_model(self):
+ return self._timeline_model
+
+ def SetEvaluateJavaScriptResult(self, result):
+ self._javascript_result = result
+
+ def EvaluateJavaScript(self, _):
+ return self._javascript_result
+
+
class IncludedPaintEventsTest(unittest.TestCase):
def testNumberPaintEvents(self):
+ impl = speedindex.PaintRectSpeedIndexImpl(None)
# In the sample data, there's one event that occurs before the layout event,
# and one paint event that's not a leaf paint event.
- events = speedindex._IncludedPaintEvents(_SAMPLE_EVENTS)
+ events = impl._IncludedPaintEvents(_SAMPLE_EVENTS)
self.assertEquals(len(events), 5)
class TimeAreaDictTest(unittest.TestCase):
def testAdjustedAreaDict(self):
- paint_events = speedindex._IncludedPaintEvents(_SAMPLE_EVENTS)
+ impl = speedindex.PaintRectSpeedIndexImpl(None)
+ paint_events = impl._IncludedPaintEvents(_SAMPLE_EVENTS)
viewport = 1000, 1000
- time_area_dict = speedindex._TimeAreaDict(paint_events, viewport)
+ time_area_dict = impl._TimeAreaDict(paint_events, viewport)
self.assertEquals(len(time_area_dict), 4)
# The event that ends at time 100 is a fullscreen; it's discounted by half.
self.assertEquals(time_area_dict[100], 500000)
@@ -42,6 +73,8 @@ class TimeAreaDictTest(unittest.TestCase):
class SpeedIndexTest(unittest.TestCase):
def testWithSampleData(self):
+ tab = FakeTab()
+ impl = speedindex.PaintRectSpeedIndexImpl(tab)
viewport = 1000, 1000
# Add up the parts of the speed index for each time interval.
# Each part is the time interval multiplied by the proportion of the
@@ -52,7 +85,9 @@ class SpeedIndexTest(unittest.TestCase):
parts.append(100 * 0.4)
parts.append(400 * 0.2)
expected = sum(parts) # 330.0
- actual = speedindex._SpeedIndex(_SAMPLE_EVENTS, viewport)
+ tab.timeline_model.SetAllEvents(_SAMPLE_EVENTS)
+ tab.SetEvaluateJavaScriptResult(viewport)
+ actual = impl.CalculateSpeedIndex()
self.assertEqual(actual, expected)
@@ -71,11 +106,15 @@ class WPTComparisonTest(unittest.TestCase):
filename: Filename of a json file which contains a
expected: The result expected based on the WPT result.
"""
+ tab = FakeTab()
+ impl = speedindex.PaintRectSpeedIndexImpl(tab)
file_path = os.path.join(_TEST_DIR, filename)
with open(file_path) as json_file:
raw_events = json.load(json_file)
- events = model.TimelineModel(event_data=raw_events).GetAllEvents()
- actual = speedindex._SpeedIndex(events, viewport)
+ tab.timeline_model.SetAllEvents(
+ model.TimelineModel(event_data=raw_events).GetAllEvents())
+ tab.SetEvaluateJavaScriptResult(viewport)
+ actual = impl.CalculateSpeedIndex()
# The result might differ by 1 or more milliseconds due to rounding,
# so compare to the nearest 10 milliseconds.
self.assertAlmostEqual(actual, expected, places=-1)
diff --git a/tools/perf/metrics/timeline.py b/tools/perf/metrics/timeline.py
index c7280078c3..c363612474 100644
--- a/tools/perf/metrics/timeline.py
+++ b/tools/perf/metrics/timeline.py
@@ -9,7 +9,7 @@ TRACING_MODE = 'tracing-mode'
TIMELINE_MODE = 'timeline-mode'
class TimelineMetric(Metric):
- def __init__(self, mode, thread_filter = None):
+ def __init__(self, mode):
''' Initializes a TimelineMetric object.
mode: TRACING_MODE or TIMELINE_MODE
@@ -25,14 +25,10 @@ class TimelineMetric(Metric):
assert mode in (TRACING_MODE, TIMELINE_MODE)
super(TimelineMetric, self).__init__()
self._mode = mode
- self._thread_filter = thread_filter
self._model = None
- self._renderer_process = None
def Start(self, page, tab):
self._model = None
- self._renderer_process = None
-
if self._mode == TRACING_MODE:
if not tab.browser.supports_tracing:
raise Exception('Not supported')
@@ -45,18 +41,32 @@ class TimelineMetric(Metric):
if self._mode == TRACING_MODE:
trace_result = tab.browser.StopTracing()
self._model = trace_result.AsTimelineModel()
- self._renderer_process = self._model.GetRendererProcessFromTab(tab)
else:
tab.StopTimelineRecording()
self._model = tab.timeline_model
- self._renderer_process = self._model.GetAllProcesses()[0]
+
+ def GetRendererProcess(self, tab):
+ if self._mode == TRACING_MODE:
+ return self._model.GetRendererProcessFromTab(tab)
+ else:
+ return self._model.GetAllProcesses()[0]
+
+ def AddResults(self, tab, results):
+ return
+
+
+class LoadTimesTimelineMetric(TimelineMetric):
+ def __init__(self, mode, thread_filter = None):
+ super(LoadTimesTimelineMetric, self).__init__(mode)
+ self._thread_filter = thread_filter
def AddResults(self, tab, results):
assert self._model
+ renderer_process = self.GetRendererProcess(tab)
events_by_name = collections.defaultdict(list)
- for thread in self._renderer_process.threads.itervalues():
+ for thread in renderer_process.threads.itervalues():
if self._thread_filter and not thread.name in self._thread_filter:
continue
@@ -76,7 +86,65 @@ class TimelineMetric(Metric):
results.Add(full_name + '_max', 'ms', biggest_jank)
results.Add(full_name + '_avg', 'ms', total / len(times))
- for counter_name, counter in self._renderer_process.counters.iteritems():
+ for counter_name, counter in renderer_process.counters.iteritems():
total = sum(counter.totals)
results.Add(counter_name, 'count', total)
results.Add(counter_name + '_avg', 'count', total / len(counter.totals))
+
+
+# We want to generate a consistant picture of our thread usage, despite
+# having several process configurations (in-proc-gpu/single-proc).
+# Since we can't isolate renderer threads in single-process mode, we
+# always sum renderer-process threads' times. We also sum all io-threads
+# for simplicity.
+TimelineThreadCategories = {
+ # These are matched exactly
+ "Chrome_InProcGpuThread": "GPU",
+ "CrGPUMain" : "GPU",
+ "AsyncTransferThread" : "GPU_transfer",
+ "CrBrowserMain" : "browser_main",
+ "Browser Compositor" : "browser_compositor",
+ "CrRendererMain" : "renderer_main",
+ "Compositor" : "renderer_compositor",
+ # These are matched by substring
+ "IOThread" : "IO",
+ "CompositorRasterWorker": "raster"
+}
+
+class ThreadTimesTimelineMetric(TimelineMetric):
+ def __init__(self):
+ super(ThreadTimesTimelineMetric, self).__init__(TRACING_MODE)
+
+ def AddResults(self, tab, results):
+ # Default each category to zero for consistant results.
+ category_clock_times = collections.defaultdict(float)
+ for category in TimelineThreadCategories.values():
+ category_clock_times[category] = 0
+
+ # Add up thread time for all threads we care about.
+ for thread in self._model.GetAllThreads():
+ # First determine if we care about this thread.
+ # Check substrings first, followed by exact matches
+ thread_category = None
+ for substring, category in TimelineThreadCategories.iteritems():
+ if substring in thread.name:
+ thread_category = category
+ if thread.name in TimelineThreadCategories:
+ thread_category = TimelineThreadCategories[thread.name]
+ if thread_category == None:
+ thread_category = "other"
+
+ # Sum and add top-level slice durations
+ clock_time = sum([event.duration for event in thread.toplevel_slices])
+ category_clock_times[thread_category] += clock_time
+
+ # Now report each category. We report the percentage of time that
+ # the thread is running rather than absolute time, to represent how
+ # busy the thread is. This needs to be interpretted when throughput
+ # is changed due to scheduling changes (eg. more frames produced
+ # in the same time period). It would be nice if we could correct
+ # for that somehow.
+ for category, category_time in category_clock_times.iteritems():
+ report_name = "thread_time_" + category + "_running_percentage"
+ time_as_percentage = (category_time / self._model.bounds.bounds) * 100
+ results.Add(report_name, '%', time_as_percentage)
diff --git a/tools/perf/page_sets/PRESUBMIT.py b/tools/perf/page_sets/PRESUBMIT.py
index bc183d6f40..f1c67f7c65 100644
--- a/tools/perf/page_sets/PRESUBMIT.py
+++ b/tools/perf/page_sets/PRESUBMIT.py
@@ -7,13 +7,20 @@ import re
import sys
-# Avoid leaking changes to global sys.path.
-_old_sys_path = sys.path
-try:
- sys.path.append(os.path.join(os.pardir, os.pardir, 'telemetry'))
- from telemetry.page import cloud_storage
-finally:
- sys.path = _old_sys_path
+def LoadSupport(input_api):
+ if 'cloud_storage' not in globals():
+ # Avoid leaking changes to global sys.path.
+ _old_sys_path = sys.path
+ try:
+ telemetry_path = os.path.join(os.path.dirname(os.path.dirname(
+ input_api.PresubmitLocalPath())), 'telemetry')
+ sys.path = [telemetry_path] + sys.path
+ from telemetry.page import cloud_storage
+ globals()['cloud_storage'] = cloud_storage
+ finally:
+ sys.path = _old_sys_path
+
+ return globals()['cloud_storage']
def _SyncFilesToCloud(input_api, output_api):
@@ -21,6 +28,9 @@ def _SyncFilesToCloud(input_api, output_api):
It validates all the hashes and skips upload if not necessary.
"""
+
+ cloud_storage = LoadSupport(input_api)
+
# Look in both buckets, in case the user uploaded the file manually. But this
# script focuses on WPR archives, so it only uploads to the internal bucket.
hashes_in_cloud_storage = cloud_storage.List(cloud_storage.INTERNAL_BUCKET)
diff --git a/tools/perf/page_sets/data/2012Q3.json b/tools/perf/page_sets/data/2012Q3.json
index acce1208ca..2a60b9520d 100644
--- a/tools/perf/page_sets/data/2012Q3.json
+++ b/tools/perf/page_sets/data/2012Q3.json
@@ -1,6 +1,5 @@
{
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
- "page_set": "../2012Q3.json",
"archives": {
"2012Q3_000.wpr" : [
"http://www.facebook.com/barackobama",
diff --git a/tools/perf/page_sets/data/browsermark.json b/tools/perf/page_sets/data/browsermark.json
index c5a9a18faf..d647c6e294 100644
--- a/tools/perf/page_sets/data/browsermark.json
+++ b/tools/perf/page_sets/data/browsermark.json
@@ -1,9 +1,8 @@
{
- "page_set": "../../benchmarks/browsermark.py",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"browsermark_000.wpr": [
"http://browsermark.rightware.com/tests/"
]
}
-} \ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/calendar_forward_backward.json b/tools/perf/page_sets/data/calendar_forward_backward.json
index cb6887e494..f4caeae6d9 100644
--- a/tools/perf/page_sets/data/calendar_forward_backward.json
+++ b/tools/perf/page_sets/data/calendar_forward_backward.json
@@ -1,5 +1,4 @@
{
- "page_set": "../calendar_forward_backward.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"calendar_forward_backward_000.wpr": [
diff --git a/tools/perf/page_sets/data/canvasmark.json b/tools/perf/page_sets/data/canvasmark.json
index d03a33a345..e7f64f260a 100644
--- a/tools/perf/page_sets/data/canvasmark.json
+++ b/tools/perf/page_sets/data/canvasmark.json
@@ -1,5 +1,4 @@
{
- "page_set": "../canvasmark.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"canvasmark_000.wpr": [
diff --git a/tools/perf/page_sets/data/gmail_alt_threadlist_conversation.json b/tools/perf/page_sets/data/gmail_alt_threadlist_conversation.json
index 131b3f0e22..d07ea456b1 100644
--- a/tools/perf/page_sets/data/gmail_alt_threadlist_conversation.json
+++ b/tools/perf/page_sets/data/gmail_alt_threadlist_conversation.json
@@ -1,5 +1,4 @@
{
- "page_set": "../gmail_alt_threadlist_conversation.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"gmail_alt_threadlist_conversation_000.wpr": [
diff --git a/tools/perf/page_sets/data/gmail_alt_two_labels.json b/tools/perf/page_sets/data/gmail_alt_two_labels.json
index 10e9244530..f5ff578719 100644
--- a/tools/perf/page_sets/data/gmail_alt_two_labels.json
+++ b/tools/perf/page_sets/data/gmail_alt_two_labels.json
@@ -1,5 +1,4 @@
{
- "page_set": "../gmail_alt_two_labels.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"gmail_alt_two_labels_000.wpr": [
diff --git a/tools/perf/page_sets/data/gmail_expand_collapse_conversation.json b/tools/perf/page_sets/data/gmail_expand_collapse_conversation.json
index 5500e9b29f..59b4ae629d 100644
--- a/tools/perf/page_sets/data/gmail_expand_collapse_conversation.json
+++ b/tools/perf/page_sets/data/gmail_expand_collapse_conversation.json
@@ -1,5 +1,4 @@
{
- "page_set": "../gmail_expand_collapse_conversation.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"gmail_expand_collapse_conversation_000.wpr": [
diff --git a/tools/perf/page_sets/data/html5gaming.json b/tools/perf/page_sets/data/html5gaming.json
index a556292069..a50abc33b2 100644
--- a/tools/perf/page_sets/data/html5gaming.json
+++ b/tools/perf/page_sets/data/html5gaming.json
@@ -1,9 +1,8 @@
{
- "page_set": "../html5gaming.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"html5gaming_000.wpr": [
"http://html5-benchmark.com/"
]
}
-} \ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/intl_ar_fa_he.json b/tools/perf/page_sets/data/intl_ar_fa_he.json
index 245bd4de8a..db4a85eb08 100644
--- a/tools/perf/page_sets/data/intl_ar_fa_he.json
+++ b/tools/perf/page_sets/data/intl_ar_fa_he.json
@@ -1,5 +1,4 @@
{
- "page_set": "../intl_ar_fa_he.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"intl_ar_fa_he_000.wpr": [
diff --git a/tools/perf/page_sets/data/intl_es_fr_pt-BR.json b/tools/perf/page_sets/data/intl_es_fr_pt-BR.json
index e39cd638d1..a042deb7bc 100644
--- a/tools/perf/page_sets/data/intl_es_fr_pt-BR.json
+++ b/tools/perf/page_sets/data/intl_es_fr_pt-BR.json
@@ -1,5 +1,4 @@
{
- "page_set": "../intl_es_fr_pt-BR.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"intl_es_fr_pt-BR_000.wpr": [
diff --git a/tools/perf/page_sets/data/intl_hi_ru.json b/tools/perf/page_sets/data/intl_hi_ru.json
index 7b43151dbb..4060a1b00f 100644
--- a/tools/perf/page_sets/data/intl_hi_ru.json
+++ b/tools/perf/page_sets/data/intl_hi_ru.json
@@ -1,5 +1,4 @@
{
- "page_set": "../intl_hi_ru.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"intl_hi_ru_000.wpr": [
diff --git a/tools/perf/page_sets/data/intl_ja_zh.json b/tools/perf/page_sets/data/intl_ja_zh.json
index f9b216bdc8..6bb5a6894a 100644
--- a/tools/perf/page_sets/data/intl_ja_zh.json
+++ b/tools/perf/page_sets/data/intl_ja_zh.json
@@ -1,5 +1,4 @@
{
- "page_set": "../intl_ja_zh.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"intl_ja_zh_000.wpr": [
diff --git a/tools/perf/page_sets/data/intl_ko_th_vi.json b/tools/perf/page_sets/data/intl_ko_th_vi.json
index 47f0eaa48d..a698736f05 100644
--- a/tools/perf/page_sets/data/intl_ko_th_vi.json
+++ b/tools/perf/page_sets/data/intl_ko_th_vi.json
@@ -1,5 +1,4 @@
{
- "page_set": "../intl_ko_th_vi.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"intl_ko_th_vi_000.wpr": [
diff --git a/tools/perf/page_sets/data/jsgamebench.json b/tools/perf/page_sets/data/jsgamebench.json
index 13d904dd75..cb4ed6a644 100644
--- a/tools/perf/page_sets/data/jsgamebench.json
+++ b/tools/perf/page_sets/data/jsgamebench.json
@@ -1,6 +1,5 @@
{
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
- "page_set": "../jsgamebench.json",
"archives": {
"jsgamebench_000.wpr" : [
"http://localhost/"
diff --git a/tools/perf/page_sets/data/key_desktop_sites.json b/tools/perf/page_sets/data/key_desktop_sites.json
index f380b078e9..7db50e4f7a 100644
--- a/tools/perf/page_sets/data/key_desktop_sites.json
+++ b/tools/perf/page_sets/data/key_desktop_sites.json
@@ -1,6 +1,5 @@
{
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
- "page_set": "../key_desktop_sites.json",
"archives": {
"key_desktop_sites_000.wpr" : [
"http://facebook.com",
diff --git a/tools/perf/page_sets/data/key_mobile_sites.json b/tools/perf/page_sets/data/key_mobile_sites.json
index 92e125f489..0a5a933df4 100644
--- a/tools/perf/page_sets/data/key_mobile_sites.json
+++ b/tools/perf/page_sets/data/key_mobile_sites.json
@@ -1,7 +1,19 @@
{
- "page_set": "../key_mobile_sites.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
+ "key_mobile_sites_005.wpr": [
+ "http://mobile-news.sandbox.google.com/news/pt1",
+ "http://mobile-news.sandbox.google.com/news/pt0"
+ ],
+ "key_mobile_sites_001.wpr": [
+ "http://mobile-news.sandbox.google.com/news/p#2"
+ ],
+ "key_mobile_sites_004.wpr": [
+ "http://groupcloned.com/test/list-images-variable/index.html"
+ ],
+ "key_mobile_sites_003.wpr": [
+ "https://facebook.com/barackobama"
+ ],
"key_mobile_sites_000.wpr": [
"http://www.androidpolice.com/2012/10/03/rumor-evidence-mounts-that-an-lg-optimus-g-nexus-is-coming-along-with-a-nexus-phone-certification-program/",
"http://nytimes.com/",
@@ -45,15 +57,6 @@
"https://www.linkedin.com/in/linustorvalds",
"http://mlb.com/",
"http://groupcloned.com"
- ],
- "key_mobile_sites_001.wpr": [
- "http://mobile-news.sandbox.google.com/news/p#2"
- ],
- "key_mobile_sites_004.wpr": [
- "http://groupcloned.com/test/list-images-variable/index.html"
- ],
- "key_mobile_sites_003.wpr": [
- "https://facebook.com/barackobama"
]
}
-}
+} \ No newline at end of file
diff --git a/tools/perf/page_sets/data/key_mobile_sites_005.wpr.sha1 b/tools/perf/page_sets/data/key_mobile_sites_005.wpr.sha1
new file mode 100644
index 0000000000..fa782bc505
--- /dev/null
+++ b/tools/perf/page_sets/data/key_mobile_sites_005.wpr.sha1
@@ -0,0 +1 @@
+1e5935c4976839ebe0b6dbc964821eb7fcbcca54 \ No newline at end of file
diff --git a/tools/perf/page_sets/data/key_silk_cases.json b/tools/perf/page_sets/data/key_silk_cases.json
index 48532bfdbb..21c1def93c 100644
--- a/tools/perf/page_sets/data/key_silk_cases.json
+++ b/tools/perf/page_sets/data/key_silk_cases.json
@@ -1,5 +1,4 @@
{
- "page_set": "../key_silk_cases.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"key_silk_cases_000.wpr": [
@@ -15,4 +14,4 @@
"http://jsfiddle.net/3yDKh/3/show"
]
}
-} \ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/kraken.json b/tools/perf/page_sets/data/kraken.json
index 61a59f0f65..a2cd25f936 100644
--- a/tools/perf/page_sets/data/kraken.json
+++ b/tools/perf/page_sets/data/kraken.json
@@ -1,6 +1,5 @@
{
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
- "page_set": "../kraken.json",
"archives": {
"kraken_000.wpr" : [
"http://krakenbenchmark.mozilla.org/kraken-1.1/driver.html"
diff --git a/tools/perf/page_sets/data/maps.json b/tools/perf/page_sets/data/maps.json
index 6430c1a21b..b2cb6e3ee2 100644
--- a/tools/perf/page_sets/data/maps.json
+++ b/tools/perf/page_sets/data/maps.json
@@ -1,9 +1,8 @@
{
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
- "page_set": "../maps_tests.json",
"archives": {
"maps_001.wpr": [
"http://localhost:10020/tracker.html"
]
}
-} \ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/mobile_memory.json b/tools/perf/page_sets/data/mobile_memory.json
new file mode 100644
index 0000000000..825d7c3015
--- /dev/null
+++ b/tools/perf/page_sets/data/mobile_memory.json
@@ -0,0 +1,9 @@
+{
+ "description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
+ "archives": {
+ "mobile_memory_006.wpr": [
+ "https://mail.google.com/mail/mu",
+ "https://www.google.com/search?site=&tbm=isch&q=google"
+ ]
+ }
+} \ No newline at end of file
diff --git a/tools/perf/page_sets/data/mobile_memory_006.wpr.sha1 b/tools/perf/page_sets/data/mobile_memory_006.wpr.sha1
new file mode 100644
index 0000000000..a99efb6485
--- /dev/null
+++ b/tools/perf/page_sets/data/mobile_memory_006.wpr.sha1
@@ -0,0 +1 @@
+8e1ba5a4d40784288e95babed16ac3ad53b3fce6 \ No newline at end of file
diff --git a/tools/perf/page_sets/data/octane.json b/tools/perf/page_sets/data/octane.json
index 462f05e6b4..494f65ef5d 100644
--- a/tools/perf/page_sets/data/octane.json
+++ b/tools/perf/page_sets/data/octane.json
@@ -1,9 +1,8 @@
{
- "page_set": "../octane.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"octane_000.wpr": [
"http://octane-benchmark.googlecode.com/svn/latest/index.html?auto=1"
]
}
-} \ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/peacekeeper_array.json b/tools/perf/page_sets/data/peacekeeper_array.json
index adc7bf53d8..9da685b244 100644
--- a/tools/perf/page_sets/data/peacekeeper_array.json
+++ b/tools/perf/page_sets/data/peacekeeper_array.json
@@ -1,5 +1,4 @@
{
- "page_set": "../peacekeeper_array.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"peacekeeper_array_000.wpr": [
@@ -7,4 +6,4 @@
"http://peacekeeper.futuremark.com/run.action?debug=true&repeat=false&forceSuiteName=array&forceTestName=arrayWeighted"
]
}
-} \ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/peacekeeper_dom.json b/tools/perf/page_sets/data/peacekeeper_dom.json
index e8aafffa29..a6e3340e93 100644
--- a/tools/perf/page_sets/data/peacekeeper_dom.json
+++ b/tools/perf/page_sets/data/peacekeeper_dom.json
@@ -1,5 +1,4 @@
{
- "page_set": "../peacekeeper_dom.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"peacekeeper_dom_000.wpr": [
@@ -14,4 +13,4 @@
"http://peacekeeper.futuremark.com/run.action?debug=true&repeat=false&forceSuiteName=dom&forceTestName=domQueryselector"
]
}
-} \ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/peacekeeper_experimental.json b/tools/perf/page_sets/data/peacekeeper_experimental.json
index 4f1d7e4f56..816ebaa1ca 100644
--- a/tools/perf/page_sets/data/peacekeeper_experimental.json
+++ b/tools/perf/page_sets/data/peacekeeper_experimental.json
@@ -1,5 +1,4 @@
{
- "page_set": "../peacekeeper_experimental.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"peacekeeper_experimental_000.wpr": [
@@ -7,4 +6,4 @@
"http://peacekeeper.futuremark.com/run.action?debug=true&repeat=false&forceSuiteName=experimental&forceTestName=experimentalRipple02"
]
}
-} \ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/peacekeeper_html5.json b/tools/perf/page_sets/data/peacekeeper_html5.json
index 51dc16d506..19eb0153a4 100644
--- a/tools/perf/page_sets/data/peacekeeper_html5.json
+++ b/tools/perf/page_sets/data/peacekeeper_html5.json
@@ -1,5 +1,4 @@
{
- "page_set": "../peacekeeper_html5.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"peacekeeper_html5_000.wpr": [
@@ -13,4 +12,4 @@
"http://peacekeeper.futuremark.com/run.action?debug=true&repeat=false&forceSuiteName=html5&forceTestName=workerContrast02"
]
}
-} \ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/peacekeeper_render.json b/tools/perf/page_sets/data/peacekeeper_render.json
index 6c95bc6fc7..da37d021d7 100644
--- a/tools/perf/page_sets/data/peacekeeper_render.json
+++ b/tools/perf/page_sets/data/peacekeeper_render.json
@@ -1,5 +1,4 @@
{
- "page_set": "../peacekeeper_render.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"peacekeeper_render_000.wpr": [
@@ -9,4 +8,4 @@
"http://peacekeeper.futuremark.com/run.action?debug=true&repeat=false&forceSuiteName=render&forceTestName=renderPhysics"
]
}
-} \ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/peacekeeper_string.json b/tools/perf/page_sets/data/peacekeeper_string.json
index f412aceabc..ed48943a36 100644
--- a/tools/perf/page_sets/data/peacekeeper_string.json
+++ b/tools/perf/page_sets/data/peacekeeper_string.json
@@ -1,5 +1,4 @@
{
- "page_set": "../peacekeeper_string.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"peacekeeper_string_000.wpr": [
@@ -10,4 +9,4 @@
"http://peacekeeper.futuremark.com/run.action?debug=true&repeat=false&forceSuiteName=string&forceTestName=stringValidateForm"
]
}
-} \ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/pica.json b/tools/perf/page_sets/data/pica.json
index d369da6028..2c37735760 100644
--- a/tools/perf/page_sets/data/pica.json
+++ b/tools/perf/page_sets/data/pica.json
@@ -1,9 +1,8 @@
{
- "page_set": "../pica.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"pica_002.wpr": [
"http://localhost/polymer/projects/pica/"
]
}
-} \ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/plus_alt_posts_photos.json b/tools/perf/page_sets/data/plus_alt_posts_photos.json
index b47d0a322a..c248c03c5a 100644
--- a/tools/perf/page_sets/data/plus_alt_posts_photos.json
+++ b/tools/perf/page_sets/data/plus_alt_posts_photos.json
@@ -1,5 +1,4 @@
{
- "page_set": "../plus_alt_posts_photos.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"plus_alt_posts_photos_000.wpr": [
diff --git a/tools/perf/page_sets/data/robohornetpro.json b/tools/perf/page_sets/data/robohornet_pro.json
index f1b0e69003..acd614eba5 100644
--- a/tools/perf/page_sets/data/robohornetpro.json
+++ b/tools/perf/page_sets/data/robohornet_pro.json
@@ -1,8 +1,7 @@
{
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
- "page_set": "../robohornetpro.json",
"archives": {
- "robohornetpro_000.wpr" : [
+ "robohornet_pro_000.wpr" : [
"http://ie.microsoft.com/testdrive/performance/robohornetpro/"
]
}
diff --git a/tools/perf/page_sets/data/robohornetpro_000.wpr.sha1 b/tools/perf/page_sets/data/robohornet_pro_000.wpr.sha1
index 1e4d344e2d..1e4d344e2d 100644
--- a/tools/perf/page_sets/data/robohornetpro_000.wpr.sha1
+++ b/tools/perf/page_sets/data/robohornet_pro_000.wpr.sha1
diff --git a/tools/perf/page_sets/data/scirra.json b/tools/perf/page_sets/data/scirra.json
index ee43390d45..ca5011e97d 100644
--- a/tools/perf/page_sets/data/scirra.json
+++ b/tools/perf/page_sets/data/scirra.json
@@ -1,5 +1,4 @@
{
- "page_set": "../scirra.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"scirra_000.wpr": [
@@ -8,4 +7,4 @@
"http://www.scirra.com/labs/renderperf3/"
]
}
-} \ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/sunspider.json b/tools/perf/page_sets/data/sunspider.json
index 72bb10063a..0e81571d03 100644
--- a/tools/perf/page_sets/data/sunspider.json
+++ b/tools/perf/page_sets/data/sunspider.json
@@ -1,9 +1,8 @@
{
- "page_set": "../../benchmarks/sunspider.py",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"sunspider_000.wpr": [
"http://www.webkit.org/perf/sunspider-1.0.2/sunspider-1.0.2/driver.html"
]
}
-} \ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/test.json b/tools/perf/page_sets/data/test.json
index 5700714b7d..57d4f468bc 100644
--- a/tools/perf/page_sets/data/test.json
+++ b/tools/perf/page_sets/data/test.json
@@ -1,5 +1,4 @@
{
- "page_set": "../test.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"test_000.wpr": [
diff --git a/tools/perf/page_sets/data/top_10.json b/tools/perf/page_sets/data/top_10.json
index 62bcd82efc..61acd27014 100644
--- a/tools/perf/page_sets/data/top_10.json
+++ b/tools/perf/page_sets/data/top_10.json
@@ -1,5 +1,4 @@
{
- "page_set": "../top_10.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"top_10_000.wpr": [
diff --git a/tools/perf/page_sets/data/top_10_mobile.json b/tools/perf/page_sets/data/top_10_mobile.json
index a24b0f0a34..c28f9ad372 100644
--- a/tools/perf/page_sets/data/top_10_mobile.json
+++ b/tools/perf/page_sets/data/top_10_mobile.json
@@ -1,5 +1,4 @@
{
- "page_set": "../top_10_mobile.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"top_10_mobile_002.wpr": [
@@ -18,4 +17,4 @@
"https://mobile.twitter.com/justinbieber&"
]
}
-} \ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/top_25.json b/tools/perf/page_sets/data/top_25.json
index 4ecf28b4ed..6f916900cf 100644
--- a/tools/perf/page_sets/data/top_25.json
+++ b/tools/perf/page_sets/data/top_25.json
@@ -1,5 +1,4 @@
{
- "page_set": "../top_25.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"top_25_001.wpr": [
@@ -35,4 +34,4 @@
"http://techcrunch.com"
]
}
-} \ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/tough_canvas_cases.json b/tools/perf/page_sets/data/tough_canvas_cases.json
index c1923ca772..8fc8f1d957 100644
--- a/tools/perf/page_sets/data/tough_canvas_cases.json
+++ b/tools/perf/page_sets/data/tough_canvas_cases.json
@@ -1,5 +1,4 @@
{
- "page_set": "../tough_canvas_cases.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"tough_canvas_cases_001.wpr": [
diff --git a/tools/perf/page_sets/data/tough_layout_cases.json b/tools/perf/page_sets/data/tough_layout_cases.json
index b027a58de6..93b890fb9b 100644
--- a/tools/perf/page_sets/data/tough_layout_cases.json
+++ b/tools/perf/page_sets/data/tough_layout_cases.json
@@ -1,5 +1,4 @@
{
- "page_set": "../tough_layout_cases.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"tough_layout_cases_001.wpr": [
diff --git a/tools/perf/page_sets/data/typical_25.json b/tools/perf/page_sets/data/typical_25.json
index e89fa3b081..e038b6573f 100644
--- a/tools/perf/page_sets/data/typical_25.json
+++ b/tools/perf/page_sets/data/typical_25.json
@@ -1,5 +1,4 @@
{
- "page_set": "../typical_25.json",
"description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.",
"archives": {
"typical_25_000.wpr": [
diff --git a/tools/perf/page_sets/key_mobile_sites.json b/tools/perf/page_sets/key_mobile_sites.json
index b201573cd4..1fd21d56a9 100644
--- a/tools/perf/page_sets/key_mobile_sites.json
+++ b/tools/perf/page_sets/key_mobile_sites.json
@@ -257,6 +257,25 @@
"disabled": "Doesn't scroll; crbug.com/249736",
"url": "http://forecast.io",
"why": "crbug.com/231413"
+ },
+ {
+ "url": "http://mobile-news.sandbox.google.com/news/pt1",
+ "why": "Google News: accelerated scrolling version"
+ },
+ {
+ "url": "http://mobile-news.sandbox.google.com/news/pt0",
+ "why": "Google News: this iOS version is slower than accelerated scrolling",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "wait", "javascript": "document.getElementById(':h') != null" },
+ { "action": "wait", "seconds": 1 }
+ ],
+ "smoothness": {
+ "action": "scroll",
+ "scroll_requires_touch": true,
+ "scrollable_element_function": "function(callback) { callback(document.getElementById(':5')); }",
+ "remaining_scroll_distance_function": "function() { return Math.max(0, 2500 + document.getElementById(':h').getBoundingClientRect().top); }"
+ }
}
]
}
diff --git a/tools/perf/page_sets/media_cns_cases.json b/tools/perf/page_sets/media_cns_cases.json
new file mode 100644
index 0000000000..7a54d2cae6
--- /dev/null
+++ b/tools/perf/page_sets/media_cns_cases.json
@@ -0,0 +1,76 @@
+{
+ "description": "Media benchmark on network constrained conditions.",
+ "media_metrics": {"action": "play-action"},
+ "add_browser_metrics": true,
+ "play-action": [
+ { "action": "play",
+ "wait_for_playing": true,
+ "wait_for_ended": true
+ }
+ ],
+ "pages": [
+ {
+ "url": "file://tough_video_cases/video.html?id=no_constraints_webm&src=tulip2.webm&net=none"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=cable_webm&src=tulip2.webm&net=cable"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=wifi_webm&src=tulip2.webm&net=wifi"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=no_constraints_ogv&src=tulip2.ogv&net=none"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=cable_ogv&src=tulip2.ogv&net=cable"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=wifi_ogv&src=tulip2.ogv&net=wifi"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=no_constraints_mp4&src=tulip2.mp4&net=none"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=cable_mp4&src=tulip2.mp4&net=cable"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=wifi_mp4&src=tulip2.mp4&net=wifi"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=no_constraints_wav&src=tulip2.wav&type=audio&net=none"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=cable_wav&src=tulip2.wav&type=audio&net=cable"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=wifi_wav&src=tulip2.wav&type=audio&net=wifi"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=no_constraints_ogg&src=tulip2.ogg&type=audio&net=none"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=cable_ogg&src=tulip2.ogg&type=audio&net=cable"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=wifi_ogg&src=tulip2.ogg&type=audio&net=wifi"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=no_constraints_mp3&src=tulip2.mp3&type=audio&net=none"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=cable_mp3&src=tulip2.mp3&type=audio&net=cable"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=wifi_mp3&src=tulip2.mp3&type=audio&net=wifi"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=no_constraints_m4a&src=tulip2.m4a&type=audio&net=none"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=cable_m4a&src=tulip2.m4a&type=audio&net=cable"
+ },
+ {
+ "url": "file://tough_video_cases/video.html?id=wifi_m4a&src=tulip2.m4a&type=audio&net=wifi"
+ }
+ ]
+}
diff --git a/tools/perf/page_sets/mobile_memory.json b/tools/perf/page_sets/mobile_memory.json
index 819d9172a2..963b39d293 100644
--- a/tools/perf/page_sets/mobile_memory.json
+++ b/tools/perf/page_sets/mobile_memory.json
@@ -3,16 +3,30 @@
"archive_data_file": "data/mobile_memory.json",
"credentials_path": "data/credentials.json",
"user_agent_type": "mobile",
- "reload_and_gc": [
- {"action": "reload" },
- {"action": "wait", "seconds": 15 },
- {"action": "js_collect_garbage" }
- ],
- "stress_memory": { "action": "reload_and_gc", "repeat": 3 },
"pages": [
{
"url": "https://mail.google.com/mail/mu",
- "credentials": "google"
+ "credentials": "google",
+ "reload_and_gc": [
+ {"action": "reload" },
+ {"action": "wait", "seconds": 15 },
+ {"action": "js_collect_garbage" }
+ ],
+ "stress_memory": { "action": "reload_and_gc", "repeat": 3 }
+ },
+ {
+ "url": "https://www.google.com/search?site=&tbm=isch&q=google",
+ "why": "Tests usage of discardable memory",
+ "stress_memory": [
+ { "action": "scroll" },
+ { "action": "wait", "seconds": 3 },
+ { "action": "scroll" },
+ { "action": "wait", "seconds": 3 },
+ { "action": "scroll" },
+ { "action": "wait", "seconds": 3 },
+ { "action": "scroll" },
+ { "action": "wait", "javascript": "document.body.scrollHeight > 6000" }
+ ]
}
]
}
diff --git a/tools/perf/page_sets/mse_cases/audio.mp4.sha1 b/tools/perf/page_sets/mse_cases/audio.mp4.sha1
new file mode 100644
index 0000000000..96d89c6470
--- /dev/null
+++ b/tools/perf/page_sets/mse_cases/audio.mp4.sha1
@@ -0,0 +1 @@
+2d0fc9b7d1db36c94eadad0c231bdae7a11b0d0d \ No newline at end of file
diff --git a/tools/perf/page_sets/mse_cases/startup_test.js b/tools/perf/page_sets/mse_cases/startup_test.js
index de9923d392..2838a25f39 100644
--- a/tools/perf/page_sets/mse_cases/startup_test.js
+++ b/tools/perf/page_sets/mse_cases/startup_test.js
@@ -453,6 +453,7 @@
runAppendTest(video, appenders, function(stats, timestamps) {
displayResults(stats);
plotTimestamps(timestamps, testParams.graphDuration, video);
+ window.__testDone = true;
});
}
@@ -481,7 +482,5 @@
}
window["setupTest"] = setupTest;
- window.__testDone = function() {
- return testDone;
- };
+ window.__testDone = false;
})();
diff --git a/tools/perf/page_sets/mse_cases/video.mp4.sha1 b/tools/perf/page_sets/mse_cases/video.mp4.sha1
new file mode 100644
index 0000000000..a3ed38f3de
--- /dev/null
+++ b/tools/perf/page_sets/mse_cases/video.mp4.sha1
@@ -0,0 +1 @@
+0dccf2486fa7d35503c574b31a0c9377aea2501b \ No newline at end of file
diff --git a/tools/perf/page_sets/presubmit_unittest.py b/tools/perf/page_sets/presubmit_unittest.py
index 0852d81520..c3d3d7ef09 100644
--- a/tools/perf/page_sets/presubmit_unittest.py
+++ b/tools/perf/page_sets/presubmit_unittest.py
@@ -3,10 +3,15 @@
# found in the LICENSE file.
import collections
+import os
+import sys
import unittest
+PERF_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+sys.path.insert(0, os.path.join(os.path.dirname(PERF_ROOT), 'telemetry'))
from telemetry.unittest import system_stub
+sys.path.insert(0, PERF_ROOT)
from page_sets import PRESUBMIT
@@ -35,6 +40,9 @@ class InputAPIStub(object):
def AbsoluteLocalPaths(self):
return [af.AbsoluteLocalPath() for af in self.AffectedFiles()]
+ def PresubmitLocalPath(self):
+ return PRESUBMIT.__file__
+
class OutputAPIStub(object):
class PresubmitError(Exception):
@@ -44,6 +52,8 @@ class OutputAPIStub(object):
pass
+PRESUBMIT.LoadSupport(InputAPIStub([])) # do this to support monkey patching
+
class PresubmitTest(unittest.TestCase):
def setUp(self):
success_file_hash = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'
@@ -123,3 +133,7 @@ class PresubmitTest(unittest.TestCase):
def testWrongHash(self):
results = self._CheckUpload(['/path/to/wrong_hash.wpr.sha1'])
self.assertTrue('does not match' in str(results[0]), msg=results[0])
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/perf/page_sets/tough_canvas_cases.json b/tools/perf/page_sets/tough_canvas_cases.json
index 518a5ea6bd..04a9937103 100644
--- a/tools/perf/page_sets/tough_canvas_cases.json
+++ b/tools/perf/page_sets/tough_canvas_cases.json
@@ -42,11 +42,9 @@
{ "url": "file://tough_canvas_cases/canvas2d_balls_common/bouncing_balls.html?ball=text&back=white&ball_count=15" },
{ "url": "file://tough_canvas_cases/canvas-animation-no-clear.html" },
{
- "disabled": "Trace event buffer overflow on Android. crbug.com/314380",
"url": "file://../../../chrome/test/data/perf/canvas_bench/single_image.html"
},
{
- "disabled": "Trace event buffer overflow on Android. crbug.com/314380",
"url": "file://../../../chrome/test/data/perf/canvas_bench/many_images.html"
}
]
diff --git a/tools/perf/page_sets/tough_memory_multi_tab.json b/tools/perf/page_sets/tough_memory_multi_tab.json
index c0211d26a4..47e7b2cc88 100644
--- a/tools/perf/page_sets/tough_memory_multi_tab.json
+++ b/tools/perf/page_sets/tough_memory_multi_tab.json
@@ -1,7 +1,7 @@
{
"description": "Mobile sites for exercising multi-tab memory issues",
- "archive_data_file": "../data/key_mobile_sites.json",
- "credentials_path": "../data/credentials.json",
+ "archive_data_file": "data/key_mobile_sites.json",
+ "credentials_path": "data/credentials.json",
"user_agent_type": "mobile",
"pages": [
{ "url": "https://www.google.com/#hl=en&q=barack+obama" },
diff --git a/tools/perf/page_sets/tough_scheduling_cases.json b/tools/perf/page_sets/tough_scheduling_cases.json
new file mode 100644
index 0000000000..418e0f1324
--- /dev/null
+++ b/tools/perf/page_sets/tough_scheduling_cases.json
@@ -0,0 +1,266 @@
+{
+ "description": "Tough scheduler latency test cases",
+ "smoothness": {
+ "action": "scroll"
+ },
+ "pages": [
+ {
+ "url": "file://tough_scheduling_cases/simple_text_page.html",
+ "why": "Simple scrolling baseline",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays" }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/simple_text_page.html?main_busy",
+ "why": "Simulate oversubscribed main thread",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays",
+ "delays": {
+ "cc.BeginMainFrame": { "target_duration": 0.008 }
+ }
+ }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/simple_text_page.html?main_very_busy",
+ "why": "Simulate oversubscribed main thread",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays",
+ "delays": {
+ "cc.BeginMainFrame": { "target_duration": 0.024 }
+ }
+ }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/simple_text_page.html?medium_layers",
+ "why": "Simulate a page with a a few graphics layers",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays",
+ "delays": {
+ "cc.BeginMainFrame": { "target_duration": 0.004 },
+ "cc.DrawAndSwap": { "target_duration": 0.004 },
+ "gpu.SwapBuffers": { "target_duration": 0.004 }
+ }
+ }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/simple_text_page.html?many_layers",
+ "why": "Simulate a page with many graphics layers",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays",
+ "delays": {
+ "cc.BeginMainFrame": { "target_duration": 0.012 },
+ "cc.DrawAndSwap": { "target_duration": 0.012 },
+ "gpu.SwapBuffers": { "target_duration": 0.012 }
+ }
+ }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/simple_text_page.html?medium_raster",
+ "why": "Simulate a page with expensive recording and rasterization",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays",
+ "delays": {
+ "cc.BeginMainFrame": { "target_duration": 0.004 },
+ "cc.RasterRequiredForActivation": { "target_duration": 0.004 },
+ "gpu.AsyncTexImage": { "target_duration": 0.004 }
+ }
+ }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/simple_text_page.html?heavy_raster",
+ "why": "Simulate a page with expensive recording and rasterization",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays",
+ "delays": {
+ "cc.BeginMainFrame": { "target_duration": 0.024 },
+ "cc.RasterRequiredForActivation": { "target_duration": 0.024 },
+ "gpu.AsyncTexImage": { "target_duration": 0.024 }
+ }
+ }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/touch_handler_scrolling.html",
+ "why": "Touch handler scrolling baseline",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays" }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/touch_handler_scrolling.html?medium_handler",
+ "why": "Medium cost touch handler",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays",
+ "delays": {
+ "blink.HandleInputEvent": { "target_duration": 0.008 }
+ }
+ }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/touch_handler_scrolling.html?slow_handler",
+ "why": "Slow touch handler",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays",
+ "delays": {
+ "blink.HandleInputEvent": { "target_duration": 0.024 }
+ }
+ }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/touch_handler_scrolling.html?janky_handler",
+ "why": "Touch handler that often takes a long time",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays",
+ "delays": {
+ "blink.HandleInputEvent": { "target_duration": 0.024, "mode": "alternating" }
+ }
+ }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/touch_handler_scrolling.html?occasionally_janky_handler",
+ "why": "Touch handler that occasionally takes a long time",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays",
+ "delays": {
+ "blink.HandleInputEvent": { "target_duration": 0.024, "mode": "oneshot" }
+ }
+ }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/touch_handler_scrolling.html?super_slow_handler",
+ "why": "Super expensive touch handler causes browser to scroll after a timeout",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays",
+ "delays": {
+ "blink.HandleInputEvent": { "target_duration": 0.2 }
+ }
+ }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/div_touch_handler.html",
+ "why": "Super expensive touch handler that only occupies a part of the page",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays",
+ "delays": {
+ "blink.HandleInputEvent": { "target_duration": 0.2 }
+ }
+ }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/raf.html",
+ "why": "requestAnimationFrame scrolling baseline",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays" }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/raf_canvas.html",
+ "why": "Test canvas blocking behavior",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays" }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/raf.html?medium_handler",
+ "why": "Test a moderately heavy requestAnimationFrame handler",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays",
+ "delays": {
+ "cc.BeginMainFrame": { "target_duration": 0.004 },
+ "cc.RasterRequiredForActivation": { "target_duration": 0.004 },
+ "gpu.AsyncTexImage": { "target_duration": 0.004 }
+ }
+ }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/raf.html?heavy_handler",
+ "why": "Test a moderately heavy requestAnimationFrame handler",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays",
+ "delays": {
+ "cc.BeginMainFrame": { "target_duration": 0.024 },
+ "cc.RasterRequiredForActivation": { "target_duration": 0.024 },
+ "gpu.AsyncTexImage": { "target_duration": 0.024 }
+ }
+ }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/raf.html?gpu_bound",
+ "why": "Simulate a heavily GPU bound page",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays",
+ "delays": {
+ "gpu.SwapBuffers": { "target_duration": 0.1 }
+ }
+ }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/raf_touch_animation.html",
+ "why": "Stress test for the scheduler",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays" }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/raf_touch_animation.html?medium",
+ "why": "Medium stress test for the scheduler",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays",
+ "delays": {
+ "cc.BeginMainFrame": { "target_duration": 0.004 },
+ "cc.DrawAndSwap": { "target_duration": 0.004 }
+ }
+ }
+ ]
+ },
+ {
+ "url": "file://tough_scheduling_cases/raf_touch_animation.html?heavy",
+ "why": "Heavy stress test for the scheduler",
+ "navigate_steps": [
+ { "action": "navigate" },
+ { "action": "set_synthetic_delays",
+ "delays": {
+ "cc.BeginMainFrame": { "target_duration": 0.012 },
+ "cc.DrawAndSwap": { "target_duration": 0.012 }
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/tools/perf/page_sets/tough_scheduling_cases/div_touch_handler.html b/tools/perf/page_sets/tough_scheduling_cases/div_touch_handler.html
new file mode 100644
index 0000000000..c85d853f8e
--- /dev/null
+++ b/tools/perf/page_sets/tough_scheduling_cases/div_touch_handler.html
@@ -0,0 +1,282 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta name="viewport" content="width=device-width">
+ <title>Div touch handler</title>
+ <style type="text/css">
+ body {
+ height: 10000px;
+ margin: 0px;
+ background-color: #ff2;
+ background-image: linear-gradient(rgba(0, 0, 0, .1) 50%, transparent 50%, transparent);
+ background-size: 100px 100px;
+ }
+ #handler {
+ position: absolute;
+ width: 100px;
+ height: 10000px;
+ background: red;
+ }
+ </style>
+ <script type="text/javascript">
+ function eatEvent(e) {
+ e.preventDefault();
+ }
+
+ function startExperiment() {
+ var div = document.getElementById('handler');
+ div.addEventListener('touchstart', eatEvent);
+ div.addEventListener('touchmove', eatEvent);
+ div.addEventListener('touchend', eatEvent);
+ div.addEventListener('touchleave', eatEvent);
+ div.addEventListener('touchcancel', eatEvent);
+ }
+
+ window.addEventListener('load', startExperiment);
+ </script>
+</head>
+<body>
+ <h2>Div touch handler</h2>
+ <div id="handler">om nom I eat touches</div>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+</body>
+</html>
diff --git a/tools/perf/page_sets/tough_scheduling_cases/raf.html b/tools/perf/page_sets/tough_scheduling_cases/raf.html
new file mode 100644
index 0000000000..40dbba5b74
--- /dev/null
+++ b/tools/perf/page_sets/tough_scheduling_cases/raf.html
@@ -0,0 +1,283 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta name="viewport" content="width=device-width">
+ <title>requestAnimationFrame</title>
+ <style type="text/css">
+ body {
+ height: 10000px;
+ margin: 0px;
+ background-color: #28f;
+ background-image: linear-gradient(rgba(0, 0, 0, .1) 50%, transparent 50%, transparent);
+ background-size: 100px 100px;
+ }
+ #status {
+ position: fixed;
+ left: 0px;
+ bottom: 0px;
+ background: #148;
+ }
+ </style>
+ <script type="text/javascript">
+ var g_status;
+
+ function onAnimationFrame() {
+ g_status.innerText = parseInt(g_status.innerText) + 1;
+ window.requestAnimationFrame(onAnimationFrame);
+ }
+
+ function startExperiment() {
+ g_status = document.getElementById('status');
+ window.requestAnimationFrame(onAnimationFrame);
+ }
+
+ window.addEventListener('load', startExperiment);
+ </script>
+</head>
+<body>
+ <div id="status">0</div>
+ <h2>requestAnimationFrame</h2>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+</body>
+</html>
+
+
diff --git a/tools/perf/page_sets/tough_scheduling_cases/raf_canvas.html b/tools/perf/page_sets/tough_scheduling_cases/raf_canvas.html
new file mode 100644
index 0000000000..9092ab2f17
--- /dev/null
+++ b/tools/perf/page_sets/tough_scheduling_cases/raf_canvas.html
@@ -0,0 +1,289 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta name="viewport" content="width=device-width">
+ <title>requestAnimationFrame with canvas</title>
+ <style type="text/css">
+ body {
+ height: 10000px;
+ margin: 0px;
+ background-color: #2f8;
+ background-image: linear-gradient(rgba(0, 0, 0, .1) 50%, transparent 50%, transparent);
+ background-size: 100px 100px;
+ }
+ #canvas {
+ position: fixed;
+ left: 0px;
+ bottom: 0px;
+ }
+ </style>
+ <script type="text/javascript">
+ var g_canvas;
+ var g_context;
+
+ function onAnimationFrame(time) {
+ var t = time / 1000;
+ var x = Math.cos(t) * g_canvas.width / 3 + g_canvas.width / 2;
+ var y = Math.sin(t * 2) * g_canvas.height / 3 + g_canvas.height / 2;
+ g_canvas.width = g_canvas.width;
+ g_context.fillStyle = 'green';
+ g_context.fillRect(x - 16, y - 16, 32, 32);
+ window.requestAnimationFrame(onAnimationFrame);
+ }
+
+ function startExperiment() {
+ g_canvas = document.getElementById('canvas');
+ g_context = canvas.getContext('2d');
+ window.requestAnimationFrame(onAnimationFrame);
+ }
+
+ window.addEventListener('load', startExperiment);
+ </script>
+</head>
+<body>
+ <h2>requestAnimationFrame with canvas</h2>
+ <canvas id="canvas" width="257" height="257"></canvas>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+</body>
+</html>
+
+
diff --git a/tools/perf/page_sets/tough_scheduling_cases/raf_touch_animation.html b/tools/perf/page_sets/tough_scheduling_cases/raf_touch_animation.html
new file mode 100644
index 0000000000..dfd061f9d6
--- /dev/null
+++ b/tools/perf/page_sets/tough_scheduling_cases/raf_touch_animation.html
@@ -0,0 +1,320 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta name="viewport" content="width=device-width">
+ <title>requestAnimationFrame with touch handler scrolling and CSS animation</title>
+ <style type="text/css">
+ body {
+ height: 10000px;
+ margin: 0px;
+ background-color: #82f;
+ background-image: linear-gradient(rgba(0, 0, 0, .1) 50%, transparent 50%, transparent);
+ background-size: 100px 100px;
+ }
+ #status {
+ position: fixed;
+ left: 0px;
+ bottom: 0px;
+ background: #418;
+
+ -webkit-animation: fade 5s ease-in-out infinite;
+ }
+ @-webkit-keyframes fade {
+ 0% { -webkit-transform: translate(0px); }
+ 50% { -webkit-transform: translate(200px); }
+ 100% { -webkit-transform: translate(0px); }
+ }
+ </style>
+ <script type="text/javascript">
+ var g_touchPrevY;
+
+ function onTouchStart(e) {
+ g_touchPrevY = e.touches[0].screenY;
+ e.preventDefault();
+ }
+
+ function onTouchMove(e) {
+ e.preventDefault();
+ var distance = g_touchPrevY - e.touches[0].screenY;
+ g_touchPrevY = e.touches[0].screenY;
+ window.scrollBy(0, distance);
+ }
+
+ function onTouchEnd(e) {
+ e.preventDefault();
+ g_touchPrevY = undefined;
+ }
+
+ var g_status;
+
+ function onAnimationFrame() {
+ g_status.innerText = parseInt(g_status.innerText) + 1;
+ window.requestAnimationFrame(onAnimationFrame);
+ }
+
+ function onMouseWheel(e) {
+ e.preventDefault();
+ window.scrollBy(0, -e.wheelDelta);
+ }
+
+ function startExperiment() {
+ g_status = document.getElementById('status');
+ window.addEventListener('touchstart', onTouchStart);
+ window.addEventListener('touchmove', onTouchMove);
+ window.addEventListener('touchend', onTouchEnd);
+ window.addEventListener('touchleave', onTouchEnd);
+ window.addEventListener('touchcancel', onTouchEnd);
+ window.addEventListener('mousewheel', onMouseWheel);
+ window.requestAnimationFrame(onAnimationFrame);
+ }
+
+ window.addEventListener('load', startExperiment);
+ </script>
+</head>
+<body>
+ <div id="status">0</div>
+ <h2>requestAnimationFrame with touch handler scrolling and CSS animation</h2>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+</body>
+</html>
+
+
diff --git a/tools/perf/page_sets/tough_scheduling_cases/simple_text_page.html b/tools/perf/page_sets/tough_scheduling_cases/simple_text_page.html
new file mode 100644
index 0000000000..4911546d8b
--- /dev/null
+++ b/tools/perf/page_sets/tough_scheduling_cases/simple_text_page.html
@@ -0,0 +1,260 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta name="viewport" content="width=device-width">
+ <title>Simple text page</title>
+ <style type="text/css">
+ body {
+ height: 10000px;
+ margin: 0px;
+ background-image: linear-gradient(rgba(0, 0, 0, .1) 50%, transparent 50%, transparent);
+ background-size: 100px 100px;
+ }
+ </style>
+</head>
+<body>
+ <h2>Simple text page</h2>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard’s job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard’s job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard’s job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard’s job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard’s job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard’s job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard’s job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard’s job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard’s job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard’s job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard’s job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard’s job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+</body>
+</html>
+
+
diff --git a/tools/perf/page_sets/tough_scheduling_cases/touch_handler_scrolling.html b/tools/perf/page_sets/tough_scheduling_cases/touch_handler_scrolling.html
new file mode 100644
index 0000000000..9077202873
--- /dev/null
+++ b/tools/perf/page_sets/tough_scheduling_cases/touch_handler_scrolling.html
@@ -0,0 +1,297 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta name="viewport" content="width=device-width">
+ <title>Touch handler scrolling</title>
+ <style type="text/css">
+ body {
+ height: 10000px;
+ margin: 0px;
+ background-color: #f82;
+ background-image: linear-gradient(rgba(0, 0, 0, .1) 50%, transparent 50%, transparent);
+ background-size: 100px 100px;
+ }
+ </style>
+ <script type="text/javascript">
+ var g_touchPrevY;
+
+ function onTouchStart(e) {
+ g_touchPrevY = e.touches[0].screenY;
+ e.preventDefault();
+ }
+
+ function onTouchMove(e) {
+ e.preventDefault();
+ var distance = g_touchPrevY - e.touches[0].screenY;
+ g_touchPrevY = e.touches[0].screenY;
+ window.scrollBy(0, distance);
+ }
+
+ function onTouchEnd(e) {
+ e.preventDefault();
+ g_touchPrevY = undefined;
+ }
+
+ function onMouseWheel(e) {
+ e.preventDefault();
+ window.scrollBy(0, -e.wheelDelta);
+ }
+
+ function startExperiment() {
+ window.addEventListener('touchstart', onTouchStart);
+ window.addEventListener('touchmove', onTouchMove);
+ window.addEventListener('touchend', onTouchEnd);
+ window.addEventListener('touchleave', onTouchEnd);
+ window.addEventListener('touchcancel', onTouchEnd);
+ window.addEventListener('mousewheel', onMouseWheel);
+ }
+
+ window.addEventListener('load', startExperiment);
+ </script>
+</head>
+<body>
+ <h2>Touch handler scrolling</h2>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+ <p>The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps.</p>
+ <p>Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex! Fox nymphs grab quick-jived waltz.</p>
+ <p>Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex.</p>
+ <p>Two driven jocks help fax my big quiz. Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled.</p>
+ <p>Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq. Cozy sphinx waves quart jug of bad milk.</p>
+ <p>A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox.</p>
+ <p>The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV for luck.</p>
+ <p>A wizard's job is to vex chumps quickly in fog. Watch "Jeopardy! ", Alex Trebek's fun TV quiz game. Woven silk pyjamas exchanged for blue quartz.</p>
+ <p>Brawny gods just flocked up to quiz and vex him. Adjusting quiver and bow, Zompyc[1] killed the fox. My faxed joke won a pager in the cable TV quiz show.</p>
+ <p>Amazingly few discotheques provide jukeboxes. My girl wove six dozen plaid jackets before she quit. Six big devils from Japan quickly forgot how to waltz.</p>
+ <p>Big July earthquakes confound zany experimental vow. Foxy parsons quiz and cajole the lovably dim wiki-girl.</p>
+ <p>Have a pick: twenty six letters - no forcing a jumbled quiz! Crazy Fredericka bought many very exquisite opal jewels.</p>
+ <p>Sixty zippers were quickly picked from the woven jute bag. A quick movement of the enemy will jeopardize six gunboats.</p>
+ <p>All questions asked by five watch experts amazed the judge. Jack quietly moved up front and seized the big ball of wax. The quick, brown fox jumps over a lazy dog.</p>
+ <p>DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs. Waltz, bad nymph, for quick jigs vex!</p>
+ <p>Fox nymphs grab quick-jived waltz. Brick quiz whangs jumpy veldt fox. Bright vixens jump; dozy fowl quack. Quick wafting zephyrs vex bold Jim.</p>
+ <p>Quick zephyrs blow, vexing daft Jim. Sex-charged fop blew my junk TV quiz. How quickly daft jumping zebras vex. Two driven jocks help fax my big quiz.</p>
+ <p>Quick, Baz, get my woven flax jodhpurs! "Now fax quiz Jack! " my brave ghost pled. Five quacking zephyrs jolt my wax bed. Flummoxed by job, kvetching W. zaps Iraq.</p>
+ <p>Cozy sphinx waves quart jug of bad milk. A very bad quack might jinx zippy fowls. Few quips galvanized the mock jury box. Quick brown dogs jump over the lazy fox. The jay, pig, fox, zebra, and my wolves quack! Blowzy red vixens fight for a quick jump. Joaquin Phoenix was gazed by MTV</p>
+</body>
+</html>
+
+
diff --git a/tools/perf/page_sets/tough_video_cases.json b/tools/perf/page_sets/tough_video_cases.json
index b11a6d34de..ae8fd258a0 100644
--- a/tools/perf/page_sets/tough_video_cases.json
+++ b/tools/perf/page_sets/tough_video_cases.json
@@ -56,22 +56,22 @@
"add_browser_metrics": true
},
{
- "url": "file://tough_video_cases/video.html?src=crowd1080.ogv&type=video",
+ "url": "file://tough_video_cases/video.html?src=crowd1080.ogv",
"media_metrics": {"action": "play-action"},
"add_browser_metrics": true
},
{
- "url": "file://tough_video_cases/video.html?src=crowd1080.webm&type=video",
+ "url": "file://tough_video_cases/video.html?src=crowd1080.webm",
"media_metrics": {"action": "play-action"},
"add_browser_metrics": true
},
{
- "url": "file://tough_video_cases/video.html?src=crowd2160.ogv&type=video",
+ "url": "file://tough_video_cases/video.html?src=crowd2160.ogv",
"media_metrics": {"action": "play-action"},
"add_browser_metrics": true
},
{
- "url": "file://tough_video_cases/video.html?src=crowd2160.webm&type=video",
+ "url": "file://tough_video_cases/video.html?src=crowd2160.webm",
"media_metrics": {"action": "play-action"},
"add_browser_metrics": true
},
@@ -86,22 +86,22 @@
"add_browser_metrics": true
},
{
- "url": "file://tough_video_cases/video.html?src=tulip2.ogv&type=video",
+ "url": "file://tough_video_cases/video.html?src=tulip2.ogv",
"media_metrics": {"action": "play-action"},
"add_browser_metrics": true
},
{
- "url": "file://tough_video_cases/video.html?src=tulip2.webm&type=video",
+ "url": "file://tough_video_cases/video.html?src=tulip2.webm",
"media_metrics": {"action": "play-action"},
"add_browser_metrics": true
},
{
- "url": "file://tough_video_cases/video.html?src=crowd1080.mp4&type=video",
+ "url": "file://tough_video_cases/video.html?src=crowd1080.mp4",
"media_metrics": {"action": "play-action"},
"add_browser_metrics": true
},
{
- "url": "file://tough_video_cases/video.html?src=crowd2160.mp4&type=video",
+ "url": "file://tough_video_cases/video.html?src=crowd2160.mp4",
"media_metrics": {"action": "play-action"},
"add_browser_metrics": true
},
@@ -111,7 +111,12 @@
"add_browser_metrics": true
},
{
- "url": "file://tough_video_cases/video.html?src=tulip2.mp4&type=video",
+ "url": "file://tough_video_cases/video.html?src=tulip2.mp4",
+ "media_metrics": {"action": "play-action"},
+ "add_browser_metrics": true
+ },
+ {
+ "url": "file://tough_video_cases/video.html?src=tulip2.m4a&type=audio",
"media_metrics": {"action": "play-action"},
"add_browser_metrics": true
},
diff --git a/tools/perf/page_sets/tough_video_cases/video.html b/tools/perf/page_sets/tough_video_cases/video.html
index 24d82ca5cb..641d82237a 100644
--- a/tools/perf/page_sets/tough_video_cases/video.html
+++ b/tools/perf/page_sets/tough_video_cases/video.html
@@ -4,35 +4,54 @@
</body>
<script>
function getQueryStrings() {
- // Gets query parameters from the URL; e.g., given a URL like:
- //
- // http://<url>/my.html?test=123&bob=456
- //
- // returns params["test"] = 123, params["bob"]=456, etc.
- var params = {};
-
- // RegEx to split out values by &.
- var r = /([^&=]+)=?([^&]*)/g;
-
- // Lambda function for decoding extracted match values. Replaces '+' with
- // space so decodeURIComponent functions properly.
- function d(s) { return decodeURIComponent(s.replace(/\+/g, ' ')); }
-
- var match;
- while (match = r.exec(window.location.search.substring(1)))
+ // Gets query parameters from the URL; e.g., given a URL like:
+ //
+ // http://<url>/my.html?test=123&bob=456
+ //
+ // returns params["test"] = 123, params["bob"]=456, etc.
+ var params = {};
+ // RegEx to split out values by &.
+ var r = /([^&=]+)=?([^&]*)/g;
+ // Lambda function for decoding extracted match values. Replaces '+' with
+ // space so decodeURIComponent functions properly.
+ function d(s) { return decodeURIComponent(s.replace(/\+/g, ' ')); }
+ var match;
+ while (match = r.exec(window.location.search.substring(1)))
params[d(match[1])] = d(match[2]);
+ return params;
+ }
+ // Each network config = [DOWNLOAD_BANDWIDTH_Kbit/s, LATENCY_MS]
+ // Numbers are chosen to be similar to webpagereplay/net_configs.py
+ var netConfig = {};
+ netConfig['cable'] = [5120, 28];
+ netConfig['dsl'] = [1536, 50];
+ netConfig['wifi'] = [1024, 60];
+ netConfig['none'] = null;
+ // Constrained network server URL.
+ var CNS_BASE_URL = 'http://cns.chrome:9000/ServeConstrained?';
- return params;
+ function getNetsimURL(net) {
+ if (!netConfig[net])
+ return CNS_BASE_URL;
+ return CNS_BASE_URL + 'bandwidth=' + netConfig[net][0] +
+ '&latency=' + netConfig[net][1]
}
- qsParams = getQueryStrings();
- if (qsParams["type"]) {
- testElement = document.createElement(qsParams["type"]);
- if (qsParams["id"])
- testElement.id = qsParams["id"];
- testElement.preload = "none";
- testElement.src = qsParams["src"];
- testElement.controls = true;
- document.body.appendChild(testElement);
+
+ function getMediaSRC() {
+ var mediaSRC = qsParams['src']
+ if (qsParams['net'])
+ return getNetsimURL(qsParams['net']) + '&f=' + mediaSRC;
+ return mediaSRC;
}
+
+ qsParams = getQueryStrings();
+ var type = qsParams['type'] || 'video';
+ var testElement = document.createElement(type);
+ testElement.preload = 'none';
+ testElement.controls = true;
+ if (qsParams['id'])
+ testElement.id = qsParams['id'];
+ testElement.src = getMediaSRC();
+ document.body.appendChild(testElement);
</script>
</html>
diff --git a/tools/perf/perf_tools/__init__.py b/tools/perf/perf_tools/__init__.py
deleted file mode 100644
index f9e87ccff0..0000000000
--- a/tools/perf/perf_tools/__init__.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# 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.
-
-"""A library for Chrome GPU test code."""
-
-import os
-import sys
-
-
-def _Init():
- telemetry_path = os.path.join(os.path.dirname(__file__),
- os.pardir, os.pardir, 'telemetry')
- absolute_telemetry_path = os.path.abspath(telemetry_path)
- sys.path.append(absolute_telemetry_path)
- telemetry_tools_path = os.path.join(os.path.dirname(__file__),
- os.pardir, os.pardir, 'telemetry_tools')
- absolute_telemetry_tools_path = os.path.abspath(telemetry_tools_path)
- sys.path.append(absolute_telemetry_tools_path)
-
-
-_Init()
-
-from telemetry import RemoveAllStalePycFiles
-RemoveAllStalePycFiles(os.path.dirname(__file__))
diff --git a/tools/perf/run_measurement b/tools/perf/run_measurement
index 7877041ce1..96b4428434 100755
--- a/tools/perf/run_measurement
+++ b/tools/perf/run_measurement
@@ -2,65 +2,87 @@
# 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 imp
import os
import sys
-import urllib
+import urllib2
+
-# Directory path in which to save bootstrap files.
-BOOTSTRAPPED_FILES_DIR = 'support/bootstrap_files'
-PERF_DIR = 'src/tools/perf'
+BASE_URL = 'http://src.chromium.org/chrome/trunk/'
DEPS_FILE = 'bootstrap_deps'
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-def BootstrapIfNeeded(module_name, module_path, module_deps_url):
- """Ensures that the given module_name is available, grab from URL if not."""
+SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__))
+# Directory containing src/ in a Chromium checkout.
+CHECKOUT_BASE_PATH = os.path.join(SCRIPT_PATH, os.pardir, os.pardir, os.pardir)
+# Directory in which to save bootstrap files.
+BOOTSTRAP_BASE_PATH = os.path.join(SCRIPT_PATH, 'support', 'bootstrap_files')
+
+PERF_DIR = os.path.join('src', 'tools', 'perf')
+TELEMETRY_DIR = os.path.join('src', 'tools', 'telemetry')
+TELEMETRY_TOOLS_DIR = os.path.join('src', 'tools', 'telemetry_tools')
+
+
+def _GetBasePath():
+ """Find the location of our Chromium or bootstrap checkout.
+
+ It tries to import Telemetry. If the import succeeds,
+ we assume that's the correct location.
+
+ Returns:
+ The path containing the src/ directory, or None if no checkout is found.
+ """
+ module_name = 'telemetry'
+ module_path = TELEMETRY_DIR
+
try:
- imp.find_module(module_name)
- return
+ paths = [os.path.join(CHECKOUT_BASE_PATH, module_path)]
+ imp.find_module(module_name, paths)
+ return CHECKOUT_BASE_PATH
except ImportError:
- sys.path.append(os.path.join(BASE_DIR, BOOTSTRAPPED_FILES_DIR, module_path))
- try:
- imp.find_module(module_name)
- return
- except ImportError:
- bootstrap_txt = urllib.urlopen('http://src.chromium.org/chrome/' +
- 'trunk/src/tools/telemetry_tools/' +
- 'telemetry_bootstrap.py').read()
- bootstrap = imp.new_module('bootstrap')
- exec bootstrap_txt in bootstrap.__dict__
- bootstrap.DownloadDepsURL(os.path.join(BASE_DIR, BOOTSTRAPPED_FILES_DIR),
- module_deps_url)
- return
-
-def ListBootstrapDeps():
- """List the deps required for telemetry.
-
- Returns: a list of telemetry deps.
- """
- # Add telemetry_tools to sys.path for the import below
- telemetry_tools_path = os.path.join(BASE_DIR, os.pardir, 'telemetry_tools')
- sys.path.append(telemetry_tools_path)
+ pass
+
+ try:
+ paths = [os.path.join(BOOTSTRAP_BASE_PATH, module_path)]
+ imp.find_module(module_name, paths)
+ return BOOTSTRAP_BASE_PATH
+ except ImportError:
+ pass
+
+ return None
+
+
+def _Bootstrap(bootstrap_deps_url):
+ """Grab all the deps listed in the file at bootstrap_deps_url."""
+ bootstrap_txt = urllib2.urlopen(
+ BASE_URL + 'src/tools/telemetry_tools/telemetry_bootstrap.py').read()
+ bootstrap = imp.new_module('bootstrap')
+ exec bootstrap_txt in bootstrap.__dict__
+ bootstrap.DownloadDeps(BOOTSTRAP_BASE_PATH, bootstrap_deps_url)
+
- import perf_tools
+def ListBootstrapDeps(base_path):
+ """List the deps required for telemetry."""
+ sys.path.append(os.path.join(base_path, TELEMETRY_TOOLS_DIR))
import telemetry_bootstrap
- deps_file = os.path.join(os.path.dirname(perf_tools.__file__), DEPS_FILE)
- return telemetry_bootstrap.ListAllDepsPaths(open(deps_file).read())
-def main():
- BootstrapIfNeeded('perf_tools', PERF_DIR,
- 'http://src.chromium.org/chrome/trunk/src/tools'
- '/perf/perf_tools/' + DEPS_FILE)
+ deps_file = os.path.join(base_path, PERF_DIR, DEPS_FILE)
+ return telemetry_bootstrap.ListAllDepsPaths(deps_file)
- # Add telemetry to sys.path for the import below
- telemetry_path = os.path.join(BASE_DIR, os.pardir, 'telemetry')
- sys.path.append(telemetry_path)
+
+def Main():
+ if not _GetBasePath():
+ _Bootstrap(BASE_URL + 'src/tools/perf/' + DEPS_FILE)
+
+ new_base_path = _GetBasePath()
+ new_perf_path = os.path.join(new_base_path, PERF_DIR)
+ new_telemetry_path = os.path.join(new_base_path, TELEMETRY_DIR)
if '--print-bootstrap-deps' in sys.argv:
- print ListBootstrapDeps()
- sys.exit(0)
+ print ListBootstrapDeps(new_base_path)
+ return 0
- from telemetry.page import page_measurement_runner
+ sys.path.append(new_perf_path)
import page_sets
page_set_filenames = page_sets.GetAllPageSetFilenames()
@@ -83,6 +105,8 @@ def main():
"tab_switching_measurement": "tab_switching",
}
+ sys.path.append(new_telemetry_path)
+ from telemetry.page import page_measurement_runner
# There are bots that are hard-coded to run some specific named tests.
# Convert these to the current naming conventions by overriding them
# in the runner.
@@ -97,7 +121,8 @@ def main():
return old_benchmark_names[arg]
runner = MeasurementRunner()
- sys.exit(runner.Run(BASE_DIR, page_set_filenames))
+ return runner.Run(new_perf_path, page_set_filenames)
+
if __name__ == '__main__':
- sys.exit(main())
+ sys.exit(Main())
diff --git a/tools/perf_expectations/make_expectations.py b/tools/perf_expectations/make_expectations.py
index 6b9efff58e..ce84c224dd 100755
--- a/tools/perf_expectations/make_expectations.py
+++ b/tools/perf_expectations/make_expectations.py
@@ -33,7 +33,7 @@ USAGE = ''
def ReadFile(filename):
try:
- file = open(filename, 'r')
+ file = open(filename, 'rb')
except IOError, e:
print >> sys.stderr, ('I/O Error reading file %s(%s): %s' %
(filename, e.errno, e.strerror))
@@ -104,7 +104,7 @@ def GetRowDigest(rowdata, key):
def WriteJson(filename, data, keys, calculate_sha1=True):
"""Write a list of |keys| in |data| to the file specified in |filename|."""
try:
- file = open(filename, 'w')
+ file = open(filename, 'wb')
except IOError, e:
print >> sys.stderr, ('I/O Error writing file %s(%s): %s' %
(filename, e.errno, e.strerror))
diff --git a/tools/perf_expectations/perf_expectations.json b/tools/perf_expectations/perf_expectations.json
index a6a7207438..0916cca6c9 100644
--- a/tools/perf_expectations/perf_expectations.json
+++ b/tools/perf_expectations/perf_expectations.json
@@ -370,7 +370,7 @@
"linux-release/sizes/chrome/chrome": {"reva": 234134, "revb": 234142, "type": "absolute", "better": "lower", "improve": 117806672, "regress": 134339509, "sha1": "4dfdfc74"},
"linux-release/sizes/libffmpegsumo.so-textrel/textrel": {"reva": 200467, "revb": 203456, "type": "absolute", "better": "lower", "improve": 1075, "regress": 1189, "sha1": "a10d4ea4"},
"linux-release/sizes/nacl_helper-bss/bss": {"reva": 193069, "revb": 193093, "type": "absolute", "better": "lower", "improve": 100506, "regress": 111086, "sha1": "805988e3"},
- "linux-release/sizes/nacl_helper-data/data": {"reva": 197112, "revb": 203172, "type": "absolute", "better": "lower", "improve": 13058, "regress": 15186, "sha1": "df291639"},
+ "linux-release/sizes/nacl_helper-data/data": {"reva": 239500, "revb": 239550, "type": "absolute", "better": "lower", "improve": 14413, "regress": 15931, "sha1": "5644adb8"},
"linux-release/sizes/nacl_helper-si/initializers": {"reva": 193069, "revb": 193093, "type": "absolute", "better": "lower", "improve": 15, "regress": 17, "sha1": "d7d27d7d"},
"linux-release/sizes/nacl_helper-text/text": {"reva": 233280, "revb": 233893, "type": "absolute", "better": "lower", "improve": 1179539, "regress": 1306340, "sha1": "46727f64"},
"linux-release/sizes/nacl_helper-textrel/textrel": {"reva": 117081, "revb": 117299, "type": "absolute", "better": "lower", "improve": 0, "regress": 0, "sha1": "309d1763"},
@@ -735,7 +735,7 @@
"win-release/media_tests_av_perf/ttp/Wifi_dartmoor2.ogg": {"reva": 17420, "revb": 174996, "type": "absolute", "better": "lower", "improve": 12396.55, "regress": 14062.125, "sha1": "9ca7f6b1"},
"win-release/media_tests_av_perf/ttp/Wifi_dartmoor2.wav": {"reva": 17420, "revb": 174996, "type": "absolute", "better": "lower", "improve": 6859.95, "regress": 24904.425, "sha1": "0e5625a5"},
"win-release/media_tests_av_perf/ttp/Wifi_roller.webm": {"reva": 175328, "revb": 176157, "type": "absolute", "better": "lower", "improve": 906.775, "regress": 1184.925, "sha1": "d49e638f"},
- "xp-release/sizes/chrome.dll/chrome.dll": {"reva": 228457, "revb": 228507, "type": "absolute", "better": "lower", "improve": 34625356, "regress": 38270132, "sha1": "76c63db9"},
+ "xp-release/sizes/chrome.dll/chrome.dll": {"reva": 237817, "revb": 237867, "type": "absolute", "better": "lower", "improve": 36327270, "regress": 40186138, "sha1": "0eb8be8e"},
"xp-release/sizes/chrome.exe/chrome.exe": {"reva": 183841, "revb": 184954, "type": "absolute", "better": "lower", "improve": 720358, "regress": 796186, "sha1": "b5aa4523"},
"xp-release/sizes/mini_installer.exe/mini_installer.exe": {"reva": 223825, "revb": 224162, "type": "absolute", "better": "lower", "improve": 30193766, "regress": 33489255, "sha1": "1cd4e3aa"},
"xp-release/sizes/npchrome_frame.dll/npchrome_frame.dll": {"reva": 230662, "revb": 230712, "type": "absolute", "better": "lower", "improve": 2426649, "regress": 2723482, "sha1": "1f39fef7"},
diff --git a/tools/run-bisect-perf-regression.py b/tools/run-bisect-perf-regression.py
index 8180ef4721..8dceea82fc 100755
--- a/tools/run-bisect-perf-regression.py
+++ b/tools/run-bisect-perf-regression.py
@@ -153,7 +153,10 @@ def _CreateBisectOptionsFromConfig(config):
raise RuntimeError('Cros build selected, but BISECT_CROS_IP or'
'BISECT_CROS_BOARD undefined.')
elif 'android' in config['command']:
- opts_dict['target_platform'] = 'android'
+ if 'android-chrome' in config['command']:
+ opts_dict['target_platform'] = 'android-chrome'
+ else:
+ opts_dict['target_platform'] = 'android'
return bisect.BisectOptions.FromDict(opts_dict)
@@ -267,7 +270,7 @@ def _SetupAndRunPerformanceTest(config, path_to_file, path_to_goma):
def _RunBisectionScript(config, working_directory, path_to_file, path_to_goma,
- dry_run):
+ path_to_extra_src, dry_run):
"""Attempts to execute src/tools/bisect-perf-regression.py with the parameters
passed in.
@@ -278,6 +281,7 @@ def _RunBisectionScript(config, working_directory, path_to_file, path_to_goma,
the depot.
path_to_file: Path to the bisect-perf-regression.py script.
path_to_goma: Path to goma directory.
+ path_to_extra_src: Path to extra source file.
dry_run: Do a dry run, skipping sync, build, and performance testing steps.
Returns:
@@ -322,11 +326,17 @@ def _RunBisectionScript(config, working_directory, path_to_file, path_to_goma,
return 1
if 'android' in config['command']:
- cmd.extend(['--target_platform', 'android'])
+ if 'android-chrome' in config['command']:
+ cmd.extend(['--target_platform', 'android-chrome'])
+ else:
+ cmd.extend(['--target_platform', 'android'])
if path_to_goma:
cmd.append('--use_goma')
+ if path_to_extra_src:
+ cmd.extend(['--extra_src', path_to_extra_src])
+
if dry_run:
cmd.extend(['--debug_ignore_build', '--debug_ignore_sync',
'--debug_ignore_perf_test'])
@@ -359,6 +369,10 @@ def main():
type='str',
help='Path to goma directory. If this is supplied, goma '
'builds will be enabled.')
+ parser.add_option('--extra_src',
+ type='str',
+ help='Path to extra source file. If this is supplied, '
+ 'bisect script will use this to override default behavior.')
parser.add_option('--dry_run',
action="store_true",
help='The script will perform the full bisect, but '
@@ -383,7 +397,8 @@ def main():
return 1
return _RunBisectionScript(config, opts.working_directory,
- path_to_current_directory, opts.path_to_goma, opts.dry_run)
+ path_to_current_directory, opts.path_to_goma, opts.extra_src,
+ opts.dry_run)
else:
perf_cfg_files = ['run-perf-test.cfg', os.path.join('..', 'third_party',
'WebKit', 'Tools', 'run-perf-test.cfg')]
diff --git a/tools/telemetry/bin/README.chromium b/tools/telemetry/bin/README.chromium
index 38b7e80ba2..6c7e485df5 100644
--- a/tools/telemetry/bin/README.chromium
+++ b/tools/telemetry/bin/README.chromium
@@ -1,3 +1,6 @@
+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.
diff --git a/tools/telemetry/bin/avconv.sha1 b/tools/telemetry/bin/avconv.sha1
new file mode 100644
index 0000000000..86838ed605
--- /dev/null
+++ b/tools/telemetry/bin/avconv.sha1
@@ -0,0 +1 @@
+03896ec9bdc9f1d6fa388f893d8f62f454e7e707 \ No newline at end of file
diff --git a/tools/telemetry/telemetry/__init__.py b/tools/telemetry/telemetry/__init__.py
index 791f5cfbeb..b395a68c75 100644
--- a/tools/telemetry/telemetry/__init__.py
+++ b/tools/telemetry/telemetry/__init__.py
@@ -9,15 +9,6 @@ import logging
import os
import sys
-from telemetry import exception_formatter
-
-from telemetry.core.browser import Browser
-from telemetry.core.browser_options import BrowserFinderOptions
-from telemetry.core.tab import Tab
-
-from telemetry.page.page_measurement import PageMeasurement
-from telemetry.page.page_runner import Run as RunPage
-
# Ensure Python >= 2.6
if sys.version_info < (2, 6):
@@ -25,8 +16,16 @@ if sys.version_info < (2, 6):
sys.exit(1)
+from telemetry import exception_formatter
exception_formatter.InstallUnhandledExceptionFormatter()
+from telemetry.core.browser import Browser
+from telemetry.core.browser_options import BrowserFinderOptions
+from telemetry.core.tab import Tab
+
+from telemetry.page.page_measurement import PageMeasurement
+from telemetry.page.page_runner import Run as RunPage
+
__all__ = []
diff --git a/tools/telemetry/telemetry/core/backends/adb_commands.py b/tools/telemetry/telemetry/core/backends/adb_commands.py
index 4dc52abf16..9a00d82d92 100644
--- a/tools/telemetry/telemetry/core/backends/adb_commands.py
+++ b/tools/telemetry/telemetry/core/backends/adb_commands.py
@@ -23,7 +23,8 @@ try:
from pylib import constants # pylint: disable=F0401
from pylib import forwarder # pylint: disable=F0401
from pylib import ports # pylint: disable=F0401
- from pylib.utils import apk_helper # #pylint: disable=F0401
+ from pylib.utils import apk_helper # pylint: disable=F0401
+ from pylib.utils import test_environment # pylint: disable=F0401
except Exception:
android_commands = None
@@ -40,6 +41,10 @@ def GetAttachedDevices():
return android_commands.GetAttachedDevices()
+def CleanupLeftoverProcesses():
+ test_environment.CleanupLeftoverProcesses()
+
+
def AllocateTestServerPort():
return ports.AllocateTestServerPort()
@@ -52,12 +57,16 @@ class AdbCommands(object):
"""A thin wrapper around ADB"""
def __init__(self, device):
- self._adb = android_commands.AndroidCommands(device)
+ self._adb = android_commands.AndroidCommands(device, api_strict_mode=True)
self._device = device
def device(self):
return self._device
+ @property
+ def system_properties(self):
+ return self._adb.system_properties
+
def Adb(self):
return self._adb
@@ -81,6 +90,9 @@ class AdbCommands(object):
"""
return self._adb.RunShellCommand(command, timeout_time, log_result)
+ def RunShellCommandWithSU(self, command, timeout_time=20, log_result=False):
+ return self._adb.RunShellCommandWithSU(command, timeout_time, log_result)
+
def CloseApplication(self, package):
"""Attempt to close down the application, using increasing violence.
@@ -173,6 +185,9 @@ class AdbCommands(object):
def RestartAdbdOnDevice(self):
return self._adb.RestartAdbdOnDevice()
+ def IsUserBuild(self):
+ return self._adb.GetBuildType() == 'user'
+
def GetBuildTypeOfPath(path):
if not path:
@@ -188,8 +203,8 @@ def SetupPrebuiltTools(adb):
# Prebuilt tools from r226197.
has_prebuilt = sys.platform.startswith('linux')
if has_prebuilt:
- abi = adb.RunShellCommand('getprop ro.product.cpu.abi')
- has_prebuilt = abi and abi[0].startswith('armeabi')
+ abi = adb.system_properties['ro.product.cpu.abi']
+ has_prebuilt = abi.startswith('armeabi')
if not has_prebuilt:
logging.error(
'Prebuilt android tools only available for Linux host and ARM device.')
diff --git a/tools/telemetry/telemetry/core/backends/android_rndis.py b/tools/telemetry/telemetry/core/backends/android_rndis.py
index c2c0e4bbd9..71367469ce 100644
--- a/tools/telemetry/telemetry/core/backends/android_rndis.py
+++ b/tools/telemetry/telemetry/core/backends/android_rndis.py
@@ -113,7 +113,7 @@ class RndisForwarderWithRoot(object):
['sudo', 'bash', '-c', 'echo -e "%s" > %s' % (contents, path)])
def _DisableRndis(self):
- self._adb.RunShellCommand('setprop sys.usb.config adb')
+ self._adb.system_properties['sys.usb.config'] = 'adb'
self._WaitForDevice()
def _EnableRndis(self):
@@ -326,8 +326,8 @@ doit &
default_routes = [route[0] for route in routes if route[1] == '00000000']
return (
default_routes[0] if default_routes else None,
- self._adb.RunShellCommand('getprop net.dns1')[0],
- self._adb.RunShellCommand('getprop net.dns2')[0],
+ self._adb.system_properties['net.dns1'],
+ self._adb.system_properties['net.dns2'],
)
def _OverrideDns(self, iface, dns1, dns2):
@@ -341,12 +341,11 @@ doit &
return # If there is no route, then nobody cares about DNS.
# DNS proxy in older versions of Android is configured via properties.
# TODO(szym): run via su -c if necessary.
- self._adb.RunShellCommand('setprop net.dns1 ' + dns1)
- self._adb.RunShellCommand('setprop net.dns2 ' + dns2)
- dnschange = self._adb.RunShellCommand('getprop net.dnschange')[0]
+ self._adb.system_properties['net.dns1'] = dns1
+ self._adb.system_properties['net.dns2'] = dns2
+ dnschange = self._adb.system_properties['net.dnschange']
if dnschange:
- self._adb.RunShellCommand('setprop net.dnschange %s' %
- (int(dnschange) + 1))
+ self._adb.system_properties['net.dnschange'] = int(dnschange) + 1
# Since commit 8b47b3601f82f299bb8c135af0639b72b67230e6 to frameworks/base
# the net.dns1 properties have been replaced with explicit commands for netd
self._adb.RunShellCommand('ndc netd resolver setifdns %s %s %s' %
diff --git a/tools/telemetry/telemetry/core/backends/chrome/android_browser_backend.py b/tools/telemetry/telemetry/core/backends/chrome/android_browser_backend.py
index 7141a62664..f286d50a82 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/android_browser_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/android_browser_backend.py
@@ -31,11 +31,10 @@ class AndroidBrowserBackendSettings(object):
raise NotImplementedError()
def RemoveProfile(self):
- files = self.adb.RunShellCommand('su -c ls "%s"' % self.profile_dir)
+ files = self.adb.RunShellCommandWithSU('ls "%s"' % self.profile_dir)
# Don't delete lib, since it is created by the installer.
- files.remove('lib')
- paths = ['"%s/%s"' % (self.profile_dir, f) for f in files]
- self.adb.RunShellCommand('su -c rm -r %s' % ' '.join(paths))
+ paths = ['"%s/%s"' % (self.profile_dir, f) for f in files if f != 'lib']
+ self.adb.RunShellCommandWithSU('rm -r %s' % ' '.join(paths))
def PushProfile(self, _):
logging.critical('Profiles cannot be overriden with current configuration')
@@ -54,11 +53,18 @@ class ChromeBackendSettings(AndroidBrowserBackendSettings):
# Stores a default Preferences file, re-used to speed up "--page-repeat".
_default_preferences_file = None
+ @staticmethod
+ def _GetCommandLineFile(adb):
+ if adb.IsUserBuild():
+ return '/data/local/tmp/chrome-command-line'
+ else:
+ return '/data/local/chrome-command-line'
+
def __init__(self, adb, package):
super(ChromeBackendSettings, self).__init__(
adb=adb,
activity='com.google.android.apps.chrome.Main',
- cmdline_file='/data/local/chrome-command-line',
+ cmdline_file=ChromeBackendSettings._GetCommandLineFile(adb),
package=package,
pseudo_exec_name='chrome',
supports_tab_control=True)
@@ -89,7 +95,7 @@ class ChromeBackendSettings(AndroidBrowserBackendSettings):
dumpsys = self.adb.RunShellCommand('dumpsys package %s' % self.package)
id_line = next(line for line in dumpsys if 'userId=' in line)
uid = re.search('\d+', id_line).group()
- files = self.adb.RunShellCommand('su -c ls "%s"' % self.profile_dir)
+ files = self.adb.RunShellCommandWithSU('ls "%s"' % self.profile_dir)
files.remove('lib')
paths = ['%s/%s' % (self.profile_dir, f) for f in files]
for path in paths:
@@ -348,8 +354,8 @@ class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
# pulled down is really needed e.g. .pak files.
if not os.path.exists(self._output_profile_path):
os.makedirs(self._output_profile_pathame)
- files = self.adb.RunShellCommand(
- 'su -c ls "%s"' % self._backend_settings.profile_dir)
+ files = self.adb.RunShellCommandWithSU(
+ 'ls "%s"' % self._backend_settings.profile_dir)
for f in files:
# Don't pull lib, since it is created by the installer.
if f != 'lib':
diff --git a/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py b/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py
index 09eee0b799..32c036121d 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py
@@ -203,7 +203,7 @@ def FindAllAvailableBrowsers(finder_options, logging=real_logging):
# flake out during the test. We skip this if Telemetry is running under a
# buildbot because build/android/test_runner.py wrapper already took care
# of it before starting the shards.
- adb.RestartAdbdOnDevice()
+ adb_commands.CleanupLeftoverProcesses()
packages = adb.RunShellCommand('pm list packages')
possible_browsers = []
diff --git a/tools/telemetry/telemetry/core/backends/chrome/cros_browser_backend.py b/tools/telemetry/telemetry/core/backends/chrome/cros_browser_backend.py
index 76921b7a3e..d991f86efb 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/cros_browser_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/cros_browser_backend.py
@@ -101,8 +101,9 @@ class CrOSBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
# Workaround for crbug.com/308224. TODO(achuith): Remove this flag.
'--multi-profiles',
# Debug logging for login flake (crbug.com/263527).
- '--vmodule=*/browser/automation/*=2,*/chromeos/net/*=2,' +
- '*/chromeos/login/*=2,*/extensions/*=2'])
+ '--vmodule=*/browser/automation/*=2,*/chromeos/net/*=2,'
+ '*/chromeos/login/*=2,*/extensions/*=2,'
+ '*/device_policy_decoder_chromeos.cc=2'])
if self._is_guest:
args.extend([
diff --git a/tools/telemetry/telemetry/core/backends/chrome/cros_interface.py b/tools/telemetry/telemetry/core/backends/chrome/cros_interface.py
index 952afa4a49..a6c3c29418 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/cros_interface.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/cros_interface.py
@@ -255,7 +255,8 @@ class CrOSInterface(object):
continue
m = re.match('^\s*(\d+)\s+(\d+)\s+(.+)\s+(.+)', l, re.DOTALL)
assert m
- procs.append((int(m.group(1)), m.group(3), int(m.group(2)), m.group(4)))
+ 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
diff --git a/tools/telemetry/telemetry/core/backends/chrome/inspector_backend.py b/tools/telemetry/telemetry/core/backends/chrome/inspector_backend.py
index 61925b1b85..64beccb6b2 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/inspector_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/inspector_backend.py
@@ -7,9 +7,9 @@ import socket
import sys
import time
+from telemetry.core import bitmap
from telemetry.core import exceptions
from telemetry.core import util
-from telemetry.core.backends import png_bitmap
from telemetry.core.backends.chrome import inspector_console
from telemetry.core.backends.chrome import inspector_memory
from telemetry.core.backends.chrome import inspector_network
@@ -139,7 +139,7 @@ class InspectorBackend(object):
})()
""")
if snap:
- return png_bitmap.PngBitmap.FromBase64(snap['data'])
+ return bitmap.Bitmap.FromBase64Png(snap['data'])
return None
# Console public methods.
@@ -266,12 +266,19 @@ class InspectorBackend(object):
self._socket.close()
self._socket = None
def IsBack():
- return self._browser_backend.tab_list_backend.DoesDebuggerUrlExist(
- self._debugger_url)
+ if not self._browser_backend.tab_list_backend.DoesDebuggerUrlExist(
+ self._debugger_url):
+ return False
+ try:
+ self._Connect()
+ except exceptions.TabCrashException, ex:
+ if ex.message.message.find('Handshake Status 500') == 0:
+ return False
+ raise
+ return True
util.WaitFor(IsBack, 512)
sys.stderr.write('\n')
sys.stderr.write('Inspector\'s UI closed. Telemetry will now resume.\n')
- self._Connect()
def SyncRequest(self, req, timeout=10):
self._Connect(timeout)
diff --git a/tools/telemetry/telemetry/core/backends/chrome/inspector_timeline.py b/tools/telemetry/telemetry/core/backends/chrome/inspector_timeline.py
index 92e83d3a6d..9492782139 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/inspector_timeline.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/inspector_timeline.py
@@ -1,15 +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.core.timeline import model
+
class TabBackendException(Exception):
+ """An exception which indicates an error response from devtools inspector."""
pass
+
class InspectorTimeline(object):
"""Implementation of dev tools timeline."""
+
class Recorder(object):
- """Utility class to Start / Stop recording timeline."""
+ """Utility class to Start and Stop recording timeline.
+
+ Example usage:
+
+ with inspector_timeline.InspectorTimeline.Recorder(tab):
+ # Something to run while the timeline is recording.
+
+ This is an alternative to directly calling the Start and Stop methods below.
+ """
def __init__(self, tab):
self._tab = tab
@@ -23,49 +36,65 @@ class InspectorTimeline(object):
self._inspector_backend = inspector_backend
self._is_recording = False
self._timeline_model = None
- self._raw_events = None
@property
def timeline_model(self):
return self._timeline_model
def Start(self):
- if self._is_recording:
- return
+ """Starts recording."""
+ assert not self._is_recording, 'Start should only be called once.'
self._is_recording = True
- self._timeline_model = None
- self._raw_events = []
- self._inspector_backend.RegisterDomain('Timeline',
- self._OnNotification, self._OnClose)
- req = {'method': 'Timeline.start'}
- self._SendSyncRequest(req)
+ self._inspector_backend.RegisterDomain(
+ 'Timeline', self._OnNotification, self._OnClose)
+ # The 'bufferEvents' parameter below means that events should not be sent
+ # individually as messages, but instead all at once when a Timeline.stop
+ # request is sent.
+ request = {
+ 'method': 'Timeline.start',
+ 'params': {'bufferEvents': True},
+ }
+ self._SendSyncRequest(request)
def Stop(self):
- if not self._is_recording:
- raise TabBackendException('Stop() called but not started')
- self._is_recording = False
- self._timeline_model = model.TimelineModel(event_data=self._raw_events,
- shift_world_to_zero=False)
- req = {'method': 'Timeline.stop'}
- self._SendSyncRequest(req)
+ """Stops recording and makes a TimelineModel with the event data."""
+ assert self._is_recording, 'Stop should be called after Start.'
+ request = {'method': 'Timeline.stop'}
+ result = self._SendSyncRequest(request)
+ raw_events = result['events']
+ self._timeline_model = model.TimelineModel(
+ event_data=raw_events, shift_world_to_zero=False)
self._inspector_backend.UnregisterDomain('Timeline')
+ self._is_recording = False
- def _SendSyncRequest(self, req, timeout=60):
- res = self._inspector_backend.SyncRequest(req, timeout)
- if 'error' in res:
- raise TabBackendException(res['error']['message'])
- return res['result']
+ def _SendSyncRequest(self, request, timeout=60):
+ """Sends a devtools remote debugging protocol request.
- def _OnNotification(self, msg):
- if not self._is_recording:
- return
- if 'method' in msg and msg['method'] == 'Timeline.eventRecorded':
- self._OnEventRecorded(msg)
+ The types of request that are valid is determined by protocol.json:
+ https://src.chromium.org/viewvc/blink/trunk/Source/devtools/protocol.json
+
+ Args:
+ request: Request dict, may contain the keys 'method' and 'params'.
+ timeout: Number of seconds to wait for a response.
+
+ Returns:
+ The result given in the response message.
- def _OnEventRecorded(self, msg):
- record = msg.get('params', {}).get('record')
- if record:
- self._raw_events.append(record)
+ Raises:
+ TabBackendException: The response indicates an error occurred.
+ """
+ response = self._inspector_backend.SyncRequest(request, timeout)
+ if 'error' in response:
+ raise TabBackendException(response['error']['message'])
+ return response['result']
+
+ def _OnNotification(self, msg):
+ """Handler called when a message is received."""
+ # Since 'Timeline.start' was invoked with the 'bufferEvents' parameter,
+ # there will be no timeline notifications while recording.
+ pass
def _OnClose(self):
+ """Handler called when a domain is unregistered."""
pass
+
diff --git a/tools/telemetry/telemetry/core/backends/chrome/inspector_timeline_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/inspector_timeline_unittest.py
index e5686608cf..01eeb27c54 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/inspector_timeline_unittest.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/inspector_timeline_unittest.py
@@ -9,32 +9,39 @@ from telemetry.core import util
from telemetry.core.backends.chrome import inspector_timeline
from telemetry.unittest import tab_test_case
+
class InspectorTimelineTabTest(tab_test_case.TabTestCase):
- def _StartServer(self):
- self._browser.SetHTTPServerDirectories(util.GetUnittestDataDir())
+ """Test case that opens a browser and creates and then checks an event."""
def _WaitForAnimationFrame(self):
+ """Wait until the variable window.done is set on the tab."""
def _IsDone():
- js_is_done = """done"""
- return bool(self._tab.EvaluateJavaScript(js_is_done))
+ return bool(self._tab.EvaluateJavaScript('window.done'))
util.WaitFor(_IsDone, 5)
def testGotTimeline(self):
if sys.platform in ('win32', 'cygwin'):
raise unittest.SkipTest('Test flaky on windows. http://crbug.com/321529')
+ # While the timeline is recording, call window.webkitRequestAnimationFrame.
+ # This will create a FireAnimationEvent, which can be checked below. See:
+ # https://developer.mozilla.org/en/docs/Web/API/window.requestAnimationFrame
with inspector_timeline.InspectorTimeline.Recorder(self._tab):
self._tab.ExecuteJavaScript(
-"""
-var done = false;
-function sleep(ms) {
- var endTime = (new Date().getTime()) + ms;
- while ((new Date().getTime()) < endTime);
-}
-window.webkitRequestAnimationFrame(function() { sleep(10); done = true; });
-""")
+ """
+ var done = false;
+ function sleep(ms) {
+ var endTime = (new Date().getTime()) + ms;
+ while ((new Date().getTime()) < endTime);
+ }
+ window.webkitRequestAnimationFrame(function() {
+ sleep(10);
+ window.done = true;
+ });
+ """)
self._WaitForAnimationFrame()
- r = self._tab.timeline_model.GetAllEventsOfName('FireAnimationFrame')
- self.assertTrue(len(r) > 0)
- self.assertTrue(r[0].duration > 0)
+ # There should be at least a FireAnimationFrame record with some duration.
+ events = self._tab.timeline_model.GetAllEventsOfName('FireAnimationFrame')
+ self.assertTrue(len(events) > 0)
+ self.assertTrue(events[0].duration > 0)
diff --git a/tools/telemetry/telemetry/core/backends/png_bitmap.py b/tools/telemetry/telemetry/core/backends/png_bitmap.py
deleted file mode 100644
index 7505282952..0000000000
--- a/tools/telemetry/telemetry/core/backends/png_bitmap.py
+++ /dev/null
@@ -1,168 +0,0 @@
-# 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 cStringIO
-
-from telemetry.core import util
-
-util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'png')
-import png # pylint: disable=F0401
-
-
-class PngColor(object):
- """Encapsulates an RGB color retreived from a PngBitmap"""
-
- def __init__(self, r, g, b, a=255):
- self.r = r
- self.g = g
- self.b = b
- self.a = a
-
- 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(PngColor(r, g, b), tolerance)
-
- def AssertIsRGBA(self, r, g, b, a, tolerance=0):
- assert self.IsEqual(PngColor(r, g, b, a), tolerance)
-
-
-class PngBitmap(object):
- """Utilities for parsing and inspecting a PNG"""
-
- def __init__(self, png_data):
- self._png_data = png_data
- self._png = png.Reader(bytes=self._png_data)
- rgba8_data = self._png.asRGBA8()
- self._width = rgba8_data[0]
- self._height = rgba8_data[1]
- self._pixels = list(rgba8_data[2])
- self._metadata = rgba8_data[3]
-
- @property
- def width(self):
- """Width of the snapshot"""
- return self._width
-
- @property
- def height(self):
- """Height of the snapshot"""
- return self._height
-
- def GetPixelColor(self, x, y):
- """Returns a PngColor for the pixel at (x, y)"""
- row = self._pixels[y]
- offset = x * 4
- return PngColor(row[offset], row[offset+1], row[offset+2], row[offset+3])
-
- def WriteFile(self, path):
- with open(path, "wb") as f:
- f.write(self._png_data)
-
- @staticmethod
- def FromFile(path):
- with open(path, "rb") as f:
- return PngBitmap(f.read())
-
- @staticmethod
- def FromBase64(base64_png):
- return PngBitmap(base64.b64decode(base64_png))
-
- def IsEqual(self, expected_png, tolerance=0):
- """Verifies that two PngBitmaps are identical within a given tolerance"""
-
- # Dimensions must be equal
- if self.width != expected_png.width or self.height != expected_png.height:
- return False
-
- # Loop over each pixel and test for equality
- for y in range(self.height):
- for x in range(self.width):
- c0 = self.GetPixelColor(x, y)
- c1 = expected_png.GetPixelColor(x, y)
- if not c0.IsEqual(c1, tolerance):
- return False
-
- return True
-
- def Diff(self, other_png):
- """Returns a new PngBitmap that represents the difference between this image
- and another PngBitmap"""
-
- # Output dimensions will be the maximum of the two input dimensions
- out_width = max(self.width, other_png.width)
- out_height = max(self.height, other_png.height)
-
- diff = [[0 for x in xrange(out_width * 3)] for x in xrange(out_height)]
-
- # Loop over each pixel and write out the difference
- for y in range(out_height):
- for x in range(out_width):
- if x < self.width and y < self.height:
- c0 = self.GetPixelColor(x, y)
- else:
- c0 = PngColor(0, 0, 0, 0)
-
- if x < other_png.width and y < other_png.height:
- c1 = other_png.GetPixelColor(x, y)
- else:
- c1 = PngColor(0, 0, 0, 0)
-
- offset = x * 3
- diff[y][offset] = abs(c0.r - c1.r)
- diff[y][offset+1] = abs(c0.g - c1.g)
- diff[y][offset+2] = abs(c0.b - c1.b)
-
- # This particular method can only save to a file, so the result will be
- # written into an in-memory buffer and read back into a PngBitmap
- diff_img = png.from_array(diff, mode='RGB')
- output = cStringIO.StringIO()
- try:
- diff_img.save(output)
- diff_png = PngBitmap(output.getvalue())
- finally:
- output.close()
-
- return diff_png
-
- def Crop(self, left, top, width, height):
- """Returns a new PngBitmap that represents the specified sub-rect of this
- PngBitmap"""
-
- if (left < 0 or top < 0 or
- (left + width) > self.width or
- (top + height) > self.height):
- raise Exception('Invalid dimensions')
-
- img_data = [[0 for x in xrange(width * 4)] for x in xrange(height)]
-
- # Copy each pixel in the sub-rect
- for y in range(height):
- for x in range(width):
- c = self.GetPixelColor(x + left, y + top)
- offset = x * 4
- img_data[y][offset] = c.r
- img_data[y][offset+1] = c.g
- img_data[y][offset+2] = c.b
- img_data[y][offset+3] = c.a
-
- # This particular method can only save to a file, so the result will be
- # written into an in-memory buffer and read back into a PngBitmap
- crop_img = png.from_array(img_data, mode='RGBA')
- output = cStringIO.StringIO()
- try:
- crop_img.save(output)
- crop_png = PngBitmap(output.getvalue())
- finally:
- output.close()
-
- return crop_png
diff --git a/tools/telemetry/telemetry/core/backends/png_bitmap_unittest.py b/tools/telemetry/telemetry/core/backends/png_bitmap_unittest.py
deleted file mode 100644
index 0e3c233c8c..0000000000
--- a/tools/telemetry/telemetry/core/backends/png_bitmap_unittest.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# 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 os
-
-from telemetry.core import util
-from telemetry.core.backends import png_bitmap
-
-
-# This is a simple base64 encoded 2x2 PNG which contains, in order, a single
-# Red, Yellow, Blue, and Green pixel.
-test_png = """
-iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91
-JpzAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACx
-MBAJqcGAAAABZJREFUCNdj/M/AwPCfgYGB4T/DfwY
-AHAAD/iOWZXsAAAAASUVORK5CYII=
-"""
-
-
-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 PngBitmapTest(unittest.TestCase):
- def testReadFromBase64(self):
- png = png_bitmap.PngBitmap.FromBase64(test_png)
-
- self.assertEquals(2, png.width)
- self.assertEquals(2, png.height)
-
- png.GetPixelColor(0, 0).AssertIsRGB(255, 0, 0)
- png.GetPixelColor(1, 1).AssertIsRGB(0, 255, 0)
- png.GetPixelColor(0, 1).AssertIsRGB(0, 0, 255)
- png.GetPixelColor(1, 0).AssertIsRGB(255, 255, 0)
-
- def testReadFromFile(self):
- file_png = png_bitmap.PngBitmap.FromFile(test_png_path)
-
- self.assertEquals(2, file_png.width)
- self.assertEquals(2, file_png.height)
-
- file_png.GetPixelColor(0, 0).AssertIsRGB(255, 0, 0)
- file_png.GetPixelColor(1, 1).AssertIsRGB(0, 255, 0)
- file_png.GetPixelColor(0, 1).AssertIsRGB(0, 0, 255)
- file_png.GetPixelColor(1, 0).AssertIsRGB(255, 255, 0)
-
- def testIsEqual(self):
- png = png_bitmap.PngBitmap.FromBase64(test_png)
- file_png = png_bitmap.PngBitmap.FromFile(test_png_path)
- self.assertTrue(png.IsEqual(file_png))
-
- def testDiff(self):
- file_png = png_bitmap.PngBitmap.FromFile(test_png_path)
- file_png_2 = png_bitmap.PngBitmap.FromFile(test_png_2_path)
-
- diff_png = file_png.Diff(file_png)
-
- self.assertEquals(2, diff_png.width)
- self.assertEquals(2, diff_png.height)
-
- diff_png.GetPixelColor(0, 0).AssertIsRGB(0, 0, 0)
- diff_png.GetPixelColor(1, 1).AssertIsRGB(0, 0, 0)
- diff_png.GetPixelColor(0, 1).AssertIsRGB(0, 0, 0)
- diff_png.GetPixelColor(1, 0).AssertIsRGB(0, 0, 0)
-
- diff_png = file_png.Diff(file_png_2)
-
- self.assertEquals(3, diff_png.width)
- self.assertEquals(3, diff_png.height)
-
- diff_png.GetPixelColor(0, 0).AssertIsRGB(0, 255, 255)
- diff_png.GetPixelColor(1, 1).AssertIsRGB(255, 0, 255)
- diff_png.GetPixelColor(0, 1).AssertIsRGB(255, 255, 0)
- diff_png.GetPixelColor(1, 0).AssertIsRGB(0, 0, 255)
-
- diff_png.GetPixelColor(0, 2).AssertIsRGB(255, 255, 255)
- diff_png.GetPixelColor(1, 2).AssertIsRGB(255, 255, 255)
- diff_png.GetPixelColor(2, 0).AssertIsRGB(255, 255, 255)
- diff_png.GetPixelColor(2, 1).AssertIsRGB(255, 255, 255)
- diff_png.GetPixelColor(2, 2).AssertIsRGB(255, 255, 255)
diff --git a/tools/telemetry/telemetry/core/backends/webdriver/webdriver_tab_backend.py b/tools/telemetry/telemetry/core/backends/webdriver/webdriver_tab_backend.py
index d4dd75c0d5..e566e35f6c 100644
--- a/tools/telemetry/telemetry/core/backends/webdriver/webdriver_tab_backend.py
+++ b/tools/telemetry/telemetry/core/backends/webdriver/webdriver_tab_backend.py
@@ -4,7 +4,7 @@
import logging
-from telemetry.core.backends import png_bitmap
+from telemetry.core import bitmap
class WebDriverTabBackend(object):
def __init__(self, browser_backend, window_handle):
@@ -53,7 +53,7 @@ class WebDriverTabBackend(object):
self._browser_backend.driver.switch_to_window(self._window_handle)
snap = self._browser_backend.driver.get_screenshot_as_base64()
if snap:
- return png_bitmap.PngBitmap(snap)
+ return bitmap.Bitmap.FromPng(snap)
return None
@property
diff --git a/tools/telemetry/telemetry/core/bitmap.py b/tools/telemetry/telemetry/core/bitmap.py
new file mode 100644
index 0000000000..623ced9380
--- /dev/null
+++ b/tools/telemetry/telemetry/core/bitmap.py
@@ -0,0 +1,191 @@
+# 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 cStringIO
+
+from telemetry.core import util
+
+util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'png')
+import png # pylint: disable=F0401
+
+
+class RgbaColor(object):
+ """Encapsulates an RGBA color retreived from a Bitmap"""
+
+ def __init__(self, r, g, b, a=255):
+ self.r = r
+ self.g = g
+ self.b = b
+ self.a = a
+
+ 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)
+
+
+class Bitmap(object):
+ """Utilities for parsing and inspecting a bitmap."""
+
+ def __init__(self, bpp, width, height, pixels, metadata=None):
+ assert bpp in [3, 4], 'Invalid bytes per pixel'
+ assert width > 0, 'Invalid width'
+ assert height > 0, 'Invalid height'
+ assert pixels, 'Must specify pixels'
+ assert bpp * width * height == len(pixels), 'Dimensions and pixels mismatch'
+
+ self._bpp = bpp
+ self._width = width
+ self._height = height
+ self._pixels = pixels
+ self._metadata = metadata
+ if not self._metadata:
+ self._metadata = {'size': (width, height)}
+
+ @property
+ def width(self):
+ """Width of the snapshot"""
+ return self._width
+
+ @property
+ def height(self):
+ """Height of the snapshot"""
+ return self._height
+
+ def GetPixelColor(self, x, y):
+ """Returns a RgbaColor for the pixel at (x, y)"""
+ base = self._bpp * (y * self._width + x)
+ if self._bpp == 4:
+ return RgbaColor(self._pixels[base + 0], self._pixels[base + 1],
+ self._pixels[base + 2], self._pixels[base + 3])
+ return RgbaColor(self._pixels[base + 0], self._pixels[base + 1],
+ self._pixels[base + 2])
+
+ def WritePngFile(self, path):
+ with open(path, "wb") as f:
+ png.Writer(**self._metadata).write_array(f, self._pixels)
+
+ @staticmethod
+ def FromPng(png_data):
+ width, height, pixels, meta = png.Reader(bytes=png_data).read_flat()
+ return Bitmap(4 if meta['alpha'] else 3, width, height, pixels, meta)
+
+ @staticmethod
+ def FromPngFile(path):
+ with open(path, "rb") as f:
+ return Bitmap.FromPng(f.read())
+
+ @staticmethod
+ def FromBase64Png(base64_png):
+ return Bitmap.FromPng(base64.b64decode(base64_png))
+
+ # pylint: disable=W0212
+ def IsEqual(self, other, tolerance=0):
+ """Determines whether two Bitmaps are identical within a given tolerance"""
+
+ # Dimensions must be equal
+ if self.width != other.width or self.height != other.height:
+ return False
+
+ # Loop over each pixel and test for equality
+ if tolerance or self._bpp != other._bpp:
+ for y in range(self.height):
+ for x in range(self.width):
+ c0 = self.GetPixelColor(x, y)
+ c1 = other.GetPixelColor(x, y)
+ if not c0.IsEqual(c1, tolerance):
+ return False
+ else:
+ if type(self._pixels) is not bytearray:
+ self._pixels = bytearray(self._pixels)
+ if type(other._pixels) is not bytearray:
+ other._pixels = bytearray(other._pixels)
+ return self._pixels == other._pixels
+
+ return True
+
+ def Diff(self, other):
+ """Returns a new Bitmap that represents the difference between this image
+ and another Bitmap."""
+
+ # Output dimensions will be the maximum of the two input dimensions
+ out_width = max(self.width, other.width)
+ out_height = max(self.height, other.height)
+
+ diff = [[0 for x in xrange(out_width * 3)] for x in xrange(out_height)]
+
+ # Loop over each pixel and write out the difference
+ for y in range(out_height):
+ for x in range(out_width):
+ if x < self.width and y < self.height:
+ c0 = self.GetPixelColor(x, y)
+ else:
+ c0 = RgbaColor(0, 0, 0, 0)
+
+ if x < other.width and y < other.height:
+ c1 = other.GetPixelColor(x, y)
+ else:
+ c1 = RgbaColor(0, 0, 0, 0)
+
+ offset = x * 3
+ diff[y][offset] = abs(c0.r - c1.r)
+ diff[y][offset+1] = abs(c0.g - c1.g)
+ diff[y][offset+2] = abs(c0.b - c1.b)
+
+ # This particular method can only save to a file, so the result will be
+ # written into an in-memory buffer and read back into a Bitmap
+ diff_img = png.from_array(diff, mode='RGB')
+ output = cStringIO.StringIO()
+ try:
+ diff_img.save(output)
+ diff = Bitmap.FromPng(output.getvalue())
+ finally:
+ output.close()
+
+ return diff
+
+ def Crop(self, left, top, width, height):
+ """Returns a new Bitmap that represents the specified sub-rect of this."""
+
+ if (left < 0 or top < 0 or
+ (left + width) > self.width or
+ (top + height) > self.height):
+ raise ValueError('Invalid dimensions')
+
+ img_data = [[0 for x in xrange(width * 4)] for x in xrange(height)]
+
+ # Copy each pixel in the sub-rect.
+ # TODO(tonyg): Make this faster by avoiding the copy and artificially
+ # restricting the dimensions.
+ for y in range(height):
+ for x in range(width):
+ c = self.GetPixelColor(x + left, y + top)
+ offset = x * 4
+ img_data[y][offset] = c.r
+ img_data[y][offset+1] = c.g
+ img_data[y][offset+2] = c.b
+ img_data[y][offset+3] = c.a
+
+ # This particular method can only save to a file, so the result will be
+ # written into an in-memory buffer and read back into a Bitmap
+ crop_img = png.from_array(img_data, mode='RGBA')
+ output = cStringIO.StringIO()
+ try:
+ crop_img.save(output)
+ crop = Bitmap.FromPng(output.getvalue())
+ finally:
+ output.close()
+
+ return crop
diff --git a/tools/telemetry/telemetry/core/bitmap_unittest.py b/tools/telemetry/telemetry/core/bitmap_unittest.py
new file mode 100644
index 0000000000..4d37e1490a
--- /dev/null
+++ b/tools/telemetry/telemetry/core/bitmap_unittest.py
@@ -0,0 +1,90 @@
+# 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
+import os
+import unittest
+
+from telemetry.core import bitmap
+from telemetry.core import util
+
+
+# This is a simple base64 encoded 2x2 PNG which contains, in order, a single
+# Red, Yellow, Blue, and Green pixel.
+test_png = """
+iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91
+JpzAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACx
+MBAJqcGAAAABZJREFUCNdj/M/AwPCfgYGB4T/DfwY
+AHAAD/iOWZXsAAAAASUVORK5CYII=
+"""
+
+
+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 BitmapTest(unittest.TestCase):
+ def testReadFromBase64Png(self):
+ bmp = bitmap.Bitmap.FromBase64Png(test_png)
+
+ self.assertEquals(2, bmp.width)
+ self.assertEquals(2, bmp.height)
+
+ bmp.GetPixelColor(0, 0).AssertIsRGB(255, 0, 0)
+ bmp.GetPixelColor(1, 1).AssertIsRGB(0, 255, 0)
+ bmp.GetPixelColor(0, 1).AssertIsRGB(0, 0, 255)
+ bmp.GetPixelColor(1, 0).AssertIsRGB(255, 255, 0)
+
+ def testReadFromPngFile(self):
+ file_bmp = bitmap.Bitmap.FromPngFile(test_png_path)
+
+ self.assertEquals(2, file_bmp.width)
+ self.assertEquals(2, file_bmp.height)
+
+ file_bmp.GetPixelColor(0, 0).AssertIsRGB(255, 0, 0)
+ file_bmp.GetPixelColor(1, 1).AssertIsRGB(0, 255, 0)
+ file_bmp.GetPixelColor(0, 1).AssertIsRGB(0, 0, 255)
+ file_bmp.GetPixelColor(1, 0).AssertIsRGB(255, 255, 0)
+
+ def testWritePngFile(self):
+ orig = bitmap.Bitmap.FromPngFile(test_png_path)
+ temp_file = tempfile.NamedTemporaryFile().name
+ orig.WritePngFile(temp_file)
+ new_file = bitmap.Bitmap.FromPngFile(temp_file)
+ self.assertTrue(orig.IsEqual(new_file))
+
+ def testIsEqual(self):
+ bmp = bitmap.Bitmap.FromBase64Png(test_png)
+ file_bmp = bitmap.Bitmap.FromPngFile(test_png_path)
+ self.assertTrue(bmp.IsEqual(file_bmp))
+
+ def testDiff(self):
+ file_bmp = bitmap.Bitmap.FromPngFile(test_png_path)
+ file_bmp_2 = bitmap.Bitmap.FromPngFile(test_png_2_path)
+
+ diff_bmp = file_bmp.Diff(file_bmp)
+
+ self.assertEquals(2, diff_bmp.width)
+ self.assertEquals(2, diff_bmp.height)
+
+ diff_bmp.GetPixelColor(0, 0).AssertIsRGB(0, 0, 0)
+ diff_bmp.GetPixelColor(1, 1).AssertIsRGB(0, 0, 0)
+ diff_bmp.GetPixelColor(0, 1).AssertIsRGB(0, 0, 0)
+ diff_bmp.GetPixelColor(1, 0).AssertIsRGB(0, 0, 0)
+
+ diff_bmp = file_bmp.Diff(file_bmp_2)
+
+ self.assertEquals(3, diff_bmp.width)
+ self.assertEquals(3, diff_bmp.height)
+
+ diff_bmp.GetPixelColor(0, 0).AssertIsRGB(0, 255, 255)
+ diff_bmp.GetPixelColor(1, 1).AssertIsRGB(255, 0, 255)
+ diff_bmp.GetPixelColor(0, 1).AssertIsRGB(255, 255, 0)
+ diff_bmp.GetPixelColor(1, 0).AssertIsRGB(0, 0, 255)
+
+ diff_bmp.GetPixelColor(0, 2).AssertIsRGB(255, 255, 255)
+ diff_bmp.GetPixelColor(1, 2).AssertIsRGB(255, 255, 255)
+ diff_bmp.GetPixelColor(2, 0).AssertIsRGB(255, 255, 255)
+ diff_bmp.GetPixelColor(2, 1).AssertIsRGB(255, 255, 255)
+ diff_bmp.GetPixelColor(2, 2).AssertIsRGB(255, 255, 255)
diff --git a/tools/telemetry/telemetry/core/browser_options.py b/tools/telemetry/telemetry/core/browser_options.py
index 117be50149..92e66b2854 100644
--- a/tools/telemetry/telemetry/core/browser_options.py
+++ b/tools/telemetry/telemetry/core/browser_options.py
@@ -45,6 +45,8 @@ class BrowserFinderOptions(optparse.Values):
self.page_filter = None
self.page_filter_exclude = None
+ self.page_label_filter = None
+ self.page_label_filter_exclude = None
self.repeat_options = repeat_options.RepeatOptions()
self.browser_options = BrowserOptions()
diff --git a/tools/telemetry/telemetry/core/platform/__init__.py b/tools/telemetry/telemetry/core/platform/__init__.py
index ad5909ce77..a72004fec6 100644
--- a/tools/telemetry/telemetry/core/platform/__init__.py
+++ b/tools/telemetry/telemetry/core/platform/__init__.py
@@ -128,6 +128,37 @@ class Platform(object):
"""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.
+
+ Yields:
+ (time_ms, bitmap) tuples representing each video keyframe. Only the first
+ frame in a run of sequential duplicate bitmaps is included.
+ time_ms is milliseconds relative to the first frame.
+ bitmap is a telemetry.core.Bitmap.
+ """
+ for t in self._platform_backend.StopVideoCapture():
+ yield t
+
def CreatePlatformBackendForCurrentOS():
if sys.platform.startswith('linux'):
diff --git a/tools/telemetry/telemetry/core/platform/android_platform_backend.py b/tools/telemetry/telemetry/core/platform/android_platform_backend.py
index 9cd6f677bb..a0f8229956 100644
--- a/tools/telemetry/telemetry/core/platform/android_platform_backend.py
+++ b/tools/telemetry/telemetry/core/platform/android_platform_backend.py
@@ -3,7 +3,10 @@
# found in the LICENSE file.
import logging
+import subprocess
+import tempfile
+from telemetry.core import bitmap
from telemetry.core import exceptions
from telemetry.core import platform
from telemetry.core import util
@@ -11,6 +14,7 @@ from telemetry.core.platform import proc_supporting_platform_backend
# Get build/android scripts into our path.
util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android')
+from pylib import screenshot # pylint: disable=F0401
from pylib.perf import cache_control # pylint: disable=F0401
from pylib.perf import perf_control # pylint: disable=F0401
from pylib.perf import thermal_throttle # pylint: disable=F0401
@@ -22,6 +26,7 @@ except Exception:
_HOST_APPLICATIONS = [
+ 'avconv',
'ipfw',
]
@@ -39,6 +44,8 @@ class AndroidPlatformBackend(
self._host_platform_backend = platform.CreatePlatformBackendForCurrentOS()
self._can_access_protected_file_contents = \
self._adb.CanAccessProtectedFileContents()
+ self._video_recorder = None
+ self._video_output = None
if self._no_performance_mode:
logging.warning('CPU governor will not be set!')
@@ -135,6 +142,9 @@ class AndroidPlatformBackend(
def GetOSName(self):
return 'android'
+ def GetOSVersionName(self):
+ return self._adb.GetBuildId()[0]
+
def CanFlushIndividualFilesFromSystemCache(self):
return False
@@ -170,11 +180,85 @@ class AndroidPlatformBackend(
raise NotImplementedError(
'Please teach Telemetry how to install ' + application)
+ def CanCaptureVideo(self):
+ return self.GetOSVersionName() >= 'K'
+
+ def StartVideoCapture(self, min_bitrate_mbps):
+ assert not self._video_recorder, 'Already started video capture'
+ 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)
+ self._video_output = tempfile.mkstemp()[1]
+ self._video_recorder = screenshot.VideoRecorder(
+ self._adb, self._video_output, megabits_per_second=min_bitrate_mbps)
+ self._video_recorder.Start()
+ util.WaitFor(self._video_recorder.IsStarted, 5)
+
+ def StopVideoCapture(self):
+ assert self._video_recorder, 'Must start video capture first'
+ self._video_recorder.Stop()
+ self._video_output = self._video_recorder.Pull()
+ self._video_recorder = None
+ for frame in self._FramesFromMp4(self._video_output):
+ yield frame
+
+ def _FramesFromMp4(self, mp4_file):
+ if not self.CanLaunchApplication('avconv'):
+ self.InstallApplication('avconv')
+
+ def GetDimensions(video):
+ proc = subprocess.Popen(['avconv', '-i', video], stderr=subprocess.PIPE)
+ for line in proc.stderr.readlines():
+ if 'Video:' in line:
+ dimensions = line.split(',')[2]
+ dimensions = map(int, dimensions.split()[0].split('x'))
+ break
+ proc.wait()
+ assert dimensions, 'Failed to determine video dimensions'
+ 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),
+ bitmap.Bitmap(3, dimensions[0], dimensions[1], frame_data))
+
def _GetFileContents(self, fname):
if not self._can_access_protected_file_contents:
logging.warning('%s cannot be retrieved on non-rooted device.' % fname)
return ''
- return ''.join(self._adb.GetProtectedFileContents(fname, log_result=False))
+ return '\n'.join(
+ self._adb.GetProtectedFileContents(fname, log_result=False))
def _GetPsOutput(self, columns, pid=None):
assert columns == ['pid', 'name'] or columns == ['pid'], \
diff --git a/tools/telemetry/telemetry/core/platform/android_platform_backend_unittest.py b/tools/telemetry/telemetry/core/platform/android_platform_backend_unittest.py
index 8bb827383e..285b97edd9 100644
--- a/tools/telemetry/telemetry/core/platform/android_platform_backend_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/android_platform_backend_unittest.py
@@ -1,8 +1,13 @@
# 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 bitmap
+from telemetry.core import util
from telemetry.core.platform import android_platform_backend
from telemetry.unittest import system_stub
@@ -47,3 +52,34 @@ class AndroidPlatformBackendTest(unittest.TestCase):
adb_empty_proc_stat, False)
cpu_stats = backend.GetCpuStats('7702')
self.assertEquals(cpu_stats, {})
+
+ def testFramesFromMp4(self):
+ mock_adb = MockAdbCommands([])
+ backend = android_platform_backend.AndroidPlatformBackend(mock_adb, False)
+
+ try:
+ backend.InstallApplication('avconv')
+ finally:
+ if not backend.CanLaunchApplication('avconv'):
+ logging.warning('Test not supported on this platform')
+ return # pylint: disable=W0150
+
+ vid = os.path.join(util.GetUnittestDataDir(), 'vid.mp4')
+ expected_timestamps = [
+ 0,
+ 763,
+ 783,
+ 940,
+ 1715,
+ 1732,
+ 1842,
+ 1926,
+ ]
+
+ # pylint: disable=W0212
+ for i, timestamp_bitmap in enumerate(backend._FramesFromMp4(vid)):
+ timestamp, bmp = timestamp_bitmap
+ self.assertEquals(timestamp, expected_timestamps[i])
+ expected_bitmap = bitmap.Bitmap.FromPngFile(os.path.join(
+ util.GetUnittestDataDir(), 'frame%d.png' % i))
+ self.assertTrue(expected_bitmap.IsEqual(bmp))
diff --git a/tools/telemetry/telemetry/core/platform/cros_platform_backend.py b/tools/telemetry/telemetry/core/platform/cros_platform_backend.py
index 37056d671d..f35816df46 100644
--- a/tools/telemetry/telemetry/core/platform/cros_platform_backend.py
+++ b/tools/telemetry/telemetry/core/platform/cros_platform_backend.py
@@ -52,6 +52,10 @@ class CrosPlatformBackend(
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
diff --git a/tools/telemetry/telemetry/core/platform/linux_platform_backend.py b/tools/telemetry/telemetry/core/platform/linux_platform_backend.py
index d2b467130d..f13f76e03c 100644
--- a/tools/telemetry/telemetry/core/platform/linux_platform_backend.py
+++ b/tools/telemetry/telemetry/core/platform/linux_platform_backend.py
@@ -43,16 +43,19 @@ class LinuxPlatformBackend(
p.wait()
assert p.returncode == 0, 'Failed to flush system cache'
- def CanRunApplication(self, application):
+ def CanLaunchApplication(self, application):
if application == 'ipfw' and not self._IsIpfwKernelModuleInstalled():
return False
- return super(LinuxPlatformBackend, self).CanRunApplication(application)
+ return super(LinuxPlatformBackend, self).CanLaunchApplication(application)
def InstallApplication(self, application):
- if application != 'ipfw':
+ if application == 'ipfw':
+ self._InstallIpfw()
+ elif application == 'avconv':
+ self._InstallAvconv()
+ else:
raise NotImplementedError(
'Please teach Telemetry how to install ' + application)
- self._InstallIpfw()
def _IsIpfwKernelModuleInstalled(self):
return 'ipfw_mod' in subprocess.Popen(
@@ -73,10 +76,25 @@ class LinuxPlatformBackend(
'http://info.iet.unipi.it/~luigi/dummynet/')
sys.exit(1)
- if changed or not self.CanRunApplication('ipfw'):
+ if changed or not self.CanLaunchApplication('ipfw'):
if not self._IsIpfwKernelModuleInstalled():
subprocess.check_call(['sudo', 'insmod', ipfw_mod])
os.chmod(ipfw_bin, 0755)
subprocess.check_call(['sudo', 'cp', ipfw_bin, '/usr/local/sbin'])
- assert self.CanRunApplication('ipfw'), 'Failed to install ipfw'
+ assert self.CanLaunchApplication('ipfw'), 'Failed to install ipfw'
+
+ def _InstallAvconv(self):
+ telemetry_bin_dir = os.path.join(util.GetTelemetryDir(), 'bin')
+ avconv_bin = os.path.join(telemetry_bin_dir, 'avconv')
+ os.environ['PATH'] += os.pathsep + telemetry_bin_dir
+
+ try:
+ cloud_storage.GetIfChanged(cloud_storage.INTERNAL_BUCKET, avconv_bin)
+ except cloud_storage.CloudStorageError, e:
+ logging.error(e)
+ logging.error('You may proceed by manually installing avconv via:\n'
+ 'sudo apt-get install libav-tools')
+ sys.exit(1)
+
+ assert self.CanLaunchApplication('avconv'), 'Failed to install avconv'
diff --git a/tools/telemetry/telemetry/core/platform/platform_backend.py b/tools/telemetry/telemetry/core/platform/platform_backend.py
index 7f74deabf1..cc86b857e9 100644
--- a/tools/telemetry/telemetry/core/platform/platform_backend.py
+++ b/tools/telemetry/telemetry/core/platform/platform_backend.py
@@ -75,3 +75,12 @@ class PlatformBackend(object):
def InstallApplication(self, application):
raise NotImplementedError()
+
+ def CanCaptureVideo(self):
+ return False
+
+ def StartVideoCapture(self, min_bitrate_mbps):
+ raise NotImplementedError()
+
+ def StopVideoCapture(self):
+ raise NotImplementedError()
diff --git a/tools/telemetry/telemetry/core/platform/posix_platform_backend.py b/tools/telemetry/telemetry/core/platform/posix_platform_backend.py
index 349796e072..631be6dba5 100644
--- a/tools/telemetry/telemetry/core/platform/posix_platform_backend.py
+++ b/tools/telemetry/telemetry/core/platform/posix_platform_backend.py
@@ -56,5 +56,5 @@ class PosixPlatformBackend(desktop_platform_backend.DesktopPlatformBackend):
def GetFlushUtilityName(self):
return 'clear_system_cache'
- def CanRunApplication(self, application):
+ def CanLaunchApplication(self, application):
return bool(distutils.spawn.find_executable(application))
diff --git a/tools/telemetry/telemetry/core/platform/posix_platform_backend_unittest.py b/tools/telemetry/telemetry/core/platform/posix_platform_backend_unittest.py
index 71b6e435c0..4a58e60731 100644
--- a/tools/telemetry/telemetry/core/platform/posix_platform_backend_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/posix_platform_backend_unittest.py
@@ -6,34 +6,43 @@ import unittest
from telemetry.core.platform import posix_platform_backend
-class PosixPlatformBackendTest(unittest.TestCase):
+class TestBackend(posix_platform_backend.PosixPlatformBackend):
+
+ # pylint: disable=W0223
- def _GetChildPids(self, mock_ps_output, pid):
- class TestBackend(posix_platform_backend.PosixPlatformBackend):
+ def __init__(self):
+ super(TestBackend, self).__init__()
+ self._mock_ps_output = None
- # pylint: disable=W0223
+ def SetMockPsOutput(self, output):
+ self._mock_ps_output = output
- def _GetPsOutput(self, columns, pid=None):
- return mock_ps_output
+ def _GetPsOutput(self, columns, pid=None):
+ return self._mock_ps_output
- return TestBackend().GetChildPids(pid)
+
+class PosixPlatformBackendTest(unittest.TestCase):
def testGetChildPidsWithGrandChildren(self):
- lines = ['1 0 S', '2 1 R', '3 2 S', '4 1 R', '5 4 R']
- result = self._GetChildPids(lines, 1)
+ 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):
- lines = ['1 0 S', '2 1 R', '3 2 S', '4 1 R', '5 4 R']
- result = self._GetChildPids(lines, 6)
+ 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):
- lines = ['1 0 S', '2 1 R', '3 2 S', '4 1 R', '5 4 Z']
- result = self._GetChildPids(lines, 1)
+ 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):
- lines = [' 1 0 S ', ' 2 1', '3 2 ']
- result = self._GetChildPids(lines, 1)
+ backend = TestBackend()
+ backend.SetMockPsOutput([' 1 0 S ', ' 2 1', '3 2 '])
+ result = backend.GetChildPids(1)
self.assertEquals(set(result), set([2, 3]))
diff --git a/tools/telemetry/telemetry/core/platform/proc_supporting_platform_backend.py b/tools/telemetry/telemetry/core/platform/proc_supporting_platform_backend.py
index 9841f41d73..57d3a25287 100644
--- a/tools/telemetry/telemetry/core/platform/proc_supporting_platform_backend.py
+++ b/tools/telemetry/telemetry/core/platform/proc_supporting_platform_backend.py
@@ -46,10 +46,16 @@ class ProcSupportingPlatformBackend(platform_backend.PlatformBackend):
status = self._GetProcFileDict(status_contents)
if not status or not stats or 'Z' in status['State']:
return {}
- return {'VM': int(stats[22]),
- 'VMPeak': self._ConvertKbToByte(status['VmPeak']),
- 'WorkingSetSize': int(stats[23]) * resource.getpagesize(),
- 'WorkingSetSizePeak': self._ConvertKbToByte(status['VmHWM'])}
+ vm = int(stats[22])
+ vm_peak = (self._ConvertKbToByte(status['VmPeak'])
+ if 'VmPeak' in status else vm)
+ wss = int(stats[23]) * resource.getpagesize()
+ wss_peak = (self._ConvertKbToByte(status['VmHWM'])
+ if 'VmHWM' in status else wss)
+ return {'VM': vm,
+ 'VMPeak': vm_peak,
+ 'WorkingSetSize': wss,
+ 'WorkingSetSizePeak': wss_peak}
def GetIOStats(self, pid):
io_contents = self._GetProcFileForPid(pid, 'io')
diff --git a/tools/telemetry/telemetry/core/platform/proc_supporting_platform_backend_unittest.py b/tools/telemetry/telemetry/core/platform/proc_supporting_platform_backend_unittest.py
new file mode 100644
index 0000000000..bd3ab230ae
--- /dev/null
+++ b/tools/telemetry/telemetry/core/platform/proc_supporting_platform_backend_unittest.py
@@ -0,0 +1,64 @@
+# 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 logging
+import unittest
+
+from telemetry.core import util
+from telemetry.core.platform import proc_supporting_platform_backend
+
+
+class TestBackend(
+ proc_supporting_platform_backend.ProcSupportingPlatformBackend):
+
+ # pylint: disable=W0223
+
+ def __init__(self):
+ super(TestBackend, self).__init__()
+ self._mock_files = {}
+
+ def SetMockFile(self, filename, output):
+ self._mock_files[filename] = output
+
+ def _GetFileContents(self, filename):
+ return self._mock_files[filename]
+
+
+class ProcSupportingPlatformBackendTest(unittest.TestCase):
+
+ def testGetMemoryStatsBasic(self):
+ if not proc_supporting_platform_backend.resource:
+ logging.warning('Test not supported')
+ return
+
+ backend = TestBackend()
+ backend.SetMockFile(
+ '/proc/1/stat',
+ open(os.path.join(util.GetUnittestDataDir(), 'stat')).read())
+ backend.SetMockFile(
+ '/proc/1/status',
+ open(os.path.join(util.GetUnittestDataDir(), 'status')).read())
+ result = backend.GetMemoryStats(1)
+ self.assertEquals(result, {'VM': 1025978368,
+ 'VMPeak': 1050099712,
+ 'WorkingSetSize': 84000768,
+ 'WorkingSetSizePeak': 144547840})
+
+ def testGetMemoryStatsNoHWM(self):
+ if not proc_supporting_platform_backend.resource:
+ logging.warning('Test not supported')
+ return
+
+ backend = TestBackend()
+ backend.SetMockFile(
+ '/proc/1/stat',
+ open(os.path.join(util.GetUnittestDataDir(), 'stat')).read())
+ backend.SetMockFile(
+ '/proc/1/status',
+ open(os.path.join(util.GetUnittestDataDir(), 'status_nohwm')).read())
+ result = backend.GetMemoryStats(1)
+ self.assertEquals(result, {'VM': 1025978368,
+ 'VMPeak': 1025978368,
+ 'WorkingSetSize': 84000768,
+ 'WorkingSetSizePeak': 84000768})
diff --git a/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py
index 4634c028cb..1680094eb8 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py
@@ -6,7 +6,6 @@ import logging
import os
import re
import signal
-import stat
import subprocess
import sys
import tempfile
@@ -76,7 +75,7 @@ Try rerunning this script under sudo or setting
self._GetStdOut()))
finally:
self._tmp_output_file.close()
- cmd = 'perf report'
+ cmd = 'perf report -n -i %s' % self._output_file
if self._is_android:
self._browser_backend.adb.Adb().Adb().Pull(
self._device_output_file,
@@ -99,12 +98,36 @@ Try rerunning this script under sudo or setting
os.environ.get('CHROMIUM_OUT_DIR', 'out'),
'Release', 'lib')),
os.path.join(host_symfs, device_dir[0]))
+
+ # Also pull copies of common system libraries from the device so perf can
+ # resolve their symbols. Only copy a subset of libraries to make this
+ # faster.
+ host_system_symfs = os.path.join(os.path.dirname(self._output_file),
+ 'system', 'lib')
+ if not os.path.exists(host_system_symfs):
+ os.makedirs(host_system_symfs)
+ common_system_libs = [
+ 'libandroid*.so',
+ 'libart.so',
+ 'libc.so',
+ 'libdvm.so',
+ 'libEGL*.so',
+ 'libGL*.so',
+ 'libm.so',
+ 'libRS.so',
+ 'libskia.so',
+ 'libstdc++.so',
+ 'libstlport.so',
+ 'libz.so',
+ ]
+ for lib in common_system_libs:
+ self._browser_backend.adb.Adb().Adb().Pull('/system/lib/%s' % lib,
+ host_system_symfs)
print 'On Android, assuming $CHROMIUM_OUT_DIR/Release/lib has a fresh'
print 'symbolized library matching the one on device.'
- cmd = (android_prebuilt_profiler_helper.GetHostPath('perfhost') +
- ' report --symfs %s' % os.path.dirname(self._output_file))
+ cmd += ' --symfs %s' % os.path.dirname(self._output_file)
print 'To view the profile, run:'
- print ' %s -n -i %s' % (cmd, self._output_file)
+ print ' ', cmd
return self._output_file
def _GetStdOut(self):
@@ -124,9 +147,6 @@ class PerfProfiler(profiler.Profiler):
process_output_file_map = self._GetProcessOutputFileMap()
self._process_profilers = []
if platform_backend.GetOSName() == 'android':
- android_prebuilt_profiler_helper.GetIfChanged('perfhost')
- os.chmod(android_prebuilt_profiler_helper.GetHostPath('perfhost'),
- stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
android_prebuilt_profiler_helper.InstallOnDevice(
browser_backend.adb, 'perf')
for pid, output_file in process_output_file_map.iteritems():
@@ -171,14 +191,12 @@ class PerfProfiler(profiler.Profiler):
return output_files
@classmethod
- def GetTopSamples(cls, os_name, file_name, number):
+ 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)
cmd = 'perf'
- if os_name == 'android':
- cmd = 'perfhost'
report = subprocess.Popen(
[cmd, 'report', '--show-total-period', '-U', '-t', '^', '-i',
file_name],
diff --git a/tools/telemetry/telemetry/core/platform/profiler/perf_profiler_unittest.py b/tools/telemetry/telemetry/core/platform/profiler/perf_profiler_unittest.py
index b9370d8bf0..3f01825501 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/perf_profiler_unittest.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/perf_profiler_unittest.py
@@ -33,7 +33,7 @@ class TestPerfProfiler(unittest.TestCase):
perf_profiler.subprocess = mock_subprocess
try:
self.assertEqual(
- perf_profiler.PerfProfiler.GetTopSamples('linux', profile_file, 10),
+ perf_profiler.PerfProfiler.GetTopSamples(profile_file, 10),
{ 'v8::internal::StaticMarkingVisitor::MarkMapContents': 63615201,
'v8::internal::RelocIterator::next': 38271931,
'v8::internal::LAllocator::MeetConstraintsBetween': 42913933,
diff --git a/tools/telemetry/telemetry/core/platform/profiler/tcmalloc_heap_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/tcmalloc_heap_profiler.py
index e3231dad1a..fd6ab5ebde 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/tcmalloc_heap_profiler.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/tcmalloc_heap_profiler.py
@@ -36,13 +36,9 @@ class _TCMallocHeapProfilerAndroid(object):
# This profiler requires adb root to set properties.
self._browser_backend.adb.Adb().EnableAdbRoot()
for values in properties.itervalues():
- device_property = self._browser_backend.adb.RunShellCommand(
- 'getprop ' + values[0])
- if (not device_property or len(device_property) != 1 or
- not device_property[0].strip()):
- print 'Setting device property ', values[0], values[1]
- self._browser_backend.adb.RunShellCommand(
- 'setprop ' + values[0] + ' ' + str(values[1]))
+ device_property = self._browser_backend.adb.system_properties[values[0]]
+ if not device_property or not device_property.strip():
+ self._browser_backend.adb.system_properties[values[0]] = values[1]
device_configured = True
if not self._browser_backend.adb.Adb().FileExistsOnDevice(
self._DEFAULT_DEVICE_DIR):
diff --git a/tools/telemetry/telemetry/core/tab.py b/tools/telemetry/telemetry/core/tab.py
index b129a8371d..eb7c1083da 100644
--- a/tools/telemetry/telemetry/core/tab.py
+++ b/tools/telemetry/telemetry/core/tab.py
@@ -64,13 +64,50 @@ class Tab(web_contents.WebContents):
@property
def screenshot_supported(self):
- """True if the browser instance is capable of capturing screenshots"""
+ """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 window for rendering validation"""
+ """Capture a screenshot of the tab's contents.
+
+ Returns:
+ A telemetry.core.Bitmap.
+ """
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 StartVideoCapture(self, min_bitrate_mbps):
+ """Starts capturing video of the tab's contents.
+
+ 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.
+ """
+ self.browser.platform.StartVideoCapture(min_bitrate_mbps)
+
+ def StopVideoCapture(self):
+ """Stops recording video of the tab's contents.
+
+ Yields:
+ (time_ms, bitmap) tuples representing each video keyframe. Only the first
+ frame in a run of sequential duplicate bitmaps is included.
+ time_ms is milliseconds since navigationStart.
+ bitmap is a telemetry.core.Bitmap.
+ """
+ # TODO(tonyg/szym): platform's video capture may include offscreen content
+ # from a webcam, OS framing and browser framing. However, this API must
+ # return only the tab contents. So we need to display a color flash in the
+ # tab and then crop out all the other pixels. We also need to translate that
+ # initial flash time into navigationStart timespace.
+
+ for t in self.browser.platform.StopVideoCapture():
+ return t
+
def PerformActionAndWaitForNavigate(
self, action_function, timeout=DEFAULT_TAB_TIMEOUT):
"""Executes action_function, and waits for the navigation to complete.
diff --git a/tools/telemetry/telemetry/core/timeline/counter.py b/tools/telemetry/telemetry/core/timeline/counter.py
index f21f5162fe..a35bf2ba7a 100644
--- a/tools/telemetry/telemetry/core/timeline/counter.py
+++ b/tools/telemetry/telemetry/core/timeline/counter.py
@@ -32,6 +32,18 @@ class CounterSample(object):
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.
diff --git a/tools/telemetry/telemetry/core/timeline/event.py b/tools/telemetry/telemetry/core/timeline/event.py
index 8b9db4cef7..916d540eab 100644
--- a/tools/telemetry/telemetry/core/timeline/event.py
+++ b/tools/telemetry/telemetry/core/timeline/event.py
@@ -3,26 +3,50 @@
# found in the LICENSE file.
class TimelineEvent(object):
- """Represents a timeline event."""
- def __init__(self, category, name, start, duration, args=None):
+ """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 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%s)" % (
- self.name,
- self.start,
- self.duration,
- 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/tools/telemetry/telemetry/core/timeline/inspector_importer.py b/tools/telemetry/telemetry/core/timeline/inspector_importer.py
index fa468cbc64..39d2036192 100644
--- a/tools/telemetry/telemetry/core/timeline/inspector_importer.py
+++ b/tools/telemetry/telemetry/core/timeline/inspector_importer.py
@@ -18,15 +18,14 @@ class InspectorTimelineImporter(importer.TimelineImporter):
'''
if isinstance(event_data, list) and len(event_data):
event_datum = event_data[0]
- return 'startTime' in event_datum and 'endTime' in event_datum
+ return 'startTime' in event_datum and 'type' in event_datum
return False
def ImportEvents(self):
render_process = self._model.GetOrCreateProcess(0)
- render_thread = render_process.GetOrCreateThread(0)
for raw_event in self._event_data:
- InspectorTimelineImporter.AddRawEventToThreadRecursive(
- render_thread, raw_event)
+ thread = render_process.GetOrCreateThread(raw_event.get('thread', 0))
+ InspectorTimelineImporter.AddRawEventToThreadRecursive(thread, raw_event)
def FinalizeImport(self):
pass
@@ -35,7 +34,7 @@ class InspectorTimelineImporter(importer.TimelineImporter):
def AddRawEventToThreadRecursive(thread, raw_inspector_event):
did_begin_slice = False
if ('startTime' in raw_inspector_event and
- 'endTime' in raw_inspector_event):
+ 'type' in raw_inspector_event):
args = {}
for x in raw_inspector_event:
if x in ('startTime', 'endTime', 'children'):
@@ -43,10 +42,12 @@ class InspectorTimelineImporter(importer.TimelineImporter):
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)
thread.BeginSlice('inspector',
raw_inspector_event['type'],
- raw_inspector_event['startTime'],
- args)
+ start_time,
+ args=args)
did_begin_slice = True
for child in raw_inspector_event.get('children', []):
@@ -54,7 +55,7 @@ class InspectorTimelineImporter(importer.TimelineImporter):
thread, child)
if did_begin_slice:
- thread.EndSlice(raw_inspector_event['endTime'])
+ thread.EndSlice(end_time)
@staticmethod
def RawEventToTimelineEvent(raw_inspector_event):
diff --git a/tools/telemetry/telemetry/core/timeline/inspector_importer_unittest.py b/tools/telemetry/telemetry/core/timeline/inspector_importer_unittest.py
index afb99e6344..94aedecce7 100644
--- a/tools/telemetry/telemetry/core/timeline/inspector_importer_unittest.py
+++ b/tools/telemetry/telemetry/core/timeline/inspector_importer_unittest.py
@@ -6,6 +6,12 @@ import unittest
from telemetry.core.timeline import inspector_importer
from telemetry.core.timeline import model
+_BACKGROUND_MESSAGE = {
+ 'data': {},
+ 'type': 'BeginFrame',
+ 'thread': '2',
+ 'startTime': 1352783525921.824}
+
_SAMPLE_MESSAGE = {
'children': [
{'data': {},
@@ -93,20 +99,28 @@ class InspectorEventParsingTest(unittest.TestCase):
RawEventToTimelineEvent(raw_event))
self.assertEquals(None, event)
- def testEventsWithNoEndTimeAreDropped(self):
+ def testEventsWithNoEndTimeAreOk(self):
raw_event = {'type': 'Foo',
- 'endTime': 1,
+ 'startTime': 1,
'children': []}
event = (inspector_importer.InspectorTimelineImporter.
RawEventToTimelineEvent(raw_event))
- self.assertEquals(None, event)
+ self.assertEquals(1, event.start)
+ self.assertEquals(1, event.end)
class InspectorImporterTest(unittest.TestCase):
def testImport(self):
- m = model.TimelineModel([_SAMPLE_MESSAGE], shift_world_to_zero=False)
+ messages = [_BACKGROUND_MESSAGE, _SAMPLE_MESSAGE]
+ m = model.TimelineModel(messages, shift_world_to_zero=False)
self.assertEquals(1, len(m.processes))
- self.assertEquals(1, len(m.processes.values()[0].threads))
- renderer_thread = m.GetAllThreads()[0]
+ 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/tools/telemetry/telemetry/core/timeline/model.py b/tools/telemetry/telemetry/core/timeline/model.py
index 762ddb15f1..7514c342eb 100644
--- a/tools/telemetry/telemetry/core/timeline/model.py
+++ b/tools/telemetry/telemetry/core/timeline/model.py
@@ -40,6 +40,7 @@ class MarkerOverlapError(Exception):
class TimelineModel(object):
def __init__(self, event_data=None, shift_world_to_zero=True):
self._bounds = bounds.Bounds()
+ self._thread_time_bounds = bounds.Bounds()
self._processes = {}
self._frozen = False
self.import_errors = []
@@ -57,6 +58,10 @@ class TimelineModel(object):
return self._bounds
@property
+ def thread_time_bounds(self):
+ return self._thread_time_bounds
+
+ @property
def processes(self):
return self._processes
@@ -77,7 +82,8 @@ class TimelineModel(object):
self.UpdateBounds()
if not self.bounds.is_empty:
for process in self._processes.itervalues():
- process.AutoCloseOpenSlices(self.bounds.max)
+ process.AutoCloseOpenSlices(self.bounds.max,
+ self.thread_time_bounds.max)
for importer in importers:
importer.FinalizeImport()
@@ -97,15 +103,23 @@ class TimelineModel(object):
self.UpdateBounds()
if self._bounds.is_empty:
return
- shift_amount = -self._bounds.min
+ shift_amount = self._bounds.min
+ thread_shift_amount = self._thread_time_bounds.min
for event in self.IterAllEvents():
- event.start += shift_amount
+ event.start -= shift_amount
+ if event.thread_start != None:
+ event.thread_start -= thread_shift_amount
def UpdateBounds(self):
self._bounds.Reset()
+ self._thread_time_bounds.Reset()
for event in self.IterAllEvents():
self._bounds.AddValue(event.start)
self._bounds.AddValue(event.end)
+ if event.thread_start != None:
+ self._thread_time_bounds.AddValue(event.thread_start)
+ if event.thread_end != None:
+ self._thread_time_bounds.AddValue(event.thread_end)
def GetAllContainers(self):
containers = []
diff --git a/tools/telemetry/telemetry/core/timeline/process.py b/tools/telemetry/telemetry/core/timeline/process.py
index 1b65d66f41..d249fff7f7 100644
--- a/tools/telemetry/telemetry/core/timeline/process.py
+++ b/tools/telemetry/telemetry/core/timeline/process.py
@@ -61,9 +61,9 @@ class Process(event_container.TimelineEventContainer):
self._counters[ctr.full_name] = ctr
return ctr
- def AutoCloseOpenSlices(self, max_timestamp):
+ def AutoCloseOpenSlices(self, max_timestamp, max_thread_timestamp):
for thread in self._threads.itervalues():
- thread.AutoCloseOpenSlices(max_timestamp)
+ thread.AutoCloseOpenSlices(max_timestamp, max_thread_timestamp)
def FinalizeImport(self):
for thread in self._threads.itervalues():
diff --git a/tools/telemetry/telemetry/core/timeline/slice.py b/tools/telemetry/telemetry/core/timeline/slice.py
index 5ce2b28a84..26c4dc240d 100644
--- a/tools/telemetry/telemetry/core/timeline/slice.py
+++ b/tools/telemetry/telemetry/core/timeline/slice.py
@@ -13,10 +13,11 @@ class Slice(timeline_event.TimelineEvent):
All time units are stored in milliseconds.
"""
- def __init__(self, parent_thread, category, name, timestamp,
- args=None, duration=0):
+ 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, args=args)
+ category, name, timestamp, duration, thread_timestamp, thread_duration,
+ args)
self.parent_thread = parent_thread
self.parent_slice = None
self.sub_slices = []
@@ -39,6 +40,23 @@ class Slice(timeline_event.TimelineEvent):
[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():
diff --git a/tools/telemetry/telemetry/core/timeline/slice_unittest.py b/tools/telemetry/telemetry/core/timeline/slice_unittest.py
index e2d6b82270..8a75f0b46a 100644
--- a/tools/telemetry/telemetry/core/timeline/slice_unittest.py
+++ b/tools/telemetry/telemetry/core/timeline/slice_unittest.py
@@ -11,10 +11,14 @@ class SliceTest(unittest.TestCase):
# [ top ]
# [ a ] [ b ]
# [x]
- top = Slice(None, 'cat', 'top', 0, duration=10)
- a = Slice(None, 'cat', 'a', 1, duration=2)
- x = Slice(None, 'cat', 'x', 1.5, duration=0.25)
- b = Slice(None, 'cat', 'b', 5, duration=2)
+ 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)
@@ -23,4 +27,8 @@ class SliceTest(unittest.TestCase):
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(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/tools/telemetry/telemetry/core/timeline/thread.py b/tools/telemetry/telemetry/core/timeline/thread.py
index bd5f43907c..31418c1ea1 100644
--- a/tools/telemetry/telemetry/core/timeline/thread.py
+++ b/tools/telemetry/telemetry/core/timeline/thread.py
@@ -83,7 +83,8 @@ class Thread(event_container.TimelineEventContainer):
def AddAsyncSlice(self, async_slice):
self._async_slices.append(async_slice)
- def BeginSlice(self, category, name, timestamp, args=None):
+ 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.
@@ -91,6 +92,8 @@ class Thread(event_container.TimelineEventContainer):
* 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
@@ -98,17 +101,21 @@ class Thread(event_container.TimelineEventContainer):
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 = tracing_slice.Slice(self, category, name, timestamp, args=args)
+ new_slice = tracing_slice.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):
+ 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.
"""
@@ -120,15 +127,26 @@ class Thread(event_container.TimelineEventContainer):
raise ValueError(
'Slice %s end time is before its start.' % curr_slice.name)
curr_slice.duration = end_timestamp - curr_slice.start
+ if end_thread_timestamp != None:
+ if curr_slice.thread_start == None:
+ raise ValueError(
+ 'EndSlice with thread_timestamp called on open slice without ' +
+ 'thread_timestamp')
+ 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, args=None):
- new_slice = tracing_slice.Slice(self, category, name, timestamp, args=args)
+ def PushCompleteSlice(self, category, name, timestamp, duration,
+ thread_timestamp, thread_duration, args=None):
+ new_slice = tracing_slice.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
@@ -136,11 +154,14 @@ class Thread(event_container.TimelineEventContainer):
self._newly_added_slices.append(new_slice)
return new_slice
- def AutoCloseOpenSlices(self, max_timestamp):
+ 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):
diff --git a/tools/telemetry/telemetry/core/timeline/trace_event_importer.py b/tools/telemetry/telemetry/core/timeline/trace_event_importer.py
index 029c6c959a..120e695abd 100644
--- a/tools/telemetry/telemetry/core/timeline/trace_event_importer.py
+++ b/tools/telemetry/telemetry/core/timeline/trace_event_importer.py
@@ -148,6 +148,7 @@ class TraceEventTimelineImporter(importer.TimelineImporter):
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'])
@@ -161,7 +162,9 @@ class TraceEventTimelineImporter(importer.TimelineImporter):
'E phase event without a matching B phase event.')
return
- new_slice = thread.EndSlice(event['ts'] / 1000.0)
+ 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(
@@ -173,11 +176,14 @@ class TraceEventTimelineImporter(importer.TimelineImporter):
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['args'])
+ 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 _ProcessMetadataEvent(self, event):
if event['name'] == 'thread_name':
@@ -199,7 +205,7 @@ class TraceEventTimelineImporter(importer.TimelineImporter):
thread.BeginSlice(event['cat'],
event['name'],
event['ts'] / 1000.0,
- event.get('args'))
+ args=event.get('args'))
thread.EndSlice(event['ts'] / 1000.0)
def _ProcessSampleEvent(self, event):
@@ -208,7 +214,7 @@ class TraceEventTimelineImporter(importer.TimelineImporter):
thread.AddSample(event['cat'],
event['name'],
event['ts'] / 1000.0,
- args=event.get('args'))
+ event.get('args'))
def ImportEvents(self):
''' Walks through the events_ list and outputs the structures discovered to
diff --git a/tools/telemetry/telemetry/core/timeline/trace_event_importer_unittest.py b/tools/telemetry/telemetry/core/timeline/trace_event_importer_unittest.py
index 2467913b61..25ac993dcf 100644
--- a/tools/telemetry/telemetry/core/timeline/trace_event_importer_unittest.py
+++ b/tools/telemetry/telemetry/core/timeline/trace_event_importer_unittest.py
@@ -24,13 +24,17 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
def testBasicSingleThreadNonnestedParsing(self):
events = [
- {'name': 'a', 'args': {}, 'pid': 52, 'ts': 520, 'cat': 'foo',
+ {'name': 'a', 'args': {}, 'pid': 52, 'ts': 520, 'tts': 280, 'cat': 'foo',
'tid': 53, 'ph': 'B'},
- {'name': 'a', 'args': {}, 'pid': 52, 'ts': 560, 'cat': 'foo',
+ {'name': 'a', 'args': {}, 'pid': 52, 'ts': 560, 'tts': 310, 'cat': 'foo',
'tid': 53, 'ph': 'E'},
- {'name': 'b', 'args': {}, 'pid': 52, 'ts': 629, 'cat': 'bar',
+ {'name': 'b', 'args': {}, 'pid': 52, 'ts': 629, 'tts': 356, 'cat': 'bar',
'tid': 53, 'ph': 'B'},
- {'name': 'b', 'args': {}, 'pid': 52, 'ts': 631, 'cat': 'bar',
+ {'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'}
]
@@ -42,13 +46,17 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
self.assertEqual(1, len(p.threads))
t = p.threads[53]
- self.assertEqual(2, len(t.all_slices))
+ 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(0, slice_event.thread_start)
+ self.assertAlmostEqual((310 - 280) / 1000.0, slice_event.thread_duration)
+ self.assertAlmostEqual((310 - 280) / 1000.0, slice_event.thread_end)
self.assertEqual(0, len(slice_event.sub_slices))
slice_event = t.all_slices[1]
@@ -56,8 +64,23 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
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 - 280) / 1000.0, slice_event.thread_start)
+ self.assertAlmostEqual((357 - 356) / 1000.0, slice_event.thread_duration)
+ self.assertAlmostEqual((357 - 280) / 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',
@@ -98,13 +121,13 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
def testNestedParsing(self):
events = [
- {'name': 'a', 'args': {}, 'pid': 1, 'ts': 1, 'cat': 'foo',
+ {'name': 'a', 'args': {}, 'pid': 1, 'ts': 1, 'tts': 2, 'cat': 'foo',
'tid': 1, 'ph': 'B'},
- {'name': 'b', 'args': {}, 'pid': 1, 'ts': 2, 'cat': 'bar',
+ {'name': 'b', 'args': {}, 'pid': 1, 'ts': 3, 'tts': 3, 'cat': 'bar',
'tid': 1, 'ph': 'B'},
- {'name': 'b', 'args': {}, 'pid': 1, 'ts': 3, 'cat': 'bar',
+ {'name': 'b', 'args': {}, 'pid': 1, 'ts': 5, 'tts': 4, 'cat': 'bar',
'tid': 1, 'ph': 'E'},
- {'name': 'a', 'args': {}, 'pid': 1, 'ts': 4, 'cat': 'foo',
+ {'name': 'a', 'args': {}, 'pid': 1, 'ts': 7, 'tts': 5, 'cat': 'foo',
'tid': 1, 'ph': 'E'}
]
m = timeline_model.TimelineModel(event_data=events,
@@ -117,23 +140,27 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
self.assertEqual('a', slice_a.name)
self.assertEqual('foo', slice_a.category)
self.assertAlmostEqual(0.001, slice_a.start)
- self.assertAlmostEqual(0.003, slice_a.duration)
+ 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.002, slice_b.start)
- self.assertAlmostEqual(0.001, slice_b.duration)
+ 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 = [
# Slice that doesn't finish.
- {'name': 'a', 'args': {}, 'pid': 1, 'ts': 1, 'cat': 'foo',
+ {'name': 'a', 'args': {}, 'pid': 1, 'ts': 1, 'tts': 1, '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': 'bar',
+ {'name': 'b', 'args': {}, 'pid': 1, 'ts': 2, 'tts': 2, 'cat': 'bar',
'tid': 2, 'ph': 'B'},
- {'name': 'b', 'args': {}, 'pid': 1, 'ts': 2, 'cat': 'bar',
+ {'name': 'b', 'args': {}, 'pid': 1, 'ts': 4, 'tts': 3, 'cat': 'bar',
'tid': 2, 'ph': 'E'}
]
m = timeline_model.TimelineModel(event_data=events)
@@ -144,12 +171,14 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
self.assertEqual('foo', slice_event.category)
self.assertTrue(slice_event.did_not_finish)
self.assertAlmostEqual(0, slice_event.start)
- self.assertAlmostEqual((2 - 1) / 1000.0, slice_event.duration)
+ self.assertAlmostEqual((4 - 1) / 1000.0, slice_event.duration)
+ self.assertAlmostEqual(0, slice_event.thread_start)
+ self.assertAlmostEqual((3 - 1) / 1000.0, slice_event.thread_duration)
def testAutoclosingLoneBegin(self):
events = [
# Slice that doesn't finish.
- {'name': 'a', 'args': {}, 'pid': 1, 'ts': 1, 'cat': 'foo',
+ {'name': 'a', 'args': {}, 'pid': 1, 'ts': 1, 'tts': 1, 'cat': 'foo',
'tid': 1, 'ph': 'B'}
]
m = timeline_model.TimelineModel(event_data=events)
@@ -161,6 +190,8 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
self.assertTrue(slice_event.did_not_finish)
self.assertAlmostEqual(0, slice_event.start)
self.assertAlmostEqual(0, slice_event.duration)
+ self.assertAlmostEqual(0, slice_event.thread_start)
+ self.assertAlmostEqual(0, slice_event.thread_duration)
def testAutoclosingWithSubTasks(self):
events = [
@@ -188,15 +219,15 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
def testAutoclosingWithEventsOutsideBounds(self):
events = [
# Slice that begins before min and ends after max of the other threads.
- {'name': 'a', 'args': {}, 'pid': 1, 'ts': 0, 'cat': 'foo',
+ {'name': 'a', 'args': {}, 'pid': 1, 'ts': 0, 'tts': 0, 'cat': 'foo',
'tid': 1, 'ph': 'B'},
- {'name': 'b', 'args': {}, 'pid': 1, 'ts': 3, 'cat': 'foo',
+ {'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': 1, 'cat': 'bar',
+ {'name': 'c', 'args': {}, 'pid': 1, 'ts': 2, 'tts': 1, 'cat': 'bar',
'tid': 2, 'ph': 'B'},
- {'name': 'c', 'args': {}, 'pid': 1, 'ts': 2, 'cat': 'bar',
+ {'name': 'c', 'args': {}, 'pid': 1, 'ts': 4, 'tts': 2, 'cat': 'bar',
'tid': 2, 'ph': 'E'}
]
m = timeline_model.TimelineModel(event_data=events,
@@ -209,17 +240,24 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
self.assertEqual('a', slice_event.name)
self.assertEqual('foo', slice_event.category)
self.assertAlmostEqual(0, slice_event.start)
- self.assertAlmostEqual(0.003, slice_event.duration)
+ 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]
slice2 = FindEventNamed(t2.all_slices, 'c')
self.assertEqual('c', slice2.name)
self.assertEqual('bar', slice2.category)
- self.assertAlmostEqual(0.001, slice2.start)
- self.assertAlmostEqual(0.001, slice2.duration)
+ 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.003, m.bounds.max)
+ self.assertAlmostEqual(0.006, m.bounds.max)
+ self.assertAlmostEqual(0.000, m.thread_time_bounds.min)
+ self.assertAlmostEqual(0.003, m.thread_time_bounds.max)
+
def testNestedAutoclosing(self):
events = [
@@ -249,13 +287,13 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
def testMultipleThreadParsing(self):
events = [
- {'name': 'a', 'args': {}, 'pid': 1, 'ts': 1, 'cat': 'foo',
+ {'name': 'a', 'args': {}, 'pid': 1, 'ts': 2, 'tts': 1, 'cat': 'foo',
'tid': 1, 'ph': 'B'},
- {'name': 'a', 'args': {}, 'pid': 1, 'ts': 2, 'cat': 'foo',
+ {'name': 'a', 'args': {}, 'pid': 1, 'ts': 4, 'tts': 2, 'cat': 'foo',
'tid': 1, 'ph': 'E'},
- {'name': 'b', 'args': {}, 'pid': 1, 'ts': 3, 'cat': 'bar',
+ {'name': 'b', 'args': {}, 'pid': 1, 'ts': 6, 'tts': 3, 'cat': 'bar',
'tid': 2, 'ph': 'B'},
- {'name': 'b', 'args': {}, 'pid': 1, 'ts': 4, 'cat': 'bar',
+ {'name': 'b', 'args': {}, 'pid': 1, 'ts': 8, 'tts': 4, 'cat': 'bar',
'tid': 2, 'ph': 'E'}
]
m = timeline_model.TimelineModel(event_data=events)
@@ -274,7 +312,9 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
self.assertEqual('a', slice_event.name)
self.assertEqual('foo', slice_event.category)
self.assertAlmostEqual(0, slice_event.start)
- self.assertAlmostEqual((2 - 1) / 1000.0, slice_event.duration)
+ self.assertAlmostEqual((4 - 2) / 1000.0, slice_event.duration)
+ self.assertAlmostEqual(0, slice_event.thread_start)
+ self.assertAlmostEqual((2 - 1) / 1000.0, slice_event.thread_duration)
# Check thread 2.
t = p.threads[2]
@@ -284,18 +324,20 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
slice_event = t.all_slices[0]
self.assertEqual('b', slice_event.name)
self.assertEqual('bar', slice_event.category)
- self.assertAlmostEqual((3 - 1) / 1000.0, slice_event.start)
- self.assertAlmostEqual((4 - 3) / 1000.0, slice_event.duration)
+ self.assertAlmostEqual((6 - 2) / 1000.0, slice_event.start)
+ self.assertAlmostEqual((8 - 6) / 1000.0, slice_event.duration)
+ self.assertAlmostEqual((3 - 1) / 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': 1, 'cat': 'foo',
+ {'name': 'a', 'args': {}, 'pid': 1, 'ts': 2, 'tts': 1, 'cat': 'foo',
'tid': 1, 'ph': 'B'},
- {'name': 'a', 'args': {}, 'pid': 1, 'ts': 2, 'cat': 'foo',
+ {'name': 'a', 'args': {}, 'pid': 1, 'ts': 4, 'tts': 2, 'cat': 'foo',
'tid': 1, 'ph': 'E'},
- {'name': 'b', 'args': {}, 'pid': 2, 'ts': 3, 'cat': 'bar',
+ {'name': 'b', 'args': {}, 'pid': 2, 'ts': 6, 'tts': 3, 'cat': 'bar',
'tid': 2, 'ph': 'B'},
- {'name': 'b', 'args': {}, 'pid': 2, 'ts': 4, 'cat': 'bar',
+ {'name': 'b', 'args': {}, 'pid': 2, 'ts': 8, 'tts': 4, 'cat': 'bar',
'tid': 2, 'ph': 'E'}
]
@@ -316,7 +358,9 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
self.assertEqual('a', slice_event.name)
self.assertEqual('foo', slice_event.category)
self.assertAlmostEqual(0, slice_event.start)
- self.assertAlmostEqual((2 - 1) / 1000.0, slice_event.duration)
+ self.assertAlmostEqual((4 - 2) / 1000.0, slice_event.duration)
+ self.assertAlmostEqual(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?
@@ -330,8 +374,10 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
slice_event = t.all_slices[0]
self.assertEqual('b', slice_event.name)
self.assertEqual('bar', slice_event.category)
- self.assertAlmostEqual((3 - 1) / 1000.0, slice_event.start)
- self.assertAlmostEqual((4 - 3) / 1000.0, slice_event.duration)
+ self.assertAlmostEqual((6 - 2) / 1000.0, slice_event.start)
+ self.assertAlmostEqual((8 - 6) / 1000.0, slice_event.duration)
+ self.assertAlmostEqual((3 - 1) / 1000.0, slice_event.thread_start)
+ self.assertAlmostEqual((4 - 3) / 1000.0, slice_event.thread_duration)
# Check getAllThreads.
self.assertEqual([processes[0].threads[1],
@@ -360,11 +406,11 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
def testParsingWhenEndComesFirst(self):
events = [
- {'name': 'a', 'args': {}, 'pid': 1, 'ts': 1, 'cat': 'foo',
+ {'name': 'a', 'args': {}, 'pid': 1, 'ts': 1, 'tts': 1, 'cat': 'foo',
'tid': 1, 'ph': 'E'},
- {'name': 'a', 'args': {}, 'pid': 1, 'ts': 4, 'cat': 'foo',
+ {'name': 'a', 'args': {}, 'pid': 1, 'ts': 4, 'tts': 4, 'cat': 'foo',
'tid': 1, 'ph': 'B'},
- {'name': 'a', 'args': {}, 'pid': 1, 'ts': 5, 'cat': 'foo',
+ {'name': 'a', 'args': {}, 'pid': 1, 'ts': 5, 'tts': 5, 'cat': 'foo',
'tid': 1, 'ph': 'E'}
]
m = timeline_model.TimelineModel(event_data=events,
@@ -376,19 +422,21 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
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': 1, 'cat': 'foo',
+ {'name': 'a', 'args': {}, 'pid': 1, 'ts': 2, 'tts': 1, 'cat': 'foo',
'tid': 1, 'ph': 'B'},
- {'name': 'immediate', 'args': {}, 'pid': 1, 'ts': 2, 'cat': 'bar',
+ {'name': 'immediate', 'args': {}, 'pid': 1, 'ts': 4, 'cat': 'bar',
'tid': 1, 'ph': 'I'},
- {'name': 'slower', 'args': {}, 'pid': 1, 'ts': 4, 'cat': 'baz',
+ {'name': 'slower', 'args': {}, 'pid': 1, 'ts': 8, 'cat': 'baz',
'tid': 1, 'ph': 'i'},
- {'name': 'a', 'args': {}, 'pid': 1, 'ts': 4, 'cat': 'foo',
+ {'name': 'a', 'args': {}, 'pid': 1, 'ts': 8, 'tts': 4, 'cat': 'foo',
'tid': 1, 'ph': 'E'}
]
m = timeline_model.TimelineModel(event_data=events,
@@ -398,29 +446,25 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
self.assertEqual(3, len(t.all_slices))
i = m.GetAllEventsOfName('immediate')[0]
- self.assertAlmostEqual(0.002, i.start)
+ 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.assertAlmostEqual(0.004, slower.start)
+ 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.assertAlmostEqual(0.001, a.start)
- self.assertAlmostEqual(0.003, a.duration)
-
self.assertEqual('a', a.name)
self.assertEqual('foo', a.category)
- self.assertEqual(0.003, a.duration)
+ 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)
- self.assertEqual('immediate', i.name)
- self.assertEqual('bar', i.category)
- self.assertAlmostEqual(0.002, i.start)
- self.assertAlmostEqual(0, i.duration)
-
- self.assertEqual('slower', slower.name)
- self.assertEqual('baz', slower.category)
- self.assertAlmostEqual(0.004, slower.start)
- self.assertAlmostEqual(0, slower.duration)
def testSimpleCounter(self):
events = [
@@ -922,11 +966,11 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
def testImportCompleteEvent(self):
events = [
- {'name': 'a', 'args': {}, 'pid': 52, 'ts': 629, 'dur': 1, 'cat': 'baz',
- 'tid': 53, 'ph': 'X'},
- {'name': 'b', 'args': {}, 'pid': 52, 'ts': 730, 'dur': 20, 'cat': 'foo',
- 'tid': 53, 'ph': 'X'},
- {'name': 'c', 'args': {}, 'pid': 52, 'ts': 740, 'cat': 'baz',
+ {'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'},
]
m = timeline_model.TimelineModel(event_data=events)
@@ -938,6 +982,8 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
self.assertEqual('a', slice_event.name)
self.assertAlmostEqual(0.0, slice_event.start)
self.assertAlmostEqual(1 / 1000.0, slice_event.duration)
+ self.assertAlmostEqual(0.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))
@@ -945,6 +991,8 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
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 - 538) / 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])
@@ -953,5 +1001,7 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
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 - 538) / 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))
diff --git a/tools/telemetry/telemetry/core/util.py b/tools/telemetry/telemetry/core/util.py
index 24e3eb5b9f..be8212e659 100644
--- a/tools/telemetry/telemetry/core/util.py
+++ b/tools/telemetry/telemetry/core/util.py
@@ -2,6 +2,7 @@
# 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 socket
import sys
@@ -47,23 +48,36 @@ def WaitFor(condition, timeout):
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:
- elapsed_time = time.time() - start_time
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:
- if condition.__name__ == '<lambda>':
- try:
- condition_string = inspect.getsource(condition).strip()
- except IOError:
- condition_string = condition.__name__
- else:
- condition_string = condition.__name__
raise TimeoutException('Timed out while waiting %ds for %s.' %
- (timeout, condition_string))
- poll_interval = min(max(elapsed_time / 10., .1), 5)
+ (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/tools/telemetry/telemetry/exception_formatter.py b/tools/telemetry/telemetry/exception_formatter.py
index 95876c8630..8dafd44c2d 100644
--- a/tools/telemetry/telemetry/exception_formatter.py
+++ b/tools/telemetry/telemetry/exception_formatter.py
@@ -45,8 +45,12 @@ def PrintFormattedException(exception_class, exception, tb):
exception_class, exception, tb)
extracted_tb = traceback.extract_tb(tb)
traceback_header = formatted_exception[0].strip()
- exception = formatted_exception[-1].strip()
- local_variables = _GetFinalFrame(tb).tb_frame.f_locals
+ exception = ''.join([l[2:] if l[:2] == ' ' else l for l in
+ traceback.format_exception_only(exception_class,
+ exception)])
+ local_variables = [(variable, value) for variable, value in
+ _GetFinalFrame(tb).tb_frame.f_locals.iteritems()
+ if variable != 'self']
# Format the traceback.
print >> sys.stderr
@@ -62,12 +66,10 @@ def PrintFormattedException(exception_class, exception, tb):
if local_variables:
print >> sys.stderr
print >> sys.stderr, 'Locals:'
- longest_variable = max([len(v) for v in local_variables.keys()])
- for variable, value in sorted(local_variables.iteritems()):
- if variable == 'self':
- continue
+ longest_variable = max([len(v) for v, _ in local_variables])
+ for variable, value in sorted(local_variables):
value = repr(value)
- possibly_truncated_value = _AbbreviateMiddle(value, ' ... ', 128)
+ possibly_truncated_value = _AbbreviateMiddle(value, ' ... ', 1024)
truncation_indication = ''
if len(possibly_truncated_value) != len(value):
truncation_indication = ' (truncated)'
diff --git a/tools/telemetry/telemetry/page/actions/click_element.py b/tools/telemetry/telemetry/page/actions/click_element.py
index abfbd9370f..91339c76fd 100644
--- a/tools/telemetry/telemetry/page/actions/click_element.py
+++ b/tools/telemetry/telemetry/page/actions/click_element.py
@@ -8,6 +8,9 @@ from telemetry.core import util
from telemetry.core import exceptions
from telemetry.page.actions import page_action
+def _EscapeSelector(selector):
+ return selector.replace('\'', '\\\'')
+
class ClickElementAction(page_action.PageAction):
def __init__(self, attributes=None):
super(ClickElementAction, self).__init__(attributes)
@@ -15,7 +18,8 @@ class ClickElementAction(page_action.PageAction):
def RunAction(self, page, tab, previous_action):
def DoClick():
if hasattr(self, 'selector'):
- code = 'document.querySelector(\'' + self.selector + '\').click();'
+ code = ('document.querySelector(\'' + _EscapeSelector(self.selector) +
+ '\').click();')
try:
tab.ExecuteJavaScript(code)
except exceptions.EvaluateException:
diff --git a/tools/telemetry/telemetry/page/actions/click_element_unittest.py b/tools/telemetry/telemetry/page/actions/click_element_unittest.py
index e0eec82fbf..2032d4f16c 100644
--- a/tools/telemetry/telemetry/page/actions/click_element_unittest.py
+++ b/tools/telemetry/telemetry/page/actions/click_element_unittest.py
@@ -27,6 +27,25 @@ class ClickElementActionTest(tab_test_case.TabTestCase):
self._tab.EvaluateJavaScript('document.location.pathname;'),
'/blank.html')
+ def testClickWithSingleQuoteSelectorWaitForNavigation(self):
+ self._browser.SetHTTPServerDirectories(util.GetUnittestDataDir())
+ self._tab.Navigate(
+ self._browser.http_server.UrlOf('page_with_link.html'))
+ self._tab.WaitForDocumentReadyStateToBeComplete()
+ self.assertEquals(
+ self._tab.EvaluateJavaScript('document.location.pathname;'),
+ '/page_with_link.html')
+
+ data = {'selector': 'a[id=\'clickme\']'}
+ i = click_element.ClickElementAction(data)
+ data = {'condition': 'href_change'}
+ j = wait.WaitAction(data)
+ j.RunAction(None, self._tab, i)
+
+ self.assertEquals(
+ self._tab.EvaluateJavaScript('document.location.pathname;'),
+ '/blank.html')
+
def testClickWithTextWaitForRefChange(self):
self._browser.SetHTTPServerDirectories(util.GetUnittestDataDir())
self._tab.Navigate(
diff --git a/tools/telemetry/telemetry/page/actions/scroll.js b/tools/telemetry/telemetry/page/actions/scroll.js
index 218fae0516..79a09a2332 100644
--- a/tools/telemetry/telemetry/page/actions/scroll.js
+++ b/tools/telemetry/telemetry/page/actions/scroll.js
@@ -102,7 +102,7 @@
else
clientHeight = this.element_.clientHeight;
- return this.scrollHeight_ - this.element_.scrollTop - clientHeight;
+ return this.element_.scrollHeight - this.element_.scrollTop - clientHeight;
}
ScrollAction.prototype.start = function(opt_options) {
@@ -110,22 +110,17 @@
// 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_;
- // Some pages load more content when you scroll to the bottom. Record
- // the original element height here and only scroll to that point.
- // -1 to allow for rounding errors on scaled viewports (like mobile).
- this.scrollHeight_ = Math.min(MAX_SCROLL_LENGTH_PIXELS,
- this.element_.scrollHeight - 1);
requestAnimationFrame(this.startPass_.bind(this));
};
ScrollAction.prototype.startPass_ = function() {
- this.element_.scrollTop = 0;
-
this.beginMeasuringHook();
+ var distance = Math.min(MAX_SCROLL_LENGTH_PIXELS,
+ this.getRemainingScrollDistance_());
+
this.gesture_ = new SmoothScrollDownGesture(this.options_);
- this.gesture_.start(this.getRemainingScrollDistance_(),
- this.onGestureComplete_.bind(this));
+ this.gesture_.start(distance, this.onGestureComplete_.bind(this));
};
ScrollAction.prototype.onGestureComplete_ = function() {
diff --git a/tools/telemetry/telemetry/page/gtest_test_results.py b/tools/telemetry/telemetry/page/gtest_test_results.py
index 7dd25226c5..ff1b115968 100644
--- a/tools/telemetry/telemetry/page/gtest_test_results.py
+++ b/tools/telemetry/telemetry/page/gtest_test_results.py
@@ -2,6 +2,7 @@
# 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 time
import unittest
@@ -58,6 +59,14 @@ class GTestTestResults(page_test_results.PageTestResults):
'(%0.f ms)' % self._GetMs())
sys.stdout.flush()
+ def addSkip(self, test, reason):
+ super(GTestTestResults, self).addSkip(test, reason)
+ test_name = GTestTestResults._formatTestname(test)
+ logging.warning('===== SKIPPING TEST %s: %s =====', test_name, reason)
+ print >> self._output_stream, '[ OK ]', test_name, (
+ '(%0.f ms)' % self._GetMs())
+ sys.stdout.flush()
+
def PrintSummary(self):
unit = 'test' if len(self.successes) == 1 else 'tests'
print >> self._output_stream, '[ PASSED ]', (
diff --git a/tools/telemetry/telemetry/page/page_filter.py b/tools/telemetry/telemetry/page/page_filter.py
index a1c213af8e..54a30ef8e1 100644
--- a/tools/telemetry/telemetry/page/page_filter.py
+++ b/tools/telemetry/telemetry/page/page_filter.py
@@ -4,6 +4,13 @@
import optparse
import re
+def HasLabelIn(obj, labels):
+ for label in labels:
+ if hasattr(obj, label) and getattr(obj, label):
+ return True
+ return False
+
+
class PageFilter(object):
"""Filters pages in the page set based on command line flags."""
@@ -24,16 +31,30 @@ class PageFilter(object):
else:
self._page_exclude_regex = None
+ self._include_labels = None
+ self._exclude_labels = None
+ if options.page_label_filter:
+ self._include_labels = options.page_label_filter.split(',')
+ if options.page_label_filter_exclude:
+ self._exclude_labels = options.page_label_filter_exclude.split(',')
+
def IsSelected(self, page):
+ # Exclude filters take priority
+ if self._exclude_labels and HasLabelIn(page, self._exclude_labels):
+ return False
if self._page_exclude_regex and (
self._page_exclude_regex.search(page.url) or
(page.name and self._page_exclude_regex.search(page.name))):
return False
+ # Apply all filters
+ filter_result = True
+ if self._include_labels:
+ filter_result = filter_result and HasLabelIn(page, self._include_labels)
if self._page_regex:
- return (
+ filter_result = filter_result and (
self._page_regex.search(page.url) or
(page.name and self._page_regex.search(page.name)))
- return True
+ return filter_result
@staticmethod
def AddCommandLineOptions(parser):
@@ -42,4 +63,10 @@ class PageFilter(object):
help='Use only pages whose URLs match the given filter regexp.')
group.add_option('--page-filter-exclude', dest='page_filter_exclude',
help='Exclude pages whose URLs match the given filter regexp.')
+ group.add_option('--page-label-filter', dest='page_label_filter',
+ help='Use only pages that have any of these labels')
+ group.add_option('--page-label-filter-exclude',
+ dest='page_label_filter_exclude',
+ help='Exclude pages that have any of these labels')
+
parser.add_option_group(group)
diff --git a/tools/telemetry/telemetry/page/page_filter_unittest.py b/tools/telemetry/telemetry/page/page_filter_unittest.py
index 924d46a502..96e03999cb 100644
--- a/tools/telemetry/telemetry/page/page_filter_unittest.py
+++ b/tools/telemetry/telemetry/page/page_filter_unittest.py
@@ -7,10 +7,19 @@ from telemetry.page import page as page_module
from telemetry.page import page_filter as page_filter_module
from telemetry.page import page_set
-class MockOptions(object):
+class MockUrlFilterOptions(object):
def __init__(self, page_filter, page_filter_exclude):
self.page_filter = page_filter
self.page_filter_exclude = page_filter_exclude
+ self.page_label_filter = None
+ self.page_label_filter_exclude = None
+
+class MockLabelFilterOptions(object):
+ def __init__(self, page_label_filter, page_label_filter_exclude):
+ self.page_filter = None
+ self.page_filter_exclude = None
+ self.page_label_filter = page_label_filter
+ self.page_label_filter_exclude = page_label_filter_exclude
class PageFilterTest(unittest.TestCase):
def setUp(self):
@@ -29,41 +38,62 @@ class PageFilterTest(unittest.TestCase):
{ 'name': None })
def testURLPattern(self):
- options = MockOptions('conformance/textures', '')
+ options = MockUrlFilterOptions('conformance/textures', '')
page_filter = page_filter_module.PageFilter(options)
self.assertTrue(page_filter.IsSelected(self.p1))
self.assertFalse(page_filter.IsSelected(self.p2))
- options = MockOptions('textures', '')
+ options = MockUrlFilterOptions('textures', '')
page_filter = page_filter_module.PageFilter(options)
self.assertTrue(page_filter.IsSelected(self.p1))
self.assertTrue(page_filter.IsSelected(self.p2))
- options = MockOptions('somethingelse', '')
+ options = MockUrlFilterOptions('somethingelse', '')
page_filter = page_filter_module.PageFilter(options)
self.assertFalse(page_filter.IsSelected(self.p1))
self.assertFalse(page_filter.IsSelected(self.p2))
def testName(self):
- options = MockOptions('somethingelse', '')
+ options = MockUrlFilterOptions('somethingelse', '')
page_filter = page_filter_module.PageFilter(options)
self.assertFalse(page_filter.IsSelected(self.p1))
self.assertFalse(page_filter.IsSelected(self.p2))
- options = MockOptions('textures_tex_sub_image', '')
+ options = MockUrlFilterOptions('textures_tex_sub_image', '')
page_filter = page_filter_module.PageFilter(options)
self.assertTrue(page_filter.IsSelected(self.p1))
self.assertTrue(page_filter.IsSelected(self.p2))
- options = MockOptions('WebglConformance', '')
+ options = MockUrlFilterOptions('WebglConformance', '')
page_filter = page_filter_module.PageFilter(options)
self.assertTrue(page_filter.IsSelected(self.p1))
self.assertFalse(page_filter.IsSelected(self.p2))
- options = MockOptions('OtherSuite', '')
+ options = MockUrlFilterOptions('OtherSuite', '')
page_filter = page_filter_module.PageFilter(options)
self.assertFalse(page_filter.IsSelected(self.p1))
self.assertTrue(page_filter.IsSelected(self.p2))
def testNameNone(self):
- options = MockOptions('othersuite/textures', '')
+ options = MockUrlFilterOptions('othersuite/textures', '')
page_filter = page_filter_module.PageFilter(options)
self.assertTrue(page_filter.IsSelected(self.p3))
- options = MockOptions('conformance/textures', '')
+ options = MockUrlFilterOptions('conformance/textures', '')
+ page_filter = page_filter_module.PageFilter(options)
+ self.assertFalse(page_filter.IsSelected(self.p3))
+
+ def testLabelFilters(self):
+ self.p1.label1 = True
+ self.p2.label1 = True
+ self.p3.label1 = False
+ self.p1.label2 = True
+ self.p2.label2 = False
+ self.p3.label2 = True
+
+ # Include both labels
+ options = MockLabelFilterOptions('label1,label2', '')
page_filter = page_filter_module.PageFilter(options)
+ self.assertTrue(page_filter.IsSelected(self.p1))
+ self.assertTrue(page_filter.IsSelected(self.p2))
+ self.assertTrue(page_filter.IsSelected(self.p3))
+ # Exclude takes priority
+ options = MockLabelFilterOptions('label1', 'label2')
+ page_filter = page_filter_module.PageFilter(options)
+ self.assertFalse(page_filter.IsSelected(self.p1))
+ self.assertTrue(page_filter.IsSelected(self.p2))
self.assertFalse(page_filter.IsSelected(self.p3))
diff --git a/tools/telemetry/telemetry/page/page_measurement_unittest.py b/tools/telemetry/telemetry/page/page_measurement_unittest.py
index 3228263611..01fdb0f3b0 100644
--- a/tools/telemetry/telemetry/page/page_measurement_unittest.py
+++ b/tools/telemetry/telemetry/page/page_measurement_unittest.py
@@ -23,13 +23,19 @@ class MeasurementThatHasDefaults(page_measurement.PageMeasurement):
parser.add_option('-x', dest='x', default=3)
def MeasurePage(self, page, tab, results):
- assert self.options.x == 3
+ if not hasattr(self.options, 'x'):
+ raise page_measurement.MeasurementFailure('Default option was not set.')
+ if self.options.x != 3:
+ raise page_measurement.MeasurementFailure(
+ 'Expected x == 3, got x == ' + self.options.x)
results.Add('x', 'ms', 7)
class MeasurementForBlank(page_measurement.PageMeasurement):
def MeasurePage(self, page, tab, results):
contents = tab.EvaluateJavaScript('document.body.textContent')
- assert contents.strip() == 'Hello world'
+ if contents.strip() != 'Hello world':
+ raise page_measurement.MeasurementFailure(
+ 'Page contents were: ' + contents)
class MeasurementForReplay(page_measurement.PageMeasurement):
def MeasurePage(self, page, tab, results):
@@ -41,7 +47,10 @@ class MeasurementForReplay(page_measurement.PageMeasurement):
class MeasurementQueryParams(page_measurement.PageMeasurement):
def MeasurePage(self, page, tab, results):
query = tab.EvaluateJavaScript('window.location.search')
- assert query.strip() == '?foo=1'
+ expected = '?foo=1'
+ if query.strip() != expected:
+ raise page_measurement.MeasurementFailure(
+ 'query was %s, not %s.' % (query, expected))
class MeasurementWithAction(page_measurement.PageMeasurement):
def __init__(self):
@@ -63,11 +72,9 @@ class PageMeasurementUnitTest(
all_results = self.RunMeasurement(measurement, ps, options=self._options)
self.assertEquals(0, len(all_results.failures))
- def disabled_testGotQueryParams(self):
- # Disabled due to http://crbug.com/288631
+ def testGotQueryParams(self):
ps = self.CreatePageSet('file://blank.html?foo=1')
measurement = MeasurementQueryParams()
- ps.pages[-1].query_params = '?foo=1'
all_results = self.RunMeasurement(measurement, ps, options=self._options)
self.assertEquals(0, len(all_results.failures))
diff --git a/tools/telemetry/telemetry/page/page_runner.py b/tools/telemetry/telemetry/page/page_runner.py
index 7d1f024131..0b1dc2cd71 100644
--- a/tools/telemetry/telemetry/page/page_runner.py
+++ b/tools/telemetry/telemetry/page/page_runner.py
@@ -11,6 +11,7 @@ import time
import traceback
import random
+from telemetry import exception_formatter
from telemetry.core import browser_finder
from telemetry.core import exceptions
from telemetry.core import util
@@ -457,7 +458,9 @@ def _RunPage(test, page, state, expectation, results, finder_options):
# Run() catches these exceptions to relaunch the tab/browser, so re-raise.
raise
except Exception:
- raise
+ logging.warning('While running %s', page.url)
+ exception_formatter.PrintFormattedException(*sys.exc_info())
+ results.AddFailure(page, sys.exc_info())
else:
if expectation == 'fail':
logging.warning('%s was expected to fail, but passed.\n', page.url)
diff --git a/tools/telemetry/telemetry/page/page_runner_unittest.py b/tools/telemetry/telemetry/page/page_runner_unittest.py
index 75c89dd522..020337b5c6 100644
--- a/tools/telemetry/telemetry/page/page_runner_unittest.py
+++ b/tools/telemetry/telemetry/page/page_runner_unittest.py
@@ -62,6 +62,35 @@ class PageRunnerTests(unittest.TestCase):
self.assertEquals(0, len(results.failures))
self.assertEquals(1, len(results.errors))
+ def testHandlingOfTestThatRaisesWithNonFatalUnknownExceptions(self):
+ ps = page_set.PageSet()
+ expectations = test_expectations.TestExpectations()
+ ps.pages.append(page_module.Page(
+ 'file://blank.html', ps, base_dir=util.GetUnittestDataDir()))
+ ps.pages.append(page_module.Page(
+ 'file://blank.html', ps, base_dir=util.GetUnittestDataDir()))
+
+ class ExpectedException(Exception):
+ pass
+
+ class Test(page_test.PageTest):
+ def __init__(self, *args):
+ super(Test, self).__init__(*args)
+ self.run_count = 0
+ def RunTest(self, *_):
+ old_run_count = self.run_count
+ self.run_count += 1
+ if old_run_count == 0:
+ raise ExpectedException()
+
+ options = options_for_unittests.GetCopy()
+ options.output_format = 'none'
+ test = Test('RunTest')
+ results = page_runner.Run(test, ps, expectations, options)
+ self.assertEquals(2, test.run_count)
+ self.assertEquals(1, len(results.successes))
+ self.assertEquals(1, len(results.failures))
+
def testHandlingOfCrashedTabWithExpectedFailure(self):
ps = page_set.PageSet()
expectations = test_expectations.TestExpectations()
@@ -70,12 +99,13 @@ class PageRunnerTests(unittest.TestCase):
ps.pages.append(page1)
class Test(page_test.PageTest):
- def RunTest(self, *args):
+ def RunTest(self, *_):
pass
options = options_for_unittests.GetCopy()
options.output_format = 'none'
- results = page_runner.Run(Test('RunTest'), ps, expectations, options)
+ results = page_runner.Run(
+ Test('RunTest'), ps, expectations, options)
self.assertEquals(1, len(results.successes))
self.assertEquals(0, len(results.failures))
self.assertEquals(0, len(results.errors))
diff --git a/tools/telemetry/telemetry/page/page_set.py b/tools/telemetry/telemetry/page/page_set.py
index c5a34cc977..b05250d6b9 100644
--- a/tools/telemetry/telemetry/page/page_set.py
+++ b/tools/telemetry/telemetry/page/page_set.py
@@ -30,7 +30,7 @@ class PageSet(object):
# Create a PageSetArchiveInfo object.
if self.archive_data_file:
self.wpr_archive_info = page_set_archive_info.PageSetArchiveInfo.FromFile(
- os.path.join(self._base_dir, self.archive_data_file), file_path)
+ os.path.join(self._base_dir, self.archive_data_file))
else:
self.wpr_archive_info = None
diff --git a/tools/telemetry/telemetry/page/page_set_archive_info.py b/tools/telemetry/telemetry/page/page_set_archive_info.py
index c3ed26d289..8fc8d15b8a 100644
--- a/tools/telemetry/telemetry/page/page_set_archive_info.py
+++ b/tools/telemetry/telemetry/page/page_set_archive_info.py
@@ -13,16 +13,13 @@ from telemetry.page import cloud_storage
class PageSetArchiveInfo(object):
- def __init__(self, archive_data_file_path, page_set_file_path, data):
- self._archive_data_file_path = archive_data_file_path
- self._archive_data_file_dir = os.path.dirname(archive_data_file_path)
+ def __init__(self, file_path, data):
+ self._file_path = file_path
+ self._base_dir = os.path.dirname(file_path)
# Ensure directory exists.
- if not os.path.exists(self._archive_data_file_dir):
- os.makedirs(self._archive_data_file_dir)
-
- # Back pointer to the page set file.
- self._page_set_file_path = page_set_file_path
+ if not os.path.exists(self._base_dir):
+ os.makedirs(self._base_dir)
# Download all .wpr files.
for archive_path in data['archives']:
@@ -61,12 +58,12 @@ class PageSetArchiveInfo(object):
self.temp_target_wpr_file_path = None
@classmethod
- def FromFile(cls, file_path, page_set_file_path):
+ def FromFile(cls, file_path):
if os.path.exists(file_path):
with open(file_path, 'r') as f:
data = json.load(f)
- return cls(file_path, page_set_file_path, data)
- return cls(file_path, page_set_file_path, {'archives': {}})
+ return cls(file_path, data)
+ return cls(file_path, {'archives': {}})
def WprFilePathForPage(self, page):
if self.temp_target_wpr_file_path:
@@ -119,21 +116,18 @@ class PageSetArchiveInfo(object):
metadata['description'] = (
'Describes the Web Page Replay archives for a page set. Don\'t edit by '
'hand! Use record_wpr for updating.')
- # Pointer from the metadata to the page set .json file.
- metadata['page_set'] = os.path.relpath(self._page_set_file_path,
- self._archive_data_file_dir)
metadata['archives'] = self._wpr_file_to_urls.copy()
# Don't write data for abandoned archives.
abandoned_wpr_files = self._AbandonedWprFiles()
for wpr_file in abandoned_wpr_files:
del metadata['archives'][wpr_file]
- with open(self._archive_data_file_path, 'w') as f:
+ with open(self._file_path, 'w') as f:
json.dump(metadata, f, indent=4)
f.flush()
def _WprFileNameToPath(self, wpr_file):
- return os.path.abspath(os.path.join(self._archive_data_file_dir, 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."""
@@ -152,7 +146,7 @@ class PageSetArchiveInfo(object):
if not base:
# If we're creating a completely new info file, use the base name of the
# page set file.
- base = os.path.splitext(os.path.basename(self._page_set_file_path))[0]
+ 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)
diff --git a/tools/telemetry/telemetry/page/page_set_archive_info_unittest.py b/tools/telemetry/telemetry/page/page_set_archive_info_unittest.py
index 470d104d74..a7ccbc3663 100644
--- a/tools/telemetry/telemetry/page/page_set_archive_info_unittest.py
+++ b/tools/telemetry/telemetry/page/page_set_archive_info_unittest.py
@@ -48,8 +48,7 @@ class TestPageSetArchiveInfo(unittest.TestCase):
# Create the PageSetArchiveInfo object to be tested.
self.archive_info = page_set_archive_info.PageSetArchiveInfo.FromFile(
- self.page_set_archive_info_file,
- os.path.join(tempfile.gettempdir(), 'pageset.json'))
+ self.page_set_archive_info_file)
def tearDown(self):
shutil.rmtree(self.tmp_dir)
@@ -113,7 +112,7 @@ class TestPageSetArchiveInfo(unittest.TestCase):
# Write only the page set without the corresponding metadata file.
page_set_contents = ("""
{
- archive_data_file": "new-metadata.json",
+ archive_data_file": "new_archive_info.json",
"pages": [
{
"url": "%s",
@@ -121,16 +120,16 @@ class TestPageSetArchiveInfo(unittest.TestCase):
]
}""" % url1)
- page_set_file = os.path.join(self.tmp_dir, 'new.json')
+ page_set_file = os.path.join(self.tmp_dir, 'new_page_set.json')
with open(page_set_file, 'w') as f:
f.write(page_set_contents)
self.page_set_archive_info_file = os.path.join(self.tmp_dir,
- 'new-metadata.json')
+ 'new_archive_info.json')
# Create the PageSetArchiveInfo object to be tested.
self.archive_info = page_set_archive_info.PageSetArchiveInfo.FromFile(
- self.page_set_archive_info_file, page_set_file)
+ self.page_set_archive_info_file)
# Add a recording for all the pages.
new_temp_recording = os.path.join(self.tmp_dir, 'recording.wpr')
@@ -145,7 +144,7 @@ class TestPageSetArchiveInfo(unittest.TestCase):
self.archive_info.AddRecordedPages([page1.url])
# Expected name for the recording (decided by PageSetArchiveInfo).
- new_recording = os.path.join(self.tmp_dir, 'new_000.wpr')
+ new_recording = os.path.join(self.tmp_dir, 'new_archive_info_000.wpr')
self.assertTrue(os.path.exists(new_recording))
self.assertFalse(os.path.exists(new_temp_recording))
@@ -154,7 +153,6 @@ class TestPageSetArchiveInfo(unittest.TestCase):
# Check that the archive info was written correctly.
self.assertTrue(os.path.exists(self.page_set_archive_info_file))
read_archive_info = page_set_archive_info.PageSetArchiveInfo.FromFile(
- self.page_set_archive_info_file,
- os.path.join(tempfile.gettempdir(), 'pageset.json'))
+ self.page_set_archive_info_file)
self.assertEquals(new_recording,
read_archive_info.WprFilePathForPage(page1))
diff --git a/tools/telemetry/telemetry/page/page_test.py b/tools/telemetry/telemetry/page/page_test.py
index 6eb36a6fcd..368aa89470 100644
--- a/tools/telemetry/telemetry/page/page_test.py
+++ b/tools/telemetry/telemetry/page/page_test.py
@@ -180,6 +180,14 @@ class PageTest(object):
all waiting for completion has occurred."""
pass
+ def WillRunActions(self, page, tab):
+ """Override to do operations before running the actions on the page."""
+ pass
+
+ def DidRunActions(self, page, tab):
+ """Override to do operations after running the actions on the page."""
+ pass
+
def WillRunAction(self, page, tab, action):
"""Override to do operations before running the action on the page."""
pass
@@ -213,7 +221,9 @@ class PageTest(object):
interactive = options and options.interactive
compound_action = GetCompoundActionFromPage(
page, self._action_name_to_run, interactive)
+ self.WillRunActions(page, tab)
self._RunCompoundAction(page, tab, compound_action)
+ self.DidRunActions(page, tab)
try:
self._test_method(page, tab, results)
finally:
diff --git a/tools/telemetry/telemetry/unittest/system_stub.py b/tools/telemetry/telemetry/unittest/system_stub.py
index de629bc146..10ec1ce1fe 100644
--- a/tools/telemetry/telemetry/unittest/system_stub.py
+++ b/tools/telemetry/telemetry/unittest/system_stub.py
@@ -70,6 +70,9 @@ class AdbCommandsModuleStub(object):
def RestartAdbdOnDevice(self):
pass
+ def IsUserBuild(self):
+ return False
+
def __init__(self):
self.attached_devices = []
self.shell_command_handlers = {}
@@ -85,13 +88,12 @@ class AdbCommandsModuleStub(object):
def GetAttachedDevices(self):
return self.attached_devices
- @staticmethod
- def HasForwarder(_=None):
- return True
-
def SetupPrebuiltTools(self, _):
return True
+ def CleanupLeftoverProcesses(self):
+ pass
+
class CloudStorageModuleStub(object):
INTERNAL_BUCKET = None
diff --git a/tools/telemetry/telemetry/value/histogram_unittest.py b/tools/telemetry/telemetry/value/histogram_unittest.py
index 6e76bb969c..faf50abf44 100644
--- a/tools/telemetry/telemetry/value/histogram_unittest.py
+++ b/tools/telemetry/telemetry/value/histogram_unittest.py
@@ -6,7 +6,7 @@ import unittest
from telemetry import value
from telemetry.page import page_set
-from telemetry.value.histogram import HistogramValue
+from telemetry.value import histogram as histogram_module
class TestBase(unittest.TestCase):
def setUp(self):
@@ -27,7 +27,7 @@ class TestBase(unittest.TestCase):
class ValueTest(TestBase):
def testHistogramBasic(self):
page0 = self.pages[0]
- histogram = HistogramValue(
+ histogram = histogram_module.HistogramValue(
page0, 'x', 'counts',
raw_value_json='{"buckets": [{"low": 1, "high": 2, "count": 1}]}',
important=False)
diff --git a/tools/telemetry/telemetry/value/list_of_scalar_values_unittest.py b/tools/telemetry/telemetry/value/list_of_scalar_values_unittest.py
index d223bc8b2b..c54ca26306 100644
--- a/tools/telemetry/telemetry/value/list_of_scalar_values_unittest.py
+++ b/tools/telemetry/telemetry/value/list_of_scalar_values_unittest.py
@@ -6,7 +6,7 @@ import unittest
from telemetry import value
from telemetry.page import page_set
-from telemetry.value.list_of_scalar_values import ListOfScalarValues
+from telemetry.value import list_of_scalar_values
class TestBase(unittest.TestCase):
def setUp(self):
@@ -27,15 +27,16 @@ class TestBase(unittest.TestCase):
class ValueTest(TestBase):
def testListSamePageMergingWithSamePageConcatenatePolicy(self):
page0 = self.pages[0]
- v0 = ListOfScalarValues(
+ v0 = list_of_scalar_values.ListOfScalarValues(
page0, 'x', 'unit',
[1,2], same_page_merge_policy=value.CONCATENATE)
- v1 = ListOfScalarValues(
+ v1 = list_of_scalar_values.ListOfScalarValues(
page0, 'x', 'unit',
[3,4], same_page_merge_policy=value.CONCATENATE)
self.assertTrue(v1.IsMergableWith(v0))
- vM = ListOfScalarValues.MergeLikeValuesFromSamePage([v0, v1])
+ vM = (list_of_scalar_values.ListOfScalarValues.
+ MergeLikeValuesFromSamePage([v0, v1]))
self.assertEquals(page0, vM.page)
self.assertEquals('x', vM.name)
self.assertEquals('unit', vM.units)
@@ -45,15 +46,16 @@ class ValueTest(TestBase):
def testListSamePageMergingWithPickFirstPolicy(self):
page0 = self.pages[0]
- v0 = ListOfScalarValues(
+ v0 = list_of_scalar_values.ListOfScalarValues(
page0, 'x', 'unit',
[1,2], same_page_merge_policy=value.PICK_FIRST)
- v1 = ListOfScalarValues(
+ v1 = list_of_scalar_values.ListOfScalarValues(
page0, 'x', 'unit',
[3,4], same_page_merge_policy=value.PICK_FIRST)
self.assertTrue(v1.IsMergableWith(v0))
- vM = ListOfScalarValues.MergeLikeValuesFromSamePage([v0, v1])
+ vM = (list_of_scalar_values.ListOfScalarValues.
+ MergeLikeValuesFromSamePage([v0, v1]))
self.assertEquals(page0, vM.page)
self.assertEquals('x', vM.name)
self.assertEquals('unit', vM.units)
@@ -63,15 +65,16 @@ class ValueTest(TestBase):
def testListDifferentPageMerging(self):
page0 = self.pages[0]
- v0 = ListOfScalarValues(
+ v0 = list_of_scalar_values.ListOfScalarValues(
page0, 'x', 'unit',
[1, 2], same_page_merge_policy=value.PICK_FIRST)
- v1 = ListOfScalarValues(
+ v1 = list_of_scalar_values.ListOfScalarValues(
page0, 'x', 'unit',
[3, 4], same_page_merge_policy=value.PICK_FIRST)
self.assertTrue(v1.IsMergableWith(v0))
- vM = ListOfScalarValues.MergeLikeValuesFromDifferentPages([v0, v1])
+ vM = (list_of_scalar_values.ListOfScalarValues.
+ MergeLikeValuesFromDifferentPages([v0, v1]))
self.assertEquals(None, vM.page)
self.assertEquals('x', vM.name)
self.assertEquals('unit', vM.units)
diff --git a/tools/telemetry/telemetry/value/merge_values_unittest.py b/tools/telemetry/telemetry/value/merge_values_unittest.py
index 1b8e8d3c8f..781595c40f 100644
--- a/tools/telemetry/telemetry/value/merge_values_unittest.py
+++ b/tools/telemetry/telemetry/value/merge_values_unittest.py
@@ -5,9 +5,9 @@ import os
import unittest
from telemetry.page import page_set
+from telemetry.value import list_of_scalar_values
from telemetry.value import merge_values
-from telemetry.value.list_of_scalar_values import ListOfScalarValues
-from telemetry.value.scalar import ScalarValue
+from telemetry.value import scalar
class TestBase(unittest.TestCase):
def setUp(self):
@@ -30,10 +30,10 @@ class MergeValueTest(TestBase):
page0 = self.pages[0]
page1 = self.pages[1]
- all_values = [ScalarValue(page0, 'x', 'units', 1),
- ScalarValue(page1, 'x', 'units', 4),
- ScalarValue(page0, 'x', 'units', 2),
- ScalarValue(page1, 'x', 'units', 5)]
+ all_values = [scalar.ScalarValue(page0, 'x', 'units', 1),
+ scalar.ScalarValue(page1, 'x', 'units', 4),
+ scalar.ScalarValue(page0, 'x', 'units', 2),
+ scalar.ScalarValue(page1, 'x', 'units', 5)]
merged_values = merge_values.MergeLikeValuesFromSamePage(all_values)
# Sort the results so that their order is predictable for the subsequent
@@ -53,7 +53,7 @@ class MergeValueTest(TestBase):
def testSamePageMergeOneValue(self):
page0 = self.pages[0]
- all_values = [ScalarValue(page0, 'x', 'units', 1)]
+ all_values = [scalar.ScalarValue(page0, 'x', 'units', 1)]
# Sort the results so that their order is predictable for the subsequent
# assertions.
@@ -66,10 +66,10 @@ class MergeValueTest(TestBase):
page0 = self.pages[0]
page1 = self.pages[1]
- all_values = [ScalarValue(page0, 'x', 'units', 1),
- ScalarValue(page1, 'x', 'units', 2),
- ScalarValue(page0, 'y', 'units', 10),
- ScalarValue(page1, 'y', 'units', 20)]
+ all_values = [scalar.ScalarValue(page0, 'x', 'units', 1),
+ scalar.ScalarValue(page1, 'x', 'units', 2),
+ scalar.ScalarValue(page0, 'y', 'units', 10),
+ scalar.ScalarValue(page1, 'y', 'units', 20)]
# Sort the results so that their order is predictable for the subsequent
# assertions.
@@ -88,7 +88,7 @@ class MergeValueTest(TestBase):
def testDifferentPageMergeSingleValueStillMerges(self):
page0 = self.pages[0]
- all_values = [ScalarValue(page0, 'x', 'units', 1)]
+ all_values = [scalar.ScalarValue(page0, 'x', 'units', 1)]
# Sort the results so that their order is predictable for the subsequent
# assertions.
@@ -97,15 +97,16 @@ class MergeValueTest(TestBase):
self.assertEquals((None, 'x'),
(merged_values[0].page, merged_values[0].name))
- self.assertTrue(isinstance(merged_values[0], ListOfScalarValues))
+ self.assertTrue(
+ isinstance(merged_values[0], list_of_scalar_values.ListOfScalarValues))
self.assertEquals([1], merged_values[0].values)
def testDifferentPageMergeBasicIgnoreTraceName(self):
page0 = self.pages[0]
page1 = self.pages[1]
- all_values = [ScalarValue(page0, 'x.score', 'units', 1),
- ScalarValue(page1, 'y.score', 'units', 2)]
+ all_values = [scalar.ScalarValue(page0, 'x.score', 'units', 1),
+ scalar.ScalarValue(page1, 'y.score', 'units', 2)]
# Sort the results so that their order is predictable for the subsequent
# assertions.
merged_values = merge_values.MergeLikeValuesFromDifferentPages(
diff --git a/tools/telemetry/telemetry/value/scalar.py b/tools/telemetry/telemetry/value/scalar.py
index 8fec048c93..4b57654de8 100644
--- a/tools/telemetry/telemetry/value/scalar.py
+++ b/tools/telemetry/telemetry/value/scalar.py
@@ -4,10 +4,10 @@
import numbers
-from telemetry.value import Value
-from telemetry.value.list_of_scalar_values import ListOfScalarValues
+from telemetry import value as value_module
+from telemetry.value import list_of_scalar_values
-class ScalarValue(Value):
+class ScalarValue(value_module.Value):
def __init__(self, page, name, units, value, important=True):
"""A single value (float or integer) result from a test.
@@ -50,7 +50,7 @@ class ScalarValue(Value):
def MergeLikeValuesFromSamePage(cls, values):
assert len(values) > 0
v0 = values[0]
- return ListOfScalarValues(
+ return list_of_scalar_values.ListOfScalarValues(
v0.page, v0.name, v0.units,
[v.value for v in values],
important=v0.important)
@@ -64,7 +64,7 @@ class ScalarValue(Value):
name = v0.name
else:
name = v0.name_suffix
- return ListOfScalarValues(
+ return list_of_scalar_values.ListOfScalarValues(
None, name, v0.units,
[v.value for v in values],
important=v0.important)
diff --git a/tools/telemetry/telemetry/value/scalar_unittest.py b/tools/telemetry/telemetry/value/scalar_unittest.py
index bb6d1d38b1..e375d01a24 100644
--- a/tools/telemetry/telemetry/value/scalar_unittest.py
+++ b/tools/telemetry/telemetry/value/scalar_unittest.py
@@ -6,7 +6,7 @@ import unittest
from telemetry import value
from telemetry.page import page_set
-from telemetry.value.scalar import ScalarValue
+from telemetry.value import scalar
class TestBase(unittest.TestCase):
def setUp(self):
@@ -27,25 +27,25 @@ class TestBase(unittest.TestCase):
class ValueTest(TestBase):
def testBuildbotValueType(self):
page0 = self.pages[0]
- v = ScalarValue(page0, 'x', 'unit', 3, important=True)
+ v = scalar.ScalarValue(page0, 'x', 'unit', 3, important=True)
self.assertEquals('default', v.GetBuildbotDataType(
value.MERGED_PAGES_RESULT_OUTPUT_CONTEXT))
self.assertEquals([3], v.GetBuildbotValue())
self.assertEquals(('x_by_url', page0.display_name),
v.GetBuildbotMeasurementAndTraceNameForPerPageResult())
- v = ScalarValue(page0, 'x', 'unit', 3, important=False)
+ v = scalar.ScalarValue(page0, 'x', 'unit', 3, important=False)
self.assertEquals(
'unimportant',
v.GetBuildbotDataType(value.MERGED_PAGES_RESULT_OUTPUT_CONTEXT))
def testScalarSamePageMerging(self):
page0 = self.pages[0]
- v0 = ScalarValue(page0, 'x', 'unit', 1)
- v1 = ScalarValue(page0, 'x', 'unit', 2)
+ v0 = scalar.ScalarValue(page0, 'x', 'unit', 1)
+ v1 = scalar.ScalarValue(page0, 'x', 'unit', 2)
self.assertTrue(v1.IsMergableWith(v0))
- vM = ScalarValue.MergeLikeValuesFromSamePage([v0, v1])
+ vM = scalar.ScalarValue.MergeLikeValuesFromSamePage([v0, v1])
self.assertEquals(page0, vM.page)
self.assertEquals('x', vM.name)
self.assertEquals('unit', vM.units)
@@ -55,10 +55,10 @@ class ValueTest(TestBase):
def testScalarDifferentSiteMerging(self):
page0 = self.pages[0]
page1 = self.pages[1]
- v0 = ScalarValue(page0, 'x', 'unit', 1)
- v1 = ScalarValue(page1, 'x', 'unit', 2)
+ v0 = scalar.ScalarValue(page0, 'x', 'unit', 1)
+ v1 = scalar.ScalarValue(page1, 'x', 'unit', 2)
- vM = ScalarValue.MergeLikeValuesFromDifferentPages([v0, v1])
+ vM = scalar.ScalarValue.MergeLikeValuesFromDifferentPages([v0, v1])
self.assertEquals(None, vM.page)
self.assertEquals('x', vM.name)
self.assertEquals('unit', vM.units)
diff --git a/tools/telemetry/telemetry/value/value_backcompat.py b/tools/telemetry/telemetry/value/value_backcompat.py
index 0b6d9ce74b..6d970de778 100644
--- a/tools/telemetry/telemetry/value/value_backcompat.py
+++ b/tools/telemetry/telemetry/value/value_backcompat.py
@@ -9,9 +9,9 @@ implementation and update the PageMeasurementResults API once we know the
underlying implementation is solid.
"""
from telemetry import value as value_module
-from telemetry.value.histogram import HistogramValue
-from telemetry.value.list_of_scalar_values import ListOfScalarValues
-from telemetry.value.scalar import ScalarValue
+from telemetry.value import histogram
+from telemetry.value import list_of_scalar_values
+from telemetry.value import scalar
def ConvertOldCallingConventionToValue(page, trace_name, units,
@@ -20,26 +20,26 @@ def ConvertOldCallingConventionToValue(page, trace_name, units,
trace_name, chart_name)
if data_type == 'default':
if isinstance(value, list):
- return ListOfScalarValues(page, value_name, units,
- value, important=True)
+ return list_of_scalar_values.ListOfScalarValues(
+ page, value_name, units, value, important=True)
else:
- return ScalarValue(page, value_name, units,
- value, important=True)
+ return scalar.ScalarValue(page, value_name, units,
+ value, important=True)
elif data_type == 'unimportant':
if isinstance(value, list):
- return ListOfScalarValues(page, value_name, units,
- value, important=False)
+ return list_of_scalar_values.ListOfScalarValues(
+ page, value_name, units, value, important=False)
else:
- return ScalarValue(page, value_name, units,
- value, important=False)
+ return scalar.ScalarValue(page, value_name, units,
+ value, important=False)
elif data_type == 'histogram':
assert isinstance(value, basestring)
- return HistogramValue(page, value_name, units,
- raw_value_json=value, important=True)
+ return histogram.HistogramValue(
+ page, value_name, units, raw_value_json=value, important=True)
elif data_type == 'unimportant-histogram':
assert isinstance(value, basestring)
- return HistogramValue(page, value_name, units,
- raw_value_json=value, important=False)
+ return histogram.HistogramValue(
+ page, value_name, units, raw_value_json=value, important=False)
elif data_type == 'informational':
raise NotImplementedError()
else:
diff --git a/tools/telemetry/unittest_data/frame0.png b/tools/telemetry/unittest_data/frame0.png
new file mode 100644
index 0000000000..956b62c5d3
--- /dev/null
+++ b/tools/telemetry/unittest_data/frame0.png
Binary files differ
diff --git a/tools/telemetry/unittest_data/frame1.png b/tools/telemetry/unittest_data/frame1.png
new file mode 100644
index 0000000000..8f59394b6f
--- /dev/null
+++ b/tools/telemetry/unittest_data/frame1.png
Binary files differ
diff --git a/tools/telemetry/unittest_data/frame2.png b/tools/telemetry/unittest_data/frame2.png
new file mode 100644
index 0000000000..f0bf178944
--- /dev/null
+++ b/tools/telemetry/unittest_data/frame2.png
Binary files differ
diff --git a/tools/telemetry/unittest_data/frame3.png b/tools/telemetry/unittest_data/frame3.png
new file mode 100644
index 0000000000..f36b4539c7
--- /dev/null
+++ b/tools/telemetry/unittest_data/frame3.png
Binary files differ
diff --git a/tools/telemetry/unittest_data/frame4.png b/tools/telemetry/unittest_data/frame4.png
new file mode 100644
index 0000000000..ef9d38a06f
--- /dev/null
+++ b/tools/telemetry/unittest_data/frame4.png
Binary files differ
diff --git a/tools/telemetry/unittest_data/frame5.png b/tools/telemetry/unittest_data/frame5.png
new file mode 100644
index 0000000000..ef1926dad9
--- /dev/null
+++ b/tools/telemetry/unittest_data/frame5.png
Binary files differ
diff --git a/tools/telemetry/unittest_data/frame6.png b/tools/telemetry/unittest_data/frame6.png
new file mode 100644
index 0000000000..0a3028f3b6
--- /dev/null
+++ b/tools/telemetry/unittest_data/frame6.png
Binary files differ
diff --git a/tools/telemetry/unittest_data/frame7.png b/tools/telemetry/unittest_data/frame7.png
new file mode 100644
index 0000000000..9b6b6e5d65
--- /dev/null
+++ b/tools/telemetry/unittest_data/frame7.png
Binary files differ
diff --git a/tools/telemetry/unittest_data/stat b/tools/telemetry/unittest_data/stat
new file mode 100644
index 0000000000..a2c20d1ac2
--- /dev/null
+++ b/tools/telemetry/unittest_data/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/tools/telemetry/unittest_data/status b/tools/telemetry/unittest_data/status
new file mode 100644
index 0000000000..cb3d4f6bd9
--- /dev/null
+++ b/tools/telemetry/unittest_data/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/tools/telemetry/unittest_data/status_nohwm b/tools/telemetry/unittest_data/status_nohwm
new file mode 100644
index 0000000000..5d94aa49e8
--- /dev/null
+++ b/tools/telemetry/unittest_data/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/tools/telemetry/unittest_data/vid.mp4 b/tools/telemetry/unittest_data/vid.mp4
new file mode 100644
index 0000000000..117ff0cc8e
--- /dev/null
+++ b/tools/telemetry/unittest_data/vid.mp4
Binary files differ
diff --git a/tools/telemetry_tools/telemetry_bootstrap.py b/tools/telemetry_tools/telemetry_bootstrap.py
index 6fd78e1230..acb976c8dc 100644
--- a/tools/telemetry_tools/telemetry_bootstrap.py
+++ b/tools/telemetry_tools/telemetry_bootstrap.py
@@ -21,19 +21,21 @@ import os
import urllib
import urlparse
+# Dummy module for DAVclient.
+davclient = None
+
+
# Link to file containing the 'davclient' WebDAV client library.
-_DAVCLIENT_URL = ('https://src.chromium.org/chrome/trunk/src/tools/' +
+_DAVCLIENT_URL = ('https://src.chromium.org/chrome/trunk/src/tools/'
'telemetry/third_party/davclient/davclient.py')
-# Dummy module for Davclient.
-_davclient = None
-def _download_and_import_davclient_module():
+def _DownloadAndImportDAVClientModule():
"""Dynamically import davclient helper library."""
- global _davclient
+ global davclient
davclient_src = urllib.urlopen(_DAVCLIENT_URL).read()
- _davclient = imp.new_module('davclient')
- exec davclient_src in _davclient.__dict__
+ davclient = imp.new_module('davclient')
+ exec davclient_src in davclient.__dict__
class DAVClientWrapper():
@@ -46,7 +48,7 @@ class DAVClientWrapper():
root_url: string url of SVN/WebDAV server
"""
self.root_url = root_url
- self.client = _davclient.DAVClient(root_url)
+ self.client = davclient.DAVClient(root_url)
@staticmethod
def __norm_path_keys(dict_with_path_keys):
@@ -78,10 +80,13 @@ class DAVClientWrapper():
"""
if self.IsFile(src_path):
if not os.path.exists(os.path.dirname(dst_path)):
- logging.info("creating %s", os.path.dirname(dst_path))
+ logging.info('Creating %s', os.path.dirname(dst_path))
os.makedirs(os.path.dirname(dst_path))
- logging.info("Saving %s to %s", self.root_url + src_path, dst_path)
- urllib.urlretrieve(self.root_url + src_path, dst_path)
+ if os.path.isfile(dst_path):
+ logging.info('Skipping %s', dst_path)
+ else:
+ logging.info('Saving %s to %s', self.root_url + src_path, dst_path)
+ urllib.urlretrieve(self.root_url + src_path, dst_path)
return
else:
for subdir in self.GetDirList(src_path):
@@ -89,7 +94,7 @@ class DAVClientWrapper():
os.path.join(dst_path, subdir))
-def ListAllDepsPaths(deps_content):
+def ListAllDepsPaths(deps_file):
"""Recursively returns a list of all paths indicated in this deps file.
Note that this discards information about where path dependencies come from,
@@ -97,58 +102,53 @@ def ListAllDepsPaths(deps_content):
already fetched all dependencies.
Args:
- deps_content: String containing deps information to be evaluated, in the
- format given in the header of this file.
- Returns: A list of string paths starting under src that are required by the
- given deps file, and all of its sub-dependencies. This amounts to
- the keys of the 'deps' dictionary.
+ deps_file: File containing deps information to be evaluated, in the
+ format given in the header of this file.
+ Returns:
+ A list of string paths starting under src that are required by the
+ given deps file, and all of its sub-dependencies. This amounts to
+ the keys of the 'deps' dictionary.
"""
+ deps = {}
+ deps_includes = {}
+
chrome_root = os.path.dirname(__file__)
while os.path.basename(chrome_root) != 'src':
- chrome_root = os.path.abspath(os.path.join(chrome_root, '..'))
- deps = imp.new_module('deps')
- exec deps_content in deps.__dict__
+ chrome_root = os.path.abspath(os.path.join(chrome_root, os.pardir))
- deps_paths = deps.deps.keys()
+ exec open(deps_file).read()
- if hasattr(deps, 'deps_includes'):
- for path in deps.deps_includes.keys():
- # Need to localize the paths.
- path = os.path.join(chrome_root, '..', path)
- deps_paths = deps_paths + ListAllDepsPaths(open(path).read())
+ deps_paths = deps.keys()
- return deps_paths
+ for path in deps_includes.keys():
+ # Need to localize the paths.
+ path = os.path.join(chrome_root, os.pardir, path)
+ deps_paths += ListAllDepsPaths(path)
-
-def DownloadDepsURL(destination_dir, url):
- """Wrapper around DownloadDeps that takes a string URL to the deps file.
-
- Args:
- destination_dir: String path to local directory to download files into.
- url: URL of deps file (see DownloadDeps for format).
- """
- logging.warning('Downloading deps from %s...', url)
- DownloadDeps(destination_dir, urllib.urlopen(url).read())
+ return deps_paths
-def DownloadDeps(destination_dir, deps_content):
+def DownloadDeps(destination_dir, url):
"""Saves all the dependencies in deps_path.
- Reads deps_content, assuming the contents are in the simple DEPS-like file
+ Opens and reads url, assuming the contents are in the simple DEPS-like file
format specified in the header of this file, then download all
files/directories listed to the destination_dir.
Args:
destination_dir: String path to directory to download files into.
- deps_content: String containing deps information to be evaluated.
+ url: URL containing deps information to be evaluated.
"""
+ logging.warning('Downloading deps from %s...', url)
# TODO(wiltzius): Add a parameter for which revision to pull.
- _download_and_import_davclient_module()
+ _DownloadAndImportDAVClientModule()
+
+ deps = {}
+ deps_includes = {}
- deps = imp.new_module('deps')
- exec deps_content in deps.__dict__
+ exec urllib.urlopen(url).read()
- for dst_path, src_path in deps.deps.iteritems():
+ for dst_path, src_path in deps.iteritems():
full_dst_path = os.path.join(destination_dir, dst_path)
parsed_url = urlparse.urlparse(src_path)
root_url = parsed_url.scheme + '://' + parsed_url.netloc
@@ -156,7 +156,5 @@ def DownloadDeps(destination_dir, deps_content):
dav_client = DAVClientWrapper(root_url)
dav_client.Traverse(parsed_url.path, full_dst_path)
- if hasattr(deps, 'deps_includes'):
- for url in deps.deps_includes.values():
- DownloadDepsURL(destination_dir, url)
-
+ for url in deps_includes.values():
+ DownloadDeps(destination_dir, url)
diff --git a/tools/valgrind/asan/asan_symbolize.py b/tools/valgrind/asan/asan_symbolize.py
index c1f93bd16e..06ff592509 100755
--- a/tools/valgrind/asan/asan_symbolize.py
+++ b/tools/valgrind/asan/asan_symbolize.py
@@ -6,6 +6,7 @@
from third_party import asan_symbolize
+import os
import re
import sys
@@ -16,7 +17,31 @@ def fix_filename(file_name):
file_name = re.sub(".*crtstuff.c:0", "???:0", file_name)
return file_name
+class Unbuffered(object):
+ """Disable buffering on a file object."""
+ def __init__(self, stream):
+ self.stream = stream
+
+ def write(self, data):
+ self.stream.write(data)
+ if '\n' in data:
+ self.stream.flush()
+
+ def __getattr__(self, attr):
+ return getattr(self.stream, attr)
+
+
+def disable_buffering():
+ """Makes this process and child processes stdout unbuffered."""
+ if not os.environ.get('PYTHONUNBUFFERED'):
+ # Since sys.stdout is a C++ object, it's impossible to do
+ # sys.stdout.write = lambda...
+ sys.stdout = Unbuffered(sys.stdout)
+ os.environ['PYTHONUNBUFFERED'] = 'x'
+
+
def main():
+ disable_buffering()
loop = asan_symbolize.SymbolizationLoop(binary_name_filter=fix_filename)
loop.process_stdin()
diff --git a/tools/valgrind/chrome_tests.py b/tools/valgrind/chrome_tests.py
index b9c11da801..b92536cb52 100755
--- a/tools/valgrind/chrome_tests.py
+++ b/tools/valgrind/chrome_tests.py
@@ -119,6 +119,9 @@ class ChromeTests:
# Valgrind runs tests slowly, so slow tests hurt more; show elapased time
# so we can find the slowpokes.
cmd.append("--gtest_print_time")
+ # Built-in test launcher for gtest-based executables runs tests using
+ # multiple process by default. Force the single-process mode back.
+ cmd.append("--single-process-tests")
if self._options.gtest_repeat:
cmd.append("--gtest_repeat=%s" % self._options.gtest_repeat)
if self._options.gtest_shuffle:
diff --git a/tools/valgrind/drmemory/suppressions.txt b/tools/valgrind/drmemory/suppressions.txt
index 467c94b64c..7678adb3b2 100644
--- a/tools/valgrind/drmemory/suppressions.txt
+++ b/tools/valgrind/drmemory/suppressions.txt
@@ -303,23 +303,6 @@ name=http://crbug.com/106522
npapi_test_plugin.dll!NPAPIClient::PluginTest::id
npapi_test_plugin.dll!NPAPIClient::ExecuteGetJavascriptUrlTest::TimerProc
-# Bad GDI teardown sequence.
-GDI USAGE ERROR
-name=http://crbug.com/109963 a
-system call NtGdiDeleteObjectApp
-# usually one or two GDI32.dll frames here but sometimes in light mode
-# there are zero. still pretty narrow b/c of frames on either side.
-...
-*!skia::BitmapPlatformDevice::BitmapPlatformDeviceData::~BitmapPlatformDeviceData
-
-GDI USAGE ERROR
-name=http://crbug.com/109963 b
-system call NtGdiDeleteObjectApp
-# usually one or two GDI32.dll frames here but sometimes in light mode
-# there are zero. still pretty narrow b/c of frames on either side.
-...
-*!skia::BitmapPlatformDevice::BitmapPlatformDeviceData::ReleaseBitmapDC
-
GDI USAGE ERROR
name=http://crbug.com/109963 c
system call NtGdiDeleteObjectApp
diff --git a/tools/valgrind/drmemory/suppressions_full.txt b/tools/valgrind/drmemory/suppressions_full.txt
index 4f22200890..76a1dfb4ce 100644
--- a/tools/valgrind/drmemory/suppressions_full.txt
+++ b/tools/valgrind/drmemory/suppressions_full.txt
@@ -27,9 +27,12 @@ name=GoogleDesktop LEAK
GoogleDesktopNetwork3.DLL!DllUnregisterServer
# They deliberately use uninit local var in sqlite random generator
+# random byte may mess up the call stack between randomByte and
+# sqlite3_randomness
UNINITIALIZED READ
name=sqlite3_randomness UNINIT
*!randomByte
+...
*!sqlite3_randomness
# Intentional leak in WebKit Template Framework for ThreadData.
diff --git a/tools/valgrind/gtest_exclude/content_unittests.gtest_mac.txt b/tools/valgrind/gtest_exclude/content_unittests.gtest_mac.txt
index 0f5039dd34..f8f29260fd 100644
--- a/tools/valgrind/gtest_exclude/content_unittests.gtest_mac.txt
+++ b/tools/valgrind/gtest_exclude/content_unittests.gtest_mac.txt
@@ -48,9 +48,6 @@ WebRTCAudioDeviceTest.WebRtcRecordingSetupTime
FontSerializationTest.StyledFonts
MacSandboxTest.FontLoadingTest
-# http://crbug.com/280495
-SyntheticGestureControllerTest.Tick
-
# http://crbug.com/280583
DesktopCaptureDeviceTest.ScreenResolutionChangeVariableResolution
DesktopCaptureDeviceTest.Capture
diff --git a/tools/valgrind/gtest_exclude/net_unittests.gtest-drmemory_win32.txt b/tools/valgrind/gtest_exclude/net_unittests.gtest-drmemory_win32.txt
index f0d641122c..f59f4d2acd 100644
--- a/tools/valgrind/gtest_exclude/net_unittests.gtest-drmemory_win32.txt
+++ b/tools/valgrind/gtest_exclude/net_unittests.gtest-drmemory_win32.txt
@@ -22,3 +22,6 @@ ClientSocketPoolBaseTest.DisableCleanupTimer
# Flaky, see http://crbug.com/108422
SSLClientSocketTest.ConnectMismatched
+
+# See http://crbug.com/327695
+ProxyScriptFetcherImplTest.NoCache
diff --git a/tools/valgrind/gtest_exclude/unit_tests.gtest_linux.txt b/tools/valgrind/gtest_exclude/unit_tests.gtest_linux.txt
index c6141b3b01..2c6334050b 100644
--- a/tools/valgrind/gtest_exclude/unit_tests.gtest_linux.txt
+++ b/tools/valgrind/gtest_exclude/unit_tests.gtest_linux.txt
@@ -29,6 +29,3 @@ HttpPipeliningCompatibilityClientTest.*
# http://crbug.com/238964
CpuInfoProviderTest.*
-
-# Fails flakily. http://crbug.com/255771
-NetworkStatsTestUDP.UDPEcho*
diff --git a/tools/valgrind/memcheck/suppressions.txt b/tools/valgrind/memcheck/suppressions.txt
index 4866fbc9c2..ff4c2eb8ca 100644
--- a/tools/valgrind/memcheck/suppressions.txt
+++ b/tools/valgrind/memcheck/suppressions.txt
@@ -2479,13 +2479,6 @@
fun:_ZN3net13CookieMonster17CookieMonsterTask14InvokeCallbackEN4base8CallbackIFvvEEE
}
{
- bug_96365
- Memcheck:Leak
- fun:_Znw*
- fun:_ZN27PasswordManagerDelegateImpl33AddSavePasswordInfoBarIfPermittedEP19PasswordFormManager
- fun:_ZN15PasswordManager23OnPasswordFormsRenderedERKSt6vectorIN6webkit5forms12PasswordFormESaIS3_EE
-}
-{
bug_96369
Memcheck:Leak
fun:malloc
@@ -4219,15 +4212,6 @@
fun:_ZN7content18ResourceDispatcher17OnRequestCompleteEiRKN3net16URLRequestStatusERKSsRKN4base9TimeTicksE
}
{
- bug_145698
- Memcheck:Leak
- fun:_Znw*
- fun:_ZN6chrome24ShowSessionCrashedPromptEP7Browser
- fun:_ZN25StartupBrowserCreatorImpl22AddInfoBarsIfNecessaryEP7BrowserN6chrome7startup16IsProcessStartupE
- fun:_ZN25StartupBrowserCreatorImpl17ProcessLaunchURLsEbRKSt6vectorI4GURLSaIS1_EE
- fun:_ZN25StartupBrowserCreatorImpl6LaunchEP7ProfileRKSt6vectorI4GURLSaIS3_EEb
-}
-{
bug_145699
Memcheck:Leak
fun:_Znw*
@@ -4295,16 +4279,6 @@
fun:_ZN10extensions13ExtensionHost9OnRequestERK31ExtensionHostMsg_Request_Params
}
{
- bug_145733
- Memcheck:Leak
- fun:_Znw*
- fun:_ZN25ExtensionInstallUIDefault35GetNewThemeInstalledInfoBarDelegateEP11TabContentsPKN10extensions9ExtensionERKSsb
- fun:_ZN25ExtensionInstallUIDefault16ShowThemeInfoBarERKSsbPKN10extensions9ExtensionEP7Profile
- fun:_ZN25ExtensionInstallUIDefault16OnInstallSuccessEPKN10extensions9ExtensionEP8SkBitmap
- fun:_ZN22ExtensionInstallPrompt16OnInstallSuccessEPKN10extensions9ExtensionEP8SkBitmap
- fun:_ZN10extensions12CrxInstaller25ReportSuccessFromUIThreadEv
-}
-{
bug_145735
Memcheck:Leak
fun:_Znw*
@@ -4315,24 +4289,6 @@
fun:_ZN4base5files12_GLOBAL__N_121InotifyReaderCallbackEPNS1_13InotifyReaderEii
}
{
- bug_145747a
- Memcheck:Leak
- fun:_Znw*
- fun:_ZN24TranslateInfoBarDelegate19CreateErrorDelegateEN15TranslateErrors4TypeEP14InfoBarServiceP11PrefServiceRKSsS7_
- fun:_ZN16TranslateManager14PageTranslatedEPN7content11WebContentsEP21PageTranslatedDetails
- fun:_ZN16TranslateManager7ObserveEiRKN7content18NotificationSourceERKNS0_19NotificationDetailsE
- fun:_ZN23NotificationServiceImpl6NotifyEiRKN7content18NotificationSourceERKNS0_19NotificationDetailsE
- fun:_ZN18TranslateTabHelper16OnPageTranslatedEiRKSsS1_N15TranslateErrors4TypeE
-}
-{
- bug_145747b
- Memcheck:Leak
- fun:_Znw*
- fun:_ZN24TranslateInfoBarDelegate19CreateErrorDelegateEN15TranslateErrors4TypeEP14InfoBarServiceP11PrefServiceRKSsS7_
- fun:_ZN16TranslateManager18OnURLFetchCompleteEPKN3net10URLFetcherE
- fun:_ZN20TranslateManagerTest31SimulateTranslateScriptURLFetchEb
-}
-{
bug_146464
Memcheck:Leak
fun:realloc
diff --git a/tools/valgrind/tsan/ignores.txt b/tools/valgrind/tsan/ignores.txt
index 39d43244a3..6d3f73de63 100644
--- a/tools/valgrind/tsan/ignores.txt
+++ b/tools/valgrind/tsan/ignores.txt
@@ -186,3 +186,4 @@ fun:arena_thread_freeres
# __strncasecmp_l_ssse3 overreads the buffer causing TSan to report a data race
# on another object. See http://crbug.com/177074
fun:*strncasecmp*
+fun:*strcasecmp*
diff --git a/tools/valgrind/tsan/suppressions_win32.txt b/tools/valgrind/tsan/suppressions_win32.txt
index 8c59febc77..57f5676f2b 100644
--- a/tools/valgrind/tsan/suppressions_win32.txt
+++ b/tools/valgrind/tsan/suppressions_win32.txt
@@ -273,3 +273,10 @@
fun:CoCreateInstance
fun:base::win::ScopedComPtr::CreateInstance
}
+{
+ bug_326282
+ ThreadSanitizer:Race
+ ...
+ fun:file_util::OpenFile
+ fun:base::ReadFileToString
+}
diff --git a/tools/valgrind/tsan_v2/suppressions.txt b/tools/valgrind/tsan_v2/suppressions.txt
index 6cf08f765d..2cac4ee25e 100644
--- a/tools/valgrind/tsan_v2/suppressions.txt
+++ b/tools/valgrind/tsan_v2/suppressions.txt
@@ -134,3 +134,6 @@ race:net::ProxyResolverV8Tracing::Job::~Job
# http://crbug.com/313726
race:CallbackWasCalled
+
+# http://crbug.com/327722
+race:tracked_objects::ThreadData::Now
diff --git a/tools/win/toolchain/get_toolchain_if_necessary.py b/tools/win/toolchain/get_toolchain_if_necessary.py
index e93e6f029f..da21f95983 100644
--- a/tools/win/toolchain/get_toolchain_if_necessary.py
+++ b/tools/win/toolchain/get_toolchain_if_necessary.py
@@ -2,7 +2,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import ctypes.wintypes
import hashlib
+import json
import os
import subprocess
import sys
@@ -11,23 +13,83 @@ import sys
BASEDIR = os.path.dirname(os.path.abspath(__file__))
+GetFileAttributes = ctypes.windll.kernel32.GetFileAttributesW
+GetFileAttributes.argtypes = (ctypes.wintypes.LPWSTR,)
+GetFileAttributes.restype = ctypes.wintypes.DWORD
+FILE_ATTRIBUTE_HIDDEN = 0x2
+FILE_ATTRIBUTE_SYSTEM = 0x4
+
+
+def IsHidden(file_path):
+ """Returns whether the given |file_path| has the 'system' or 'hidden'
+ attribute set."""
+ p = GetFileAttributes(file_path)
+ assert p != 0xffffffff
+ return bool(p & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM))
+
+
+def GetFileList(root):
+ """Gets a normalized list of files under |root|."""
+ assert not os.path.isabs(root)
+ assert os.path.normpath(root) == root
+ file_list = []
+ for base, _, files in os.walk(root):
+ paths = [os.path.join(base, f) for f in files]
+ file_list.extend(x.lower() for x in paths if not IsHidden(x))
+ return sorted(file_list)
+
+
+def MakeTimestampsFileName(root):
+ return os.path.join(root, '..', '.timestamps')
+
+
def CalculateHash(root):
"""Calculates the sha1 of the paths to all files in the given |root| and the
contents of those files, and returns as a hex string."""
- assert not os.path.isabs(root)
- assert os.path.normpath(root) == root
+ file_list = GetFileList(root)
+
+ # Check whether we previously saved timestamps in $root/../.timestamps. If
+ # we didn't, or they don't match, then do the full calculation, otherwise
+ # return the saved value.
+ timestamps_file = MakeTimestampsFileName(root)
+ timestamps_data = {'files': [], 'sha1': ''}
+ if os.path.exists(timestamps_file):
+ with open(timestamps_file, 'rb') as f:
+ try:
+ timestamps_data = json.load(f)
+ except ValueError:
+ # json couldn't be loaded, empty data will force a re-hash.
+ pass
+
+ matches = len(file_list) == len(timestamps_data['files'])
+ if matches:
+ for disk, cached in zip(file_list, timestamps_data['files']):
+ if disk != cached[0] or os.stat(disk).st_mtime != cached[1]:
+ matches = False
+ break
+ if matches:
+ return timestamps_data['sha1']
+
digest = hashlib.sha1()
- count = 0
- for root, dirs, files in os.walk(root):
- dirs.sort()
- for name in sorted(f.lower() for f in files):
- path = os.path.join(root, name)
- digest.update(path.lower())
- with open(path, 'rb') as f:
- digest.update(f.read())
+ for path in file_list:
+ digest.update(path)
+ with open(path, 'rb') as f:
+ digest.update(f.read())
return digest.hexdigest()
+def SaveTimestampsAndHash(root, sha1):
+ """Save timestamps and the final hash to be able to early-out more quickly
+ next time."""
+ file_list = GetFileList(root)
+ timestamps_data = {
+ 'files': [[f, os.stat(f).st_mtime] for f in file_list],
+ 'sha1': sha1,
+ }
+ with open(MakeTimestampsFileName(root), 'wb') as f:
+ json.dump(timestamps_data, f)
+
+
def main():
if sys.platform not in ('win32', 'cygwin'):
return 0
@@ -48,9 +110,9 @@ def main():
desired_hash = f.read().strip()
# If the current hash doesn't match what we want in the file, nuke and pave.
- # Note that this script is only run when a .sha1 file is updated (per DEPS)
- # so this relatively expensive step of hashing everything only happens when
- # the toolchain is updated.
+ # Typically this script is only run when the .sha1 one file is updated, but
+ # directly calling "gclient runhooks" will also run it, so we cache
+ # based on timestamps to make that case fast.
current_hash = CalculateHash(target_dir)
if current_hash != desired_hash:
print 'Windows toolchain out of date or doesn\'t exist, updating...'
@@ -60,14 +122,15 @@ def main():
sys.executable,
'src\\tools\\win\\toolchain\\toolchain2013.py',
'--targetdir', target_dir])
+ current_hash = CalculateHash(target_dir)
+ if current_hash != desired_hash:
+ print >> sys.stderr, (
+ 'Got wrong hash after pulling a new toolchain. '
+ 'Wanted \'%s\', got \'%s\'.' % (
+ desired_hash, current_hash))
+ return 1
+ SaveTimestampsAndHash(target_dir, current_hash)
- current_hash = CalculateHash(target_dir)
- if current_hash != desired_hash:
- print >> sys.stderr, (
- 'Got wrong hash after pulling a new toolchain. '
- 'Wanted \'%s\', got \'%s\'.' % (
- desired_hash, current_hash))
- return 1
return 0