//===-- LeakDetector.cpp - Implement LeakDetector interface ---------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements the LeakDetector class. // //===----------------------------------------------------------------------===// #include "llvm/Support/LeakDetector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Streams.h" #include "llvm/System/RWMutex.h" #include "llvm/System/Threading.h" #include "llvm/Value.h" using namespace llvm; namespace { template struct VISIBILITY_HIDDEN PrinterTrait { static void print(const T* P) { cerr << P; } }; template<> struct VISIBILITY_HIDDEN PrinterTrait { static void print(const Value* P) { cerr << *P; } }; ManagedStatic LeakDetectorLock; template struct VISIBILITY_HIDDEN LeakDetectorImpl { explicit LeakDetectorImpl(const char* const name = "") : Cache(0), Name(name) { } void clear() { Cache = 0; Ts.clear(); } void setName(const char* n) { Name = n; } // Because the most common usage pattern, by far, is to add a // garbage object, then remove it immediately, we optimize this // case. When an object is added, it is not added to the set // immediately, it is added to the CachedValue Value. If it is // immediately removed, no set search need be performed. void addGarbage(const T* o) { if (llvm_is_multithreaded()) { sys::ScopedWriter Writer(&*LeakDetectorLock); if (Cache) { assert(Ts.count(Cache) == 0 && "Object already in set!"); Ts.insert(Cache); } Cache = o; } else { if (Cache) { assert(Ts.count(Cache) == 0 && "Object already in set!"); Ts.insert(Cache); } Cache = o; } } void removeGarbage(const T* o) { if (llvm_is_multithreaded()) { sys::ScopedWriter Writer(&*LeakDetectorLock); if (o == Cache) Cache = 0; // Cache hit else Ts.erase(o); } else { if (o == Cache) Cache = 0; // Cache hit else Ts.erase(o); } } bool hasGarbage(const std::string& Message) { addGarbage(0); // Flush the Cache if (llvm_is_multithreaded()) LeakDetectorLock->reader_acquire(); assert(Cache == 0 && "No value should be cached anymore!"); if (!Ts.empty()) { cerr << "Leaked " << Name << " objects found: " << Message << ":\n"; for (typename SmallPtrSet::iterator I = Ts.begin(), E = Ts.end(); I != E; ++I) { cerr << "\t"; PrinterTrait::print(*I); cerr << "\n"; } cerr << '\n'; if (llvm_is_multithreaded()) LeakDetectorLock->reader_release(); return true; } if (llvm_is_multithreaded()) LeakDetectorLock->reader_release(); return false; } private: SmallPtrSet Ts; const T* Cache; const char* Name; }; static ManagedStatic > Objects; static ManagedStatic > LLVMObjects; static void clearGarbage() { Objects->clear(); LLVMObjects->clear(); } } void LeakDetector::addGarbageObjectImpl(void *Object) { Objects->addGarbage(Object); } void LeakDetector::addGarbageObjectImpl(const Value *Object) { LLVMObjects->addGarbage(Object); } void LeakDetector::removeGarbageObjectImpl(void *Object) { Objects->removeGarbage(Object); } void LeakDetector::removeGarbageObjectImpl(const Value *Object) { LLVMObjects->removeGarbage(Object); } void LeakDetector::checkForGarbageImpl(const std::string &Message) { Objects->setName("GENERIC"); LLVMObjects->setName("LLVM"); // use non-short-circuit version so that both checks are performed if (Objects->hasGarbage(Message) | LLVMObjects->hasGarbage(Message)) cerr << "\nThis is probably because you removed an object, but didn't " << "delete it. Please check your code for memory leaks.\n"; // Clear out results so we don't get duplicate warnings on // next call... clearGarbage(); }