ReportMemoryUsage.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /*
  2. * Copyright (C) 2012 Google Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are
  6. * met:
  7. *
  8. * * Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * * Redistributions in binary form must reproduce the above
  11. * copyright notice, this list of conditions and the following disclaimer
  12. * in the documentation and/or other materials provided with the
  13. * distribution.
  14. * * Neither the name of Google Inc. nor the names of its
  15. * contributors may be used to endorse or promote products derived from
  16. * this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. #include "clang/AST/AST.h"
  31. #include "clang/AST/ASTConsumer.h"
  32. #include "clang/AST/RecursiveASTVisitor.h"
  33. #include "clang/Frontend/CompilerInstance.h"
  34. #include "clang/Frontend/FrontendPluginRegistry.h"
  35. #include "llvm/Support/raw_ostream.h"
  36. using namespace clang;
  37. namespace {
  38. using namespace std;
  39. typedef vector<string> Strings;
  40. Strings instrumentationMethods;
  41. const char instrumentingMethodName[] = "reportMemoryUsage";
  42. class AddMemberCallVisitor : public RecursiveASTVisitor<AddMemberCallVisitor> {
  43. public:
  44. bool VisitCallExpr(CallExpr*);
  45. const Strings& instrumentedMembers() const { return m_instrumentedMembers; }
  46. private:
  47. Strings m_instrumentedMembers;
  48. };
  49. class ReportMemoryUsageVisitor : public RecursiveASTVisitor<ReportMemoryUsageVisitor> {
  50. public:
  51. explicit ReportMemoryUsageVisitor(CompilerInstance& instance, ASTContext* context)
  52. : m_instance(instance)
  53. , m_context(context) { }
  54. bool VisitCXXMethodDecl(clang::CXXMethodDecl*);
  55. private:
  56. void emitWarning(SourceLocation, const char* rawError);
  57. CXXMethodDecl* findInstrumentationMethod(CXXRecordDecl*);
  58. bool needsToBeInstrumented(const Type*);
  59. void checkMembersCoverage(const CXXRecordDecl* instrumentedClass, const Strings& instrumentedMembers, SourceLocation);
  60. CompilerInstance& m_instance;
  61. ASTContext* m_context;
  62. };
  63. class ReportMemoryUsageConsumer : public ASTConsumer {
  64. public:
  65. explicit ReportMemoryUsageConsumer(CompilerInstance& instance, ASTContext* context)
  66. : m_visitor(instance, context)
  67. {
  68. instrumentationMethods.push_back("addMember");
  69. instrumentationMethods.push_back("addRawBuffer");
  70. instrumentationMethods.push_back("addWeakPointer");
  71. instrumentationMethods.push_back("ignoreMember");
  72. }
  73. virtual void HandleTranslationUnit(clang::ASTContext& context)
  74. {
  75. m_visitor.TraverseDecl(context.getTranslationUnitDecl());
  76. }
  77. private:
  78. ReportMemoryUsageVisitor m_visitor;
  79. };
  80. class ReportMemoryUsageAction : public PluginASTAction {
  81. protected:
  82. ASTConsumer* CreateASTConsumer(CompilerInstance& CI, llvm::StringRef)
  83. {
  84. return new ReportMemoryUsageConsumer(CI, &CI.getASTContext());
  85. }
  86. bool ParseArgs(const CompilerInstance& CI, const vector<string>& args)
  87. {
  88. if (args.size() && args[0] == "help")
  89. llvm::errs() << m_helpText;
  90. return true;
  91. }
  92. static const char* m_helpText;
  93. };
  94. const char* ReportMemoryUsageAction::m_helpText =
  95. "This plugin is checking native memory instrumentation code.\n"
  96. "The class is instrumented if it has reportMemoryUsage member function.\n"
  97. "Sample:\n"
  98. "class InstrumentedClass : public BaseInstrumentedClass {\n"
  99. "public:\n"
  100. " void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const\n"
  101. " {\n"
  102. " MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::DOM);\n"
  103. " BaseInstrumentedClass::reportMemoryUsage(memoryObjectInfo);\n"
  104. " info.addMember(m_notInstrumentedObject);\n"
  105. " info.addMember(m_instrumentedObject);\n"
  106. " info.addRawBuffer(m_pointer, m_length);\n"
  107. " info.ignoreMember(m_pointerToInternalField);\n"
  108. " }\n"
  109. "\n"
  110. "private:\n"
  111. " NotInstrumentedClass* m_notInstrumentedPtr;\n"
  112. " InstrumentedClass m_instrumentedObject;\n"
  113. " long m_length;\n"
  114. " double* m_pointer;\n"
  115. " Data* m_pointerToInternalField;\n"
  116. "}\n";
  117. bool AddMemberCallVisitor::VisitCallExpr(CallExpr* callExpr)
  118. {
  119. CXXMemberCallExpr* methodCallExpr = dyn_cast<CXXMemberCallExpr>(callExpr);
  120. bool instrumented = false;
  121. if (methodCallExpr) {
  122. string methodName = methodCallExpr->getMethodDecl()->getNameAsString();
  123. Strings::iterator i = find(instrumentationMethods.begin(), instrumentationMethods.end(), methodName);
  124. instrumented = i != instrumentationMethods.end();
  125. }
  126. if (instrumented || !methodCallExpr) {
  127. for (CallExpr::arg_iterator i = callExpr->arg_begin(); i != callExpr->arg_end(); ++i) {
  128. Expr* expr = *i;
  129. while (ImplicitCastExpr::classof(expr))
  130. expr = static_cast<ImplicitCastExpr*>(expr)->getSubExpr();
  131. if (MemberExpr* memberExpr = dyn_cast<MemberExpr>(expr))
  132. m_instrumentedMembers.push_back(memberExpr->getMemberNameInfo().getAsString());
  133. }
  134. }
  135. return true;
  136. }
  137. bool ReportMemoryUsageVisitor::VisitCXXMethodDecl(clang::CXXMethodDecl* decl)
  138. {
  139. if (decl->doesThisDeclarationHaveABody() && decl->getNameAsString() == instrumentingMethodName) {
  140. FullSourceLoc fullLocation = m_context->getFullLoc(decl->getLocStart());
  141. if (fullLocation.isValid()) {
  142. AddMemberCallVisitor visitor;
  143. visitor.TraverseStmt(decl->getBody());
  144. checkMembersCoverage(decl->getParent(), visitor.instrumentedMembers(), decl->getLocStart());
  145. }
  146. }
  147. return true;
  148. }
  149. void ReportMemoryUsageVisitor::emitWarning(SourceLocation loc, const char* rawError)
  150. {
  151. FullSourceLoc full(loc, m_instance.getSourceManager());
  152. string err("[webkit-style] ");
  153. err += rawError;
  154. DiagnosticsEngine& diagnostic = m_instance.getDiagnostics();
  155. DiagnosticsEngine::Level level = diagnostic.getWarningsAsErrors() ? DiagnosticsEngine::Error : DiagnosticsEngine::Warning;
  156. unsigned id = diagnostic.getCustomDiagID(level, err);
  157. DiagnosticBuilder builder = diagnostic.Report(full, id);
  158. }
  159. CXXMethodDecl* ReportMemoryUsageVisitor::findInstrumentationMethod(CXXRecordDecl* record)
  160. {
  161. for (CXXRecordDecl::method_iterator m = record->method_begin(); m != record->method_end(); ++m) {
  162. if (m->getNameInfo().getAsString() == instrumentingMethodName)
  163. return *m;
  164. }
  165. return 0;
  166. }
  167. bool ReportMemoryUsageVisitor::needsToBeInstrumented(const Type* type)
  168. {
  169. if (type->isBuiltinType())
  170. return false;
  171. if (type->isEnumeralType())
  172. return false;
  173. if (type->isClassType()) {
  174. const RecordType* recordType = dyn_cast<RecordType>(type);
  175. if (recordType) {
  176. CXXRecordDecl* decl = dyn_cast<CXXRecordDecl>(recordType->getDecl());
  177. if (decl->getNameAsString() == "String")
  178. return true;
  179. if (!decl || !findInstrumentationMethod(decl))
  180. return false;
  181. }
  182. }
  183. if (type->isArrayType()) {
  184. const ArrayType* arrayType = dyn_cast<const ArrayType>(type);
  185. return needsToBeInstrumented(arrayType->getElementType().getTypePtr());
  186. }
  187. return true;
  188. }
  189. void ReportMemoryUsageVisitor::checkMembersCoverage(const CXXRecordDecl* instrumentedClass, const Strings& instrumentedMembers, SourceLocation location)
  190. {
  191. for (CXXRecordDecl::field_iterator i = instrumentedClass->field_begin(); i != instrumentedClass->field_end(); ++i) {
  192. string fieldName = i->getNameAsString();
  193. if (find(instrumentedMembers.begin(), instrumentedMembers.end(), fieldName) == instrumentedMembers.end()) {
  194. if (!needsToBeInstrumented(i->getType().getTypePtr()))
  195. continue;
  196. emitWarning(i->getSourceRange().getBegin(), "class member needs to be instrumented in reportMemoryUsage function");
  197. emitWarning(location, "located here");
  198. }
  199. }
  200. }
  201. } // namespace
  202. static FrontendPluginRegistry::Add<ReportMemoryUsageAction>
  203. X("report-memory-usage", "Checks reportMemoryUsage function consistency");