You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@impala.apache.org by jb...@apache.org on 2017/09/07 03:50:22 UTC

[4/8] incubator-impala git commit: IMPALA-5902: add ThreadSanitizer build

IMPALA-5902: add ThreadSanitizer build

This is sufficient to get Impala to come up and run queries with
thread sanitizer enabled.

I have not triaged or fixed the data races that are reported, that
is left for follow-on work.

Change-Id: I22f8faeefa5e157279c5973fe28bc573b7606d50
Reviewed-on: http://gerrit.cloudera.org:8080/7977
Reviewed-by: Tim Armstrong <ta...@cloudera.com>
Tested-by: Impala Public Jenkins


Project: http://git-wip-us.apache.org/repos/asf/incubator-impala/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-impala/commit/b1edaf21
Tree: http://git-wip-us.apache.org/repos/asf/incubator-impala/tree/b1edaf21
Diff: http://git-wip-us.apache.org/repos/asf/incubator-impala/diff/b1edaf21

Branch: refs/heads/master
Commit: b1edaf215e537d8cef5ffb305973b6c5baa63583
Parents: be98aaa
Author: Tim Armstrong <ta...@cloudera.com>
Authored: Thu Aug 31 23:18:20 2017 -0700
Committer: Impala Public Jenkins <im...@gerrit.cloudera.org>
Committed: Thu Sep 7 01:22:41 2017 +0000

----------------------------------------------------------------------
 CMakeLists.txt                                |  3 ++-
 be/CMakeLists.txt                             | 16 ++++++++++---
 be/src/common/init.cc                         | 18 +++++++-------
 be/src/gutil/atomicops-internals-tsan.h       | 18 ++++++++++++++
 be/src/runtime/bufferpool/system-allocator.cc |  2 +-
 be/src/runtime/exec-env.cc                    |  2 +-
 be/src/runtime/query-exec-mgr.cc              |  4 ++--
 be/src/util/default-path-handlers.cc          |  6 ++---
 be/src/util/memory-metrics.cc                 | 10 ++++----
 be/src/util/memory-metrics.h                  | 16 ++++++-------
 be/src/util/metrics-test.cc                   |  2 +-
 be/src/util/pprof-path-handlers.cc            | 12 +++++-----
 bin/make_impala.sh                            |  3 ++-
 bin/run-backend-tests.sh                      |  1 +
 bin/start-catalogd.sh                         |  1 +
 bin/start-impalad.sh                          |  1 +
 bin/start-statestored.sh                      |  1 +
 buildall.sh                                   |  7 ++++++
 common/thrift/metrics.json                    |  6 ++---
 tests/common/environ.py                       | 28 +++++++++++++++-------
 20 files changed, 105 insertions(+), 52 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e5c2fdb..e8a2355 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -209,7 +209,8 @@ find_package(LlvmBinaries REQUIRED)
 if ("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG"
     OR "${CMAKE_BUILD_TYPE}" STREQUAL "ADDRESS_SANITIZER"
     OR "${CMAKE_BUILD_TYPE}" STREQUAL "TIDY"
-    OR "${CMAKE_BUILD_TYPE}" STREQUAL "UBSAN")
+    OR "${CMAKE_BUILD_TYPE}" STREQUAL "UBSAN"
+    OR "${CMAKE_BUILD_TYPE}" STREQUAL "TSAN")
   # Use the LLVM libaries with assertions for debug builds.
   set(LLVM_ROOT ${LLVM_DEBUG_ROOT})
 endif()

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/be/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/be/CMakeLists.txt b/be/CMakeLists.txt
index 5b0d89d..bf7aa26 100644
--- a/be/CMakeLists.txt
+++ b/be/CMakeLists.txt
@@ -110,6 +110,11 @@ SET(CXX_FLAGS_UBSAN "${CXX_FLAGS_UBSAN} -fno-wrapv")
 # To ease debugging, turn off all optimizations:
 SET(CXX_FLAGS_UBSAN "${CXX_FLAGS_UBSAN} -O0")
 
+# Set the flags to the thread sanitizer, also known as "tsan"
+# Turn on sanitizer and debug symbols to get stack traces:
+SET(CXX_FLAGS_TSAN "${CXX_CLANG_FLAGS} -O1 -ggdb3 -fno-omit-frame-pointer")
+SET(CXX_FLAGS_TSAN "${CXX_FLAGS_TSAN} -fsanitize=thread -DTHREAD_SANITIZER")
+
 SET(CXX_FLAGS_TIDY "${CXX_CLANG_FLAGS}")
 # Catching unused variables requires an optimization level greater than 0
 SET(CXX_FLAGS_TIDY "${CXX_FLAGS_TIDY} -O1")
@@ -136,6 +141,8 @@ elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "TIDY")
   SET(CMAKE_CXX_FLAGS "${CXX_FLAGS_TIDY}")
 elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "UBSAN")
   SET(CMAKE_CXX_FLAGS "${CXX_FLAGS_UBSAN}")
+elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "TSAN")
+  SET(CMAKE_CXX_FLAGS "${CXX_FLAGS_TSAN}")
 else()
   message(FATAL_ERROR "Unknown build type: ${CMAKE_BUILD_TYPE}")
 endif()
@@ -159,7 +166,8 @@ if (CCACHE AND NOT DEFINED ENV{DISABLE_CCACHE})
   set(RULE_LAUNCH_PREFIX ccache)
   if ("${CMAKE_BUILD_TYPE}" STREQUAL "ADDRESS_SANITIZER"
       OR "${CMAKE_BUILD_TYPE}" STREQUAL "TIDY"
-      OR "${CMAKE_BUILD_TYPE}" STREQUAL "UBSAN")
+      OR "${CMAKE_BUILD_TYPE}" STREQUAL "UBSAN"
+      OR "${CMAKE_BUILD_TYPE}" STREQUAL "TSAN")
     # Need to set CCACHE_CPP so that ccache calls clang with the original source file for
     # both preprocessing and compilation. Otherwise, ccache will use clang to preprocess
     # the file and then call clang with the preprocessed output if not cached. However,
@@ -294,7 +302,8 @@ add_definitions(-fPIC)
 # set compile output directory
 if ("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG" OR
     "${CMAKE_BUILD_TYPE}" STREQUAL "ADDRESS_SANITIZER" OR
-    "${CMAKE_BUILD_TYPE}" STREQUAL "UBSAN")
+    "${CMAKE_BUILD_TYPE}" STREQUAL "UBSAN" OR
+    "${CMAKE_BUILD_TYPE}" STREQUAL "TSAN")
   set(BUILD_OUTPUT_ROOT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/build/debug/")
 else()
   set(BUILD_OUTPUT_ROOT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/build/release/")
@@ -436,7 +445,8 @@ set (IMPALA_LINK_LIBS ${IMPALA_LINK_LIBS}
 # sanitizer build. Address sanitizer is incompatible with tcmalloc (they both intercept
 # malloc/free)
 set (IMPALA_LINK_LIBS_NO_TCMALLOC ${IMPALA_LINK_LIBS})
-if (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "ADDRESS_SANITIZER")
+if (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "ADDRESS_SANITIZER" AND
+    NOT "${CMAKE_BUILD_TYPE}" STREQUAL "TSAN")
   set (IMPALA_LINK_LIBS ${IMPALA_LINK_LIBS} tcmallocstatic)
 endif()
 

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/be/src/common/init.cc
----------------------------------------------------------------------
diff --git a/be/src/common/init.cc b/be/src/common/init.cc
index f2df173..ce55067 100644
--- a/be/src/common/init.cc
+++ b/be/src/common/init.cc
@@ -137,17 +137,15 @@ static scoped_ptr<impala::Thread> pause_monitor;
       BufferPool* buffer_pool = env->buffer_pool();
       if (buffer_pool != nullptr) buffer_pool->Maintenance();
 
-#ifndef ADDRESS_SANITIZER
-      // When using tcmalloc, the process limit as measured by our trackers will
-      // be out of sync with the process usage. The metric is refreshed whenever
-      // memory is consumed or released via a MemTracker, so on a system with
-      // queries executing it will be refreshed frequently. However if the system
-      // is idle, we need to refresh the tracker occasionally since untracked
-      // memory may be allocated or freed, e.g. by background threads.
+      // The process limit as measured by our trackers may get out of sync with the
+      // process usage if memory is allocated or freed without updating a MemTracker.
+      // The metric is refreshed whenever memory is consumed or released via a MemTracker,
+      // so on a system with queries executing it will be refreshed frequently. However
+      // if the system is idle, we need to refresh the tracker occasionally since
+      // untracked memory may be allocated or freed, e.g. by background threads.
       if (env->process_mem_tracker() != nullptr) {
         env->process_mem_tracker()->RefreshConsumptionFromMetric();
       }
-#endif
     }
     // Periodically refresh values of the aggregate memory metrics to ensure they are
     // somewhat up-to-date.
@@ -198,7 +196,9 @@ void impala::InitCommonRuntime(int argc, char** argv, bool init_jvm,
   impala::InitGoogleLoggingSafe(argv[0]);
   // Breakpad needs flags and logging to initialize.
   ABORT_IF_ERROR(RegisterMinidump(argv[0]));
+#ifndef THREAD_SANITIZER
   AtomicOps_x86CPUFeaturesInit();
+#endif
   impala::InitThreading();
   impala::TimestampParser::Init();
   impala::SeedOpenSSLRNG();
@@ -243,7 +243,7 @@ void impala::InitCommonRuntime(int argc, char** argv, bool init_jvm,
 
   if (impala::KuduIsAvailable()) impala::InitKuduLogging();
 
-#ifndef ADDRESS_SANITIZER
+#if !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER)
   // tcmalloc and address sanitizer can not be used together
   if (FLAGS_enable_process_lifetime_heap_profiling) {
     HeapProfilerStart(FLAGS_heap_profile_dir.c_str());

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/be/src/gutil/atomicops-internals-tsan.h
----------------------------------------------------------------------
diff --git a/be/src/gutil/atomicops-internals-tsan.h b/be/src/gutil/atomicops-internals-tsan.h
index aecaefc..a1fa71b 100644
--- a/be/src/gutil/atomicops-internals-tsan.h
+++ b/be/src/gutil/atomicops-internals-tsan.h
@@ -202,6 +202,24 @@ inline Atomic64 Release_CompareAndSwap(volatile Atomic64 *ptr,
   return cmp;
 }
 
+inline Atomic32 Barrier_CompareAndSwap(volatile Atomic32 *ptr,
+                                  Atomic32 old_value,
+                                  Atomic32 new_value) {
+  Atomic32 cmp = old_value;
+  __tsan_atomic32_compare_exchange_strong(ptr, &cmp, new_value,
+      __tsan_memory_order_acq_rel, __tsan_memory_order_relaxed);
+  return cmp;
+}
+
+inline Atomic64 Barrier_CompareAndSwap(volatile Atomic64 *ptr,
+                                  Atomic64 old_value,
+                                  Atomic64 new_value) {
+  Atomic64 cmp = old_value;
+  __tsan_atomic64_compare_exchange_strong(ptr, &cmp, new_value,
+      __tsan_memory_order_acq_rel, __tsan_memory_order_relaxed);
+  return cmp;
+}
+
 inline void MemoryBarrier() {
   __tsan_atomic_thread_fence(__tsan_memory_order_seq_cst);
 }

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/be/src/runtime/bufferpool/system-allocator.cc
----------------------------------------------------------------------
diff --git a/be/src/runtime/bufferpool/system-allocator.cc b/be/src/runtime/bufferpool/system-allocator.cc
index 8bbce70..b3ba2b8 100644
--- a/be/src/runtime/bufferpool/system-allocator.cc
+++ b/be/src/runtime/bufferpool/system-allocator.cc
@@ -46,7 +46,7 @@ static int64_t HUGE_PAGE_SIZE = 2LL * 1024 * 1024;
 SystemAllocator::SystemAllocator(int64_t min_buffer_len)
   : min_buffer_len_(min_buffer_len) {
   DCHECK(BitUtil::IsPowerOf2(min_buffer_len));
-#ifndef ADDRESS_SANITIZER
+#if !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER)
   // Free() assumes that aggressive decommit is enabled for TCMalloc.
   size_t aggressive_decommit_enabled;
   MallocExtension::instance()->GetNumericProperty(

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/be/src/runtime/exec-env.cc
----------------------------------------------------------------------
diff --git a/be/src/runtime/exec-env.cc b/be/src/runtime/exec-env.cc
index 2907768..fe4825d 100644
--- a/be/src/runtime/exec-env.cc
+++ b/be/src/runtime/exec-env.cc
@@ -294,7 +294,7 @@ Status ExecEnv::StartServices() {
         BufferPoolMetric::UNUSED_RESERVATION_BYTES));
   obj_pool_->Add(new MemTracker(negated_unused_reservation, -1,
       "Buffer Pool: Unused Reservation", mem_tracker_.get()));
-#ifndef ADDRESS_SANITIZER
+#if !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER)
   // Aggressive decommit is required so that unused pages in the TCMalloc page heap are
   // not backed by physical pages and do not contribute towards memory consumption.
   size_t aggressive_decommit_enabled = 0;

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/be/src/runtime/query-exec-mgr.cc
----------------------------------------------------------------------
diff --git a/be/src/runtime/query-exec-mgr.cc b/be/src/runtime/query-exec-mgr.cc
index 22c2826..901ddbd 100644
--- a/be/src/runtime/query-exec-mgr.cc
+++ b/be/src/runtime/query-exec-mgr.cc
@@ -111,8 +111,8 @@ QueryState* QueryExecMgr::GetOrCreateQueryState(
 void QueryExecMgr::StartQueryHelper(QueryState* qs) {
   qs->StartFInstances();
 
-#ifndef ADDRESS_SANITIZER
-  // tcmalloc and address sanitizer cannot be used together
+#if !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER)
+  // tcmalloc and address or thread sanitizer cannot be used together
   if (FLAGS_log_mem_usage_interval > 0) {
     uint64_t num_complete = ImpaladMetrics::IMPALA_SERVER_NUM_FRAGMENTS->value();
     if (num_complete % FLAGS_log_mem_usage_interval == 0) {

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/be/src/util/default-path-handlers.cc
----------------------------------------------------------------------
diff --git a/be/src/util/default-path-handlers.cc b/be/src/util/default-path-handlers.cc
index 9334316..88d23f1 100644
--- a/be/src/util/default-path-handlers.cc
+++ b/be/src/util/default-path-handlers.cc
@@ -135,8 +135,8 @@ void MemUsageHandler(MemTracker* mem_tracker, MetricGroup* metric_group,
   document->AddMember("consumption", consumption, document->GetAllocator());
 
   stringstream ss;
-#ifdef ADDRESS_SANITIZER
-  ss << "Memory tracking is not available with address sanitizer builds.";
+#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)
+  ss << "Memory tracking is not available with address or thread sanitizer builds.";
 #else
   char buf[2048];
   MallocExtension::instance()->GetStats(buf, 2048);
@@ -238,7 +238,7 @@ void AddDefaultUrlCallbacks(
     webserver->RegisterUrlCallback("/memz", "memz.tmpl", callback);
   }
 
-#ifndef ADDRESS_SANITIZER
+#if !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER)
   // Remote (on-demand) profiling is disabled if the process is already being profiled.
   if (!FLAGS_enable_process_lifetime_heap_profiling) {
     AddPprofUrlCallbacks(webserver);

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/be/src/util/memory-metrics.cc
----------------------------------------------------------------------
diff --git a/be/src/util/memory-metrics.cc b/be/src/util/memory-metrics.cc
index 7c41bdf..d1e7a16 100644
--- a/be/src/util/memory-metrics.cc
+++ b/be/src/util/memory-metrics.cc
@@ -47,7 +47,7 @@ TcmallocMetric* TcmallocMetric::TOTAL_BYTES_RESERVED = nullptr;
 TcmallocMetric* TcmallocMetric::PAGEHEAP_UNMAPPED_BYTES = nullptr;
 TcmallocMetric::PhysicalBytesMetric* TcmallocMetric::PHYSICAL_BYTES_RESERVED = nullptr;
 
-AsanMallocMetric* AsanMallocMetric::BYTES_ALLOCATED = nullptr;
+SanitizerMallocMetric* SanitizerMallocMetric::BYTES_ALLOCATED = nullptr;
 
 BufferPoolMetric* BufferPoolMetric::LIMIT = nullptr;
 BufferPoolMetric* BufferPoolMetric::SYSTEM_ALLOCATED = nullptr;
@@ -81,10 +81,10 @@ Status impala::RegisterMemoryMetrics(MetricGroup* metrics, bool register_jvm_met
     used_metrics.push_back(BufferPoolMetric::SYSTEM_ALLOCATED);
   }
 
-#ifdef ADDRESS_SANITIZER
-  AsanMallocMetric::BYTES_ALLOCATED = metrics->RegisterMetric(
-      new AsanMallocMetric(MetricDefs::Get("asan-total-bytes-allocated")));
-  used_metrics.push_back(AsanMallocMetric::BYTES_ALLOCATED);
+#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)
+  SanitizerMallocMetric::BYTES_ALLOCATED = metrics->RegisterMetric(
+      new SanitizerMallocMetric(MetricDefs::Get("sanitizer-total-bytes-allocated")));
+  used_metrics.push_back(SanitizerMallocMetric::BYTES_ALLOCATED);
 #else
   MetricGroup* tcmalloc_metrics = metrics->GetOrCreateChildGroup("tcmalloc");
   // We rely on TCMalloc for our global memory metrics, so skip setting them up

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/be/src/util/memory-metrics.h
----------------------------------------------------------------------
diff --git a/be/src/util/memory-metrics.h b/be/src/util/memory-metrics.h
index e1f1e4a..5491f8c 100644
--- a/be/src/util/memory-metrics.h
+++ b/be/src/util/memory-metrics.h
@@ -23,7 +23,7 @@
 #include <boost/bind.hpp>
 #include <boost/thread/mutex.hpp>
 #include <gperftools/malloc_extension.h>
-#ifdef ADDRESS_SANITIZER
+#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)
 #include <sanitizer/allocator_interface.h>
 #endif
 
@@ -125,7 +125,7 @@ class TcmallocMetric : public IntGauge {
       : IntGauge(def, 0), tcmalloc_var_(tcmalloc_var) { }
 
   virtual void CalculateValue() {
-#ifndef ADDRESS_SANITIZER
+#if !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER)
     DCHECK_EQ(sizeof(size_t), sizeof(value_));
     MallocExtension::instance()->GetNumericProperty(tcmalloc_var_.c_str(),
         reinterpret_cast<size_t*>(&value_));
@@ -133,15 +133,15 @@ class TcmallocMetric : public IntGauge {
   }
 };
 
-/// Alternative to TCMallocMetric if we're running under Address Sanitizer, which
-/// does not provide the same metrics.
-class AsanMallocMetric : public IntGauge {
+/// Alternative to TCMallocMetric if we're running under a sanitizer that replaces
+/// malloc(), e.g. address or thread sanitizer.
+class SanitizerMallocMetric : public IntGauge {
  public:
-  AsanMallocMetric(const TMetricDef& def) : IntGauge(def, 0) {}
-  static AsanMallocMetric* BYTES_ALLOCATED;
+  SanitizerMallocMetric(const TMetricDef& def) : IntGauge(def, 0) {}
+  static SanitizerMallocMetric* BYTES_ALLOCATED;
  private:
   virtual void CalculateValue() override {
-#ifdef ADDRESS_SANITIZER
+#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)
     value_ = __sanitizer_get_current_allocated_bytes();
 #endif
   }

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/be/src/util/metrics-test.cc
----------------------------------------------------------------------
diff --git a/be/src/util/metrics-test.cc b/be/src/util/metrics-test.cc
index 08ca266..0126281 100644
--- a/be/src/util/metrics-test.cc
+++ b/be/src/util/metrics-test.cc
@@ -215,7 +215,7 @@ TEST_F(MetricsTest, StatsMetricsSingle) {
 }
 
 TEST_F(MetricsTest, MemMetric) {
-#ifndef ADDRESS_SANITIZER
+#if !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER)
   MetricGroup metrics("MemMetrics");
   ASSERT_OK(RegisterMemoryMetrics(&metrics, false, nullptr, nullptr));
   // Smoke test to confirm that tcmalloc metrics are returning reasonable values.

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/be/src/util/pprof-path-handlers.cc
----------------------------------------------------------------------
diff --git a/be/src/util/pprof-path-handlers.cc b/be/src/util/pprof-path-handlers.cc
index b8f245c..bc04b2c 100644
--- a/be/src/util/pprof-path-handlers.cc
+++ b/be/src/util/pprof-path-handlers.cc
@@ -54,9 +54,9 @@ void PprofCmdLineHandler(const Webserver::ArgumentMap& args, stringstream* outpu
 // by calling HeapProfileStart(filename), continue to do work, and then, some number of
 // seconds later, call GetHeapProfile() followed by HeapProfilerStop().
 void PprofHeapHandler(const Webserver::ArgumentMap& args, stringstream* output) {
-#ifdef ADDRESS_SANITIZER
+#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)
   (void)PPROF_DEFAULT_SAMPLE_SECS; // Avoid unused variable warning.
-  (*output) << "Heap profiling is not available with address sanitizer builds.";
+  (*output) << "Heap profiling is not available with address/thread sanitizer builds.";
 #else
   Webserver::ArgumentMap::const_iterator it = args.find("seconds");
   int seconds = PPROF_DEFAULT_SAMPLE_SECS;
@@ -78,8 +78,8 @@ void PprofHeapHandler(const Webserver::ArgumentMap& args, stringstream* output)
 // The server should respond by calling ProfilerStart(), continuing to do its work,
 // and then, XX seconds later, calling ProfilerStop().
 void PprofCpuProfileHandler(const Webserver::ArgumentMap& args, stringstream* output) {
-#ifdef ADDRESS_SANITIZER
-  (*output) << "CPU profiling is not available with address sanitizer builds.";
+#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)
+  (*output) << "CPU profiling is not available with address/thread sanitizer builds.";
 #else
   Webserver::ArgumentMap::const_iterator it = args.find("seconds");
   int seconds = PPROF_DEFAULT_SAMPLE_SECS;
@@ -106,8 +106,8 @@ void PprofCpuProfileHandler(const Webserver::ArgumentMap& args, stringstream* ou
 // The server should respond by calling:
 // MallocExtension::instance()->GetHeapGrowthStacks(&output);
 void PprofGrowthHandler(const Webserver::ArgumentMap& args, stringstream* output) {
-#ifdef ADDRESS_SANITIZER
-  (*output) << "Growth profiling is not available with address sanitizer builds.";
+#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)
+  (*output) << "Growth profiling is not available with address/thread sanitizer builds.";
 #else
   string heap_growth_stack;
   MallocExtension::instance()->GetHeapGrowthStacks(&heap_growth_stack);

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/bin/make_impala.sh
----------------------------------------------------------------------
diff --git a/bin/make_impala.sh b/bin/make_impala.sh
index 8d164c2..cf05278 100755
--- a/bin/make_impala.sh
+++ b/bin/make_impala.sh
@@ -149,7 +149,8 @@ then
 
       if [[ ("$TARGET_BUILD_TYPE" == "ADDRESS_SANITIZER") \
                 || ("$TARGET_BUILD_TYPE" == "TIDY") \
-                || ("$TARGET_BUILD_TYPE" == "UBSAN") ]]
+                || ("$TARGET_BUILD_TYPE" == "UBSAN") \
+                || ("$TARGET_BUILD_TYPE" == "TSAN") ]]
       then
         CMAKE_ARGS+=(-DCMAKE_TOOLCHAIN_FILE=$IMPALA_HOME/cmake_modules/clang_toolchain.cmake)
       else

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/bin/run-backend-tests.sh
----------------------------------------------------------------------
diff --git a/bin/run-backend-tests.sh b/bin/run-backend-tests.sh
index 98630b0..d4d3142 100755
--- a/bin/run-backend-tests.sh
+++ b/bin/run-backend-tests.sh
@@ -40,5 +40,6 @@ export CTEST_OUTPUT_ON_FAILURE=1
 export ASAN_OPTIONS="handle_segv=0 detect_leaks=0 allocator_may_return_null=1"
 export UBSAN_OPTIONS="print_stacktrace=1"
 UBSAN_OPTIONS="${UBSAN_OPTIONS} suppressions=${IMPALA_HOME}/bin/ubsan-suppressions.txt"
+export TSAN_OPTIONS="halt_on_error=1 history_size=7"
 export PATH="${IMPALA_TOOLCHAIN}/llvm-${IMPALA_LLVM_VERSION}/bin:${PATH}"
 "${MAKE_CMD:-make}" test ARGS="${BE_TEST_ARGS}"

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/bin/start-catalogd.sh
----------------------------------------------------------------------
diff --git a/bin/start-catalogd.sh b/bin/start-catalogd.sh
index 3eeaf2e..4ec6846 100755
--- a/bin/start-catalogd.sh
+++ b/bin/start-catalogd.sh
@@ -73,5 +73,6 @@ fi
 export ASAN_OPTIONS="handle_segv=0 detect_leaks=0 allocator_may_return_null=1"
 export UBSAN_OPTIONS="print_stacktrace=1"
 UBSAN_OPTIONS="${UBSAN_OPTIONS} suppressions=${IMPALA_HOME}/bin/ubsan-suppressions.txt"
+export TSAN_OPTIONS="halt_on_error=0 history_size=7"
 export PATH="${IMPALA_TOOLCHAIN}/llvm-${IMPALA_LLVM_VERSION}/bin:${PATH}"
 exec ${BINARY_BASE_DIR}/${BUILD_TYPE}/catalog/catalogd ${CATALOGD_ARGS}

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/bin/start-impalad.sh
----------------------------------------------------------------------
diff --git a/bin/start-impalad.sh b/bin/start-impalad.sh
index d4602b5..76a5f2c 100755
--- a/bin/start-impalad.sh
+++ b/bin/start-impalad.sh
@@ -101,5 +101,6 @@ fi
 export ASAN_OPTIONS="handle_segv=0 detect_leaks=0 allocator_may_return_null=1"
 export UBSAN_OPTIONS="print_stacktrace=1"
 UBSAN_OPTIONS="${UBSAN_OPTIONS} suppressions=${IMPALA_HOME}/bin/ubsan-suppressions.txt"
+export TSAN_OPTIONS="halt_on_error=0 history_size=7"
 export PATH="${IMPALA_TOOLCHAIN}/llvm-${IMPALA_LLVM_VERSION}/bin:${PATH}"
 exec ${TOOL_PREFIX} ${IMPALA_CMD} ${IMPALAD_ARGS}

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/bin/start-statestored.sh
----------------------------------------------------------------------
diff --git a/bin/start-statestored.sh b/bin/start-statestored.sh
index 85fe221..02cf09f 100755
--- a/bin/start-statestored.sh
+++ b/bin/start-statestored.sh
@@ -62,5 +62,6 @@ fi
 export ASAN_OPTIONS="handle_segv=0 detect_leaks=0 allocator_may_return_null=1"
 export UBSAN_OPTIONS="print_stacktrace=1"
 UBSAN_OPTIONS="${UBSAN_OPTIONS} suppressions=${IMPALA_HOME}/bin/ubsan-suppressions.txt"
+export TSAN_OPTIONS="halt_on_error=0 history_size=7"
 export PATH="${IMPALA_TOOLCHAIN}/llvm-${IMPALA_LLVM_VERSION}/bin:${PATH}"
 exec ${BINARY_BASE_DIR}/${BUILD_TYPE}/statestore/statestored ${STATESTORED_ARGS}

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/buildall.sh
----------------------------------------------------------------------
diff --git a/buildall.sh b/buildall.sh
index bcc6ae3..d14c2ea 100755
--- a/buildall.sh
+++ b/buildall.sh
@@ -60,6 +60,7 @@ BUILD_ASAN=0
 BUILD_FE_ONLY=0
 BUILD_TIDY=0
 BUILD_UBSAN=0
+BUILD_TSAN=0
 # Export MAKE_CMD so it is visible in scripts that invoke make, e.g. copy-udfs-udas.sh
 export MAKE_CMD=make
 LZO_CMAKE_ARGS=
@@ -118,6 +119,9 @@ do
     -ubsan)
       BUILD_UBSAN=1
       ;;
+    -tsan)
+      BUILD_TSAN=1
+      ;;
     -testpairwise)
       EXPLORATION_STRATEGY=pairwise
       ;;
@@ -268,6 +272,9 @@ fi
 if [[ ${BUILD_UBSAN} -eq 1 ]]; then
   CMAKE_BUILD_TYPE=UBSAN
 fi
+if [[ ${BUILD_TSAN} -eq 1 ]]; then
+  CMAKE_BUILD_TYPE=TSAN
+fi
 
 MAKE_IMPALA_ARGS+=" -build_type=${CMAKE_BUILD_TYPE}"
 

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/common/thrift/metrics.json
----------------------------------------------------------------------
diff --git a/common/thrift/metrics.json b/common/thrift/metrics.json
index 567e1bf..4ba94be 100644
--- a/common/thrift/metrics.json
+++ b/common/thrift/metrics.json
@@ -1114,16 +1114,16 @@
     "key": "tcmalloc.total-bytes-reserved"
   },
   {
-    "description": "Bytes allocated from Address Sanitizer's malloc (Address Sanitizer debug builds only)",
+    "description": "Bytes allocated from the sanitizer malloc (Sanitizer debug builds only)",
     "contexts": [
       "STATESTORE",
       "CATALOGSERVER",
       "IMPALAD"
     ],
-    "label": "Address Sanitizer Malloc Bytes Allocated",
+    "label": "Sanitizer Malloc Bytes Allocated",
     "units": "BYTES",
     "kind": "GAUGE",
-    "key": "asan-total-bytes-allocated"
+    "key": "sanitizer-total-bytes-allocated"
   },
   {
     "description": "Maximum allowed bytes allocated by the buffer pool.",

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b1edaf21/tests/common/environ.py
----------------------------------------------------------------------
diff --git a/tests/common/environ.py b/tests/common/environ.py
index 571dc65..48c4cba 100644
--- a/tests/common/environ.py
+++ b/tests/common/environ.py
@@ -35,7 +35,6 @@ except ImportError as e:
 
 LOG = logging.getLogger('tests.common.environ')
 
-
 test_start_cluster_args = os.environ.get("TEST_START_CLUSTER_ARGS", "")
 
 # Find the likely BuildType of the running Impala. Assume it's found through the path
@@ -58,8 +57,8 @@ IMPALAD_PATH = os.path.join(impalad_basedir, 'service', 'impalad')
 
 class SpecificImpaladBuildTypes:
   """
-  Represent a specific build type. In reality, there 5 specific build types. These
-  specific build types are needed by Python test code.
+  Represent a specific build type. These specific build types are needed by Python test
+  code.
 
   The specific build types and their *most distinguishing* compiler options are:
 
@@ -68,6 +67,7 @@ class SpecificImpaladBuildTypes:
   3. DEBUG_CODE_COVERAGE (gcc -ggdb -ftest-coverage)
   4. RELEASE (gcc)
   5. RELEASE_CODE_COVERAGE (gcc -ftest-coverage)
+  6. THREAD_SANITIZER (clang -fsanitize=thread)
   """
   # ./buildall.sh -asan
   ADDRESS_SANITIZER = 'address_sanitizer'
@@ -79,6 +79,8 @@ class SpecificImpaladBuildTypes:
   RELEASE = 'release'
   # ./buildall.sh -release -codecoverage
   RELEASE_CODE_COVERAGE = 'release_code_coverage'
+  # ./buildall.sh -tsan
+  THREAD_SANITIZER = 'thread_sanitizer'
 
 
 class ImpaladBuild(object):
@@ -111,6 +113,12 @@ class ImpaladBuild(object):
     """
     return self.specific_build_type == SpecificImpaladBuildTypes.ADDRESS_SANITIZER
 
+  def is_tsan(self):
+    """
+    Return whether the Impala under test was compiled with TSAN.
+    """
+    return self.specific_build_type == SpecificImpaladBuildTypes.THREAD_SANITIZER
+
   def is_dev(self):
     """
     Return whether the Impala under test is a development build (i.e., any debug or ASAN
@@ -118,14 +126,15 @@ class ImpaladBuild(object):
     """
     return self.specific_build_type in (
         SpecificImpaladBuildTypes.ADDRESS_SANITIZER, SpecificImpaladBuildTypes.DEBUG,
-        SpecificImpaladBuildTypes.DEBUG_CODE_COVERAGE)
+        SpecificImpaladBuildTypes.DEBUG_CODE_COVERAGE,
+        SpecificImpaladBuildTypes.THREAD_SANITIZER)
 
   def runs_slowly(self):
     """
     Return whether the Impala under test "runs slowly". For our purposes this means
-    either compiled with code coverage enabled or ASAN.
+    either compiled with code coverage enabled, ASAN or TSAN.
     """
-    return self.has_code_coverage() or self.is_asan()
+    return self.has_code_coverage() or self.is_asan() or self.is_tsan()
 
   def _get_impalad_dwarf_info(self):
     """
@@ -170,7 +179,8 @@ class ImpaladBuild(object):
     assuming a debug build and log a warning.
     """
     ASAN_CU_NAME = 'asan_preinit.cc'
-    NON_ASAN_CU_NAME = 'daemon-main.cc'
+    TSAN_CU_NAME = 'tsan_clock.cc'
+    DEFAULT_CU_NAME = 'daemon-main.cc'
     GDB_FLAG = '-ggdb'
     CODE_COVERAGE_FLAG = '-ftest-coverage'
 
@@ -185,7 +195,9 @@ class ImpaladBuild(object):
 
     if die_name.endswith(ASAN_CU_NAME):
       specific_build_type = SpecificImpaladBuildTypes.ADDRESS_SANITIZER
-    elif not die_name.endswith(NON_ASAN_CU_NAME):
+    if die_name.endswith(TSAN_CU_NAME):
+      specific_build_type = SpecificImpaladBuildTypes.THREAD_SANITIZER
+    elif not die_name.endswith(DEFAULT_CU_NAME):
       LOG.warn('Unexpected DW_AT_name in first CU: {0}; choosing '
                'DEBUG'.format(die_name))
       specific_build_type = SpecificImpaladBuildTypes.DEBUG