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