Merge "perfetto: add CTS tests and make integration tests run on CTS"
diff --git a/.travis.yml b/.travis.yml
index b96bf0c..b05c966 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -98,19 +98,9 @@
       env: CFG=android-clang-arm-release GN_ARGS="is_debug=false target_os=\"android\" target_cpu=\"arm\""
     - os: linux
       dist: trusty
-      sudo: false
-      compiler: clang
-      env: CFG=android-clang-arm64-release GN_ARGS="is_debug=false target_os=\"android\" target_cpu=\"arm64\""
-    - os: linux
-      dist: trusty
       sudo: true
       compiler: clang
       env: CFG=android-clang-arm-asan GN_ARGS="is_debug=false target_os=\"android\" target_cpu=\"arm\" is_asan=true"
-    - os: linux
-      dist: trusty
-      sudo: true
-      compiler: clang
-      env: CFG=android-clang-arm64-asan GN_ARGS="is_debug=false target_os=\"android\" target_cpu=\"arm64\" is_asan=true"
 
 # Cache the deps that get git-pulled to avoid hitting quota limits (b/68252114).
 # Do not cache NDK/SDK and things that come from .zip files rather than git.
@@ -119,6 +109,7 @@
   timeout: 1800  # 30 mins
   directories:
     - buildtools/benchmark
+    - buildtools/emulator
     - buildtools/googletest
     - buildtools/libcxx
     - buildtools/libcxxabi
@@ -135,8 +126,7 @@
     if [[ "$CFG" != android-* ]]; then
       tools/install-build-deps --no-android
     else
-      ARCH=$(echo "$CFG" | cut -d- -f3)
-      tools/install-build-deps --emulator="$ARCH"
+      tools/install-build-deps
     fi
   - |
     if [[ -e buildtools/clang/bin/llvm-symbolizer ]]; then
@@ -148,6 +138,7 @@
   - tools/ninja -C out/dist -j8 all
   - |
     TEST_TARGETS="
+    perfetto_integrationtests
     perfetto_unittests
     perfetto_benchmarks
     "
@@ -157,7 +148,7 @@
       done
     else
       TARGET_ARCH=$(echo $CFG | cut -d- -f3)
-      tools/run_android_emulator $TARGET_ARCH --pid /tmp/emulator.pid -v &
+      tools/run_android_emulator --pid /tmp/emulator.pid -v &
       for TEST_TARGET in $TEST_TARGETS; do
         tools/run_android_test out/dist "$TEST_TARGET"
       done
diff --git a/buildtools/.gitignore b/buildtools/.gitignore
index 04e4d62..ca745de 100644
--- a/buildtools/.gitignore
+++ b/buildtools/.gitignore
@@ -1,8 +1,9 @@
 android_sdk/
+aosp-*/
 benchmark/
 clang/
 clang_format/
-emulator_images/
+emulator/
 googletest/
 libcxx/
 libcxxabi/
diff --git a/infra/perfetto-ci.appspot.com/static/index.html b/infra/perfetto-ci.appspot.com/static/index.html
index 934f9a9..f8231ca 100644
--- a/infra/perfetto-ci.appspot.com/static/index.html
+++ b/infra/perfetto-ci.appspot.com/static/index.html
@@ -64,26 +64,18 @@
           <td rowspan="4">Status</td>
           <td rowspan="4">Owner</td>
           <td rowspan="4">Updated</td>
-          <td colspan="16">Bots</td>
+          <td colspan="9">Bots</td>
         </tr>
         <tr>
-          <td colspan="5">mac</td>
           <td colspan="7">linux</td>
-          <td colspan="4">android</td>
+          <td colspan="2">android</td>
         </tr>
         <tr>
-          <td colspan="5">clang</td>
           <td>gcc7</td>
           <td colspan="6">clang</td>
           <td colspan="2">clang-arm</td>
-          <td colspan="2">clang-arm64</td>
         </tr>
         <tr id="cls_header">
-          <td id="mac-clang-x86_64-debug">dbg</td>
-          <td id="mac-clang-x86_64-release">rel</td>
-          <td id="mac-clang-x86_64-asan">asan</td>
-          <td id="mac-clang-x86_64-tsan">tsan</td>
-          <td id="mac-clang-x86_64-ubsan">ubsan</td>
           <td id="linux_trusty-gcc7-x86_64-release">rel</td>
           <td id="linux_trusty-clang-x86_64-debug">dbg</td>
           <td id="linux_trusty-clang-x86_64-asan">asan</td>
@@ -93,8 +85,6 @@
           <td id="linux_trusty-clang-x86_64-ubsan">ubsan</td>
           <td id="android-clang-arm-release">rel</td>
           <td id="android-clang-arm-asan">asan</td>
-          <td id="android-clang-arm64-release">rel</td>
-          <td id="android-clang-arm64-asan">asan</td>
         </tr>
       </thead>
       <tbody id="cls">
diff --git a/src/ftrace_reader/ftrace_procfs_integrationtest.cc b/src/ftrace_reader/ftrace_procfs_integrationtest.cc
index 655f77e..19690d8 100644
--- a/src/ftrace_reader/ftrace_procfs_integrationtest.cc
+++ b/src/ftrace_reader/ftrace_procfs_integrationtest.cc
@@ -128,7 +128,7 @@
   EXPECT_TRUE(ftrace.OpenPipeForCpu(0));
 }
 
-TEST(FtraceProcfsIntegrationTest, CanSetBufferSize) {
+TEST(FtraceProcfsIntegrationTest, DISABLED_CanSetBufferSize) {
   FtraceProcfs ftrace(kTracingPath);
   EXPECT_TRUE(ftrace.SetCpuBufferSizeInPages(4ul));
   EXPECT_EQ(ReadFile("buffer_size_kb"), "16\n");  // (4096 * 4) / 1024
diff --git a/src/ftrace_reader/test/explorer.html b/src/ftrace_reader/test/explorer.html
index f8ffc4f..4d15600 100755
--- a/src/ftrace_reader/test/explorer.html
+++ b/src/ftrace_reader/test/explorer.html
@@ -113,8 +113,8 @@
 }
 
 </style>
-<script src="https://unpkg.com/mithril"></script>
-<script src="http://unpkg.com/diff"></script>
+<script src="//unpkg.com/mithril"></script>
+<script src="//unpkg.com/diff"></script>
 
 <div id="content"></div>
 
@@ -124,13 +124,14 @@
 
 let THIS_URL = window.location.href;
 let gDirectoryToFormatFiles;
-let gNamesToRecords;
+let gNamesToRecords = new Map();
 let gFilterText = '';
 let gDisplayedRecords = null;
 let gDisplayedName = null;
 let gADevice = null;
 let gBDevice = null;
 let gDevices = []
+let gCache = new Map();
 
 function isdir(url) {
   return url[url.length - 1] == '/';
@@ -144,26 +145,48 @@
   return url.slice(0, url.lastIndexOf('/')+1);
 }
 
-let getdirectories = url => listdir(url).filter(isdir);
-let getfiles = url => listdir(url).filter(isfile);
+let getdirectories = url => listdir(url).then(xs => xs.filter(isdir));
+let getfiles = url => listdir(url).then(xs => xs.filter(isfile));
+
+function fetch(url) {
+  return new Promise(function(resolve, reject) {
+    let xhr = new XMLHttpRequest();
+    xhr.open("GET", url, true);
+    xhr.onload = e => resolve({
+      text: () => Promise.resolve(xhr.responseText),
+    });
+    xhr.onerror = e => reject(xhr.statusText);
+    xhr.send(null);
+  });
+}
 
 function geturl(url) {
-  let request = new XMLHttpRequest();
-  request.open("GET", url, false);
-  request.send(null);
-  let text = request.responseText;
-  return text;
+  console.log('Fetch:', url);
+  if (gCache.has(url)) return Promise.resolve(gCache.get(url));
+  return fetch(url).then(r => r.text()).then(text => {
+    gCache.set(url, text);
+    return text;
+  });
 }
 
 function listdir(url) {
-  let text = geturl(url);
-  let re = new RegExp('<li><a href="(.+)">(.+)</a>', 'g');
-  let match;
-  let matches = [];
-  while (match = re.exec(text)) {
-    matches.push(match[1]);
-  }
-  return matches;
+  return geturl(url).then(text => {
+    let re = new RegExp('<li><a href="(.+)">(.+)</a>', 'g');
+    if (window.location.href.indexOf('x20') != -1)
+      re = new RegExp('[^>]</td>\n<td>\n<a href="(.+)">(.+)</a>', 'g');
+    let match;
+    let matches = [];
+    while (match = re.exec(text)) {
+      matches.push(match[1]);
+    }
+    return matches;
+  });
+}
+
+function getfiletext(url) {
+  if (gCache.has(url)) return gCache.get(url);
+  geturl(url).then(() => m.redraw());
+  return "";
 }
 
 function makeFormatFileRecord(base_url, device, group_name, event_name) {
@@ -175,20 +198,29 @@
 
 function findFormatFilesByDirectory() {
   let url = getdir(THIS_URL) + 'data/';
-  let directories = getdirectories(url);
   let directoryToFormatFiles = new Map();
-  for (let device of directories) {
-    directoryToFormatFiles.set(device, []);
-    for (let group_name of getdirectories(url + device + 'events/')) {
-      let innerUrl = url + device + 'events/' + group_name;
-      let event_names = getdirectories(innerUrl);
-      let records = event_names.map(event_name => makeFormatFileRecord(url, device, group_name, event_name));
-      for (let record of records) {
-        directoryToFormatFiles.get(device).push(record);
-      }
-    }
-  }
-  return directoryToFormatFiles;
+  return getdirectories(url).then(directories => {
+    return Promise.all(directories.map(device => {
+      directoryToFormatFiles.set(device, []);
+      return getdirectories(url + device + 'events/').then(groups => {
+        return Promise.all(groups.map(group_name => {
+          let innerUrl = url + device + 'events/' + group_name;
+          return getdirectories(innerUrl).then(event_names => {
+            event_names.map(event_name => {
+              let record = makeFormatFileRecord(
+                  url,
+                  device,
+                  group_name,
+                  event_name);
+              directoryToFormatFiles.get(device).push(record);
+            });
+          });
+        }));
+      });
+    }));
+  }).then(_ => {
+    return directoryToFormatFiles
+  });
 }
 
 class FormatFileRecord {
@@ -232,8 +264,8 @@
   let r2 = records.filter(r => r.device == gBDevice)[0];
   if (!r1) r1 = records[0];
   if (!r2) r2 = records[0];
-  let f1 = geturl(r1.url);
-  let f2 = geturl(r2.url);
+  let f1 = getfiletext(r1.url);
+  let f2 = getfiletext(r2.url);
   let diff = JsDiff.diffChars(f1, f2);
 
   let es = diff.map(part => {
@@ -260,22 +292,11 @@
   ]);
 }
 
-gDirectoryToFormatFiles = findFormatFilesByDirectory();
-gNamesToRecords = new Map();
-gDevices = Array.from(gDirectoryToFormatFiles.keys());
-for (let records of gDirectoryToFormatFiles.values()) {
-  for (let record of records) {
-    if (gNamesToRecords.get(record.name) == null) {
-      gNamesToRecords.set(record.name, []);
-    }
-    gNamesToRecords.get(record.name).push(record);
-  }
-}
-[gADevice, gBDevice] = gDevices;
-
 let root = document.getElementById('content');
 let App = {
   view: function() {
+    if (!gDirectoryToFormatFiles)
+      return m('.main', 'Loading...');
     return m('.main', [
       contextView(gFilterText, gNamesToRecords),
       focusView(gDisplayedRecords),
@@ -283,6 +304,24 @@
   }
 }
 m.mount(root, App);
+
+findFormatFilesByDirectory().then(data => {
+  gDirectoryToFormatFiles = data;
+  gNamesToRecords = new Map();
+  gDevices = Array.from(gDirectoryToFormatFiles.keys());
+  for (let records of gDirectoryToFormatFiles.values()) {
+    for (let record of records) {
+      geturl(record.url);
+      if (gNamesToRecords.get(record.name) == null) {
+        gNamesToRecords.set(record.name, []);
+      }
+      gNamesToRecords.get(record.name).push(record);
+    }
+  }
+  [gADevice, gBDevice] = gDevices;
+  m.redraw();
+});
+
 </script>
 
 <!--
diff --git a/src/tracing/core/trace_writer_impl.cc b/src/tracing/core/trace_writer_impl.cc
index 07a4bd1..f38128e 100644
--- a/src/tracing/core/trace_writer_impl.cc
+++ b/src/tracing/core/trace_writer_impl.cc
@@ -111,8 +111,6 @@
     cur_chunk_.SetFlag(ChunkHeader::kLastPacketContinuesOnNextChunk);
     WriteRedundantVarInt(partial_size, cur_packet_->size_field());
 
-// TODO(primiano): temporarily disabled due to b/72685438.
-#if 0
     // Descend in the stack of non-finalized nested submessages (if any) and
     // detour their |size_field| into the |patch_list_|. At this point we have
     // to release the chunk and they cannot write anymore into that.
@@ -120,15 +118,26 @@
     for (auto* nested_msg = cur_packet_->nested_message(); nested_msg;
          nested_msg = nested_msg->nested_message()) {
       uint8_t* const cur_hdr = nested_msg->size_field();
-      PERFETTO_DCHECK(cur_hdr >= cur_chunk_.payload_begin() &&
-                      cur_hdr + kMessageLengthFieldSize <= cur_chunk_.end());
+#if PERFETTO_DCHECK_IS_ON()
+      // Ensure that the size field of the nested message either points to
+      // somewhere in the current chunk or a size field of a patch in the
+      // patch list.
+      bool size_in_current_chunk =
+          cur_hdr >= cur_chunk_.payload_begin() &&
+          cur_hdr + kMessageLengthFieldSize <= cur_chunk_.end();
+      if (!size_in_current_chunk) {
+        auto patch_it = std::find_if(
+            patch_list_.begin(), patch_list_.end(),
+            [cur_hdr](const Patch& p) { return p.size_field == cur_hdr; });
+        PERFETTO_DCHECK(patch_it != patch_list_.end());
+      }
+#endif
       auto cur_hdr_offset = static_cast<uint16_t>(cur_hdr - cur_chunk_.begin());
       patch_list_.emplace_front(cur_chunk_id_, cur_hdr_offset);
       Patch& patch = patch_list_.front();
       nested_msg->set_size_field(patch.size_field);
       PERFETTO_DLOG("Created new patchlist entry for protobuf nested message");
     }
-#endif
   }
 
   if (cur_chunk_.is_valid())
diff --git a/tools/android_emulators/arm.ini b/tools/android_emulators/arm.ini
deleted file mode 100644
index ee724ae..0000000
--- a/tools/android_emulators/arm.ini
+++ /dev/null
@@ -1,21 +0,0 @@
-avd.ini.encoding=UTF-8
-abi.type=armeabi-v7a
-disk.dataPartition.size=64M
-hw.accelerometer=no
-hw.audioInput=no
-hw.battery=no
-hw.camera.back=none
-hw.camera.front=none
-hw.cpu.arch=arm
-hw.dPad=no
-hw.gps=no
-hw.keyboard=yes
-hw.lcd.density=480
-hw.mainKeys=no
-hw.ramSize=512
-hw.sdCard=no
-hw.sensors.orientation=yes
-hw.sensors.proximity=yes
-hw.trackBall=no
-image.sysdir.1=system-images/android-24/default/armeabi-v7a/
-vm.heapSize=64
diff --git a/tools/android_emulators/arm64.ini b/tools/android_emulators/arm64.ini
deleted file mode 100644
index c1d56f8..0000000
--- a/tools/android_emulators/arm64.ini
+++ /dev/null
@@ -1,21 +0,0 @@
-avd.ini.encoding=UTF-8
-abi.type=arm64-v8a
-disk.dataPartition.size=64M
-hw.accelerometer=no
-hw.audioInput=no
-hw.battery=no
-hw.camera.back=none
-hw.camera.front=none
-hw.cpu.arch=arm64
-hw.dPad=no
-hw.gps=no
-hw.keyboard=yes
-hw.lcd.density=480
-hw.mainKeys=no
-hw.ramSize=512
-hw.sdCard=no
-hw.sensors.orientation=yes
-hw.sensors.proximity=yes
-hw.trackBall=no
-image.sysdir.1=system-images/android-24/default/arm64-v8a/
-vm.heapSize=64
diff --git a/tools/install-build-deps b/tools/install-build-deps
index 30e9b3c..f69dc12 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -152,15 +152,10 @@
 
 # Dependencies required to run Android tests.
 TEST_DEPS_ANDROID = [
-  # tools.zip contains the emulator binaries.
-  ('buildtools/android_sdk/tools.zip',
-   'https://dl.google.com/android/repository/tools_r25.2.5-macosx.zip',
-   'd2168d963ac5b616e3d3ddaf21511d084baf3659',
-   'darwin'
-  ),
-  ('buildtools/android_sdk/tools.zip',
-   'https://dl.google.com/android/repository/tools_r25.2.5-linux.zip',
-   '72df3aa1988c0a9003ccdfd7a13a7b8bd0f47fc1',
+  # Android emulator images.
+  ('buildtools/aosp-arm.zip',
+   'https://storage.googleapis.com/perfetto/aosp-02022018-arm.zip',
+   'a480d5e7d3ca888b0a58fe15ce76b1791537429a',
    'linux2'
   ),
 
@@ -176,16 +171,11 @@
    'linux2'
   ),
 
-  # Android emulator images.
-  ('buildtools/android_sdk/system-images/android-24/default/armeabi-v7a.zip',
-   'https://dl.google.com/android/repository/sys-img/android/armeabi-v7a-24_r07.zip',
-   '3454546b4eed2d6c3dd06d47757d6da9f4176033',
-   'android-arm'
-  ),
-  ('buildtools/android_sdk/system-images/android-24/default/arm64-v8a.zip',
-   'https://dl.google.com/android/repository/sys-img/android/arm64-v8a-24_r07.zip',
-   'e8ab2e49e4efe4b064232b33b5eeaded61437d7f',
-   'android-arm64'
+  # Android emulator binaries.
+  ('buildtools/emulator',
+   'https://android.googlesource.com/platform/prebuilts/android-emulator.git',
+   '4b260028dc27bc92c39bee9129cb2ba839970956',
+   'all'
   ),
 ]
 
@@ -235,7 +225,9 @@
     shutil.rmtree(path)
   MkdirRecursive(path)
   logging.info('Fetching %s @ %s into %s', git_url, revision, path)
-  subprocess.check_call(['git', 'clone', git_url, path], cwd=path)
+  subprocess.check_call(['git', 'init', path], cwd=path)
+  subprocess.check_call(
+    ['git', 'fetch', '--depth', '1', git_url, revision], cwd=path)
   subprocess.check_call(['git', 'checkout', revision, '--quiet'], cwd=path)
   assert(IsGitRepoCheckoutOutAtRevision(path, revision))
 
@@ -243,15 +235,12 @@
 def Main():
   parser = argparse.ArgumentParser()
   parser.add_argument('--no-android', action='store_true')
-  parser.add_argument('--emulator', action='append', help='arm|arm64')
   args = parser.parse_args()
-  andriod_emulator_images = ['android-' + arch for arch in args.emulator or []]
   deps = BUILD_DEPS_HOST
   if not args.no_android:
     deps += BUILD_DEPS_ANDROID + TEST_DEPS_ANDROID
   for rel_path, url, expected_sha1, platform in deps:
-    if (platform != 'all' and platform != sys.platform and
-        platform not in andriod_emulator_images):
+    if (platform != 'all' and platform != sys.platform):
       continue
     local_path = os.path.join(ROOT_DIR, rel_path)
     if url.endswith('.git'):
diff --git a/tools/run_android_emulator b/tools/run_android_emulator
index 6a923c7..4f6d151 100755
--- a/tools/run_android_emulator
+++ b/tools/run_android_emulator
@@ -23,38 +23,28 @@
   parser = argparse.ArgumentParser()
   parser.add_argument('--verbose', '-v', action='store_true')
   parser.add_argument('--pid', help='(optional) save pid into given file')
-  parser.add_argument('image', help='arm|arm64 (see //tools/android_emulators)')
   args = parser.parse_args()
 
   root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-  ini = os.path.join(root_dir, 'tools', 'android_emulators', args.image + '.ini')
-  assert os.path.exists(ini), 'File not found: ' + ini
+  emulator_root = os.path.join(
+    root_dir, 'buildtools', 'emulator', 'linux-x86_64')
+  emulator_path = os.path.join(emulator_root, 'qemu', 'linux-x86_64')
+  aosp_path = os.path.join(root_dir, 'buildtools', 'aosp-arm')
 
-  avd_dir = os.path.join(root_dir, 'buildtools', 'emulator_images')
-  if not os.path.exists(avd_dir):
-    os.makedirs(avd_dir)
-
-  emu_img_dir = os.path.join(avd_dir, args.image + '.avd')
-  if not os.path.exists(emu_img_dir):
-    os.makedirs(emu_img_dir)
-  shutil.copyfile(ini, os.path.join(emu_img_dir, 'config.ini'))
-
-  with open(os.path.join(avd_dir, args.image + '.ini'), 'w') as f:
-    f.write('path=' + emu_img_dir)
-
-  sdk_dir = os.path.join(root_dir, 'buildtools', 'android_sdk')
   env = {
       # Travis CI doesn't set this and causes the emulator to fallback in
       # 32-bit mode with a "Cannot decide host bitness because $SHELL" error.
       'SHELL': '/bin/bash',
-      'ANDROID_EMULATOR_DEBUG': '1' if args.verbose else '0',
-      'ANDROID_SDK_ROOT': sdk_dir,
-      'ANDROID_AVD_HOME': avd_dir,
-      'DYLD_LIBRARY_PATH': os.path.join(sdk_dir, 'tools', 'lib64', 'qt', 'lib'),
+      'LD_LIBRARY_PATH': os.path.join(emulator_root, 'lib64', 'qt', 'lib'),
   }
-  emulator_bin = os.path.join(sdk_dir, 'tools', 'emulator')
-  emulator_args = ['-no-window', '-no-snapshot',  '-gpu', 'off', '-wipe-data',
-                   '-avd', args.image]
+  emulator_bin = os.path.join(emulator_path, 'qemu-system-armel')
+  emulator_args = ['-no-window', '-no-snapshot',  '-gpu', 'off', '-no-accel',
+                   '-sysdir', aosp_path,
+                   '-system', os.path.join(aosp_path, 'system-qemu.img'),
+                   '-kernel', os.path.join(aosp_path, 'kernel-ranchu'),
+                   '-ramdisk', os.path.join(aosp_path, 'ramdisk.img'),
+                   '-vendor', os.path.join(aosp_path, 'vendor-qemu.img'),
+                   '-data', os.path.join(aosp_path, 'userdata-qemu.img')]
   print '\n'.join('='.join(x) for x in env.items())
   print ' '.join([emulator_bin] + emulator_args)
   if args.pid:
diff --git a/tools/run_android_test b/tools/run_android_test
index b34408e..37c1a36 100755
--- a/tools/run_android_test
+++ b/tools/run_android_test
@@ -124,7 +124,7 @@
 
   print 'Waiting for device ...'
   AdbCall('wait-for-device')
-  WaitForBootCompletion()
+  # WaitForBootCompletion()
   AdbCall('root')
   AdbCall('wait-for-device')