This revision was automatically updated to reflect the committed changes. Closed by commit rC327309: [analyzer] Move the GCDAsyncSemaphoreChecker to optin.performance (authored by george.karpenkov, committed by ). Herald added a subscriber: cfe-commits.
Changed prior to commit: https://reviews.llvm.org/D44228?vs=137823&id=138063#toc Repository: rC Clang https://reviews.llvm.org/D44228 Files: include/clang/StaticAnalyzer/Checkers/Checkers.td lib/StaticAnalyzer/Checkers/CMakeLists.txt lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp lib/StaticAnalyzer/Checkers/GCDAsyncSemaphoreChecker.cpp test/Analysis/gcdantipatternchecker_test.m test/Analysis/gcdasyncsemaphorechecker_test.m
Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -610,12 +610,12 @@ } // end "osx.cocoa" -let ParentPackage = OSXAlpha in { +let ParentPackage = Performance in { -def GCDAsyncSemaphoreChecker : Checker<"GCDAsyncSemaphore">, - HelpText<"Checker for performance anti-pattern when using semaphors from async API">, - DescFile<"GCDAsyncSemaphoreChecker.cpp">; -} // end "alpha.osx" +def GCDAntipattern : Checker<"GCDAntipattern">, + HelpText<"Check for performance anti-patterns when using Grand Central Dispatch">, + DescFile<"GCDAntipatternChecker.cpp">; +} // end "optin.performance" let ParentPackage = CocoaAlpha in { Index: test/Analysis/gcdantipatternchecker_test.m =================================================================== --- test/Analysis/gcdantipatternchecker_test.m +++ test/Analysis/gcdantipatternchecker_test.m @@ -0,0 +1,266 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,optin.performance.GCDAntipattern %s -fblocks -verify +typedef signed char BOOL; +@protocol NSObject - (BOOL)isEqual:(id)object; @end +@interface NSObject <NSObject> {} ++(id)alloc; +-(id)init; +-(id)autorelease; +-(id)copy; +-(id)retain; +@end + +typedef int dispatch_semaphore_t; +typedef void (^block_t)(); + +dispatch_semaphore_t dispatch_semaphore_create(int); +void dispatch_semaphore_wait(dispatch_semaphore_t, int); +void dispatch_semaphore_signal(dispatch_semaphore_t); + +void func(void (^)(void)); +void func_w_typedef(block_t); + +int coin(); + +void use_semaphor_antipattern() { + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + + func(^{ + dispatch_semaphore_signal(sema); + }); + dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}} +} + +// It's OK to use pattern in tests. +// We simply match the containing function name against ^test. +void test_no_warning() { + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + + func(^{ + dispatch_semaphore_signal(sema); + }); + dispatch_semaphore_wait(sema, 100); +} + +void use_semaphor_antipattern_multiple_times() { + dispatch_semaphore_t sema1 = dispatch_semaphore_create(0); + + func(^{ + dispatch_semaphore_signal(sema1); + }); + dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}} + + dispatch_semaphore_t sema2 = dispatch_semaphore_create(0); + + func(^{ + dispatch_semaphore_signal(sema2); + }); + dispatch_semaphore_wait(sema2, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}} +} + +void use_semaphor_antipattern_multiple_wait() { + dispatch_semaphore_t sema1 = dispatch_semaphore_create(0); + + func(^{ + dispatch_semaphore_signal(sema1); + }); + // FIXME: multiple waits on same semaphor should not raise a warning. + dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}} + dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}} +} + +void warn_incorrect_order() { + // FIXME: ASTMatchers do not allow ordered matching, so would match even + // if out of order. + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + + dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}} + func(^{ + dispatch_semaphore_signal(sema); + }); +} + +void warn_w_typedef() { + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + + func_w_typedef(^{ + dispatch_semaphore_signal(sema); + }); + dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}} +} + +void warn_nested_ast() { + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + + if (coin()) { + func(^{ + dispatch_semaphore_signal(sema); + }); + } else { + func(^{ + dispatch_semaphore_signal(sema); + }); + } + dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}} +} + +void use_semaphore_assignment() { + dispatch_semaphore_t sema; + sema = dispatch_semaphore_create(0); + + func(^{ + dispatch_semaphore_signal(sema); + }); + dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}} +} + +void use_semaphore_assignment_init() { + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + sema = dispatch_semaphore_create(1); + + func(^{ + dispatch_semaphore_signal(sema); + }); + dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}} +} + +void differentsemaphoreok() { + dispatch_semaphore_t sema1 = dispatch_semaphore_create(0); + dispatch_semaphore_t sema2 = dispatch_semaphore_create(0); + + func(^{ + dispatch_semaphore_signal(sema1); + }); + dispatch_semaphore_wait(sema2, 100); // no-warning +} + +void nosignalok() { + dispatch_semaphore_t sema1 = dispatch_semaphore_create(0); + dispatch_semaphore_wait(sema1, 100); +} + +void nowaitok() { + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + func(^{ + dispatch_semaphore_signal(sema); + }); +} + +void noblockok() { + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + dispatch_semaphore_signal(sema); + dispatch_semaphore_wait(sema, 100); +} + +void storedblockok() { + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + block_t b = ^{ + dispatch_semaphore_signal(sema); + }; + dispatch_semaphore_wait(sema, 100); +} + +void passed_semaphore_ok(dispatch_semaphore_t sema) { + func(^{ + dispatch_semaphore_signal(sema); + }); + dispatch_semaphore_wait(sema, 100); +} + +void warn_with_cast() { + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + + func(^{ + dispatch_semaphore_signal((int)sema); + }); + dispatch_semaphore_wait((int)sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}} +} + +@interface MyInterface1 : NSObject +-(void)use_method_warn; +-(void)use_objc_callback_warn; +-(void)testNoWarn; +-(void)acceptBlock:(block_t)callback; +@end + +@implementation MyInterface1 + +-(void)use_method_warn { + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + + func(^{ + dispatch_semaphore_signal(sema); + }); + dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}} +} + +-(void)testNoWarn { + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + + func(^{ + dispatch_semaphore_signal(sema); + }); + dispatch_semaphore_wait(sema, 100); +} + +-(void)acceptBlock:(block_t) callback { + callback(); +} + +-(void)use_objc_callback_warn { + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + + [self acceptBlock:^{ + dispatch_semaphore_signal(sema); + }]; + dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}} +} + +void use_objc_and_c_callback(MyInterface1 *t) { + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + + func(^{ + dispatch_semaphore_signal(sema); + }); + dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}} + + dispatch_semaphore_t sema1 = dispatch_semaphore_create(0); + + [t acceptBlock:^{ + dispatch_semaphore_signal(sema1); + }]; + dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}} +} +@end + +// No warnings: class name contains "test" +@interface Test1 : NSObject +-(void)use_method_warn; +@end + +@implementation Test1 +-(void)use_method_warn { + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + + func(^{ + dispatch_semaphore_signal(sema); + }); + dispatch_semaphore_wait(sema, 100); +} +@end + + +// No warnings: class name contains "mock" +@interface Mock1 : NSObject +-(void)use_method_warn; +@end + +@implementation Mock1 +-(void)use_method_warn { + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + + func(^{ + dispatch_semaphore_signal(sema); + }); + dispatch_semaphore_wait(sema, 100); +} +@end Index: lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp +++ lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp @@ -0,0 +1,166 @@ +//===- GCDAntipatternChecker.cpp ---------------------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines GCDAntipatternChecker which checks against a common +// antipattern when synchronous API is emulated from asynchronous callbacks +// using a semaphore: +// +// dispatch_semaphore_t sema = dispatch_semaphore_create(0); +// +// AnyCFunctionCall(^{ +// // code… +// dispatch_semaphore_signal(sema); +// }) +// dispatch_semaphore_wait(sema, *) +// +// Such code is a common performance problem, due to inability of GCD to +// properly handle QoS when a combination of queues and semaphores is used. +// Good code would either use asynchronous API (when available), or perform +// the necessary action in asynchronous callback. +// +// Currently, the check is performed using a simple heuristical AST pattern +// matching. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/Support/Debug.h" + +using namespace clang; +using namespace ento; +using namespace ast_matchers; + +namespace { + +const char *WarningBinding = "semaphore_wait"; + +class GCDAntipatternChecker : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, + AnalysisManager &AM, + BugReporter &BR) const; +}; + +class Callback : public MatchFinder::MatchCallback { + BugReporter &BR; + const GCDAntipatternChecker *C; + AnalysisDeclContext *ADC; + +public: + Callback(BugReporter &BR, + AnalysisDeclContext *ADC, + const GCDAntipatternChecker *C) : BR(BR), C(C), ADC(ADC) {} + + virtual void run(const MatchFinder::MatchResult &Result) override; +}; + +auto callsName(const char *FunctionName) + -> decltype(callee(functionDecl())) { + return callee(functionDecl(hasName(FunctionName))); +} + +auto equalsBoundArgDecl(int ArgIdx, const char *DeclName) + -> decltype(hasArgument(0, expr())) { + return hasArgument(ArgIdx, ignoringParenCasts(declRefExpr( + to(varDecl(equalsBoundNode(DeclName)))))); +} + +auto bindAssignmentToDecl(const char *DeclName) -> decltype(hasLHS(expr())) { + return hasLHS(ignoringParenImpCasts( + declRefExpr(to(varDecl().bind(DeclName))))); +} + +void GCDAntipatternChecker::checkASTCodeBody(const Decl *D, + AnalysisManager &AM, + BugReporter &BR) const { + + // The pattern is very common in tests, and it is OK to use it there. + if (const auto* ND = dyn_cast<NamedDecl>(D)) { + std::string DeclName = ND->getNameAsString(); + if (StringRef(DeclName).startswith("test")) + return; + } + if (const auto *OD = dyn_cast<ObjCMethodDecl>(D)) { + if (const auto *CD = dyn_cast<ObjCContainerDecl>(OD->getParent())) { + std::string ContainerName = CD->getNameAsString(); + StringRef CN(ContainerName); + if (CN.contains_lower("test") || CN.contains_lower("mock")) + return; + } + } + + const char *SemaphoreBinding = "semaphore_name"; + auto SemaphoreCreateM = callExpr(callsName("dispatch_semaphore_create")); + + auto SemaphoreBindingM = anyOf( + forEachDescendant( + varDecl(hasDescendant(SemaphoreCreateM)).bind(SemaphoreBinding)), + forEachDescendant(binaryOperator(bindAssignmentToDecl(SemaphoreBinding), + hasRHS(SemaphoreCreateM)))); + + auto SemaphoreWaitM = forEachDescendant( + callExpr( + allOf( + callsName("dispatch_semaphore_wait"), + equalsBoundArgDecl(0, SemaphoreBinding) + ) + ).bind(WarningBinding)); + + auto HasBlockArgumentM = hasAnyArgument(hasType( + hasCanonicalType(blockPointerType()) + )); + + auto ArgCallsSignalM = hasArgument(0, hasDescendant(callExpr( + allOf( + callsName("dispatch_semaphore_signal"), + equalsBoundArgDecl(0, SemaphoreBinding) + )))); + + auto HasBlockAndCallsSignalM = allOf(HasBlockArgumentM, ArgCallsSignalM); + + auto AcceptsBlockM = + forEachDescendant( + stmt(anyOf( + callExpr(HasBlockAndCallsSignalM), + objcMessageExpr(HasBlockAndCallsSignalM) + ))); + + auto FinalM = compoundStmt(SemaphoreBindingM, SemaphoreWaitM, AcceptsBlockM); + + MatchFinder F; + Callback CB(BR, AM.getAnalysisDeclContext(D), this); + + F.addMatcher(FinalM, &CB); + F.match(*D->getBody(), AM.getASTContext()); +} + +void Callback::run(const MatchFinder::MatchResult &Result) { + const auto *SW = Result.Nodes.getNodeAs<CallExpr>(WarningBinding); + assert(SW); + BR.EmitBasicReport( + ADC->getDecl(), C, + /*Name=*/"Semaphore performance anti-pattern", + /*Category=*/"Performance", + "Waiting on a semaphore with Grand Central Dispatch creates useless " + "threads and is subject to priority inversion; consider " + "using a synchronous API or changing the caller to be asynchronous", + PathDiagnosticLocation::createBegin(SW, BR.getSourceManager(), ADC), + SW->getSourceRange()); +} + +} + +void ento::registerGCDAntipattern(CheckerManager &Mgr) { + Mgr.registerChecker<GCDAntipatternChecker>(); +} Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -37,7 +37,7 @@ DynamicTypeChecker.cpp ExprInspectionChecker.cpp FixedAddressChecker.cpp - GCDAsyncSemaphoreChecker.cpp + GCDAntipatternChecker.cpp GenericTaintChecker.cpp GTestChecker.cpp IdenticalExprChecker.cpp Index: test/Analysis/gcdasyncsemaphorechecker_test.m =================================================================== --- test/Analysis/gcdasyncsemaphorechecker_test.m +++ test/Analysis/gcdasyncsemaphorechecker_test.m @@ -1,234 +0,0 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.osx.GCDAsyncSemaphore %s -fblocks -verify -typedef signed char BOOL; -@protocol NSObject - (BOOL)isEqual:(id)object; @end -@interface NSObject <NSObject> {} -+(id)alloc; --(id)init; --(id)autorelease; --(id)copy; --(id)retain; -@end - -typedef int dispatch_semaphore_t; -typedef void (^block_t)(); - -dispatch_semaphore_t dispatch_semaphore_create(int); -void dispatch_semaphore_wait(dispatch_semaphore_t, int); -void dispatch_semaphore_signal(dispatch_semaphore_t); - -void func(void (^)(void)); -void func_w_typedef(block_t); - -int coin(); - -void use_semaphor_antipattern() { - dispatch_semaphore_t sema = dispatch_semaphore_create(0); - - func(^{ - dispatch_semaphore_signal(sema); - }); - dispatch_semaphore_wait(sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}} -} - -// It's OK to use pattern in tests. -// We simply match the containing function name against ^test. -void test_no_warning() { - dispatch_semaphore_t sema = dispatch_semaphore_create(0); - - func(^{ - dispatch_semaphore_signal(sema); - }); - dispatch_semaphore_wait(sema, 100); -} - -void use_semaphor_antipattern_multiple_times() { - dispatch_semaphore_t sema1 = dispatch_semaphore_create(0); - - func(^{ - dispatch_semaphore_signal(sema1); - }); - dispatch_semaphore_wait(sema1, 100); // expected-warning{{Possible semaphore performance anti-pattern}} - - dispatch_semaphore_t sema2 = dispatch_semaphore_create(0); - - func(^{ - dispatch_semaphore_signal(sema2); - }); - dispatch_semaphore_wait(sema2, 100); // expected-warning{{Possible semaphore performance anti-pattern}} -} - -void use_semaphor_antipattern_multiple_wait() { - dispatch_semaphore_t sema1 = dispatch_semaphore_create(0); - - func(^{ - dispatch_semaphore_signal(sema1); - }); - // FIXME: multiple waits on same semaphor should not raise a warning. - dispatch_semaphore_wait(sema1, 100); // expected-warning{{Possible semaphore performance anti-pattern}} - dispatch_semaphore_wait(sema1, 100); // expected-warning{{Possible semaphore performance anti-pattern}} -} - -void warn_incorrect_order() { - // FIXME: ASTMatchers do not allow ordered matching, so would match even - // if out of order. - dispatch_semaphore_t sema = dispatch_semaphore_create(0); - - dispatch_semaphore_wait(sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}} - func(^{ - dispatch_semaphore_signal(sema); - }); -} - -void warn_w_typedef() { - dispatch_semaphore_t sema = dispatch_semaphore_create(0); - - func_w_typedef(^{ - dispatch_semaphore_signal(sema); - }); - dispatch_semaphore_wait(sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}} -} - -void warn_nested_ast() { - dispatch_semaphore_t sema = dispatch_semaphore_create(0); - - if (coin()) { - func(^{ - dispatch_semaphore_signal(sema); - }); - } else { - func(^{ - dispatch_semaphore_signal(sema); - }); - } - dispatch_semaphore_wait(sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}} -} - -void use_semaphore_assignment() { - dispatch_semaphore_t sema; - sema = dispatch_semaphore_create(0); - - func(^{ - dispatch_semaphore_signal(sema); - }); - dispatch_semaphore_wait(sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}} -} - -void use_semaphore_assignment_init() { - dispatch_semaphore_t sema = dispatch_semaphore_create(0); - sema = dispatch_semaphore_create(1); - - func(^{ - dispatch_semaphore_signal(sema); - }); - dispatch_semaphore_wait(sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}} -} - -void differentsemaphoreok() { - dispatch_semaphore_t sema1 = dispatch_semaphore_create(0); - dispatch_semaphore_t sema2 = dispatch_semaphore_create(0); - - func(^{ - dispatch_semaphore_signal(sema1); - }); - dispatch_semaphore_wait(sema2, 100); // no-warning -} - -void nosignalok() { - dispatch_semaphore_t sema1 = dispatch_semaphore_create(0); - dispatch_semaphore_wait(sema1, 100); -} - -void nowaitok() { - dispatch_semaphore_t sema = dispatch_semaphore_create(0); - func(^{ - dispatch_semaphore_signal(sema); - }); -} - -void noblockok() { - dispatch_semaphore_t sema = dispatch_semaphore_create(0); - dispatch_semaphore_signal(sema); - dispatch_semaphore_wait(sema, 100); -} - -void storedblockok() { - dispatch_semaphore_t sema = dispatch_semaphore_create(0); - block_t b = ^{ - dispatch_semaphore_signal(sema); - }; - dispatch_semaphore_wait(sema, 100); -} - -void passed_semaphore_ok(dispatch_semaphore_t sema) { - func(^{ - dispatch_semaphore_signal(sema); - }); - dispatch_semaphore_wait(sema, 100); -} - -void warn_with_cast() { - dispatch_semaphore_t sema = dispatch_semaphore_create(0); - - func(^{ - dispatch_semaphore_signal((int)sema); - }); - dispatch_semaphore_wait((int)sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}} -} - -@interface Test1 : NSObject --(void)use_method_warn; --(void)use_objc_callback_warn; --(void)testNoWarn; --(void)acceptBlock:(block_t)callback; -@end - -@implementation Test1 - --(void)use_method_warn { - dispatch_semaphore_t sema = dispatch_semaphore_create(0); - - func(^{ - dispatch_semaphore_signal(sema); - }); - dispatch_semaphore_wait(sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}} -} - --(void)testNoWarn { - dispatch_semaphore_t sema = dispatch_semaphore_create(0); - - func(^{ - dispatch_semaphore_signal(sema); - }); - dispatch_semaphore_wait(sema, 100); -} - --(void)acceptBlock:(block_t) callback { - callback(); -} - --(void)use_objc_callback_warn { - dispatch_semaphore_t sema = dispatch_semaphore_create(0); - - [self acceptBlock:^{ - dispatch_semaphore_signal(sema); - }]; - dispatch_semaphore_wait(sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}} -} - -void use_objc_and_c_callback(Test1 *t) { - dispatch_semaphore_t sema = dispatch_semaphore_create(0); - - func(^{ - dispatch_semaphore_signal(sema); - }); - dispatch_semaphore_wait(sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}} - - dispatch_semaphore_t sema1 = dispatch_semaphore_create(0); - - [t acceptBlock:^{ - dispatch_semaphore_signal(sema1); - }]; - dispatch_semaphore_wait(sema1, 100); // expected-warning{{Possible semaphore performance anti-pattern}} -} - -@end Index: lib/StaticAnalyzer/Checkers/GCDAsyncSemaphoreChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/GCDAsyncSemaphoreChecker.cpp +++ lib/StaticAnalyzer/Checkers/GCDAsyncSemaphoreChecker.cpp @@ -1,159 +0,0 @@ -//===- GCDAsyncSemaphoreChecker.cpp -----------------------------*- C++ -*-==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines GCDAsyncSemaphoreChecker which checks against a common -// antipattern when synchronous API is emulated from asynchronous callbacks -// using a semaphor: -// -// dispatch_semapshore_t sema = dispatch_semaphore_create(0); - -// AnyCFunctionCall(^{ -// // code… -// dispatch_semapshore_signal(sema); -// }) -// dispatch_semapshore_wait(sema, *) -// -// Such code is a common performance problem, due to inability of GCD to -// properly handle QoS when a combination of queues and semaphors is used. -// Good code would either use asynchronous API (when available), or perform -// the necessary action in asynchronous callback. -// -// Currently, the check is performed using a simple heuristical AST pattern -// matching. -// -//===----------------------------------------------------------------------===// - -#include "ClangSACheckers.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" -#include "llvm/Support/Debug.h" - -#define DEBUG_TYPE "gcdasyncsemaphorechecker" - -using namespace clang; -using namespace ento; -using namespace ast_matchers; - -namespace { - -const char *WarningBinding = "semaphore_wait"; - -class GCDAsyncSemaphoreChecker : public Checker<check::ASTCodeBody> { -public: - void checkASTCodeBody(const Decl *D, - AnalysisManager &AM, - BugReporter &BR) const; -}; - -class Callback : public MatchFinder::MatchCallback { - BugReporter &BR; - const GCDAsyncSemaphoreChecker *C; - AnalysisDeclContext *ADC; - -public: - Callback(BugReporter &BR, - AnalysisDeclContext *ADC, - const GCDAsyncSemaphoreChecker *C) : BR(BR), C(C), ADC(ADC) {} - - virtual void run(const MatchFinder::MatchResult &Result) override; -}; - -auto callsName(const char *FunctionName) - -> decltype(callee(functionDecl())) { - return callee(functionDecl(hasName(FunctionName))); -} - -auto equalsBoundArgDecl(int ArgIdx, const char *DeclName) - -> decltype(hasArgument(0, expr())) { - return hasArgument(ArgIdx, ignoringParenCasts(declRefExpr( - to(varDecl(equalsBoundNode(DeclName)))))); -} - -auto bindAssignmentToDecl(const char *DeclName) -> decltype(hasLHS(expr())) { - return hasLHS(ignoringParenImpCasts( - declRefExpr(to(varDecl().bind(DeclName))))); -} - -void GCDAsyncSemaphoreChecker::checkASTCodeBody(const Decl *D, - AnalysisManager &AM, - BugReporter &BR) const { - - // The pattern is very common in tests, and it is OK to use it there. - if (const auto* ND = dyn_cast<NamedDecl>(D)) { - std::string DeclName = ND->getNameAsString(); - if (StringRef(DeclName).startswith("test")) - return; - } - - const char *SemaphoreBinding = "semaphore_name"; - auto SemaphoreCreateM = callExpr(callsName("dispatch_semaphore_create")); - - auto SemaphoreBindingM = anyOf( - forEachDescendant( - varDecl(hasDescendant(SemaphoreCreateM)).bind(SemaphoreBinding)), - forEachDescendant(binaryOperator(bindAssignmentToDecl(SemaphoreBinding), - hasRHS(SemaphoreCreateM)))); - - auto SemaphoreWaitM = forEachDescendant( - callExpr( - allOf( - callsName("dispatch_semaphore_wait"), - equalsBoundArgDecl(0, SemaphoreBinding) - ) - ).bind(WarningBinding)); - - auto HasBlockArgumentM = hasAnyArgument(hasType( - hasCanonicalType(blockPointerType()) - )); - - auto ArgCallsSignalM = hasArgument(0, hasDescendant(callExpr( - allOf( - callsName("dispatch_semaphore_signal"), - equalsBoundArgDecl(0, SemaphoreBinding) - )))); - - auto HasBlockAndCallsSignalM = allOf(HasBlockArgumentM, ArgCallsSignalM); - - auto AcceptsBlockM = - forEachDescendant( - stmt(anyOf( - callExpr(HasBlockAndCallsSignalM), - objcMessageExpr(HasBlockAndCallsSignalM) - ))); - - auto FinalM = compoundStmt(SemaphoreBindingM, SemaphoreWaitM, AcceptsBlockM); - - MatchFinder F; - Callback CB(BR, AM.getAnalysisDeclContext(D), this); - - F.addMatcher(FinalM, &CB); - F.match(*D->getBody(), AM.getASTContext()); -} - -void Callback::run(const MatchFinder::MatchResult &Result) { - const auto *SW = Result.Nodes.getNodeAs<CallExpr>(WarningBinding); - assert(SW); - BR.EmitBasicReport( - ADC->getDecl(), C, - /*Name=*/"Semaphore performance anti-pattern", - /*Category=*/"Performance", - "Possible semaphore performance anti-pattern: wait on a semaphore " - "signalled to in a callback", - PathDiagnosticLocation::createBegin(SW, BR.getSourceManager(), ADC), - SW->getSourceRange()); -} - -} - -void ento::registerGCDAsyncSemaphoreChecker(CheckerManager &Mgr) { - Mgr.registerChecker<GCDAsyncSemaphoreChecker>(); -}
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits