Skip to content

Commit

Permalink
Merge pull request swiftlang#2076 from apple/asan
Browse files Browse the repository at this point in the history
Add experimental support for Thread Sanitizer
  • Loading branch information
AnnaZaks committed Apr 6, 2016
2 parents 709951e + 246bc7a commit 5d34f66
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 34 deletions.
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsFrontend.def
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ ERROR(error_unsupported_target_arch, none,
ERROR(error_unsupported_opt_for_target, none,
"unsupported option '%0' for target '%1'", (StringRef, StringRef))

ERROR(error_argument_not_allowed_with, none,
"argument '%0' is not allowed with '%1'", (StringRef, StringRef))

ERROR(cannot_open_file,none,
"cannot open file '%0' (%1)", (StringRef, StringRef))
ERROR(cannot_open_serialized_file,none,
Expand Down
1 change: 1 addition & 0 deletions include/swift/Basic/Sanitizers.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace swift {
enum class SanitizerKind : unsigned {
None = 0,
Address,
Thread,
};

} // end namespace swift
Expand Down
2 changes: 1 addition & 1 deletion include/swift/Runtime/Concurrent.h
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ template <class EntryTy> class ConcurrentMap {

// Try to set the edge to the new node.
if (std::atomic_compare_exchange_strong_explicit(edge, &node, newNode,
std::memory_order_release,
std::memory_order_acq_rel,
std::memory_order_acquire)) {
// If that succeeded, cache and report that we created a new node.
LastSearch.store(newNode, std::memory_order_release);
Expand Down
3 changes: 3 additions & 0 deletions lib/Driver/ToolChains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,9 @@ toolchains::Darwin::constructInvocation(const LinkJobAction &job,
if (context.OI.SelectedSanitizer == SanitizerKind::Address)
addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "asan", *this);

if (context.OI.SelectedSanitizer == SanitizerKind::Thread)
addLinkSanitizerLibArgsForDarwin(context.Args, Arguments, "tsan", *this);

if (context.Args.hasArg(options::OPT_embed_bitcode,
options::OPT_embed_bitcode_marker)) {
Arguments.push_back("-bitcode_bundle");
Expand Down
4 changes: 4 additions & 0 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1085,6 +1085,10 @@ void irgen::emitLazyCacheAccessFunction(IRGenModule &IGM,
// FIXME: Technically should be "consume", but that introduces barriers in the
// current LLVM ARM backend.
auto load = IGF.Builder.CreateLoad(cache);
// Make this barrier explicit when building for TSan to avoid false positives.
if (IGM.Opts.Sanitize == SanitizerKind::Thread)
load->setOrdering(llvm::AtomicOrdering::Acquire);


// Compare the load result against null.
auto isNullBB = IGF.createBasicBlock("cacheIsNull");
Expand Down
12 changes: 12 additions & 0 deletions lib/IRGen/IRGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ static void addAddressSanitizerPasses(const PassManagerBuilder &Builder,
PM.add(createAddressSanitizerModulePass());
}

static void addThreadSanitizerPass(const PassManagerBuilder &Builder,
legacy::PassManagerBase &PM) {
PM.add(createThreadSanitizerPass());
}

std::tuple<llvm::TargetOptions, std::string, std::vector<std::string>>
swift::getIRTargetOptions(IRGenOptions &Opts, ASTContext &Ctx) {
// Things that maybe we should collect from the command line:
Expand Down Expand Up @@ -152,6 +157,13 @@ void swift::performLLVMOptimizations(IRGenOptions &Opts, llvm::Module *Module,
addAddressSanitizerPasses);
}

if (Opts.Sanitize == SanitizerKind::Thread) {
PMBuilder.addExtension(PassManagerBuilder::EP_OptimizerLast,
addThreadSanitizerPass);
PMBuilder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
addThreadSanitizerPass);
}

// Configure the function passes.
legacy::FunctionPassManager FunctionPasses(Module);
FunctionPasses.add(createTargetTransformInfoWrapperPass(
Expand Down
2 changes: 2 additions & 0 deletions lib/IRGen/IRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ IRGenFunction::IRGenFunction(IRGenModule &IGM,
// file or via annotations.
if (IGM.Opts.Sanitize == SanitizerKind::Address)
Fn->addFnAttr(llvm::Attribute::SanitizeAddress);
if (IGM.Opts.Sanitize == SanitizerKind::Thread)
Fn->addFnAttr(llvm::Attribute::SanitizeThread);

emitPrologue();
}
Expand Down
49 changes: 40 additions & 9 deletions lib/Option/SanitizerOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@
//===----------------------------------------------------------------------===//

#include "swift/Option/SanitizerOptions.h"
#include "swift/Basic/Platform.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "llvm/ADT/StringSwitch.h"
using namespace swift;

static StringRef toStringRef(const SanitizerKind kind) {
switch (kind) {
case SanitizerKind::Address:
return "address";
case SanitizerKind::Thread:
return "thread";
case SanitizerKind::None:
llvm_unreachable("Getting a name for SanitizerKind::None");
}
Expand All @@ -36,23 +40,50 @@ SanitizerKind swift::parseSanitizerArgValues(const llvm::opt::Arg *A,
SanitizerKind kind = SanitizerKind::None;

// Find the sanitizer kind.
// TODO: Add support for dealing with multiple sanitizers.
SanitizerKind pKind = SanitizerKind::None;
for (int i = 0, n = A->getNumValues(); i != n; ++i) {
const char *Value = A->getValue(i);
if (StringRef(Value).equals("address")) {
kind = SanitizerKind::Address;
} else {
kind =
llvm::StringSwitch<SanitizerKind>(A->getValue(i))
.Case("address", SanitizerKind::Address)
.Case("thread", SanitizerKind::Thread)
.Default(SanitizerKind::None);

if (kind == SanitizerKind::None) {
Diags.diagnose(SourceLoc(), diag::error_unsupported_option_argument,
A->getOption().getName(), A->getValue(i));
A->getOption().getPrefixedName(), A->getValue(i));
return kind;
}

// Currently, more than one sanitizer cannot be enabled at the same time.
if (pKind != SanitizerKind::None && pKind != kind) {
SmallString<128> pb;
SmallString<128> b;
Diags.diagnose(SourceLoc(), diag::error_argument_not_allowed_with,
(A->getOption().getPrefixedName() + toStringRef(pKind)).toStringRef(pb),
(A->getOption().getPrefixedName() + toStringRef(kind)).toStringRef(b));
}
pKind = kind;
}

if (kind == SanitizerKind::None)
return kind;

// Check if the target is supported for this sanitizer.
if (!Triple.isOSDarwin() && kind != SanitizerKind::None) {
SmallVector<char, 128> buffer;
// None of the sanitizers work on Linux right now.
if (!Triple.isOSDarwin()) {
SmallString<128> b;
Diags.diagnose(SourceLoc(), diag::error_unsupported_opt_for_target,
(A->getOption().getPrefixedName() + toStringRef(kind)).toStringRef(b),
Triple.getTriple());
}
// Thread Sanitizer only works on OS X and the simulators. It's only supported
// on 64 bit arcitectures.
if (kind == SanitizerKind::Thread &&
(!(Triple.isMacOSX() || tripleIsAnySimulator(Triple)) ||
!Triple.isArch64Bit())) {
SmallString<128> b;
Diags.diagnose(SourceLoc(), diag::error_unsupported_opt_for_target,
(A->getOption().getName() + toStringRef(kind)).toStringRef(buffer),
(A->getOption().getPrefixedName() + toStringRef(kind)).toStringRef(b),
Triple.getTriple());
}

Expand Down
4 changes: 3 additions & 1 deletion stdlib/public/core/Arrays.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -539,8 +539,10 @@ extension ${Self} : RangeReplaceableCollection, _ArrayProtocol {
// unnecessary uniqueness check. We disable inlining here to curb code
// growth.
_buffer = ${Self}._allocateBufferUninitialized(minimumCapacity: count)
_buffer.count = count
}
_buffer.count = count
// Can’t store count here because the buffer might be pointing to the
// shared empty array.
}

/// Entry point for `Array` literal construction; builds and returns
Expand Down
3 changes: 1 addition & 2 deletions stdlib/public/runtime/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -560,10 +560,9 @@ swift::swift_conformsToProtocol(const Metadata *type,
return FoundConformance.first;
}

unsigned failedGeneration = ConformanceCacheGeneration;

// If we didn't have an up-to-date cache entry, scan the conformance records.
C.SectionsToScanLock.lock();
unsigned failedGeneration = ConformanceCacheGeneration;

// If we have no new information to pull in (and nobody else pulled in
// new information while we waited on the lock), we're done.
Expand Down
65 changes: 44 additions & 21 deletions test/Driver/sanitizers.swift
Original file line number Diff line number Diff line change
@@ -1,31 +1,54 @@
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-macosx10.9 %s | FileCheck -check-prefix=ASAN -check-prefix=OSX %s

// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-ios7.1 %s | FileCheck -check-prefix=ASAN -check-prefix=IOSSIM %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target arm64-apple-ios7.1 %s | FileCheck -check-prefix=ASAN -check-prefix=IOS %s

// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-tvos9.0 %s | FileCheck -check-prefix=ASAN -check-prefix=tvOS_SIM %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target arm64-apple-tvos9.0 %s | FileCheck -check-prefix=ASAN -check-prefix=tvOS %s

// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target i386-apple-watchos2.0 %s | FileCheck -check-prefix=ASAN -check-prefix=watchOS_SIM %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target armv7k-apple-watchos2.0 %s | FileCheck -check-prefix=ASAN -check-prefix=watchOS %s

// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-unknown-linux-gnu %s 2>&1 | FileCheck -check-prefix=LINUX %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-macosx10.9 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_OSX %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-ios7.1 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_IOSSIM %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target arm64-apple-ios7.1 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_IOS %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-apple-tvos9.0 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_tvOS_SIM %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target arm64-apple-tvos9.0 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_tvOS %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target i386-apple-watchos2.0 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_watchOS_SIM %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -target armv7k-apple-watchos2.0 %s | FileCheck -check-prefix=ASAN -check-prefix=ASAN_watchOS %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address -target x86_64-unknown-linux-gnu %s 2>&1 | FileCheck -check-prefix=ASAN_LINUX %s

// RUN: %swiftc_driver -driver-print-jobs -sanitize=thread -target x86_64-apple-macosx10.9 %s | FileCheck -check-prefix=TSAN -check-prefix=TSAN_OSX %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target x86-apple-macosx10.9 %s 2>&1 | FileCheck -check-prefix=TSAN_OSX_32 %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=thread -target x86_64-apple-ios7.1 %s | FileCheck -check-prefix=TSAN -check-prefix=TSAN_IOSSIM %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target arm64-apple-ios7.1 %s 2>&1 | FileCheck -check-prefix=TSAN_IOS %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=thread -target x86_64-apple-tvos9.0 %s | FileCheck -check-prefix=TSAN -check-prefix=TSAN_tvOS_SIM %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target arm64-apple-tvos9.0 %s 2>&1 | FileCheck -check-prefix=TSAN_tvOS %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target i386-apple-watchos2.0 %s 2>&1 | FileCheck -check-prefix=TSAN_watchOS_SIM %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target armv7k-apple-watchos2.0 %s 2>&1 | FileCheck -check-prefix=TSAN_watchOS %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=thread -target x86_64-unknown-linux-gnu %s 2>&1 | FileCheck -check-prefix=TSAN_LINUX %s

// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address,unknown %s 2>&1 | FileCheck -check-prefix=BADARG %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address -sanitize=unknown %s 2>&1 | FileCheck -check-prefix=BADARG %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address,thread %s 2>&1 | FileCheck -check-prefix=INCOMPATIBLESANITIZERS %s

// ASAN: swift
// ASAN: -sanitize=address

// OSX: lib/swift/clang/lib/darwin/libclang_rt.asan_osx_dynamic.dylib
// IOS: lib/swift/clang/lib/darwin/libclang_rt.asan_ios_dynamic.dylib
// IOSSIM: lib/swift/clang/lib/darwin/libclang_rt.asan_iossim_dynamic.dylib
// tvOS: lib/swift/clang/lib/darwin/libclang_rt.asan_tvos_dynamic.dylib
// tvOS_SIM: lib/swift/clang/lib/darwin/libclang_rt.asan_tvossim_dynamic.dylib
// watchOS: lib/swift/clang/lib/darwin/libclang_rt.asan_watchos_dynamic.dylib
// watchOS_SIM: lib/swift/clang/lib/darwin/libclang_rt.asan_watchossim_dynamic.dylib
// LINUX: unsupported option 'sanitize=address' for target 'x86_64-unknown-linux-gnu'
// ASAN_OSX: lib/swift/clang/lib/darwin/libclang_rt.asan_osx_dynamic.dylib
// ASAN_IOSSIM: lib/swift/clang/lib/darwin/libclang_rt.asan_iossim_dynamic.dylib
// ASAN_IOS: lib/swift/clang/lib/darwin/libclang_rt.asan_ios_dynamic.dylib
// ASAN_tvOS_SIM: lib/swift/clang/lib/darwin/libclang_rt.asan_tvossim_dynamic.dylib
// ASAN_tvOS: lib/swift/clang/lib/darwin/libclang_rt.asan_tvos_dynamic.dylib
// ASAN_watchOS_SIM: lib/swift/clang/lib/darwin/libclang_rt.asan_watchossim_dynamic.dylib
// ASAN_watchOS: lib/swift/clang/lib/darwin/libclang_rt.asan_watchos_dynamic.dylib
// ASAN_LINUX: unsupported option '-sanitize=address' for target 'x86_64-unknown-linux-gnu'

// ASAN: -rpath @executable_path

// BADARG: unsupported argument 'unknown' to option 'sanitize='
// TSAN: swift
// TSAN: -sanitize=thread

// TSAN_OSX: lib/swift/clang/lib/darwin/libclang_rt.tsan_osx_dynamic.dylib
// TSAN_OSX_32: unsupported option '-sanitize=thread' for target 'x86-apple-macosx10.9'
// TSAN_IOSSIM: lib/swift/clang/lib/darwin/libclang_rt.tsan_iossim_dynamic.dylib
// TSAN_IOS: unsupported option '-sanitize=thread' for target 'arm64-apple-ios7.1'
// TSAN_tvOS_SIM: lib/swift/clang/lib/darwin/libclang_rt.tsan_tvossim_dynamic.dylib
// TSAN_tvOS: unsupported option '-sanitize=thread' for target 'arm64-apple-tvos9.0'
// TSAN_watchOS_SIM: unsupported option '-sanitize=thread' for target 'i386-apple-watchos2.0'
// TSAN_watchOS: unsupported option '-sanitize=thread' for target 'armv7k-apple-watchos2.0'
// TSAN_LINUX: unsupported option '-sanitize=thread' for target 'x86_64-unknown-linux-gnu'

// TSAN: -rpath @executable_path

// BADARG: unsupported argument 'unknown' to option '-sanitize='
// INCOMPATIBLESANITIZERS: argument '-sanitize=address' is not allowed with '-sanitize=thread'
2 changes: 2 additions & 0 deletions test/IRGen/sanitizer-attributes.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// This test verifies that we add the function attributes used by sanitizers.

// RUN: %target-swift-frontend -emit-ir -sanitize=address %s | FileCheck %s -check-prefix=ASAN
// RUN: %target-swift-frontend -emit-ir -target x86_64-apple-macosx10.9 -sanitize=thread %s | FileCheck %s -check-prefix=TSAN

// XFAIL: linux

func test() {
}

// ASAN: Function Attrs: sanitize_address
// TSAN: Function Attrs: sanitize_thread

0 comments on commit 5d34f66

Please sign in to comment.