• Like

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.

Beyond build and analyze

  • 1,142 views
Uploaded on

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.

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
1,142
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
36
Comments
0
Likes
1

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide












































































Transcript

  • 1. Beyond Build & Analyze
  • 2. Build & Analyze • based on Clang • inspects abstract syntax tree • limited set of specific rules • getting edge-cases right is not easy
  • 3. Pseudocode Example dealloc testing if: not GC only
  • 4. Pseudocode Example dealloc testing if: not GC only && class contains pointer ivars
  • 5. Pseudocode Example dealloc testing if: not GC only && class contains pointer ivars that are NOT IBOutlets
  • 6. Pseudocode Example dealloc testing if: not GC only && class contains pointer ivars that are NOT IBOutlets && NOT IBOutletCollections
  • 7. Pseudocode Example dealloc testing if: not GC only && class contains pointer ivars that are NOT IBOutlets && NOT IBOutletCollections && class extends NSObject
  • 8. 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
  • 9. 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
  • 10. 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!
  • 11. 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
  • 12. 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]
  • 13. 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!
  • 14. 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…)
  • 15. Pseudocode Example dealloc testing (2)
  • 16. Pseudocode Example dealloc testing (2) for each @property
  • 17. Pseudocode Example dealloc testing (2) for each @property that is an ObjC pointer
  • 18. Pseudocode Example dealloc testing (2) for each @property that is an ObjC pointer and is not read-only
  • 19. 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)
  • 20. 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
  • 21. 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:
  • 22. 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
  • 23. 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
  • 24. 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
  • 25. 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!
  • 26. 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
  • 27. 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...
  • 28. (Ab/Mis)Using Clang
  • 29. 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
  • 30. Aletheiacode Example diving into the analyzer class AnalysisConsumer : public ASTConsumer { … }
  • 31. 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() {} };
  • 32. 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; } … }
  • 33. 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)
  • 34. 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); }
  • 35. Enough of this talk, let’s build something
  • 36. -[UIViewController viewDidUnload]
  • 37. -[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; }
  • 38. -[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; }
  • 39. -[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; }
  • 40. -[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()); } } }
  • 41. Klee http://klee.llvm.org
  • 42. Klee - WTF?
  • 43. Klee - WTF? • ‘Symbolic virtual machine’
  • 44. Klee - WTF? • ‘Symbolic virtual machine’ • Automatic test generation
  • 45. Klee - WTF? • ‘Symbolic virtual machine’ • Automatic test generation • Truth tree-style construction
  • 46. Worked Example int my_islower(int x) { if (x >= 'a' && x <= 'z') return 1; else return 0; }
  • 47. Worked Example int my_islower(int x) { if (x >= 'a' && x <= 'z') return 1; else return 0; } exit
  • 48. Worked Example int my_islower(int x) { if (x >= 'a' && x <= 'z') return 1; else return 0; } exit return 1 ‘a’≤x≤‘z’
  • 49. 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’
  • 50. 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); }
  • 51. 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
  • 52. 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'
  • 53. 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
  • 54. 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)
  • 55. Klee drawbacks • Limited ability to inspect unmodified code • Brings its own libc • Full feature set needs instrumentation • Exponential growth of test cases
  • 56. 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)
  • 57. iamleeg
  • 58. iamleeg