summaryrefslogtreecommitdiff
path: root/compiler/optimizing/graph_visualizer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/optimizing/graph_visualizer.cc')
-rw-r--r--compiler/optimizing/graph_visualizer.cc240
1 files changed, 175 insertions, 65 deletions
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index d94c1fa2fb..716fee4d3e 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -19,9 +19,12 @@
#include <dlfcn.h>
#include <cctype>
+#include <ios>
#include <sstream>
+#include "android-base/stringprintf.h"
#include "art_method.h"
+#include "art_method-inl.h"
#include "base/intrusive_forward_list.h"
#include "bounds_check_elimination.h"
#include "builder.h"
@@ -42,6 +45,12 @@
namespace art {
+// Unique pass-name to identify that the dump is for printing to log.
+constexpr const char* kDebugDumpName = "debug";
+constexpr const char* kDebugDumpGraphName = "debug_graph";
+
+using android::base::StringPrintf;
+
static bool HasWhitespace(const char* str) {
DCHECK(str != nullptr);
while (str[0] != 0) {
@@ -106,13 +115,17 @@ std::ostream& operator<<(std::ostream& os, const StringList& list) {
}
}
+#ifndef ART_STATIC_LIBART_COMPILER
using create_disasm_prototype = Disassembler*(InstructionSet, DisassemblerOptions*);
+#endif
+
class HGraphVisualizerDisassembler {
public:
HGraphVisualizerDisassembler(InstructionSet instruction_set,
const uint8_t* base_address,
const uint8_t* end_address)
: instruction_set_(instruction_set), disassembler_(nullptr) {
+#ifndef ART_STATIC_LIBART_COMPILER
constexpr const char* libart_disassembler_so_name =
kIsDebugBuild ? "libartd-disassembler.so" : "libart-disassembler.so";
libart_disassembler_handle_ = dlopen(libart_disassembler_so_name, RTLD_NOW);
@@ -128,10 +141,11 @@ class HGraphVisualizerDisassembler {
<< libart_disassembler_so_name << ": " << dlerror();
return;
}
+#endif
// Reading the disassembly from 0x0 is easier, so we print relative
// addresses. We will only disassemble the code once everything has
// been generated, so we can read data in literal pools.
- disassembler_ = std::unique_ptr<Disassembler>((*create_disassembler)(
+ disassembler_ = std::unique_ptr<Disassembler>(create_disassembler(
instruction_set,
new DisassemblerOptions(/* absolute_addresses= */ false,
base_address,
@@ -145,9 +159,11 @@ class HGraphVisualizerDisassembler {
~HGraphVisualizerDisassembler() {
// We need to call ~Disassembler() before we close the library.
disassembler_.reset();
+#ifndef ART_STATIC_LIBART_COMPILER
if (libart_disassembler_handle_ != nullptr) {
dlclose(libart_disassembler_handle_);
}
+#endif
}
void Disassemble(std::ostream& output, size_t start, size_t end) const {
@@ -168,7 +184,9 @@ class HGraphVisualizerDisassembler {
InstructionSet instruction_set_;
std::unique_ptr<Disassembler> disassembler_;
+#ifndef ART_STATIC_LIBART_COMPILER
void* libart_disassembler_handle_;
+#endif
};
@@ -182,7 +200,8 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
const char* pass_name,
bool is_after_pass,
bool graph_in_bad_state,
- const CodeGenerator& codegen,
+ const CodeGenerator* codegen,
+ const BlockNamer& namer,
const DisassemblyInformation* disasm_info = nullptr)
: HGraphDelegateVisitor(graph),
output_(output),
@@ -191,12 +210,13 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
graph_in_bad_state_(graph_in_bad_state),
codegen_(codegen),
disasm_info_(disasm_info),
+ namer_(namer),
disassembler_(disasm_info_ != nullptr
? new HGraphVisualizerDisassembler(
- codegen_.GetInstructionSet(),
- codegen_.GetAssembler().CodeBufferBaseAddress(),
- codegen_.GetAssembler().CodeBufferBaseAddress()
- + codegen_.GetAssembler().CodeSize())
+ codegen_->GetInstructionSet(),
+ codegen_->GetAssembler().CodeBufferBaseAddress(),
+ codegen_->GetAssembler().CodeBufferBaseAddress()
+ + codegen_->GetAssembler().CodeSize())
: nullptr),
indent_(0) {}
@@ -218,6 +238,11 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
output_ << "end_" << name << "\n";
}
+ void PrintProperty(const char* name, HBasicBlock* blk) {
+ AddIndent();
+ output_ << name << " \"" << namer_.GetName(blk) << "\"\n";
+ }
+
void PrintProperty(const char* name, const char* property) {
AddIndent();
output_ << name << " \"" << property << "\"\n";
@@ -253,7 +278,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
AddIndent();
output_ << "predecessors";
for (HBasicBlock* predecessor : block->GetPredecessors()) {
- output_ << " \"B" << predecessor->GetBlockId() << "\" ";
+ output_ << " \"" << namer_.GetName(predecessor) << "\" ";
}
if (block->IsEntryBlock() && (disasm_info_ != nullptr)) {
output_ << " \"" << kDisassemblyBlockFrameEntry << "\" ";
@@ -265,30 +290,35 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
AddIndent();
output_ << "successors";
for (HBasicBlock* successor : block->GetNormalSuccessors()) {
- output_ << " \"B" << successor->GetBlockId() << "\" ";
+ output_ << " \"" << namer_.GetName(successor) << "\" ";
}
output_<< "\n";
}
void PrintExceptionHandlers(HBasicBlock* block) {
+ bool has_slow_paths = block->IsExitBlock() &&
+ (disasm_info_ != nullptr) &&
+ !disasm_info_->GetSlowPathIntervals().empty();
+ if (IsDebugDump() && block->GetExceptionalSuccessors().empty() && !has_slow_paths) {
+ return;
+ }
AddIndent();
output_ << "xhandlers";
for (HBasicBlock* handler : block->GetExceptionalSuccessors()) {
- output_ << " \"B" << handler->GetBlockId() << "\" ";
+ output_ << " \"" << namer_.GetName(handler) << "\" ";
}
- if (block->IsExitBlock() &&
- (disasm_info_ != nullptr) &&
- !disasm_info_->GetSlowPathIntervals().empty()) {
+ if (has_slow_paths) {
output_ << " \"" << kDisassemblyBlockSlowPaths << "\" ";
}
output_<< "\n";
}
void DumpLocation(std::ostream& stream, const Location& location) {
+ DCHECK(codegen_ != nullptr);
if (location.IsRegister()) {
- codegen_.DumpCoreRegister(stream, location.reg());
+ codegen_->DumpCoreRegister(stream, location.reg());
} else if (location.IsFpuRegister()) {
- codegen_.DumpFloatingPointRegister(stream, location.reg());
+ codegen_->DumpFloatingPointRegister(stream, location.reg());
} else if (location.IsConstant()) {
stream << "#";
HConstant* constant = location.GetConstant();
@@ -308,13 +338,13 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
} else if (location.IsStackSlot()) {
stream << location.GetStackIndex() << "(sp)";
} else if (location.IsFpuRegisterPair()) {
- codegen_.DumpFloatingPointRegister(stream, location.low());
+ codegen_->DumpFloatingPointRegister(stream, location.low());
stream << "|";
- codegen_.DumpFloatingPointRegister(stream, location.high());
+ codegen_->DumpFloatingPointRegister(stream, location.high());
} else if (location.IsRegisterPair()) {
- codegen_.DumpCoreRegister(stream, location.low());
+ codegen_->DumpCoreRegister(stream, location.low());
stream << "|";
- codegen_.DumpCoreRegister(stream, location.high());
+ codegen_->DumpCoreRegister(stream, location.high());
} else if (location.IsUnallocated()) {
stream << "unallocated";
} else if (location.IsDoubleStackSlot()) {
@@ -379,11 +409,10 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
void VisitLoadClass(HLoadClass* load_class) override {
StartAttributeStream("load_kind") << load_class->GetLoadKind();
- const char* descriptor = load_class->GetDexFile().GetTypeDescriptor(
- load_class->GetDexFile().GetTypeId(load_class->GetTypeIndex()));
- StartAttributeStream("class_name") << PrettyDescriptor(descriptor);
- StartAttributeStream("gen_clinit_check") << std::boolalpha
- << load_class->MustGenerateClinitCheck() << std::noboolalpha;
+ StartAttributeStream("class_name")
+ << load_class->GetDexFile().PrettyType(load_class->GetTypeIndex());
+ StartAttributeStream("gen_clinit_check")
+ << std::boolalpha << load_class->MustGenerateClinitCheck() << std::noboolalpha;
StartAttributeStream("needs_access_check") << std::boolalpha
<< load_class->NeedsAccessCheck() << std::noboolalpha;
}
@@ -396,8 +425,13 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
void VisitLoadMethodType(HLoadMethodType* load_method_type) override {
StartAttributeStream("load_kind") << "RuntimeCall";
const DexFile& dex_file = load_method_type->GetDexFile();
- const dex::ProtoId& proto_id = dex_file.GetProtoId(load_method_type->GetProtoIndex());
- StartAttributeStream("method_type") << dex_file.GetProtoSignature(proto_id);
+ if (dex_file.NumProtoIds() >= load_method_type->GetProtoIndex().index_) {
+ const dex::ProtoId& proto_id = dex_file.GetProtoId(load_method_type->GetProtoIndex());
+ StartAttributeStream("method_type") << dex_file.GetProtoSignature(proto_id);
+ } else {
+ StartAttributeStream("method_type")
+ << "<<Unknown proto-idx: " << load_method_type->GetProtoIndex() << ">>";
+ }
}
void VisitLoadString(HLoadString* load_string) override {
@@ -449,14 +483,11 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
}
void VisitCompare(HCompare* compare) override {
- ComparisonBias bias = compare->GetBias();
- StartAttributeStream("bias") << (bias == ComparisonBias::kGtBias
- ? "gt"
- : (bias == ComparisonBias::kLtBias ? "lt" : "none"));
+ StartAttributeStream("bias") << compare->GetBias();
}
void VisitInvoke(HInvoke* invoke) override {
- StartAttributeStream("dex_file_index") << invoke->GetDexMethodIndex();
+ StartAttributeStream("dex_file_index") << invoke->GetMethodReference().index;
ArtMethod* method = invoke->GetResolvedMethod();
// We don't print signatures, which conflict with c1visualizer format.
static constexpr bool kWithSignature = false;
@@ -464,12 +495,15 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
// other invokes might be coming from inlined methods.
ScopedObjectAccess soa(Thread::Current());
std::string method_name = (method == nullptr)
- ? GetGraph()->GetDexFile().PrettyMethod(invoke->GetDexMethodIndex(), kWithSignature)
+ ? invoke->GetMethodReference().PrettyMethod(kWithSignature)
: method->PrettyMethod(kWithSignature);
StartAttributeStream("method_name") << method_name;
StartAttributeStream("always_throws") << std::boolalpha
<< invoke->AlwaysThrows()
<< std::noboolalpha;
+ if (method != nullptr) {
+ StartAttributeStream("method_index") << method->GetMethodIndex();
+ }
}
void VisitInvokeUnresolved(HInvokeUnresolved* invoke) override {
@@ -496,6 +530,13 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
StartAttributeStream("invoke_type") << "InvokePolymorphic";
}
+ void VisitPredicatedInstanceFieldGet(HPredicatedInstanceFieldGet* iget) override {
+ StartAttributeStream("field_name") <<
+ iget->GetFieldInfo().GetDexFile().PrettyField(iget->GetFieldInfo().GetFieldIndex(),
+ /* with type */ false);
+ StartAttributeStream("field_type") << iget->GetFieldType();
+ }
+
void VisitInstanceFieldGet(HInstanceFieldGet* iget) override {
StartAttributeStream("field_name") <<
iget->GetFieldInfo().GetDexFile().PrettyField(iget->GetFieldInfo().GetFieldIndex(),
@@ -508,6 +549,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
iset->GetFieldInfo().GetDexFile().PrettyField(iset->GetFieldInfo().GetFieldIndex(),
/* with type */ false);
StartAttributeStream("field_type") << iset->GetFieldType();
+ StartAttributeStream("predicated") << std::boolalpha << iset->GetIsPredicatedSet();
}
void VisitStaticFieldGet(HStaticFieldGet* sget) override {
@@ -595,6 +637,10 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
return strcmp(pass_name_, name) == 0;
}
+ bool IsDebugDump() {
+ return IsPass(kDebugDumpGraphName) || IsPass(kDebugDumpName);
+ }
+
void PrintInstruction(HInstruction* instruction) {
output_ << instruction->DebugName();
HConstInputsRef inputs = instruction->GetInputs();
@@ -610,6 +656,11 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
} else {
StartAttributeStream("dex_pc") << "n/a";
}
+ HBasicBlock* block = instruction->GetBlock();
+ if (IsPass(kDebugDumpName)) {
+ // Include block name for logcat use.
+ StartAttributeStream("block") << namer_.GetName(block);
+ }
instruction->Accept(this);
if (instruction->HasEnvironment()) {
StringList envs;
@@ -660,14 +711,14 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
}
}
- HLoopInformation* loop_info = instruction->GetBlock()->GetLoopInformation();
+ HLoopInformation* loop_info = (block != nullptr) ? block->GetLoopInformation() : nullptr;
if (loop_info == nullptr) {
StartAttributeStream("loop") << "none";
} else {
- StartAttributeStream("loop") << "B" << loop_info->GetHeader()->GetBlockId();
+ StartAttributeStream("loop") << namer_.GetName(loop_info->GetHeader());
HLoopInformation* outer = loop_info->GetPreHeader()->GetLoopInformation();
if (outer != nullptr) {
- StartAttributeStream("outer_loop") << "B" << outer->GetHeader()->GetBlockId();
+ StartAttributeStream("outer_loop") << namer_.GetName(outer->GetHeader());
} else {
StartAttributeStream("outer_loop") << "none";
}
@@ -678,7 +729,8 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
// For the builder and the inliner, we want to add extra information on HInstructions
// that have reference types, and also HInstanceOf/HCheckcast.
if ((IsPass(HGraphBuilder::kBuilderPassName)
- || IsPass(HInliner::kInlinerPassName))
+ || IsPass(HInliner::kInlinerPassName)
+ || IsDebugDump())
&& (instruction->GetType() == DataType::Type::kReference ||
instruction->IsInstanceOf() ||
instruction->IsCheckCast())) {
@@ -708,6 +760,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
// doesn't run or doesn't inline anything, the NullConstant remains untyped.
// So we should check NullConstants for validity only after reference type propagation.
DCHECK(graph_in_bad_state_ ||
+ IsDebugDump() ||
(!is_after_pass_ && IsPass(HGraphBuilder::kBuilderPassName)))
<< instruction->DebugName() << instruction->GetId() << " has invalid rti "
<< (is_after_pass_ ? "after" : "before") << " pass " << pass_name_;
@@ -807,12 +860,13 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
void Run() {
StartTag("cfg");
- std::string pass_desc = std::string(pass_name_)
- + " ("
- + (is_after_pass_ ? "after" : "before")
- + (graph_in_bad_state_ ? ", bad_state" : "")
- + ")";
- PrintProperty("name", pass_desc.c_str());
+ std::ostringstream oss;
+ oss << pass_name_;
+ if (!IsDebugDump()) {
+ oss << " (" << (is_after_pass_ ? "after" : "before")
+ << (graph_in_bad_state_ ? ", bad_state" : "") << ")";
+ }
+ PrintProperty("name", oss.str().c_str());
if (disasm_info_ != nullptr) {
DumpDisassemblyBlockForFrameEntry();
}
@@ -824,14 +878,21 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
Flush();
}
+ void Run(HInstruction* instruction) {
+ output_ << DataType::TypeId(instruction->GetType()) << instruction->GetId() << " ";
+ PrintInstruction(instruction);
+ Flush();
+ }
+
void VisitBasicBlock(HBasicBlock* block) override {
StartTag("block");
- PrintProperty("name", "B", block->GetBlockId());
+ PrintProperty("name", block);
if (block->GetLifetimeStart() != kNoLifetime) {
// Piggy back on these fields to show the lifetime of the block.
PrintInt("from_bci", block->GetLifetimeStart());
PrintInt("to_bci", block->GetLifetimeEnd());
- } else {
+ } else if (!IsDebugDump()) {
+ // Don't print useless information to logcat.
PrintInt("from_bci", -1);
PrintInt("to_bci", -1);
}
@@ -841,30 +902,33 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
if (block->IsCatchBlock()) {
PrintProperty("flags", "catch_block");
- } else {
+ } else if (!IsDebugDump()) {
+ // Don't print useless information to logcat
PrintEmptyProperty("flags");
}
if (block->GetDominator() != nullptr) {
- PrintProperty("dominator", "B", block->GetDominator()->GetBlockId());
+ PrintProperty("dominator", block->GetDominator());
}
- StartTag("states");
- StartTag("locals");
- PrintInt("size", 0);
- PrintProperty("method", "None");
- for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
- AddIndent();
- HInstruction* instruction = it.Current();
- output_ << instruction->GetId() << " " << DataType::TypeId(instruction->GetType())
- << instruction->GetId() << "[ ";
- for (const HInstruction* input : instruction->GetInputs()) {
- output_ << input->GetId() << " ";
+ if (!IsDebugDump() || !block->GetPhis().IsEmpty()) {
+ StartTag("states");
+ StartTag("locals");
+ PrintInt("size", 0);
+ PrintProperty("method", "None");
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ AddIndent();
+ HInstruction* instruction = it.Current();
+ output_ << instruction->GetId() << " " << DataType::TypeId(instruction->GetType())
+ << instruction->GetId() << "[ ";
+ for (const HInstruction* input : instruction->GetInputs()) {
+ output_ << input->GetId() << " ";
+ }
+ output_ << "]\n";
}
- output_ << "]\n";
+ EndTag("locals");
+ EndTag("states");
}
- EndTag("locals");
- EndTag("states");
StartTag("HIR");
PrintInstructions(block->GetPhis());
@@ -882,22 +946,33 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
const char* pass_name_;
const bool is_after_pass_;
const bool graph_in_bad_state_;
- const CodeGenerator& codegen_;
+ const CodeGenerator* codegen_;
const DisassemblyInformation* disasm_info_;
+ const BlockNamer& namer_;
std::unique_ptr<HGraphVisualizerDisassembler> disassembler_;
size_t indent_;
DISALLOW_COPY_AND_ASSIGN(HGraphVisualizerPrinter);
};
+std::ostream& HGraphVisualizer::OptionalDefaultNamer::PrintName(std::ostream& os,
+ HBasicBlock* blk) const {
+ if (namer_) {
+ return namer_->get().PrintName(os, blk);
+ } else {
+ return BlockNamer::PrintName(os, blk);
+ }
+}
+
HGraphVisualizer::HGraphVisualizer(std::ostream* output,
HGraph* graph,
- const CodeGenerator& codegen)
- : output_(output), graph_(graph), codegen_(codegen) {}
+ const CodeGenerator* codegen,
+ std::optional<std::reference_wrapper<const BlockNamer>> namer)
+ : output_(output), graph_(graph), codegen_(codegen), namer_(namer) {}
void HGraphVisualizer::PrintHeader(const char* method_name) const {
DCHECK(output_ != nullptr);
- HGraphVisualizerPrinter printer(graph_, *output_, "", true, false, codegen_);
+ HGraphVisualizerPrinter printer(graph_, *output_, "", true, false, codegen_, namer_);
printer.StartTag("compilation");
printer.PrintProperty("name", method_name);
printer.PrintProperty("method", method_name);
@@ -906,6 +981,25 @@ void HGraphVisualizer::PrintHeader(const char* method_name) const {
printer.Flush();
}
+std::string HGraphVisualizer::InsertMetaDataAsCompilationBlock(const std::string& meta_data) {
+ std::string time_str = std::to_string(time(nullptr));
+ std::string quoted_meta_data = "\"" + meta_data + "\"";
+ return StringPrintf("begin_compilation\n"
+ " name %s\n"
+ " method %s\n"
+ " date %s\n"
+ "end_compilation\n",
+ quoted_meta_data.c_str(),
+ quoted_meta_data.c_str(),
+ time_str.c_str());
+}
+
+void HGraphVisualizer::DumpGraphDebug() const {
+ DumpGraph(/* pass_name= */ kDebugDumpGraphName,
+ /* is_after_pass= */ false,
+ /* graph_in_bad_state= */ true);
+}
+
void HGraphVisualizer::DumpGraph(const char* pass_name,
bool is_after_pass,
bool graph_in_bad_state) const {
@@ -916,7 +1010,8 @@ void HGraphVisualizer::DumpGraph(const char* pass_name,
pass_name,
is_after_pass,
graph_in_bad_state,
- codegen_);
+ codegen_,
+ namer_);
printer.Run();
}
}
@@ -930,9 +1025,24 @@ void HGraphVisualizer::DumpGraphWithDisassembly() const {
/* is_after_pass= */ true,
/* graph_in_bad_state= */ false,
codegen_,
- codegen_.GetDisassemblyInformation());
+ namer_,
+ codegen_->GetDisassemblyInformation());
printer.Run();
}
}
+void HGraphVisualizer::DumpInstruction(std::ostream* output,
+ HGraph* graph,
+ HInstruction* instruction) {
+ BlockNamer namer;
+ HGraphVisualizerPrinter printer(graph,
+ *output,
+ /* pass_name= */ kDebugDumpName,
+ /* is_after_pass= */ false,
+ /* graph_in_bad_state= */ false,
+ /* codegen= */ nullptr,
+ /* namer= */ namer);
+ printer.Run(instruction);
+}
+
} // namespace art