Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(29)

Side by Side Diff: tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp

Issue 207913002: Global cycle detection analysis. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Review comments Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tools/clang/blink_gc_plugin/Config.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // This clang plugin checks various invariants of the Blink garbage 5 // This clang plugin checks various invariants of the Blink garbage
6 // collection infrastructure. 6 // collection infrastructure.
7 // 7 //
8 // Errors are described at: 8 // Errors are described at:
9 // http://www.chromium.org/developers/blink-gc-plugin-errors 9 // http://www.chromium.org/developers/blink-gc-plugin-errors
10 10
11 #include "Config.h" 11 #include "Config.h"
12 #include "JsonWriter.h"
12 #include "RecordInfo.h" 13 #include "RecordInfo.h"
13 14
14 #include "clang/AST/AST.h" 15 #include "clang/AST/AST.h"
15 #include "clang/AST/ASTConsumer.h" 16 #include "clang/AST/ASTConsumer.h"
16 #include "clang/AST/RecursiveASTVisitor.h" 17 #include "clang/AST/RecursiveASTVisitor.h"
17 #include "clang/Frontend/CompilerInstance.h" 18 #include "clang/Frontend/CompilerInstance.h"
18 #include "clang/Frontend/FrontendPluginRegistry.h" 19 #include "clang/Frontend/FrontendPluginRegistry.h"
19 20
20 using namespace clang; 21 using namespace clang;
21 using std::string; 22 using std::string;
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
103 "[blink-gc] Field %0 requiring finalization declared here:"; 104 "[blink-gc] Field %0 requiring finalization declared here:";
104 105
105 const char kManualDispatchMethodNote[] = 106 const char kManualDispatchMethodNote[] =
106 "[blink-gc] Manual dispatch %0 declared here:"; 107 "[blink-gc] Manual dispatch %0 declared here:";
107 108
108 const char kDerivesNonStackAllocated[] = 109 const char kDerivesNonStackAllocated[] =
109 "[blink-gc] Stack-allocated class %0 derives class %1" 110 "[blink-gc] Stack-allocated class %0 derives class %1"
110 " which is not stack allocated."; 111 " which is not stack allocated.";
111 112
112 struct BlinkGCPluginOptions { 113 struct BlinkGCPluginOptions {
113 BlinkGCPluginOptions() : enable_oilpan(false) {} 114 BlinkGCPluginOptions() : enable_oilpan(false), detect_cycles(false) {}
114 bool enable_oilpan; 115 bool enable_oilpan;
116 bool detect_cycles;
115 std::set<std::string> ignored_classes; 117 std::set<std::string> ignored_classes;
116 std::set<std::string> checked_namespaces; 118 std::set<std::string> checked_namespaces;
117 std::vector<std::string> ignored_directories; 119 std::vector<std::string> ignored_directories;
118 }; 120 };
119 121
120 typedef std::vector<CXXRecordDecl*> RecordVector; 122 typedef std::vector<CXXRecordDecl*> RecordVector;
121 typedef std::vector<CXXMethodDecl*> MethodVector; 123 typedef std::vector<CXXMethodDecl*> MethodVector;
122 124
123 // Test if a template specialization is an instantiation. 125 // Test if a template specialization is an instantiation.
124 static bool IsTemplateInstantiation(CXXRecordDecl* record) { 126 static bool IsTemplateInstantiation(CXXRecordDecl* record) {
(...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after
478 }; 480 };
479 481
480 // Main class containing checks for various invariants of the Blink 482 // Main class containing checks for various invariants of the Blink
481 // garbage collection infrastructure. 483 // garbage collection infrastructure.
482 class BlinkGCPluginConsumer : public ASTConsumer { 484 class BlinkGCPluginConsumer : public ASTConsumer {
483 public: 485 public:
484 BlinkGCPluginConsumer(CompilerInstance& instance, 486 BlinkGCPluginConsumer(CompilerInstance& instance,
485 const BlinkGCPluginOptions& options) 487 const BlinkGCPluginOptions& options)
486 : instance_(instance), 488 : instance_(instance),
487 diagnostic_(instance.getDiagnostics()), 489 diagnostic_(instance.getDiagnostics()),
488 options_(options) { 490 options_(options),
491 json_(0) {
489 492
490 // Only check structures in the blink, WebCore and WebKit namespaces. 493 // Only check structures in the blink, WebCore and WebKit namespaces.
491 options_.checked_namespaces.insert("blink"); 494 options_.checked_namespaces.insert("blink");
492 options_.checked_namespaces.insert("WebCore"); 495 options_.checked_namespaces.insert("WebCore");
493 options_.checked_namespaces.insert("WebKit"); 496 options_.checked_namespaces.insert("WebKit");
494 497
495 // Ignore GC implementation files. 498 // Ignore GC implementation files.
496 options_.ignored_directories.push_back("/heap/"); 499 options_.ignored_directories.push_back("/heap/");
497 500
498 // Register warning/error messages. 501 // Register warning/error messages.
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
554 diag_overridden_non_virtual_trace_note_ = diagnostic_.getCustomDiagID( 557 diag_overridden_non_virtual_trace_note_ = diagnostic_.getCustomDiagID(
555 DiagnosticsEngine::Note, kOverriddenNonVirtualTraceNote); 558 DiagnosticsEngine::Note, kOverriddenNonVirtualTraceNote);
556 diag_manual_dispatch_method_note_ = diagnostic_.getCustomDiagID( 559 diag_manual_dispatch_method_note_ = diagnostic_.getCustomDiagID(
557 DiagnosticsEngine::Note, kManualDispatchMethodNote); 560 DiagnosticsEngine::Note, kManualDispatchMethodNote);
558 } 561 }
559 562
560 virtual void HandleTranslationUnit(ASTContext& context) { 563 virtual void HandleTranslationUnit(ASTContext& context) {
561 CollectVisitor visitor; 564 CollectVisitor visitor;
562 visitor.TraverseDecl(context.getTranslationUnitDecl()); 565 visitor.TraverseDecl(context.getTranslationUnitDecl());
563 566
567 if (options_.detect_cycles) {
568 string err;
569 // TODO: Make createDefaultOutputFile or a shorter createOutputFile work.
570 json_ = JsonWriter::from(instance_.createOutputFile(
571 "", // OutputPath
572 err, // Errors
573 true, // Binary
574 true, // RemoveFileOnSignal
575 instance_.getFrontendOpts().OutputFile, // BaseInput
576 "graph.json", // Extension
577 false, // UseTemporary
578 false, // CreateMissingDirectories
579 0, // ResultPathName
580 0)); // TempPathName
581 if (err.empty() && json_) {
582 json_->OpenList();
583 } else {
584 json_ = 0;
585 llvm::errs()
586 << "[blink-gc] "
587 << "Failed to create an output file for the object graph.\n";
588 }
589 }
590
564 for (RecordVector::iterator it = visitor.record_decls().begin(); 591 for (RecordVector::iterator it = visitor.record_decls().begin();
565 it != visitor.record_decls().end(); 592 it != visitor.record_decls().end();
566 ++it) { 593 ++it) {
567 CheckRecord(cache_.Lookup(*it)); 594 CheckRecord(cache_.Lookup(*it));
568 } 595 }
569 596
570 for (MethodVector::iterator it = visitor.trace_decls().begin(); 597 for (MethodVector::iterator it = visitor.trace_decls().begin();
571 it != visitor.trace_decls().end(); 598 it != visitor.trace_decls().end();
572 ++it) { 599 ++it) {
573 CheckTracingMethod(*it); 600 CheckTracingMethod(*it);
574 } 601 }
602
603 if (json_) {
604 json_->CloseList();
605 delete json_;
606 json_ = 0;
607 }
575 } 608 }
576 609
577 // Main entry for checking a record declaration. 610 // Main entry for checking a record declaration.
578 void CheckRecord(RecordInfo* info) { 611 void CheckRecord(RecordInfo* info) {
579 if (IsIgnored(info)) 612 if (IsIgnored(info))
580 return; 613 return;
581 614
582 CXXRecordDecl* record = info->record(); 615 CXXRecordDecl* record = info->record();
583 616
584 // TODO: what should we do to check unions? 617 // TODO: what should we do to check unions?
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
627 } 660 }
628 661
629 if (info->IsGCDerived()) { 662 if (info->IsGCDerived()) {
630 CheckGCRootsVisitor visitor; 663 CheckGCRootsVisitor visitor;
631 if (visitor.ContainsGCRoots(info)) 664 if (visitor.ContainsGCRoots(info))
632 ReportClassContainsGCRoots(info, &visitor.gc_roots()); 665 ReportClassContainsGCRoots(info, &visitor.gc_roots());
633 666
634 if (info->NeedsFinalization()) 667 if (info->NeedsFinalization())
635 CheckFinalization(info); 668 CheckFinalization(info);
636 } 669 }
670
671 DumpClass(info);
637 } 672 }
638 673
639 void CheckDispatch(RecordInfo* info) { 674 void CheckDispatch(RecordInfo* info) {
640 bool finalized = info->IsGCFinalized(); 675 bool finalized = info->IsGCFinalized();
641 CXXMethodDecl* trace_dispatch = info->GetTraceDispatchMethod(); 676 CXXMethodDecl* trace_dispatch = info->GetTraceDispatchMethod();
642 CXXMethodDecl* finalize_dispatch = info->GetFinalizeDispatchMethod(); 677 CXXMethodDecl* finalize_dispatch = info->GetFinalizeDispatchMethod();
643 if (!trace_dispatch && !finalize_dispatch) 678 if (!trace_dispatch && !finalize_dispatch)
644 return; 679 return;
645 680
646 CXXRecordDecl* base = trace_dispatch ? 681 CXXRecordDecl* base = trace_dispatch ?
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after
797 it != parent->GetFields().end(); 832 it != parent->GetFields().end();
798 ++it) { 833 ++it) {
799 if (!it->second.IsProperlyTraced()) { 834 if (!it->second.IsProperlyTraced()) {
800 // Discontinue once an untraced-field error is found. 835 // Discontinue once an untraced-field error is found.
801 ReportFieldsRequireTracing(parent, trace); 836 ReportFieldsRequireTracing(parent, trace);
802 break; 837 break;
803 } 838 }
804 } 839 }
805 } 840 }
806 841
842 void DumpClass(RecordInfo* info) {
843 if (!json_) return;
844
845 json_->OpenObject();
846 json_->Write("name", info->record()->getQualifiedNameAsString());
847 json_->Write("loc", GetLocString(info->record()->getLocStart()));
848 json_->CloseObject();
849
850 class DumpEdgeVisitor : public RecursiveEdgeVisitor {
851 public:
852 DumpEdgeVisitor(JsonWriter* json) : json_(json) { }
853 void DumpEdge(RecordInfo* src,
854 RecordInfo* dst,
855 const string& lbl,
856 const Edge::LivenessKind& kind,
857 const string& loc) {
858 json_->OpenObject();
859 json_->Write("src", src->record()->getQualifiedNameAsString());
860 json_->Write("dst", dst->record()->getQualifiedNameAsString());
861 json_->Write("lbl", lbl);
862 json_->Write("kind", kind);
863 json_->Write("loc", loc);
864 json_->CloseObject();
865 }
866
867 void DumpField(RecordInfo* src, FieldPoint* point, const string& loc) {
868 src_ = src;
869 point_ = point;
870 loc_ = loc;
871 point_->edge()->Accept(this);
872 }
873
874 void AtValue(Value* e) {
875 // The liveness kind of a path from the point to this value
876 // is given by the innermost place that is non-strong.
877 Edge::LivenessKind kind = Edge::kStrong;
878 if (Config::IsIgnoreCycleAnnotated(point_->field())) {
879 kind = Edge::kWeak;
880 } else {
881 for (Context::iterator it = context().begin();
882 it != context().end();
883 ++it) {
884 Edge::LivenessKind pointer_kind = (*it)->Kind();
885 if (pointer_kind != Edge::kStrong) {
886 kind = pointer_kind;
887 break;
888 }
889 }
890 }
891 DumpEdge(src_,
892 e->value(),
893 point_->field()->getNameAsString(),
894 kind,
895 loc_);
896 }
897
898 private:
899 JsonWriter* json_;
900 RecordInfo* src_;
901 FieldPoint* point_;
902 string loc_;
903 };
904
905 DumpEdgeVisitor visitor(json_);
906
907 RecordInfo::Bases& bases = info->GetBases();
908 for (RecordInfo::Bases::iterator it = bases.begin();
909 it != bases.end();
910 ++it) {
911 visitor.DumpEdge(info,
912 it->second.info(),
913 "<super>",
914 Edge::kStrong,
915 GetLocString(it->second.spec().getLocStart()));
916 }
917
918 RecordInfo::Fields& fields = info->GetFields();
919 for (RecordInfo::Fields::iterator it = fields.begin();
920 it != fields.end();
921 ++it) {
922 visitor.DumpField(info,
923 &it->second,
924 GetLocString(it->second.field()->getLocStart()));
925 }
926 }
927
807 // Adds either a warning or error, based on the current handling of -Werror. 928 // Adds either a warning or error, based on the current handling of -Werror.
808 DiagnosticsEngine::Level getErrorLevel() { 929 DiagnosticsEngine::Level getErrorLevel() {
809 return diagnostic_.getWarningsAsErrors() ? DiagnosticsEngine::Error 930 return diagnostic_.getWarningsAsErrors() ? DiagnosticsEngine::Error
810 : DiagnosticsEngine::Warning; 931 : DiagnosticsEngine::Warning;
811 } 932 }
812 933
934 const string GetLocString(SourceLocation loc) {
935 const SourceManager& source_manager = instance_.getSourceManager();
936 PresumedLoc ploc = source_manager.getPresumedLoc(loc);
937 if (ploc.isInvalid())
938 return "";
939 string loc_str;
940 llvm::raw_string_ostream OS(loc_str);
941 OS << ploc.getFilename()
942 << ":" << ploc.getLine()
943 << ":" << ploc.getColumn();
944 return OS.str();
945 }
946
813 bool IsIgnored(RecordInfo* record) { 947 bool IsIgnored(RecordInfo* record) {
814 return !record || 948 return !record ||
815 !InCheckedNamespace(record) || 949 !InCheckedNamespace(record) ||
816 IsIgnoredClass(record) || 950 IsIgnoredClass(record) ||
817 InIgnoredDirectory(record); 951 InIgnoredDirectory(record);
818 } 952 }
819 953
820 bool IsIgnoredClass(RecordInfo* info) { 954 bool IsIgnoredClass(RecordInfo* info) {
821 // Ignore any class prefixed by SameSizeAs. These are used in 955 // Ignore any class prefixed by SameSizeAs. These are used in
822 // Blink to verify class sizes and don't need checking. 956 // Blink to verify class sizes and don't need checking.
(...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after
1125 unsigned diag_user_declared_finalizer_note_; 1259 unsigned diag_user_declared_finalizer_note_;
1126 unsigned diag_base_requires_finalization_note_; 1260 unsigned diag_base_requires_finalization_note_;
1127 unsigned diag_field_requires_finalization_note_; 1261 unsigned diag_field_requires_finalization_note_;
1128 unsigned diag_overridden_non_virtual_trace_note_; 1262 unsigned diag_overridden_non_virtual_trace_note_;
1129 unsigned diag_manual_dispatch_method_note_; 1263 unsigned diag_manual_dispatch_method_note_;
1130 1264
1131 CompilerInstance& instance_; 1265 CompilerInstance& instance_;
1132 DiagnosticsEngine& diagnostic_; 1266 DiagnosticsEngine& diagnostic_;
1133 BlinkGCPluginOptions options_; 1267 BlinkGCPluginOptions options_;
1134 RecordCache cache_; 1268 RecordCache cache_;
1269 JsonWriter* json_;
1135 }; 1270 };
1136 1271
1137 class BlinkGCPluginAction : public PluginASTAction { 1272 class BlinkGCPluginAction : public PluginASTAction {
1138 public: 1273 public:
1139 BlinkGCPluginAction() {} 1274 BlinkGCPluginAction() {}
1140 1275
1141 protected: 1276 protected:
1142 // Overridden from PluginASTAction: 1277 // Overridden from PluginASTAction:
1143 virtual ASTConsumer* CreateASTConsumer(CompilerInstance& instance, 1278 virtual ASTConsumer* CreateASTConsumer(CompilerInstance& instance,
1144 llvm::StringRef ref) { 1279 llvm::StringRef ref) {
1145 return new BlinkGCPluginConsumer(instance, options_); 1280 return new BlinkGCPluginConsumer(instance, options_);
1146 } 1281 }
1147 1282
1148 virtual bool ParseArgs(const CompilerInstance& instance, 1283 virtual bool ParseArgs(const CompilerInstance& instance,
1149 const std::vector<string>& args) { 1284 const std::vector<string>& args) {
1150 bool parsed = true; 1285 bool parsed = true;
1151 1286
1152 for (size_t i = 0; i < args.size() && parsed; ++i) { 1287 for (size_t i = 0; i < args.size() && parsed; ++i) {
1153 if (args[i] == "enable-oilpan") { 1288 if (args[i] == "enable-oilpan") {
1154 options_.enable_oilpan = true; 1289 options_.enable_oilpan = true;
1290 } else if (args[i] == "detect-cycles") {
1291 options_.detect_cycles = true;
1155 } else { 1292 } else {
1156 parsed = false; 1293 parsed = false;
1157 llvm::errs() << "Unknown blink-gc-plugin argument: " << args[i] << "\n"; 1294 llvm::errs() << "Unknown blink-gc-plugin argument: " << args[i] << "\n";
1158 } 1295 }
1159 } 1296 }
1160 1297
1161 return parsed; 1298 return parsed;
1162 } 1299 }
1163 1300
1164 private: 1301 private:
1165 BlinkGCPluginOptions options_; 1302 BlinkGCPluginOptions options_;
1166 }; 1303 };
1167 1304
1168 } // namespace 1305 } // namespace
1169 1306
1170 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( 1307 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X(
1171 "blink-gc-plugin", 1308 "blink-gc-plugin",
1172 "Check Blink GC invariants"); 1309 "Check Blink GC invariants");
OLDNEW
« no previous file with comments | « no previous file | tools/clang/blink_gc_plugin/Config.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698