build both tsan and msan libc++

std::call_once() will trigger false positive races without
a libc++ (specifically __call_once()) built with TSAN itself.

Interestingly, there's a built-in interceptor for this
use case, only on Mac: https://reviews.llvm.org/D24188.

Fixes: skia:9884
Change-Id: I7b2c031c15b78ec7302c42b4e4ac365d596c2c33
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/265567
Commit-Queue: Mike Klein <mtklein@google.com>
Reviewed-by: Ben Wagner aka dogben <benjaminwagner@google.com>
diff --git a/infra/bots/assets/clang_linux/VERSION b/infra/bots/assets/clang_linux/VERSION
index 19c7bdb..dec2bf5 100644
--- a/infra/bots/assets/clang_linux/VERSION
+++ b/infra/bots/assets/clang_linux/VERSION
@@ -1 +1 @@
-16
\ No newline at end of file
+19
\ No newline at end of file
diff --git a/infra/bots/assets/clang_linux/create.py b/infra/bots/assets/clang_linux/create.py
index ab94dec..7d5e0c9 100755
--- a/infra/bots/assets/clang_linux/create.py
+++ b/infra/bots/assets/clang_linux/create.py
@@ -59,16 +59,18 @@
                                        "-print-file-name=libstdc++.so.6"])
   subprocess.check_call(["cp", libstdcpp.strip(), target_dir + "/lib"])
 
-  # Finally, build libc++ for MSAN bots using the Clang we just built.
-  os.mkdir("../msan_out")
-  os.chdir("../msan_out")
-  subprocess.check_call(["cmake", "..", "-G", "Ninja",
-                         "-DCMAKE_BUILD_TYPE=MinSizeRel",
-                         "-DCMAKE_C_COMPILER="   + target_dir + "/bin/clang",
-                         "-DCMAKE_CXX_COMPILER=" + target_dir + "/bin/clang++",
-                         "-DLLVM_USE_SANITIZER=MemoryWithOrigins"])
-  subprocess.check_call(["ninja", "cxx"])
-  subprocess.check_call(["cp", "-r", "lib",  target_dir + "/msan"])
+  # Finally, build libc++ for TSAN and MSAN bots using the Clang we just built.
+  for (short,full) in [('tsan','Thread'), ('msan','MemoryWithOrigins')]:
+    os.mkdir("../{}_out".format(short))
+    os.chdir("../{}_out".format(short))
+    subprocess.check_call(
+        ["cmake", "..", "-G", "Ninja",
+         "-DCMAKE_BUILD_TYPE=MinSizeRel",
+         "-DCMAKE_C_COMPILER="   + target_dir + "/bin/clang",
+         "-DCMAKE_CXX_COMPILER=" + target_dir + "/bin/clang++",
+         "-DLLVM_USE_SANITIZER={}".format(full)])
+    subprocess.check_call(["ninja", "cxx"])
+    subprocess.check_call(["cp", "-r", "lib",  target_dir + "/" + short])
 
 
 def main():
diff --git a/infra/bots/recipe_modules/build/default.py b/infra/bots/recipe_modules/build/default.py
index d59ebb4..853df51 100644
--- a/infra/bots/recipe_modules/build/default.py
+++ b/infra/bots/recipe_modules/build/default.py
@@ -50,22 +50,29 @@
       'PATH': '%%(PATH)s:%s' % cmake_bin
   }
 
-  # Extra flags for MSAN, if necessary.
+  # Extra flags for MSAN/TSAN, if necessary.
+  san = None
   if 'MSAN' in extra_tokens:
+    san = ('msan','memory')
+  elif 'TSAN' in extra_tokens:
+    san = ('tsan','thread')
+
+  if san:
+    short,full = san
     clang_linux = str(api.vars.slave_dir.join('clang_linux'))
-    libcxx_msan = clang_linux + '/msan'
-    msan_cflags = ' '.join([
-      '-fsanitize=memory',
+    libcxx = clang_linux + '/' + short
+    cflags = ' '.join([
+      '-fsanitize=' + full,
       '-stdlib=libc++',
-      '-L%s/lib' % libcxx_msan,
+      '-L%s/lib' % libcxx,
       '-lc++abi',
-      '-I%s/include' % libcxx_msan,
-      '-I%s/include/c++/v1' % libcxx_msan,
+      '-I%s/include' % libcxx,
+      '-I%s/include/c++/v1' % libcxx,
     ])
     swiftshader_opts.extend([
-      '-DSWIFTSHADER_MSAN=ON',
-      '-DCMAKE_C_FLAGS=%s' % msan_cflags,
-      '-DCMAKE_CXX_FLAGS=%s' % msan_cflags,
+      '-DSWIFTSHADER_{}=ON'.format(short.upper()),
+      '-DCMAKE_C_FLAGS=%s' % cflags,
+      '-DCMAKE_CXX_FLAGS=%s' % cflags,
     ])
 
   # Build SwiftShader.
@@ -195,6 +202,8 @@
 
   if 'MSAN' in extra_tokens:
     extra_ldflags.append('-L' + clang_linux + '/msan')
+  elif 'TSAN' in extra_tokens:
+    extra_ldflags.append('-L' + clang_linux + '/tsan')
   elif api.vars.is_linux:
     extra_ldflags.append('-L' + clang_linux + '/lib')
 
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SwiftShader_TSAN.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SwiftShader_TSAN.json
new file mode 100644
index 0000000..7411527
--- /dev/null
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SwiftShader_TSAN.json
@@ -0,0 +1,200 @@
+[
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/cache/work/skia/infra/bots/assets/clang_linux/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Debug-SwiftShader_TSAN/Debug/swiftshader_out"
+    ],
+    "infra_step": true,
+    "name": "makedirs swiftshader_out"
+  },
+  {
+    "cmd": [
+      "cmake",
+      "-DSWIFTSHADER_BUILD_TESTS=OFF",
+      "-DSWIFTSHADER_WARNINGS_AS_ERRORS=0",
+      "-DSWIFTSHADER_TSAN=ON",
+      "-DCMAKE_C_FLAGS=-fsanitize=thread -stdlib=libc++ -L[START_DIR]/clang_linux/tsan/lib -lc++abi -I[START_DIR]/clang_linux/tsan/include -I[START_DIR]/clang_linux/tsan/include/c++/v1",
+      "-DCMAKE_CXX_FLAGS=-fsanitize=thread -stdlib=libc++ -L[START_DIR]/clang_linux/tsan/lib -lc++abi -I[START_DIR]/clang_linux/tsan/include -I[START_DIR]/clang_linux/tsan/include/c++/v1",
+      "[START_DIR]/cache/work/skia/third_party/externals/swiftshader",
+      "-GNinja"
+    ],
+    "cwd": "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Debug-SwiftShader_TSAN/Debug/swiftshader_out",
+    "env": {
+      "CC": "[START_DIR]/clang_linux/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "[START_DIR]/clang_linux/bin/clang++",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]:[START_DIR]/cmake_linux/bin"
+    },
+    "name": "swiftshader cmake"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Debug-SwiftShader_TSAN/Debug/swiftshader_out",
+      "libEGL.so",
+      "libGLESv2.so"
+    ],
+    "cwd": "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Debug-SwiftShader_TSAN/Debug/swiftshader_out",
+    "env": {
+      "CC": "[START_DIR]/clang_linux/bin/clang",
+      "CHROME_HEADLESS": "1",
+      "CXX": "[START_DIR]/clang_linux/bin/clang++",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]:[START_DIR]/cmake_linux/bin"
+    },
+    "name": "swiftshader ninja"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/cache/work/skia/bin/fetch-gn"
+    ],
+    "cwd": "[START_DIR]/cache/work/skia",
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cache/work/skia/bin/gn",
+      "gen",
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Debug-SwiftShader_TSAN/Debug",
+      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cc_wrapper=\"[START_DIR]/ccache_linux/bin/ccache\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\", \"-DDUMMY_clang_linux_version=42\", \"-O1\", \"-DGR_EGL_TRY_GLES3_THEN_GLES2\", \"-I[START_DIR]/cache/work/skia/third_party/externals/egl-registry/api\", \"-I[START_DIR]/cache/work/skia/third_party/externals/opengl-registry/api\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\", \"-L[START_DIR]/clang_linux/tsan\", \"-L[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Debug-SwiftShader_TSAN/Debug/swiftshader_out\"] sanitize=\"TSAN\" skia_use_egl=true target_cpu=\"x86_64\" werror=true"
+    ],
+    "cwd": "[START_DIR]/cache/work/skia",
+    "env": {
+      "CCACHE_COMPILERCHECK": "content",
+      "CCACHE_DIR": "[START_DIR]/cache/ccache",
+      "CCACHE_MAXFILES": "0",
+      "CCACHE_MAXSIZE": "75G",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Debug-SwiftShader_TSAN/Debug"
+    ],
+    "cwd": "[START_DIR]/cache/work/skia",
+    "env": {
+      "CCACHE_COMPILERCHECK": "content",
+      "CCACHE_DIR": "[START_DIR]/cache/ccache",
+      "CCACHE_MAXFILES": "0",
+      "CCACHE_MAXSIZE": "75G",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "ninja"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products:\n  path = os.path.join(src, pattern)\n  for f in glob.glob(path):\n    dst_path = os.path.join(dst, os.path.relpath(f, src))\n    if not os.path.isdir(os.path.dirname(dst_path)):\n      os.makedirs(os.path.dirname(dst_path))\n    print 'Copying build product %s to %s' % (f, dst_path)\n    shutil.move(f, dst_path)\n",
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Debug-SwiftShader_TSAN/Debug",
+      "[START_DIR]/[SWARM_OUT_DIR]/out/Debug"
+    ],
+    "infra_step": true,
+    "name": "copy build products",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@import errno@@@",
+      "@@@STEP_LOG_LINE@python.inline@import glob@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import shutil@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@src = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@dst = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@build_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(dst)@@@",
+      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.EEXIST:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@for pattern in build_products:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  path = os.path.join(src, pattern)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in glob.glob(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    dst_path = os.path.join(dst, os.path.relpath(f, src))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if not os.path.isdir(os.path.dirname(dst_path)):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      os.makedirs(os.path.dirname(dst_path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print 'Copying build product %s to %s' % (f, dst_path)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    shutil.move(f, dst_path)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products:\n  path = os.path.join(src, pattern)\n  for f in glob.glob(path):\n    dst_path = os.path.join(dst, os.path.relpath(f, src))\n    if not os.path.isdir(os.path.dirname(dst_path)):\n      os.makedirs(os.path.dirname(dst_path))\n    print 'Copying build product %s to %s' % (f, dst_path)\n    shutil.move(f, dst_path)\n",
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Debug-SwiftShader_TSAN/Debug/swiftshader_out",
+      "[START_DIR]/[SWARM_OUT_DIR]/swiftshader_out"
+    ],
+    "infra_step": true,
+    "name": "copy build products (2)",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@import errno@@@",
+      "@@@STEP_LOG_LINE@python.inline@import glob@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import shutil@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@src = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@dst = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@build_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(dst)@@@",
+      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.EEXIST:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@for pattern in build_products:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  path = os.path.join(src, pattern)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in glob.glob(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    dst_path = os.path.join(dst, os.path.relpath(f, src))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if not os.path.isdir(os.path.dirname(dst_path)):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      os.makedirs(os.path.dirname(dst_path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print 'Copying build product %s to %s' % (f, dst_path)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    shutil.move(f, dst_path)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-TSAN.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-TSAN.json
new file mode 100644
index 0000000..374ea36
--- /dev/null
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-TSAN.json
@@ -0,0 +1,110 @@
+[
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/cache/work/skia/infra/bots/assets/clang_linux/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[START_DIR]/cache/work/skia/bin/fetch-gn"
+    ],
+    "cwd": "[START_DIR]/cache/work/skia",
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "fetch-gn"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cache/work/skia/bin/gn",
+      "gen",
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Debug-TSAN/Debug",
+      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cc_wrapper=\"[START_DIR]/ccache_linux/bin/ccache\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\", \"-DDUMMY_clang_linux_version=42\", \"-O1\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\", \"-L[START_DIR]/clang_linux/tsan\"] sanitize=\"TSAN\" target_cpu=\"x86_64\" werror=true"
+    ],
+    "cwd": "[START_DIR]/cache/work/skia",
+    "env": {
+      "CCACHE_COMPILERCHECK": "content",
+      "CCACHE_DIR": "[START_DIR]/cache/ccache",
+      "CCACHE_MAXFILES": "0",
+      "CCACHE_MAXSIZE": "75G",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "gn gen"
+  },
+  {
+    "cmd": [
+      "ninja",
+      "-C",
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Debug-TSAN/Debug"
+    ],
+    "cwd": "[START_DIR]/cache/work/skia",
+    "env": {
+      "CCACHE_COMPILERCHECK": "content",
+      "CCACHE_DIR": "[START_DIR]/cache/ccache",
+      "CCACHE_MAXFILES": "0",
+      "CCACHE_MAXSIZE": "75G",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "ninja"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products:\n  path = os.path.join(src, pattern)\n  for f in glob.glob(path):\n    dst_path = os.path.join(dst, os.path.relpath(f, src))\n    if not os.path.isdir(os.path.dirname(dst_path)):\n      os.makedirs(os.path.dirname(dst_path))\n    print 'Copying build product %s to %s' % (f, dst_path)\n    shutil.move(f, dst_path)\n",
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Debug-TSAN/Debug",
+      "[START_DIR]/[SWARM_OUT_DIR]/out/Debug"
+    ],
+    "infra_step": true,
+    "name": "copy build products",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@import errno@@@",
+      "@@@STEP_LOG_LINE@python.inline@import glob@@@",
+      "@@@STEP_LOG_LINE@python.inline@import os@@@",
+      "@@@STEP_LOG_LINE@python.inline@import shutil@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@src = sys.argv[1]@@@",
+      "@@@STEP_LOG_LINE@python.inline@dst = sys.argv[2]@@@",
+      "@@@STEP_LOG_LINE@python.inline@build_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(dst)@@@",
+      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.EEXIST:@@@",
+      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@for pattern in build_products:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  path = os.path.join(src, pattern)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  for f in glob.glob(path):@@@",
+      "@@@STEP_LOG_LINE@python.inline@    dst_path = os.path.join(dst, os.path.relpath(f, src))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    if not os.path.isdir(os.path.dirname(dst_path)):@@@",
+      "@@@STEP_LOG_LINE@python.inline@      os.makedirs(os.path.dirname(dst_path))@@@",
+      "@@@STEP_LOG_LINE@python.inline@    print 'Copying build product %s to %s' % (f, dst_path)@@@",
+      "@@@STEP_LOG_LINE@python.inline@    shutil.move(f, dst_path)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/build/examples/full.py b/infra/bots/recipe_modules/build/examples/full.py
index 83ffd84..d35f8bf 100644
--- a/infra/bots/recipe_modules/build/examples/full.py
+++ b/infra/bots/recipe_modules/build/examples/full.py
@@ -41,12 +41,14 @@
   'Build-Debian9-Clang-x86_64-Debug-Chromebook_GLES',
   'Build-Debian9-Clang-x86_64-Debug-Coverage',
   'Build-Debian9-Clang-x86_64-Debug-MSAN',
+  'Build-Debian9-Clang-x86_64-Debug-TSAN',
   'Build-Debian9-Clang-x86_64-Debug-OpenCL',
   'Build-Debian9-Clang-x86_64-Debug-SK_CPU_LIMIT_SSE41',
   'Build-Debian9-Clang-x86_64-Debug-SafeStack',
   'Build-Debian9-Clang-x86_64-Debug-SkVM',
   'Build-Debian9-Clang-x86_64-Debug-SkVM_ASAN',
   'Build-Debian9-Clang-x86_64-Debug-SwiftShader_MSAN',
+  'Build-Debian9-Clang-x86_64-Debug-SwiftShader_TSAN',
   'Build-Debian9-Clang-x86_64-Debug-Tidy',
   'Build-Debian9-Clang-x86_64-Debug-Wuffs',
   'Build-Debian9-Clang-x86_64-Release-ANGLE',
diff --git a/infra/bots/recipe_modules/flavor/default.py b/infra/bots/recipe_modules/flavor/default.py
index 23bad98..39d4bc4 100644
--- a/infra/bots/recipe_modules/flavor/default.py
+++ b/infra/bots/recipe_modules/flavor/default.py
@@ -168,9 +168,11 @@
     if 'SwiftShader' in extra_tokens:
       ld_library_path.append(self.host_dirs.bin_dir.join('swiftshader_out'))
 
+    # Find the MSAN/TSAN-built libc++.
     if 'MSAN' in extra_tokens:
-      # Find the MSAN-built libc++.
       ld_library_path.append(clang_linux + '/msan')
+    elif 'TSAN' in extra_tokens:
+      ld_library_path.append(clang_linux + '/tsan')
 
     if any('SAN' in t for t in extra_tokens):
       # Sanitized binaries may want to run clang_linux/bin/llvm-symbolizer.
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
index 570f679..33c9b28 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
@@ -205,7 +205,7 @@
     "cwd": "[START_DIR]/skia",
     "env": {
       "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/clang_linux/lib",
+      "LD_LIBRARY_PATH": "[START_DIR]/clang_linux/tsan:[START_DIR]/clang_linux/lib",
       "PATH": "<PATH>:RECIPE_REPO[depot_tools]:[START_DIR]/clang_linux/bin",
       "TSAN_OPTIONS": "report_signal_unsafe=0"
     },
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
index cabdcb5..cee9ddf 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
@@ -222,7 +222,7 @@
     "cwd": "[START_DIR]/skia",
     "env": {
       "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/clang_linux/lib",
+      "LD_LIBRARY_PATH": "[START_DIR]/clang_linux/tsan:[START_DIR]/clang_linux/lib",
       "PATH": "<PATH>:RECIPE_REPO[depot_tools]:[START_DIR]/clang_linux/bin",
       "TSAN_OPTIONS": "report_signal_unsafe=0"
     },
diff --git a/infra/bots/tasks.json b/infra/bots/tasks.json
index ddaef85..fc000bb 100755
--- a/infra/bots/tasks.json
+++ b/infra/bots/tasks.json
@@ -4309,7 +4309,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/armhf_sysroot",
@@ -4674,7 +4674,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/armhf_sysroot",
@@ -5735,7 +5735,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -6189,7 +6189,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -6268,7 +6268,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -6347,7 +6347,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -6422,7 +6422,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/chromebook_x86_64_gles",
@@ -6501,7 +6501,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -6580,7 +6580,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/opencl_headers",
@@ -6669,7 +6669,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -6748,7 +6748,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -6827,7 +6827,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -6906,7 +6906,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -6985,7 +6985,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -7064,7 +7064,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -7143,7 +7143,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/cmake_linux",
@@ -7227,7 +7227,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/cmake_linux",
@@ -7311,7 +7311,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -7390,7 +7390,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -7469,7 +7469,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -7548,7 +7548,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -7627,7 +7627,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -7706,7 +7706,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -7816,7 +7816,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -7891,7 +7891,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/chromebook_x86_64_gles",
@@ -7970,7 +7970,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -8072,7 +8072,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -8150,7 +8150,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -8229,7 +8229,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -8308,7 +8308,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -8387,7 +8387,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -8466,7 +8466,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -8545,7 +8545,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -8624,7 +8624,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/cmake_linux",
@@ -8708,7 +8708,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -8787,7 +8787,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -8866,7 +8866,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -8945,7 +8945,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -14727,7 +14727,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/ccache_linux",
@@ -18834,7 +18834,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         }
       ],
       "command": [
@@ -18918,7 +18918,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         }
       ],
       "command": [
@@ -19730,7 +19730,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/linux_vulkan_sdk",
@@ -21700,7 +21700,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         }
       ],
       "command": [
@@ -21861,7 +21861,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         }
       ],
       "command": [
@@ -32394,7 +32394,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         }
       ],
       "command": [
@@ -32478,7 +32478,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         }
       ],
       "command": [
@@ -32641,7 +32641,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         }
       ],
       "command": [
@@ -32725,7 +32725,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         }
       ],
       "command": [
@@ -33125,7 +33125,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         }
       ],
       "command": [
@@ -33209,7 +33209,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         }
       ],
       "command": [
@@ -34004,7 +34004,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         }
       ],
       "command": [
@@ -34088,7 +34088,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         }
       ],
       "command": [
@@ -34407,7 +34407,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         }
       ],
       "command": [
@@ -35075,7 +35075,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/linux_vulkan_sdk",
@@ -35339,7 +35339,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/mesa_intel_driver_linux",
@@ -35427,7 +35427,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/linux_vulkan_sdk",
@@ -35696,7 +35696,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/mesa_intel_driver_linux",
@@ -35784,7 +35784,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/linux_vulkan_sdk",
@@ -35877,7 +35877,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/mesa_intel_driver_linux",
@@ -39539,7 +39539,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         }
       ],
       "command": [
@@ -39783,7 +39783,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         }
       ],
       "command": [
@@ -40188,7 +40188,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         }
       ],
       "command": [
@@ -40271,7 +40271,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/linux_vulkan_sdk",
@@ -40437,7 +40437,7 @@
         {
           "name": "skia/bots/clang_linux",
           "path": "clang_linux",
-          "version": "version:16"
+          "version": "version:19"
         },
         {
           "name": "skia/bots/linux_vulkan_sdk",