Index: tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp |
diff --git a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp |
index 89f0bd419bb94037deb240e7fb10e41ca09637a4..075dc7069d0300c163e4757e1b55dd3ff0fa5afd 100644 |
--- a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp |
+++ b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp |
@@ -9,6 +9,7 @@ |
// http://www.chromium.org/developers/blink-gc-plugin-errors |
#include "Config.h" |
+#include "JsonWriter.h" |
#include "RecordInfo.h" |
#include "clang/AST/AST.h" |
@@ -110,8 +111,9 @@ const char kDerivesNonStackAllocated[] = |
" which is not stack allocated."; |
struct BlinkGCPluginOptions { |
- BlinkGCPluginOptions() : enable_oilpan(false) {} |
+ BlinkGCPluginOptions() : enable_oilpan(false), detect_cycles(false) {} |
bool enable_oilpan; |
+ bool detect_cycles; |
std::set<std::string> ignored_classes; |
std::set<std::string> checked_namespaces; |
std::vector<std::string> ignored_directories; |
@@ -485,7 +487,8 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
const BlinkGCPluginOptions& options) |
: instance_(instance), |
diagnostic_(instance.getDiagnostics()), |
- options_(options) { |
+ options_(options), |
+ json_(0) { |
// Only check structures in the blink, WebCore and WebKit namespaces. |
options_.checked_namespaces.insert("blink"); |
@@ -561,6 +564,30 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
CollectVisitor visitor; |
visitor.TraverseDecl(context.getTranslationUnitDecl()); |
+ if (options_.detect_cycles) { |
+ string err; |
+ // TODO: Make createDefaultOutputFile or a shorter createOutputFile work. |
+ json_ = JsonWriter::from(instance_.createOutputFile( |
+ "", // OutputPath |
+ err, // Errors |
+ true, // Binary |
+ true, // RemoveFileOnSignal |
+ instance_.getFrontendOpts().OutputFile, // BaseInput |
+ "graph.json", // Extension |
+ false, // UseTemporary |
+ false, // CreateMissingDirectories |
+ 0, // ResultPathName |
+ 0)); // TempPathName |
+ if (err.empty() && json_) { |
+ json_->OpenList(); |
+ } else { |
+ json_ = 0; |
+ llvm::errs() |
+ << "[blink-gc] " |
+ << "Failed to create an output file for the object graph.\n"; |
+ } |
+ } |
+ |
for (RecordVector::iterator it = visitor.record_decls().begin(); |
it != visitor.record_decls().end(); |
++it) { |
@@ -572,6 +599,12 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
++it) { |
CheckTracingMethod(*it); |
} |
+ |
+ if (json_) { |
+ json_->CloseList(); |
+ delete json_; |
+ json_ = 0; |
+ } |
} |
// Main entry for checking a record declaration. |
@@ -634,6 +667,8 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
if (info->NeedsFinalization()) |
CheckFinalization(info); |
} |
+ |
+ DumpClass(info); |
} |
void CheckDispatch(RecordInfo* info) { |
@@ -804,12 +839,111 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
} |
} |
+ void DumpClass(RecordInfo* info) { |
+ if (!json_) return; |
+ |
+ json_->OpenObject(); |
+ json_->Write("name", info->record()->getQualifiedNameAsString()); |
+ json_->Write("loc", GetLocString(info->record()->getLocStart())); |
+ json_->CloseObject(); |
+ |
+ class DumpEdgeVisitor : public RecursiveEdgeVisitor { |
+ public: |
+ DumpEdgeVisitor(JsonWriter* json) : json_(json) { } |
+ void DumpEdge(RecordInfo* src, |
+ RecordInfo* dst, |
+ const string& lbl, |
+ const Edge::LivenessKind& kind, |
+ const string& loc) { |
+ json_->OpenObject(); |
+ json_->Write("src", src->record()->getQualifiedNameAsString()); |
+ json_->Write("dst", dst->record()->getQualifiedNameAsString()); |
+ json_->Write("lbl", lbl); |
+ json_->Write("kind", kind); |
+ json_->Write("loc", loc); |
+ json_->CloseObject(); |
+ } |
+ |
+ void DumpField(RecordInfo* src, FieldPoint* point, const string& loc) { |
+ src_ = src; |
+ point_ = point; |
+ loc_ = loc; |
+ point_->edge()->Accept(this); |
+ } |
+ |
+ void AtValue(Value* e) { |
+ // The liveness kind of a path from the point to this value |
+ // is given by the innermost place that is non-strong. |
+ Edge::LivenessKind kind = Edge::kStrong; |
+ if (Config::IsIgnoreCycleAnnotated(point_->field())) { |
+ kind = Edge::kWeak; |
+ } else { |
+ for (Context::iterator it = context().begin(); |
+ it != context().end(); |
+ ++it) { |
+ Edge::LivenessKind pointer_kind = (*it)->Kind(); |
+ if (pointer_kind != Edge::kStrong) { |
+ kind = pointer_kind; |
+ break; |
+ } |
+ } |
+ } |
+ DumpEdge(src_, |
+ e->value(), |
+ point_->field()->getNameAsString(), |
+ kind, |
+ loc_); |
+ } |
+ |
+ private: |
+ JsonWriter* json_; |
+ RecordInfo* src_; |
+ FieldPoint* point_; |
+ string loc_; |
+ }; |
+ |
+ DumpEdgeVisitor visitor(json_); |
+ |
+ RecordInfo::Bases& bases = info->GetBases(); |
+ for (RecordInfo::Bases::iterator it = bases.begin(); |
+ it != bases.end(); |
+ ++it) { |
+ visitor.DumpEdge(info, |
+ it->second.info(), |
+ "<super>", |
+ Edge::kStrong, |
+ GetLocString(it->second.spec().getLocStart())); |
+ } |
+ |
+ RecordInfo::Fields& fields = info->GetFields(); |
+ for (RecordInfo::Fields::iterator it = fields.begin(); |
+ it != fields.end(); |
+ ++it) { |
+ visitor.DumpField(info, |
+ &it->second, |
+ GetLocString(it->second.field()->getLocStart())); |
+ } |
+ } |
+ |
// Adds either a warning or error, based on the current handling of -Werror. |
DiagnosticsEngine::Level getErrorLevel() { |
return diagnostic_.getWarningsAsErrors() ? DiagnosticsEngine::Error |
: DiagnosticsEngine::Warning; |
} |
+ const string GetLocString(SourceLocation loc) { |
+ const SourceManager& source_manager = instance_.getSourceManager(); |
+ PresumedLoc ploc = source_manager.getPresumedLoc(loc); |
+ if (ploc.isInvalid()) |
+ return ""; |
+ string loc_str; |
+ llvm::raw_string_ostream OS(loc_str); |
+ OS << ploc.getFilename() |
+ << ":" << ploc.getLine() |
+ << ":" << ploc.getColumn(); |
+ return OS.str(); |
+ } |
+ |
bool IsIgnored(RecordInfo* record) { |
return !record || |
!InCheckedNamespace(record) || |
@@ -1132,6 +1266,7 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
DiagnosticsEngine& diagnostic_; |
BlinkGCPluginOptions options_; |
RecordCache cache_; |
+ JsonWriter* json_; |
}; |
class BlinkGCPluginAction : public PluginASTAction { |
@@ -1152,6 +1287,8 @@ class BlinkGCPluginAction : public PluginASTAction { |
for (size_t i = 0; i < args.size() && parsed; ++i) { |
if (args[i] == "enable-oilpan") { |
options_.enable_oilpan = true; |
+ } else if (args[i] == "detect-cycles") { |
+ options_.detect_cycles = true; |
} else { |
parsed = false; |
llvm::errs() << "Unknown blink-gc-plugin argument: " << args[i] << "\n"; |