Add support for llvm-based sanitizers.

Adds initial support for Asan/Lsan/Msan/Tsan for unbundled builds.
This CL also introduced a build_all_configs.py script to ease
local debugging, allowing to build several build configs in one go
and makes some minor improvements to the unbundled build files.

Change-Id: I7c374ffc00c55907e0e8b291b2e7e6140697c290
diff --git a/BUILD.gn b/BUILD.gn
index 1eee5b3..664f5d3 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -25,5 +25,6 @@
   testonly = true
   deps = [
     "//libtracing:libtracing_unittests",
+    "//libtracing:sanitizers_unittests",
   ]
 }
diff --git a/build/BUILD.gn b/build/BUILD.gn
index 8b3470f..6e0136f 100644
--- a/build/BUILD.gn
+++ b/build/BUILD.gn
@@ -13,9 +13,15 @@
 # limitations under the License.
 
 import("//build/android.gni")
+import("//build/c++.gni")
+import("//build/mac.gni")
+import("//build/sanitizers/sanitizers.gni")
 
 config("extra_warnings") {
-  cflags = [ "-Wextra" ]
+  cflags = [
+    "-Wextra",
+    "-Wno-missing-field-initializers",
+  ]
   if (is_clang) {
     cflags += [
       "-Weverything",
@@ -30,6 +36,14 @@
   }
 }
 
+config("no_exceptions") {
+  cflags = [ "-fno-exceptions" ]
+}
+
+config("no_rtti") {
+  cflags = [ "-fno-rtti" ]
+}
+
 config("default") {
   asmflags = []
   cflags = []
@@ -44,8 +58,6 @@
   cflags_cc += [ "-std=c++11" ]
 
   cflags += [
-    "-fno-exceptions",
-    "-fno-rtti",
     "-fstrict-aliasing",
     "-fstack-protector",
     "-fPIC",
@@ -76,6 +88,10 @@
   }
 
   if (is_android) {
+    libs += [ "log" ]
+  }
+
+  if (is_android) {
     asmflags += [ "--target=$android_abi_target" ]
     cflags += [
       "-isystem$android_ndk_root/$android_sysroot_subdir/usr/include",
@@ -125,7 +141,21 @@
       "-funwind-tables",
     ]
   } else {
-    cflags = [ "-g" ]
+    cflags = [ "-g2" ]
+  }
+}
+
+config("runtime_library") {
+  if (use_custom_libcxx) {
+    defines = [
+      "_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+      "_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ]
+    cflags_cc = [
+      "-nostdinc++",
+      "-isystem" + rebase_path("$libcxx_prefix/include", root_build_dir),
+      "-isystem" + rebase_path("$libcxxabi_prefix/include", root_build_dir),
+    ]
   }
 }
 
@@ -143,8 +173,24 @@
   defines = [ "NDEBUG" ]
 }
 
-config("executable") {
+group("exe_and_shlib_deps") {
+  public_deps = []
+  if (using_sanitizer) {
+    public_deps += [ "//build/sanitizers:deps" ]
+  }
+  if (use_custom_libcxx) {
+    public_deps += [ "//buildtools:libc++" ]
+  }
+}
+
+config("shared_library") {
   if (is_android || is_linux) {
+    ldflags = [ "-fPIC" ]
+  }
+}
+
+config("executable") {
+  if (is_android) {
     asmflags = [ "-fPIE" ]
     cflags = [ "-fPIE" ]
     ldflags = [
diff --git a/build/BUILDCONFIG.gn b/build/BUILDCONFIG.gn
index 9333196..1551cc6 100644
--- a/build/BUILDCONFIG.gn
+++ b/build/BUILDCONFIG.gn
@@ -15,10 +15,17 @@
 declare_args() {
   is_debug = true
   is_clang = true
+}
 
+declare_args() {
   ar = "ar"
-  cc = "gcc"
-  cxx = "g++"
+  if (is_clang) {
+    cc = "clang"
+    cxx = "clang++"
+  } else {
+    cc = "gcc"
+    cxx = "g++"
+  }
 }
 
 # Platform detection
@@ -44,9 +51,13 @@
 }
 
 default_configs = [
-  "//build:default",
   "//build:debug_symbols",
+  "//build:default",
   "//build:extra_warnings",
+  "//build:no_exceptions",
+  "//build:no_rtti",
+  "//build:runtime_library",
+  "//build/sanitizers:sanitizers_cflags",
 ]
 
 if (!is_debug) {
@@ -55,19 +66,28 @@
 
 set_defaults("source_set") {
   configs = default_configs
+  deps = []
 }
 
 set_defaults("static_library") {
   configs = default_configs
+  deps = []
 }
 
 set_defaults("shared_library") {
   configs = default_configs
+  configs += [ "//build:shared_library" ]
+  deps = [
+    "//build:exe_and_shlib_deps",
+  ]
 }
 
 set_defaults("executable") {
   configs = default_configs
   configs += [ "//build:executable" ]
+  deps = [
+    "//build:exe_and_shlib_deps",
+  ]
 }
 
 set_default_toolchain("//build/toolchain:gcc_like")
diff --git a/build/android.gni b/build/android.gni
index 56c3255..ef620ee 100644
--- a/build/android.gni
+++ b/build/android.gni
@@ -27,26 +27,34 @@
 }
 
 declare_args() {
+  android_llvm_dir = "$android_ndk_root/toolchains/llvm/prebuilt/$android_host"
+  android_clangrt_dir = "$android_llvm_dir/lib64/clang/5.0/lib/linux"
+
   if (current_cpu == "x86") {
     android_abi_target = "i686-linux-androideabi"
     android_sysroot_subdir = "platforms/android-${android_api_level}/arch-x86"
     android_prebuilt_arch = "android-x86"
     android_toolchain_root = "$android_ndk_root/toolchains/x86-${_android_toolchain_version}/prebuilt/$android_host"
+    android_llvm_arch = "i686"
   } else if (current_cpu == "arm") {
     android_abi_target = "arm-linux-androideabi"
     android_sysroot_subdir = "platforms/android-${android_api_level}/arch-arm"
     android_prebuilt_arch = "android-arm"
     android_toolchain_root = "$android_ndk_root/toolchains/arm-linux-androideabi-${_android_toolchain_version}/prebuilt/$android_host"
+    android_llvm_arch = "arm"
   } else if (current_cpu == "x64") {
     android_abi_target = "x86_64-linux-androideabi"
-    android_sysroot_subdir = "platforms/android-${android_api_level}/arch-x86_64"
+    android_sysroot_subdir =
+        "platforms/android-${android_api_level}/arch-x86_64"
     android_prebuilt_arch = "android-x86_64"
     android_toolchain_root = "$android_ndk_root/toolchains/x86_64-${_android_toolchain_version}/prebuilt/$android_host"
+    android_llvm_arch = "x86_64"
   } else if (current_cpu == "arm64") {
     android_abi_target = "aarch64-linux-android"
     android_sysroot_subdir = "platforms/android-${android_api_level}/arch-arm64"
     android_prebuilt_arch = "android-arm64"
     android_toolchain_root = "$android_ndk_root/toolchains/aarch64-linux-android-${_android_toolchain_version}/prebuilt/$android_host"
+    android_llvm_arch = "aarch64"
   } else {
     assert(false, "Need android libgcc support for this arch.")
   }
diff --git a/build/build_all_configs.py b/build/build_all_configs.py
new file mode 100755
index 0000000..e792205
--- /dev/null
+++ b/build/build_all_configs.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import os
+import subprocess
+import sys
+
+MAC_BUILD_CONFIGS = {
+  'mac_debug': ['is_clang=true', 'is_debug=true'],
+  'mac_release': ['is_clang=true', 'is_debug=false'],
+  'mac_asan': ['is_clang=true', 'is_debug=false', 'is_asan=true'],
+  'mac_tsan': ['is_clang=true', 'is_debug=false', 'is_tsan=true'],
+  'mac_ubsan': ['is_clang=true', 'is_debug=false', 'is_ubsan=true'],
+}
+
+ANDROID_BUILD_CONFIGS = {
+  'android_debug': ['target_os="android"', 'is_clang=true', 'is_debug=true'],
+  'android_release': ['target_os="android"', 'is_clang=true', 'is_debug=false'],
+  'android_asan': ['target_os="android"', 'is_clang=true', 'is_debug=false', 'is_asan=true'],
+  'android_lsan': ['target_os="android"', 'is_clang=true', 'is_debug=false', 'is_lsan=true'],
+}
+
+ANDROID_ARCHS = [ 'arm', 'arm64' ]
+
+LINUX_BUILD_CONFIGS = {
+  'linux_gcc_debug': ['is_clang=false', 'is_debug=true'],
+  'linux_gcc_release': ['is_clang=false', 'is_debug=false'],
+  'linux_clang_debug': ['is_clang=true', 'is_debug=true'],
+  'linux_clang_release': ['is_clang=true', 'is_debug=false'],
+  'linux_asan': ['is_clang=true', 'is_debug=false', 'is_asan=true'],
+  'linux_lsan': ['is_clang=true', 'is_debug=false', 'is_lsan=true'],
+  'linux_msan': ['is_clang=true', 'is_debug=false', 'is_msan=true'],
+  'linux_tsan': ['is_clang=true', 'is_debug=false', 'is_tsan=true'],
+  'linux_ubsan': ['is_clang=true', 'is_debug=false', 'is_ubsan=true'],
+}
+
+ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--host-only', action='store_true', default=False)
+  parser.add_argument('--build', default=None)
+  args = parser.parse_args()
+
+  configs = {}
+  if not args.host_only:
+    for config_name, gn_args in ANDROID_BUILD_CONFIGS.iteritems():
+      for arch in ANDROID_ARCHS:
+        full_config_name = '%s_%s' % (config_name, arch)
+        configs[full_config_name] = gn_args + ['target_cpu="%s"' % arch]
+
+  if sys.platform == 'linux2':
+    configs.update(LINUX_BUILD_CONFIGS)
+  elif sys.platform == 'darwin':
+    configs.update(MAC_BUILD_CONFIGS)
+  else:
+    assert(False)
+
+  out_base_dir = os.path.join(ROOT_DIR, 'out')
+  if not os.path.isdir(out_base_dir):
+    os.mkdir(out_base_dir)
+
+  gn = os.path.join(ROOT_DIR, 'build', 'gn')
+
+  for config_name, gn_args in configs.iteritems():
+    print '\n\033[32mBuilding %-20s[%s]\033[0m' % (config_name, ','.join(gn_args))
+    out_dir = os.path.join(ROOT_DIR, 'out', config_name)
+    if not os.path.isdir(out_dir):
+      os.mkdir(out_dir)
+    gn_cmd = [gn, 'args', out_dir, '--args=%s' % (' '.join(gn_args)), '--check']
+    print ' '.join(gn_cmd)
+    subprocess.check_call(gn_cmd, cwd=ROOT_DIR, env={'EDITOR':'true'})
+    if args.build:
+      ninja = os.path.join(ROOT_DIR, 'build', 'ninja')
+      ninja_cmd = [ninja, '-C', '.', args.build]
+      subprocess.check_call(ninja_cmd, cwd=out_dir)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/build/c++.gni b/build/c++.gni
new file mode 100644
index 0000000..913a12d
--- /dev/null
+++ b/build/c++.gni
@@ -0,0 +1,22 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build/sanitizers/sanitizers.gni")
+
+declare_args() {
+  use_custom_libcxx = is_msan && is_linux && is_clang
+}
+
+libcxx_prefix = "//buildtools/libcxx"
+libcxxabi_prefix = "//buildtools/libcxxabi"
diff --git a/build/install-build-deps b/build/install-build-deps
index 3a507d8..5958ea7 100755
--- a/build/install-build-deps
+++ b/build/install-build-deps
@@ -18,6 +18,7 @@
 import logging
 import os
 import shutil
+import subprocess
 import sys
 import urllib
 import zipfile
@@ -72,6 +73,21 @@
    '3caec60aa9d8eefc8c3c3201b6b8ca19935edb89',
    'all'
   ),
+
+  # libc++ and libc++abi, for clang msan that require rebuilding the C++ lib
+  # from sources. Keep the SHA1s in sync with Chrome's src/buildtools/DEPS .
+  ('buildtools/libcxx',
+   'https://chromium.googlesource.com/chromium/llvm-project/libcxx.git',
+   '3a07dd740be63878167a0ea19fe81869954badd7',
+   'all'
+  ),
+  ('buildtools/libcxxabi',
+   'https://chromium.googlesource.com/chromium/llvm-project/libcxxabi.git',
+   '4072e8fd76febee37f60aeda76d6d9f5e3791daa',
+   'all'
+  ),
+
+
 )
 
 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -84,9 +100,10 @@
       return f.read().strip()
 
 
-def MkdirRecursive(rel_path):
-  cwd = ROOT_DIR
-  for part in rel_path.split('/'):
+def MkdirRecursive(path):
+  # Works with both relative and absolute paths
+  cwd = '/' if path.startswith('/') else ROOT_DIR
+  for part in path.split('/'):
     cwd = os.path.join(cwd, part)
     if not os.path.exists(cwd):
       os.makedirs(cwd)
@@ -108,6 +125,22 @@
   os.chmod(target_path, (info.external_attr >> 16L) | min_acls)
 
 
+def IsGitRepoCheckoutOutAtRevision(path, revision):
+  return ReadFile(os.path.join(path, '.git', 'HEAD')) == revision
+
+
+def CheckoutGitRepo(path, git_url, revision):
+  if IsGitRepoCheckoutOutAtRevision(path, revision):
+    return
+  if os.path.exists(path):
+    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', 'checkout', revision, '--quiet'], cwd=path)
+  assert(IsGitRepoCheckoutOutAtRevision(path, revision))
+
+
 def Main():
   parser = argparse.ArgumentParser()
   parser.add_argument('--skip', action='append', default=[])
@@ -120,6 +153,9 @@
       logging.info('Skipping %s because of --skip cmdline arg', rel_path)
       continue
     local_path = os.path.join(ROOT_DIR, rel_path)
+    if url.endswith('.git'):
+      CheckoutGitRepo(local_path, url, expected_sha1)
+      continue
     is_zip = local_path.lower().endswith('.zip')
     zip_target_dir = local_path[:-4] if is_zip else None
     zip_dir_stamp = os.path.join(zip_target_dir, '.stamp') if is_zip else None
diff --git a/build/linux.gni b/build/linux.gni
new file mode 100644
index 0000000..84d2f87
--- /dev/null
+++ b/build/linux.gni
@@ -0,0 +1,23 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare_args() {
+  linux_clangrt_dir = ""
+}
+
+if (linux_clangrt_dir == "" && is_linux) {
+  linux_llvm_dir = exec_script("linux_find_llvm.py", [], "list lines")
+  linux_llvm_dir = linux_llvm_dir[0]
+  linux_clangrt_dir = "$linux_llvm_dir/lib/linux"
+}
diff --git a/build/linux_find_llvm.py b/build/linux_find_llvm.py
new file mode 100644
index 0000000..264a08d
--- /dev/null
+++ b/build/linux_find_llvm.py
@@ -0,0 +1,34 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import subprocess
+import sys
+
+def main():
+  res = subprocess.check_output(['clang', '-print-search-dirs'])
+  for line in res.splitlines():
+    if not line.startswith('libraries:'):
+      continue
+    libs = line.split('=', 1)[1].split(':')
+    for lib in libs:
+      if '/clang/' not in lib or not os.path.isdir(lib + '/lib'):
+        continue
+      print os.path.abspath(lib)
+      return 0
+  print 'Could not find the LLVM lib dir'
+  return 1
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/build/mac.gni b/build/mac.gni
new file mode 100644
index 0000000..ead846c
--- /dev/null
+++ b/build/mac.gni
@@ -0,0 +1,23 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare_args() {
+  mac_clangrt_dir = ""
+}
+
+if (mac_clangrt_dir == "" && is_mac) {
+  mac_toolchain_dirs_ = exec_script("mac_find_toolchain.py", [], "list lines")
+  mac_toolchain_dir = mac_toolchain_dirs_[0]
+  mac_clangrt_dir = mac_toolchain_dirs_[1]
+}
diff --git a/build/mac_find_toolchain.py b/build/mac_find_toolchain.py
new file mode 100644
index 0000000..17d1ea1
--- /dev/null
+++ b/build/mac_find_toolchain.py
@@ -0,0 +1,36 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import glob
+import os
+import subprocess
+import sys
+
+def main():
+  job = subprocess.Popen(['xcrun', '-f', 'clang++'],
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.STDOUT)
+  out, err = job.communicate()
+  if job.returncode != 0:
+    print >> sys.stderr, out
+    print >> sys.stderr, err
+    return job.returncode
+  sdk_dir = os.path.dirname(os.path.dirname(out.rstrip()))
+  print sdk_dir
+  clang_dir = glob.glob(os.path.join(sdk_dir, 'lib', 'clang', '*', 'lib', 'darwin'))
+  print clang_dir[0] if clang_dir else 'CLANG_DIR_NOT_FOUND'
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/build/sanitizers/BUILD.gn b/build/sanitizers/BUILD.gn
new file mode 100644
index 0000000..4032e2d
--- /dev/null
+++ b/build/sanitizers/BUILD.gn
@@ -0,0 +1,142 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build/sanitizers/sanitizers.gni")
+import("//build/android.gni")
+import("//build/linux.gni")
+import("//build/mac.gni")
+
+# Link dependencies for sanitizers for executables and shared_libraries.
+group("deps") {
+  visibility = [ "//build:exe_and_shlib_deps" ]
+  if (using_sanitizer) {
+    public_configs = [ ":sanitizers_ldflags" ]
+  }
+}
+
+config("sanitizers_cflags") {
+  if (using_sanitizer) {
+    blacklist_path_ = rebase_path("blacklist.txt", root_build_dir)
+    cflags = [
+      "-fno-omit-frame-pointer",
+      "-fsanitize-blacklist=$blacklist_path_",
+    ]
+    defines = []
+  }
+
+  if (is_asan) {
+    cflags += [ "-fsanitize=address" ]
+    defines += [ "ADDRESS_SANITIZER" ]
+  }
+  if (is_lsan) {
+    cflags += [ "-fsanitize=leak" ]
+    defines += [ "LEAK_SANITIZER" ]
+  }
+  if (is_tsan) {
+    cflags += [ "-fsanitize=thread" ]
+    defines += [
+      "THREAD_SANITIZER",
+      "DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL=1",
+    ]
+  }
+  if (is_msan) {
+    cflags += [
+      "-fsanitize=memory",
+      "-fsanitize-memory-track-origins=2",
+    ]
+    defines += [ "MEMORY_SANITIZER" ]
+  }
+  if (is_ubsan) {
+    cflags += [
+      "-fsanitize=bounds",
+      "-fsanitize=float-divide-by-zero",
+      "-fsanitize=integer-divide-by-zero",
+      "-fsanitize=null",
+      "-fsanitize=object-size",
+      "-fsanitize=return",
+      "-fsanitize=returns-nonnull-attribute",
+      "-fsanitize=shift-exponent",
+      "-fsanitize=signed-integer-overflow",
+      "-fsanitize=unreachable",
+      "-fsanitize=vla-bound",
+    ]
+    defines += [ "UNDEFINED_SANITIZER" ]
+  }
+}
+
+config("sanitizer_options_link_helper") {
+  if (is_mac) {
+    ldflags = [ "-Wl,-U,_sanitizer_options_link_helper" ]
+  }
+}
+
+config("sanitizers_ldflags") {
+  visibility = [ ":deps" ]
+  cflags = [ "-fno-omit-frame-pointer" ]
+  ldflags = []
+  _sanitizer_lib = ""
+  if (is_asan) {
+    cflags += [ "-fsanitize=address" ]
+    ldflags += [ "-fsanitize=address" ]
+    _sanitizer_lib = "clang_rt.asan"
+  }
+  if (is_lsan) {
+    cflags += [ "-fsanitize=leak" ]
+    ldflags += [ "-fsanitize=leak" ]
+  }
+  if (is_tsan) {
+    cflags += [ "-fsanitize=thread" ]
+    ldflags += [ "-fsanitize=thread" ]
+    _sanitizer_lib = "clang_rt.tsan"
+  }
+  if (is_msan) {
+    cflags += [ "-fsanitize=memory" ]
+    ldflags += [ "-fsanitize=memory" ]
+  }
+  if (is_ubsan) {
+    cflags += [
+      "-fsanitize=bounds",
+      "-fsanitize=float-divide-by-zero",
+      "-fsanitize=integer-divide-by-zero",
+      "-fsanitize=null",
+      "-fsanitize=object-size",
+      "-fsanitize=return",
+      "-fsanitize=returns-nonnull-attribute",
+      "-fsanitize=shift-exponent",
+      "-fsanitize=signed-integer-overflow",
+      "-fsanitize=unreachable",
+      "-fsanitize=vla-bound",
+    ]
+    _sanitizer_lib = "clang_rt.ubsan"
+    if (is_android || is_linux) {
+      _sanitizer_lib += "_standalone"
+    }
+    ldflags += [ "-fsanitize=undefined" ]
+  }
+  if (_sanitizer_lib != "") {
+    if (is_mac) {
+      lib_dirs = [ mac_clangrt_dir ]
+      libs = [ "${_sanitizer_lib}_osx_dynamic" ]
+      configs = [ ":sanitizer_options_link_helper" ]
+    }
+    if (is_linux) {
+      lib_dirs = [ linux_clangrt_dir ]
+      ldflags += [ "-Llib${_sanitizer_lib}-x86_64.a" ]
+    }
+    if (is_android) {
+      lib_dirs = [ android_clangrt_dir ]
+      libs = [ "${_sanitizer_lib}-${android_llvm_arch}-android" ]
+    }
+  }
+}
diff --git a/build/sanitizers/blacklist.txt b/build/sanitizers/blacklist.txt
new file mode 100644
index 0000000..e0e315b
--- /dev/null
+++ b/build/sanitizers/blacklist.txt
@@ -0,0 +1 @@
+# The rules in this file are only applied at compile time.
diff --git a/build/sanitizers/sanitizers.gni b/build/sanitizers/sanitizers.gni
new file mode 100644
index 0000000..1c6725c
--- /dev/null
+++ b/build/sanitizers/sanitizers.gni
@@ -0,0 +1,35 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare_args() {
+  # Address Sanitizer: memory bugs (e.g., UAF).
+  is_asan = false
+
+  # Leak Sanitizer: memory leaks.
+  is_lsan = false
+
+  # Memory Sanitizer: uninitialized reads.
+  is_msan = false
+
+  # Thread Sanitizer: threading bugs.
+  is_tsan = false
+
+  # Undefined Behaviour Sanitizer.
+  is_ubsan = false
+}
+
+using_sanitizer = is_asan || is_lsan || is_tsan || is_msan || is_ubsan
+assert(!using_sanitizer || is_clang, "is_*san requires is_clang=true'")
+assert(!is_msan || is_linux, "msan only supported on linux")
+assert(!is_tsan || (is_linux || is_mac), "tsan only supported on linux and mac")
diff --git a/build/toolchain/BUILD.gn b/build/toolchain/BUILD.gn
index d6dc1cb..2b98778 100644
--- a/build/toolchain/BUILD.gn
+++ b/build/toolchain/BUILD.gn
@@ -21,10 +21,8 @@
 
   if (is_android) {
     target_ar = "$android_toolchain_root/bin/ar"
-    target_cc =
-        "$android_ndk_root/toolchains/llvm/prebuilt/$android_host/bin/clang"
-    target_cxx =
-        "$android_ndk_root/toolchains/llvm/prebuilt/$android_host/bin/clang++"
+    target_cc = "$android_llvm_dir/bin/clang"
+    target_cxx = "$android_llvm_dir/bin/clang++"
   } else {
     target_ar = ar
     target_cc = cc
diff --git a/buildtools/.gitignore b/buildtools/.gitignore
index 4168032..79d7ef2 100644
--- a/buildtools/.gitignore
+++ b/buildtools/.gitignore
@@ -3,3 +3,5 @@
 mac/
 ndk/
 protobuf/
+libcxx/
+libcxxabi/
diff --git a/buildtools/BUILD.gn b/buildtools/BUILD.gn
index bbe15a8..8ff1e39 100644
--- a/buildtools/BUILD.gn
+++ b/buildtools/BUILD.gn
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//build/c++.gni")
+
 config("googletest_config") {
   visibility = [ ":*" ]
   defines = [ "GTEST_LANG_CXX11=1" ]
@@ -128,9 +130,7 @@
 }
 
 source_set("protobuf_full") {
-  deps = [
-    ":protobuf_lite",
-  ]
+  deps += [ ":protobuf_lite" ]
   sources = [
     "protobuf/src/google/protobuf/any.cc",
     "protobuf/src/google/protobuf/any.pb.cc",
@@ -192,9 +192,7 @@
 
 if (current_toolchain == host_toolchain) {
   executable("protoc") {
-    deps = [
-      ":protobuf_full",
-    ]
+    deps += [ ":protobuf_full" ]
     sources = [
       "protobuf/src/google/protobuf/compiler/code_generator.cc",
       "protobuf/src/google/protobuf/compiler/command_line_interface.cc",
@@ -293,3 +291,101 @@
     public_configs = [ ":protobuf_config" ]
   }
 }  # host_toolchain
+
+if (use_custom_libcxx) {
+  # Config applied to both libc++ and libc++abi targets below.
+  config("libc++config") {
+    defines = [
+      "LIBCXX_BUILDING_LIBCXXABI",
+      "_LIBCXXABI_NO_EXCEPTIONS",
+      "_LIBCPP_OVERRIDABLE_FUNC_VIS=__attribute__((__visibility__(\"default\")))",
+    ]
+    cflags = [
+      "-fPIC",
+      "-fstrict-aliasing",
+    ]
+  }
+
+  source_set("libc++abi") {
+    sources = [
+      "libcxxabi/src/abort_message.cpp",
+      "libcxxabi/src/cxa_aux_runtime.cpp",
+      "libcxxabi/src/cxa_default_handlers.cpp",
+      "libcxxabi/src/cxa_demangle.cpp",
+      "libcxxabi/src/cxa_exception.cpp",
+      "libcxxabi/src/cxa_exception_storage.cpp",
+      "libcxxabi/src/cxa_guard.cpp",
+      "libcxxabi/src/cxa_handlers.cpp",
+      "libcxxabi/src/cxa_personality.cpp",
+      "libcxxabi/src/cxa_unexpected.cpp",
+      "libcxxabi/src/cxa_vector.cpp",
+      "libcxxabi/src/cxa_virtual.cpp",
+      "libcxxabi/src/fallback_malloc.cpp",
+      "libcxxabi/src/private_typeinfo.cpp",
+      "libcxxabi/src/stdlib_exception.cpp",
+      "libcxxabi/src/stdlib_stdexcept.cpp",
+      "libcxxabi/src/stdlib_typeinfo.cpp",
+    ]
+
+    # On linux this seems to introduce an unwanted glibc 2.18 dependency.
+    if (is_android) {
+      sources += [ "libcxxabi/src/cxa_thread_atexit.cpp" ]
+    }
+    configs -= [
+      "//build:extra_warnings",
+      "//build:no_exceptions",
+      "//build:no_rtti",
+    ]
+    configs += [
+      ":libc++config",
+      "//build/sanitizers:sanitizer_options_link_helper",
+    ]
+  }
+
+  source_set("libc++") {
+    sources = [
+      "libcxx/src/algorithm.cpp",
+      "libcxx/src/any.cpp",
+      "libcxx/src/bind.cpp",
+      "libcxx/src/chrono.cpp",
+      "libcxx/src/condition_variable.cpp",
+      "libcxx/src/debug.cpp",
+      "libcxx/src/exception.cpp",
+      "libcxx/src/functional.cpp",
+      "libcxx/src/future.cpp",
+      "libcxx/src/hash.cpp",
+      "libcxx/src/ios.cpp",
+      "libcxx/src/iostream.cpp",
+      "libcxx/src/locale.cpp",
+      "libcxx/src/memory.cpp",
+      "libcxx/src/mutex.cpp",
+      "libcxx/src/new.cpp",
+      "libcxx/src/optional.cpp",
+      "libcxx/src/random.cpp",
+      "libcxx/src/regex.cpp",
+      "libcxx/src/shared_mutex.cpp",
+      "libcxx/src/stdexcept.cpp",
+      "libcxx/src/string.cpp",
+      "libcxx/src/strstream.cpp",
+      "libcxx/src/system_error.cpp",
+      "libcxx/src/thread.cpp",
+      "libcxx/src/typeinfo.cpp",
+      "libcxx/src/utility.cpp",
+      "libcxx/src/valarray.cpp",
+      "libcxx/src/variant.cpp",
+    ]
+    configs -= [
+      "//build:extra_warnings",
+      "//build:no_exceptions",
+      "//build:no_rtti",
+    ]
+    configs += [
+      ":libc++config",
+      "//build/sanitizers:sanitizer_options_link_helper",
+    ]
+    defines = [ "_LIBCPP_BUILDING_LIBRARY" ]
+    deps = [
+      ":libc++abi",
+    ]
+  }
+}  # if (use_custom_libcxx)
diff --git a/libtracing/BUILD.gn b/libtracing/BUILD.gn
index 042c38e..e192088 100644
--- a/libtracing/BUILD.gn
+++ b/libtracing/BUILD.gn
@@ -14,7 +14,7 @@
 
 executable("libtracing_unittests") {
   testonly = true
-  deps = [
+  deps += [
     "../buildtools:gmock",
     "../buildtools:gtest",
     "../buildtools:gtest_main",
@@ -23,3 +23,14 @@
     "hello_world_unittest.cc",
   ]
 }
+
+executable("sanitizers_unittests") {
+  testonly = true
+  deps += [
+    "../buildtools:gtest",
+    "../buildtools:gtest_main",
+  ]
+  sources = [
+    "sanitizers_unittest.cc",
+  ]
+}
diff --git a/libtracing/sanitizers_unittest.cc b/libtracing/sanitizers_unittest.cc
new file mode 100644
index 0000000..7ef8754
--- /dev/null
+++ b/libtracing/sanitizers_unittest.cc
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Smoke tests to verify that clang sanitizers are actually working.
+
+#include <pthread.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "gtest/gtest.h"
+
+namespace perfetto {
+namespace {
+
+#if defined(ADDRESS_SANITIZER)
+TEST(SanitizerTests, ASAN_UserAfterFree) {
+  void* alloc = malloc(16);
+  volatile char* mem = reinterpret_cast<volatile char*>(alloc);
+  mem[0] = 1;
+  mem[15] = 1;
+  free(alloc);
+  mem[0] = 2;
+}
+#endif  // ADDRESS_SANITIZER
+
+#if defined(THREAD_SANITIZER)
+TEST(SanitizerTests, TSAN_ThreadDataRace) {
+  pthread_t thread;
+  const int kNumRuns = 1000;
+  volatile int race_var = 0;
+  auto thread_main = [](void* race_var_ptr) -> void* {
+    volatile int* my_race_var = reinterpret_cast<volatile int*>(race_var_ptr);
+    printf("in thread %p", race_var_ptr);
+    for (int i = 0; i < kNumRuns; i++)
+      (*my_race_var)++;
+    return nullptr;
+  };
+  void* arg = const_cast<void*>(reinterpret_cast<volatile void*>(&race_var));
+  ASSERT_EQ(0, pthread_create(&thread, nullptr, thread_main, arg));
+  for (int i = 0; i < kNumRuns; i++)
+    race_var--;
+  ASSERT_EQ(0, pthread_join(thread, nullptr));
+}
+#endif  // THREAD_SANITIZER
+
+#if defined(MEMORY_SANITIZER)
+TEST(SanitizerTests, MSAN_UninitializedMemory) {
+  std::unique_ptr<int> mem(new int[10]);
+  volatile int* x = reinterpret_cast<volatile int*>(mem.get());
+  if(x[rand() % 10] == 42)
+    printf("\n");
+}
+#endif
+
+#if defined(LEAK_SANITIZER)
+TEST(SanitizerTests, LSAN_LeakMalloc) {
+  void* alloc = malloc(16);
+  reinterpret_cast<volatile char*>(alloc)[0] = 1;
+  alloc = malloc(16);
+  reinterpret_cast<volatile char*>(alloc)[0] = 2;
+  free(alloc);
+}
+
+TEST(SanitizerTests, LSAN_LeakCppNew) {
+  std::unique_ptr<int> alloc(new int(1));
+  *reinterpret_cast<volatile char*>(alloc.get()) = 1;
+  alloc.release();
+  alloc.reset(new int(2));
+  *reinterpret_cast<volatile char*>(alloc.get()) = 2;
+}
+#endif  // LEAK_SANITIZER
+
+#if defined(UNDEFINED_SANITIZER)
+TEST(SanitizerTests, UBSAN_DivisionByZero) {
+  volatile float div = 1;
+  float res = 3 / (div - 1);
+  ASSERT_GT(res, -1.0f);  // just use |res| to make the compiler happy.
+}
+
+TEST(SanitizerTests, UBSAN_ShiftExponent) {
+  volatile uint32_t n = 32;
+  volatile uint32_t shift = 31;
+  uint64_t res = n << (shift + 3);
+  ASSERT_NE(1u, res);  // just use |res| to make the compiler happy.
+}
+#endif  // UNDEFINED_SANITIZER
+
+}  // namespace
+}  // namespace perfetto