Loading…

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

Like this presentation? Why not share!

Beyond build and analyze

on

  • 1,423 views

A look at how the clang static analyser works and how third-party developers can add their own analysis rules, and at the klee symbolic code executor in LLVM.

A look at how the clang static analyser works and how third-party developers can add their own analysis rules, and at the klee symbolic code executor in LLVM.

Statistics

Views

Total Views
1,423
Views on SlideShare
1,398
Embed Views
25

Actions

Likes
0
Downloads
36
Comments
0

1 Embed 25

http://coderwall.com 25

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />

Beyond build and analyze Beyond build and analyze Presentation Transcript

  • Beyond Build & Analyze
  • Build & Analyze • based on Clang • inspects abstract syntax tree • limited set of specific rules • getting edge-cases right is not easy
  • Pseudocode Example dealloc testing if: not GC only
  • Pseudocode Example dealloc testing if: not GC only && class contains pointer ivars
  • Pseudocode Example dealloc testing if: not GC only && class contains pointer ivars that are NOT IBOutlets
  • Pseudocode Example dealloc testing if: not GC only && class contains pointer ivars that are NOT IBOutlets && NOT IBOutletCollections
  • Pseudocode Example dealloc testing if: not GC only && class contains pointer ivars that are NOT IBOutlets && NOT IBOutletCollections && class extends NSObject
  • Pseudocode Example dealloc testing if: not GC only && class contains pointer ivars that are NOT IBOutlets && NOT IBOutletCollections && class extends NSObject && class does not extend SenTestCase
  • Pseudocode Example dealloc testing if: not GC only && class contains pointer ivars that are NOT IBOutlets && NOT IBOutletCollections && class extends NSObject && class does not extend SenTestCase if no -dealloc found
  • Pseudocode Example dealloc testing if: not GC only && class contains pointer ivars that are NOT IBOutlets && NOT IBOutletCollections && class extends NSObject && class does not extend SenTestCase if no -dealloc found -> REPORT!
  • Pseudocode Example dealloc testing if: not GC only && class contains pointer ivars that are NOT IBOutlets && NOT IBOutletCollections && class extends NSObject && class does not extend SenTestCase if no -dealloc found -> REPORT! else
  • Pseudocode Example dealloc testing if: not GC only && class contains pointer ivars that are NOT IBOutlets && NOT IBOutletCollections && class extends NSObject && class does not extend SenTestCase if no -dealloc found -> REPORT! else if missing [super dealloc]
  • Pseudocode Example dealloc testing if: not GC only && class contains pointer ivars that are NOT IBOutlets && NOT IBOutletCollections && class extends NSObject && class does not extend SenTestCase if no -dealloc found -> REPORT! else if missing [super dealloc] -> REPORT!
  • Pseudocode Example dealloc testing if: not GC only && class contains pointer ivars that are NOT IBOutlets && NOT IBOutletCollections && class extends NSObject && class does not extend SenTestCase if no -dealloc found -> REPORT! else if missing [super dealloc] -> REPORT! (…wait, there’s more…)
  • Pseudocode Example dealloc testing (2)
  • Pseudocode Example dealloc testing (2) for each @property
  • Pseudocode Example dealloc testing (2) for each @property that is an ObjC pointer
  • Pseudocode Example dealloc testing (2) for each @property that is an ObjC pointer and is not read-only
  • Pseudocode Example dealloc testing (2) for each @property that is an ObjC pointer and is not read-only and is declared retain or copy (not assign)
  • Pseudocode Example dealloc testing (2) for each @property that is an ObjC pointer and is not read-only and is declared retain or copy (not assign) and has a synthesized implementation
  • Pseudocode Example dealloc testing (2) for each @property that is an ObjC pointer and is not read-only and is declared retain or copy (not assign) and has a synthesized implementation if -dealloc doesn’t:
  • Pseudocode Example dealloc testing (2) for each @property that is an ObjC pointer and is not read-only and is declared retain or copy (not assign) and has a synthesized implementation if -dealloc doesn’t: [prop release] or
  • Pseudocode Example dealloc testing (2) for each @property that is an ObjC pointer and is not read-only and is declared retain or copy (not assign) and has a synthesized implementation if -dealloc doesn’t: [prop release] or [self setProp: nil] or
  • Pseudocode Example dealloc testing (2) for each @property that is an ObjC pointer and is not read-only and is declared retain or copy (not assign) and has a synthesized implementation if -dealloc doesn’t: [prop release] or [self setProp: nil] or self.prop = nil
  • Pseudocode Example dealloc testing (2) for each @property that is an ObjC pointer and is not read-only and is declared retain or copy (not assign) and has a synthesized implementation if -dealloc doesn’t: [prop release] or [self setProp: nil] or self.prop = nil -> REPORT!
  • Pseudocode Example dealloc testing (2) for each @property that is an ObjC pointer and is not read-only and is declared retain or copy (not assign) and has a synthesized implementation if -dealloc doesn’t: [prop release] or [self setProp: nil] or self.prop = nil -> REPORT! And we still miss non-@property ivars, CFTypes and
  • Pseudocode Example dealloc testing (2) for each @property that is an ObjC pointer and is not read-only and is declared retain or copy (not assign) and has a synthesized implementation if -dealloc doesn’t: [prop release] or [self setProp: nil] or self.prop = nil -> REPORT! And we still miss non-@property ivars, CFTypes and malloc()ed memory...
  • (Ab/Mis)Using Clang
  • Caveat lexor • Clang’s API is not stable • Neither is LLVM’s • None of the examples work • …except the static analyzer, let’s start there
  • Aletheiacode Example diving into the analyzer class AnalysisConsumer : public ASTConsumer { … }
  • Aletheiacode Example diving into the analyzer class AnalysisConsumer : public ASTConsumer { … } class ASTConsumer { public: virtual void HandleTopLevelDecl(DeclGroupRef D); virtual void HandleTranslationUnit(ASTContext &Ctx) {} virtual void HandleTagDeclDefinition(TagDecl *D) {} virtual void CompleteTentativeDefinition(VarDecl *D) {} virtual void HandleVTable(CXXRecordDecl *RD, bool DefinitionRequired) {} virtual void PrintStats() {} };
  • Aletheiacode Example diving into the analyzer void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { TranslationUnitDecl *TU = C.getTranslationUnitDecl(); for (DeclContext::decl_iterator I = TU->decls_begin(), E = TU->decls_end(); I != E; ++I) { Decl *D = *I; switch (D->getKind()) { case Decl::ObjCMethod: { ObjCMethodDecl* MD = cast<ObjCMethodDecl>(D); if (MD->isThisDeclarationADefinition()) { … HandleCode(MD, MD->getBody(), ObjCMethodActions); } break; } … }
  • Aletheiacode Example diving into the analyzer ANALYSIS(WarnObjCDealloc, "analyzer-check-objc-missing-dealloc", "Warn about Objective-C classes that lack a correct implementation of - dealloc", ObjCImplementation)
  • Aletheiacode Example diving into the analyzer ANALYSIS(WarnObjCDealloc, "analyzer-check-objc-missing-dealloc", "Warn about Objective-C classes that lack a correct implementation of - dealloc", ObjCImplementation) static void ActionWarnObjCDealloc(AnalysisConsumer &C, AnalysisManager& mgr, Decl *D) { if (mgr.getLangOptions().getGCMode() == LangOptions::GCOnly) return; BugReporter BR(mgr); CheckObjCDealloc(cast<ObjCImplementationDecl>(D), mgr.getLangOptions(), BR); }
  • Enough of this talk, let’s build something
  • -[UIViewController viewDidUnload]
  • -[UIViewController viewDidUnload] void clang::CheckViewControllerDidUnload(const ObjCImplementationDecl *D, const LangOptions &LOpts, BugReporter &BR) { //we only care about subclasses of UIViewController. ASTContext &Ctx = BR.getContext(); IdentifierInfo *uiViewControllerInfo = &Ctx.Idents.get("UIViewController"); const ObjCInterfaceDecl *thisClassInterface = D->getClassInterface(); const ObjCInterfaceDecl *aClassInterface = thisClassInterface; for (; aClassInterface; aClassInterface = aClassInterface->getSuperClass()) { IdentifierInfo *classID = aClassInterface->getIdentifier(); if (classID == uiViewControllerInfo) { break; } } if (!aClassInterface) { //we never found UIViewController. return; }
  • -[UIViewController viewDidUnload] //Find the -viewDidUnload implementation IdentifierInfo *viewDidUnloadInfo = &Ctx.Idents.get("viewDidUnload"); Selector viewDidUnloadSel = Ctx.Selectors.getSelector(0, &viewDidUnloadInfo); ObjCMethodDecl *viewDidUnloadDecl = 0; for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(), E = D->instmeth_end(); I!=E; ++I) { if ((*I)->getSelector() == viewDidUnloadSel) { viewDidUnloadDecl = *I; break; } } //we didn't find -viewDidUnload if (!viewDidUnloadDecl) { const char *name = "Class doesn't implement -viewDidUnload"; const char *category = "Memory (UIKit)"; std::string reportBuffer; llvm::raw_string_ostream os(reportBuffer); os << "The UIViewController subclass " << thisClassInterface << " doesn't implement viewDidUnload. " "While this is not required, it means instances could miss the chance to respond to low " "memory warnings efficiently."; BR.EmitBasicReport(name, category, os.str(), D->getLocation()); return; }
  • -[UIViewController viewDidUnload] IdentifierInfo *selfIdentifier = &Ctx.Idents.get("self"); //iterate over the properties for (ObjCImplementationDecl::propimpl_iterator I = D->propimpl_begin(), E = D->propimpl_end(); I != E; I++) { //skip dynamic or user-supplied properties if ((*I)->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) { continue; } //only writable IBOutlets ObjCIvarDecl *ivarDecl = (*I)->getPropertyIvarDecl(); if (!ivarDecl) { continue; } ObjCPropertyDecl *propertyDecl = (*I)->getPropertyDecl(); if (!propertyDecl || propertyDecl->isReadOnly() || (!(ivarDecl->getAttr<IBOutletAttr>()) && !(propertyDecl->getAttr<IBOutletAttr>()))) { continue; } //that are not 'assign' properties if (propertyDecl->getSetterKind() == ObjCPropertyDecl::Assign) { continue; }
  • -[UIViewController viewDidUnload] //check for release! if (!scan_ivar_release(viewDidUnloadDecl->getBody(), ivarDecl, propertyDecl, viewDidUnloadSel, selfIdentifier, Ctx)) { const char *name = "dangling IBOutlet in -viewDidUnload"; const char *category = "Memory (UIKit)"; std::string reportBuffer; llvm::raw_string_ostream os(reportBuffer); os << "The ivar '" << ivarDecl << "' represents a retained IBOutlet " "that wasn't released in '-viewDidUnload'. Releasing the property " "here relinquishes more memory when a low memory warning is received."; BR.EmitBasicReport(name, category, os.str(), (*I)->getLocation()); } } }
  • Klee http://klee.llvm.org
  • Klee - WTF?
  • Klee - WTF? • ‘Symbolic virtual machine’
  • Klee - WTF? • ‘Symbolic virtual machine’ • Automatic test generation
  • Klee - WTF? • ‘Symbolic virtual machine’ • Automatic test generation • Truth tree-style construction
  • Worked Example int my_islower(int x) { if (x >= 'a' && x <= 'z') return 1; else return 0; }
  • Worked Example int my_islower(int x) { if (x >= 'a' && x <= 'z') return 1; else return 0; } exit
  • Worked Example int my_islower(int x) { if (x >= 'a' && x <= 'z') return 1; else return 0; } exit return 1 ‘a’≤x≤‘z’
  • Worked Example int my_islower(int x) { if (x >= 'a' && x <= 'z') return 1; else return 0; } exit return 1 return 0 ‘a’≤x≤‘z’ x<‘a’ x>‘z’
  • Worked Example #include <klee/klee.h> int my_islower(int x) { if (x >= 'a' && x <= 'z') return 1; else return 0; } int main() { char c; klee_make_symbolic(&c, sizeof(c), "input"); return my_islower(c); }
  • Worked Example $ llvm-gcc --emit-llvm -I../path/to/klee/include/ -c -g klee-test.c $ klee klee-test.o KLEE: output directory = "klee-out-0" KLEE: done: total instructions = 70 KLEE: done: completed paths = 3 KLEE: done: generated tests = 3
  • Worked Example ktest file : 'klee-last/test000001.ktest' args : ['klee-test.o'] num objects: 1 object 0: name: 'input' object 0: size: 1 object 0: data: 'b' ktest file : 'klee-last/test000002.ktest' args : ['klee-test.o'] num objects: 1 object 0: name: 'input' object 0: size: 1 object 0: data: '~' ktest file : 'klee-last/test000003.ktest' args : ['klee-test.o'] num objects: 1 object 0: name: 'input' object 0: size: 1 object 0: data: 'x00'
  • Worked Example $ gcc -I/path/to/klee/include /path/to/klee/ Release/lib/libkleeRuntest.dylib klee-test.c $ KTEST_FILE=klee-last/test000001.ktest ./ a.out $ echo $? 1
  • Klee Capabilities • Array index failures automatically reported • NULL pointer dereferences • Codify constraints with klee_assume() • Runs wherever LLVM bitcode can be emitted (C, C++, ObjC, Java, Fortran, Erlang, OpenCL, .Net…) • Easily provides 100% coverage (except in presence of dead code)
  • Klee drawbacks • Limited ability to inspect unmodified code • Brings its own libc • Full feature set needs instrumentation • Exponential growth of test cases
  • Klee Summary • Symbolic test generation is cool • NOT a substitute for TDD • Great for testing: file import, network protocols, device drivers… • …anything where the range of inputs is too big to reasonably write comprehensive unit tests (and you can’t afford an intern)
  • iamleeg
  • iamleeg