mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
83da56ea7a
This uses the "rank=source" feature of graphviz, that allows to select what are the nodes with mimimal rank in a graph. We use this to mark all the nodes that represent functions and segments so that the graph layout is more deterministic and readable across multiple runs when the input Model is very similar.
695 lines
21 KiB
C++
695 lines
21 KiB
C++
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include <vector>
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/Support/Casting.h"
|
|
|
|
#include "revng/Model/Binary.h"
|
|
#include "revng/Model/CABIFunctionDefinition.h"
|
|
#include "revng/Model/RawFunctionDefinition.h"
|
|
#include "revng/Model/StructDefinition.h"
|
|
#include "revng/Model/TypeDefinitionKind.h"
|
|
#include "revng/Model/TypeSystemPrinter.h"
|
|
#include "revng/Model/TypedefDefinition.h"
|
|
#include "revng/Model/UnionDefinition.h"
|
|
#include "revng/Support/Assert.h"
|
|
|
|
using llvm::cast;
|
|
using llvm::dyn_cast;
|
|
using llvm::isa;
|
|
using model::CABIFunctionDefinition;
|
|
using model::RawFunctionDefinition;
|
|
using model::StructDefinition;
|
|
using model::TypedefDefinition;
|
|
using model::UnionDefinition;
|
|
using std::to_string;
|
|
|
|
using FieldList = llvm::SmallVector<const model::Type *, 16>;
|
|
|
|
static constexpr const char *TableOpts = "border='0' cellborder='1' "
|
|
"cellspacing='0' cellpadding='0'";
|
|
static constexpr const char *PaddingOpts = "cellpadding='10'";
|
|
|
|
static constexpr const char *Green = "\"#8DB596\"";
|
|
static constexpr const char *Red = "\"#DC7878\"";
|
|
static constexpr const char *Blue = "\"#93ABD3\"";
|
|
static constexpr const char *Orange = "\"#EEEE00\"";
|
|
static constexpr const char *Purple = "\"#C689C6\"";
|
|
static constexpr const char *Pink = "\"#FF99CC\"";
|
|
static constexpr const char *Grey = "\"#CCCCCC\"";
|
|
static constexpr const char *White = "\"white\"";
|
|
|
|
/// Background and border color for records of a given TypeDefinitionKind
|
|
static llvm::StringRef getColor(model::TypeDefinitionKind::Values K) {
|
|
if (K == model::TypeDefinitionKind::UnionDefinition)
|
|
return Red;
|
|
else if (K == model::TypeDefinitionKind::CABIFunctionDefinition
|
|
or K == model::TypeDefinitionKind::RawFunctionDefinition)
|
|
return Green;
|
|
else if (K == model::TypeDefinitionKind::StructDefinition)
|
|
return Blue;
|
|
|
|
return Grey;
|
|
}
|
|
|
|
/// Cell with inner padding, a colored background and a white border
|
|
static void headerCell(llvm::raw_ostream &Out,
|
|
llvm::StringRef Color,
|
|
llvm::StringRef Content) {
|
|
Out << "<TD " << PaddingOpts << " color=" << White << " bgcolor=" << Color
|
|
<< ">" << Content << "</TD>";
|
|
}
|
|
|
|
/// Cell with inner padding and possibly a port identifier
|
|
static void paddedCell(llvm::raw_ostream &Out,
|
|
llvm::StringRef Content,
|
|
std::optional<size_t> Port = {}) {
|
|
Out << "<TD " << PaddingOpts;
|
|
if (Port)
|
|
Out << " PORT='P" << to_string(Port.value()) << "'";
|
|
Out << ">" << Content << "</TD>";
|
|
}
|
|
|
|
/// Collect an ordered list of the subtypes in a type (e.g. field, return
|
|
/// values, arguments ...)
|
|
static FieldList collectFields(const model::TypeDefinition *T) {
|
|
FieldList Fields;
|
|
|
|
if (auto *Struct = llvm::dyn_cast<model::StructDefinition>(T)) {
|
|
for (auto &Field : Struct->Fields())
|
|
Fields.push_back(Field.Type().get());
|
|
|
|
} else if (auto *Union = llvm::dyn_cast<model::UnionDefinition>(T)) {
|
|
for (auto &Field : Union->Fields())
|
|
Fields.push_back(Field.Type().get());
|
|
|
|
} else if (auto *CABI = llvm::dyn_cast<model::CABIFunctionDefinition>(T)) {
|
|
if (not CABI->ReturnType().isEmpty())
|
|
Fields.push_back(CABI->ReturnType().get());
|
|
for (auto &Field : CABI->Arguments())
|
|
Fields.push_back(Field.Type().get());
|
|
|
|
} else if (auto *RawFunc = llvm::dyn_cast<model::RawFunctionDefinition>(T)) {
|
|
for (auto &Field : RawFunc->ReturnValues())
|
|
Fields.push_back(Field.Type().get());
|
|
|
|
for (auto &Field : RawFunc->Arguments())
|
|
Fields.push_back(Field.Type().get());
|
|
|
|
if (not RawFunc->StackArgumentsType().isEmpty())
|
|
Fields.push_back(RawFunc->StackArgumentsType().get());
|
|
} else if (auto *Typedef = llvm::dyn_cast<model::TypedefDefinition>(T)) {
|
|
Fields.push_back(Typedef->UnderlyingType().get());
|
|
}
|
|
|
|
return Fields;
|
|
}
|
|
|
|
TypeSystemPrinter::TypeSystemPrinter(llvm::raw_ostream &Out,
|
|
const model::Binary &Binary,
|
|
bool OrthoEdges) :
|
|
Out(Out), Binary(Binary), NameBuilder(Binary) {
|
|
Out << "digraph TypeGraph {\n";
|
|
if (OrthoEdges)
|
|
Out << "splines=ortho;\n";
|
|
Out << "node [shape=none, margin=0];\n";
|
|
Out << "graph [fontname=Courier];\n";
|
|
Out << "node [fontname=Courier];\n";
|
|
Out << "edge [fontname=Courier];\n";
|
|
}
|
|
|
|
TypeSystemPrinter::~TypeSystemPrinter() {
|
|
Out << "}\n";
|
|
Out.flush();
|
|
}
|
|
|
|
/// Build a C-like string for a given Type
|
|
// TODO: replace this with a call to `getNamedCInstance` once the two repos
|
|
// are merged together.
|
|
std::string TypeSystemPrinter::buildFieldName(const model::Type &Type,
|
|
std::string &&Prefix,
|
|
std::string &&Suffix) {
|
|
if (const auto *Array = llvm::dyn_cast<model::ArrayType>(&Type)) {
|
|
revng_assert(!Array->IsConst(),
|
|
"Const arrays are not supported by this serializer.");
|
|
|
|
Suffix = "[" + std::to_string(Array->ElementCount()) + "]"
|
|
+ std::move(Suffix);
|
|
return buildFieldName(*Array->ElementType(),
|
|
std::move(Prefix),
|
|
std::move(Suffix));
|
|
|
|
} else if (const auto *D = llvm::dyn_cast<model::DefinedType>(&Type)) {
|
|
std::string Result = std::move(Prefix);
|
|
if (!Result.empty() && Result.back() != '*')
|
|
Result += ' ';
|
|
|
|
return Result += NameBuilder.name(D->unwrap()) + Suffix;
|
|
|
|
} else if (const auto *P = llvm::dyn_cast<model::PointerType>(&Type)) {
|
|
return buildFieldName(*P->PointeeType(),
|
|
std::move(Prefix += P->IsConst() ? "* const" : "*"),
|
|
std::move(Suffix));
|
|
|
|
} else if (const auto *P = llvm::dyn_cast<model::PrimitiveType>(&Type)) {
|
|
std::string Result = std::move(Prefix);
|
|
if (!Result.empty() && Result.at(Result.size() - 1) != '*')
|
|
Result += ' ';
|
|
|
|
return Result += P->getCName() + Suffix;
|
|
|
|
} else {
|
|
revng_abort("Unsupported type.");
|
|
}
|
|
}
|
|
|
|
/// Add a row in a struct table
|
|
static void addStructField(llvm::raw_ostream &Out,
|
|
size_t Offset,
|
|
size_t Size,
|
|
llvm::StringRef Content,
|
|
std::optional<size_t> Port = {}) {
|
|
Out << "<TR>";
|
|
paddedCell(Out, to_string(Offset));
|
|
paddedCell(Out, to_string(Size));
|
|
paddedCell(Out, Content, Port);
|
|
Out << "</TR>";
|
|
}
|
|
|
|
/// Generate the inner table of a struct type
|
|
void TypeSystemPrinter::dumpStructFields(llvm::raw_ostream &Out,
|
|
const model::StructDefinition *T) {
|
|
if (T->Fields().size() == 0) {
|
|
Out << "<TR><TD></TD></TR>";
|
|
return;
|
|
}
|
|
|
|
// Header
|
|
llvm::StringRef Color = getColor(model::TypeDefinitionKind::StructDefinition);
|
|
Out << "<TR>";
|
|
headerCell(Out, Color, "Offset");
|
|
headerCell(Out, Color, "Size");
|
|
headerCell(Out, Color, "Name");
|
|
Out << "</TR>";
|
|
|
|
// Struct fields are stacked vertically
|
|
uint64_t LastOffset = 0;
|
|
for (auto &&[Index, Field] : llvm::enumerate(T->Fields())) {
|
|
// Check if there's padding to be added before this field
|
|
if (Field.Offset() > LastOffset)
|
|
addStructField(Out, LastOffset, Field.Offset() - LastOffset, "padding");
|
|
|
|
auto Name = buildFieldName(*Field.Type());
|
|
uint64_t Size = Field.Type()->size().value_or(0);
|
|
addStructField(Out, Field.Offset(), Size, Name, Index);
|
|
|
|
LastOffset += Field.Offset() + Size;
|
|
}
|
|
|
|
// Check if there's trailing padding
|
|
auto StructSize = T->size().value_or(0);
|
|
if (StructSize > LastOffset)
|
|
addStructField(Out, LastOffset, StructSize - LastOffset, "padding");
|
|
}
|
|
|
|
/// Generate the inner table of a union type
|
|
void TypeSystemPrinter::dumpUnionFields(llvm::raw_ostream &Out,
|
|
const model::UnionDefinition *T) {
|
|
if (T->Fields().size() == 0) {
|
|
Out << "<TR><TD></TD></TR>";
|
|
return;
|
|
}
|
|
|
|
Out << "<TR>";
|
|
|
|
// Union fields are disposed horizontally
|
|
for (auto &&[Index, Field] : llvm::enumerate(T->Fields())) {
|
|
auto Name = buildFieldName(*Field.Type());
|
|
const auto Size = Field.Type()->size().value_or(0);
|
|
paddedCell(Out, Name + " (size: " + to_string(Size) + ")", Index);
|
|
}
|
|
Out << "</TR>";
|
|
}
|
|
|
|
/// Generate the inner table of a function type
|
|
void TypeSystemPrinter::dumpFunctionType(llvm::raw_ostream &Out,
|
|
const model::TypeDefinition *T) {
|
|
llvm::SmallVector<const model::Type *, 8> ReturnTypes;
|
|
llvm::SmallVector<const model::Type *, 8> Arguments;
|
|
|
|
// Collect arguments and return types
|
|
if (auto *RawFunc = dyn_cast<RawFunctionDefinition>(T)) {
|
|
for (auto &RetTy : RawFunc->ReturnValues())
|
|
ReturnTypes.push_back(RetTy.Type().get());
|
|
|
|
for (auto &ArgTy : RawFunc->Arguments())
|
|
Arguments.push_back(ArgTy.Type().get());
|
|
|
|
if (not RawFunc->StackArgumentsType().isEmpty())
|
|
Arguments.push_back(RawFunc->StackArgumentsType().get());
|
|
|
|
} else if (auto *CABIFunc = dyn_cast<CABIFunctionDefinition>(T)) {
|
|
|
|
if (not CABIFunc->ReturnType().isEmpty())
|
|
ReturnTypes.push_back(CABIFunc->ReturnType().get());
|
|
|
|
for (auto &ArgTy : CABIFunc->Arguments())
|
|
Arguments.push_back(ArgTy.Type().get());
|
|
}
|
|
|
|
// Inner table that divides return types and arguments
|
|
Out << "<TR><TD><TABLE " << TableOpts << ">";
|
|
|
|
// Header
|
|
llvm::StringRef Color = getColor(T->Kind());
|
|
Out << "<TR>";
|
|
headerCell(Out, Color, "Return Types");
|
|
headerCell(Out, Color, "Arguments");
|
|
Out << "</TR>";
|
|
|
|
// Second row of the inner table (actual types)
|
|
Out << "<TR>";
|
|
|
|
size_t CurPort = 0;
|
|
|
|
// Return types are disposed horizontally in a dedicated table
|
|
Out << "<TD><TABLE " << TableOpts << "><TR>";
|
|
if (ReturnTypes.empty()) {
|
|
paddedCell(Out, "void");
|
|
CurPort++;
|
|
} else {
|
|
for (auto Field : ReturnTypes)
|
|
paddedCell(Out, buildFieldName(*Field), CurPort++);
|
|
}
|
|
Out << "</TR></TABLE></TD>";
|
|
|
|
// Arguments types are disposed horizontally in a dedicated table
|
|
Out << "<TD><TABLE " << TableOpts << "><TR>";
|
|
if (Arguments.empty()) {
|
|
Out << "<TD></TD>";
|
|
} else {
|
|
for (auto Field : Arguments)
|
|
paddedCell(Out, buildFieldName(*Field), CurPort++);
|
|
}
|
|
Out << "</TR></TABLE></TD>";
|
|
|
|
// End of second row
|
|
Out << "</TR>";
|
|
|
|
// End of inner table
|
|
Out << "</TABLE></TD></TR>";
|
|
}
|
|
|
|
static std::string nodeName(uint64_t NodeID) {
|
|
return "node_" + to_string(NodeID);
|
|
}
|
|
|
|
/// Generate the inner content of a Typedef node
|
|
void TypeSystemPrinter::dumpTypedefUnderlying(llvm::raw_ostream &Out,
|
|
const model::TypedefDefinition
|
|
*T) {
|
|
Out << "<TR>";
|
|
paddedCell(Out, buildFieldName(*T->UnderlyingType()), 0);
|
|
Out << "</TR>";
|
|
}
|
|
|
|
void TypeSystemPrinter::dumpTypeNode(const model::TypeDefinition *T,
|
|
int NodeID) {
|
|
// Print the name of the node
|
|
Out << nodeName(NodeID) << "[";
|
|
|
|
// Choose the node's border color
|
|
llvm::StringRef Color = getColor(T->Kind());
|
|
Out << "color=" << Color << ", ";
|
|
|
|
// Start of HTML-style label
|
|
Out << "label= < <TABLE " << TableOpts << ">";
|
|
|
|
// Print the name of the type on top
|
|
Out << "<TR><TD bgcolor=" << Color << " " << PaddingOpts << " PORT='TOP'><B>"
|
|
<< NameBuilder.name(*T)
|
|
<< "</B> (size: " << to_string(T->trySize().value_or(0))
|
|
<< ")</TD></TR>";
|
|
|
|
// Print fields in a table
|
|
Out << "<TR><TD><TABLE " << TableOpts << "> ";
|
|
if (auto *StructT = dyn_cast<StructDefinition>(T))
|
|
dumpStructFields(Out, StructT);
|
|
else if (auto *UnionT = dyn_cast<UnionDefinition>(T))
|
|
dumpUnionFields(Out, UnionT);
|
|
else if (isa<RawFunctionDefinition>(T) or isa<CABIFunctionDefinition>(T))
|
|
dumpFunctionType(Out, T);
|
|
else if (auto *Typedef = dyn_cast<TypedefDefinition>(T))
|
|
dumpTypedefUnderlying(Out, Typedef);
|
|
else
|
|
Out << "<TR><TD>Unhandled Type</TD></TR>";
|
|
Out << "</TABLE></TD></TR>";
|
|
|
|
// End of label
|
|
Out << "</TABLE> >];\n";
|
|
}
|
|
|
|
void TypeSystemPrinter::addEdge(int SrcID, int SrcPort, int DstID) {
|
|
Out << nodeName(SrcID) << ":<P" << to_string(SrcPort) << ">";
|
|
Out << " -> ";
|
|
Out << nodeName(DstID);
|
|
Out << ":<TOP>;\n";
|
|
}
|
|
|
|
struct FieldEdge {
|
|
std::string Label;
|
|
const model::TypeDefinition *Destination;
|
|
bool IsPointer;
|
|
};
|
|
static RecursiveCoroutine<FieldEdge>
|
|
buildFieldEdgeLabel(const model::Type &Type,
|
|
std::string &&Current = {},
|
|
bool IsPointer = false) {
|
|
if (const auto *Array = llvm::dyn_cast<model::ArrayType>(&Type)) {
|
|
if (!Current.empty())
|
|
Current += ",\\n";
|
|
Current += "Array[" + std::to_string(Array->ElementCount()) + "]";
|
|
rc_return buildFieldEdgeLabel(*Array->ElementType(),
|
|
std::move(Current),
|
|
false);
|
|
|
|
} else if (const auto *D = llvm::dyn_cast<model::DefinedType>(&Type)) {
|
|
rc_return{ .Label = std::move(Current),
|
|
.Destination = &D->unwrap(),
|
|
.IsPointer = IsPointer };
|
|
|
|
} else if (const auto *P = llvm::dyn_cast<model::PointerType>(&Type)) {
|
|
if (!Current.empty())
|
|
Current += ",\\n";
|
|
Current += "Pointer (" + std::to_string(P->PointerSize()) + " bytes)";
|
|
rc_return buildFieldEdgeLabel(*P->PointeeType(), std::move(Current), true);
|
|
|
|
} else if (const auto *P = llvm::dyn_cast<model::PrimitiveType>(&Type)) {
|
|
rc_return{ .Label = std::move(Current),
|
|
.Destination = nullptr,
|
|
.IsPointer = IsPointer };
|
|
|
|
} else {
|
|
revng_abort("Unsupported type.");
|
|
}
|
|
}
|
|
|
|
void TypeSystemPrinter::addFieldEdge(std::string &&Label,
|
|
bool IsPointer,
|
|
int SrcID,
|
|
int SrcPort,
|
|
int DstID) {
|
|
// Edge
|
|
Out << nodeName(SrcID) << ":<P" << to_string(SrcPort) << ">";
|
|
Out << " -> ";
|
|
Out << nodeName(DstID) << ":<TOP>";
|
|
|
|
// Label
|
|
Out << "[label=\"" << std::move(Label) << "\"";
|
|
|
|
// Style
|
|
if (IsPointer)
|
|
Out << ", style=dotted";
|
|
|
|
Out << "];\n";
|
|
}
|
|
|
|
void TypeSystemPrinter::print(const model::TypeDefinition &T) {
|
|
// Don't repeat nodes
|
|
if (Visited.contains(&T))
|
|
return;
|
|
|
|
llvm::SmallVector<const model::TypeDefinition *, 16> ToVisit = { &T };
|
|
|
|
auto EmitNode = [this](const model::TypeDefinition *TypeToEmit) {
|
|
dumpTypeNode(TypeToEmit, NextID);
|
|
NodesMap.insert({ TypeToEmit, NextID });
|
|
NextID++;
|
|
};
|
|
|
|
// Emit the root
|
|
EmitNode(&T);
|
|
|
|
while (not ToVisit.empty()) {
|
|
const model::TypeDefinition *CurType = ToVisit.pop_back_val();
|
|
if (Visited.contains(CurType))
|
|
continue;
|
|
|
|
uint64_t CurID = NodesMap.at(CurType);
|
|
|
|
// Collect all the successors
|
|
FieldList Fields = collectFields(CurType);
|
|
for (auto &&[Index, Field] : llvm::enumerate(Fields)) {
|
|
FieldEdge Edge = buildFieldEdgeLabel(*Field);
|
|
auto &&[Label, DefinitionPointer, IsPointer] = Edge;
|
|
|
|
// Don't add edges for primitive types, as they would pollute the graph
|
|
// and add no information regarding the type system structure
|
|
if (DefinitionPointer == nullptr)
|
|
continue;
|
|
|
|
uint64_t SuccID;
|
|
auto It = NodesMap.find(DefinitionPointer);
|
|
if (It != NodesMap.end()) {
|
|
// If a node already exists for the target type, use that
|
|
SuccID = It->second;
|
|
} else {
|
|
// If the node does not already exist, create a new one
|
|
SuccID = NextID;
|
|
EmitNode(DefinitionPointer);
|
|
}
|
|
|
|
// Add an edge to the type referenced by the current field.
|
|
// Since we have created the target type if it does not exist, and
|
|
// the source node was either the root node or a successor of a
|
|
// previously visited node, we are sure that both the source and the
|
|
// destination of this edge have already been created.
|
|
addFieldEdge(std::move(Label), IsPointer, CurID, Index, SuccID);
|
|
|
|
// Push the field's type to the visit stack
|
|
ToVisit.push_back(DefinitionPointer);
|
|
}
|
|
|
|
// Mark this Type as visited: the node has been emitted, as well as
|
|
// all of its outgoing edges and their respective target nodes.
|
|
Visited.insert(CurType);
|
|
}
|
|
}
|
|
|
|
void TypeSystemPrinter::dumpFunctionNode(const model::Function &F, int NodeID) {
|
|
// Print the name of the node
|
|
Out << nodeName(NodeID) << "[";
|
|
|
|
// Choose the node's border color
|
|
llvm::StringRef Color = Purple;
|
|
Out << "color=" << Color << ", ";
|
|
|
|
// Start of HTML-style label
|
|
Out << "label= < <TABLE " << TableOpts << ">";
|
|
|
|
// Print the name of the function on top
|
|
Out << "<TR><TD bgcolor=" << Color << " " << PaddingOpts << "><B>"
|
|
<< NameBuilder.name(F) << "()</B></TD></TR>";
|
|
|
|
// Print connected types in a table
|
|
Out << "<TR><TD><TABLE " << TableOpts << "> ";
|
|
// Header
|
|
Out << "<TR>";
|
|
headerCell(Out, Color, "Prototype");
|
|
headerCell(Out, Color, "StackType");
|
|
Out << "</TR>";
|
|
|
|
// Second row of the inner table (actual types)
|
|
Out << "<TR>";
|
|
|
|
if (const model::TypeDefinition *Prototype = F.prototype())
|
|
paddedCell(Out, NameBuilder.name(*Prototype), /*port=*/0);
|
|
else
|
|
Out << "<TD></TD>";
|
|
|
|
if (const model::StructDefinition *StackFrame = F.stackFrameType())
|
|
paddedCell(Out, NameBuilder.name(*StackFrame), /*port=*/1);
|
|
else
|
|
Out << "<TD></TD>";
|
|
|
|
Out << "</TR>";
|
|
|
|
// End of inner table
|
|
Out << "</TABLE></TD></TR>";
|
|
|
|
// End of label
|
|
Out << "</TABLE> >];\n";
|
|
}
|
|
|
|
uint64_t TypeSystemPrinter::print(const model::Function &F) {
|
|
// Node corresponding to the function
|
|
uint64_t FunctionNodeID = NextID;
|
|
dumpFunctionNode(F, FunctionNodeID);
|
|
NextID++;
|
|
|
|
// Node of prototype type, if present
|
|
if (const model::TypeDefinition *Prototype = F.prototype()) {
|
|
print(*Prototype);
|
|
|
|
// Edges
|
|
uint64_t PrototypeNodeID = NodesMap.at(Prototype);
|
|
addEdge(FunctionNodeID, 0, PrototypeNodeID);
|
|
}
|
|
|
|
// Node of the stack type, if present
|
|
if (const model::StructDefinition *StackFrame = F.stackFrameType()) {
|
|
print(*StackFrame);
|
|
|
|
// Edges
|
|
uint64_t StackNodeID = NodesMap.at(StackFrame);
|
|
addEdge(FunctionNodeID, 1, StackNodeID);
|
|
}
|
|
|
|
return FunctionNodeID;
|
|
}
|
|
|
|
void TypeSystemPrinter::dumpFunctionNode(const model::DynamicFunction &F,
|
|
int NodeID) {
|
|
// Print the name of the node
|
|
Out << nodeName(NodeID) << "[";
|
|
|
|
// Choose the node's border color
|
|
llvm::StringRef Color = Pink;
|
|
Out << "color=" << Color << ", ";
|
|
|
|
// Start of HTML-style label
|
|
Out << "label= < <TABLE " << TableOpts << ">";
|
|
|
|
// Print the name of the function on top
|
|
Out << "<TR><TD bgcolor=" << Color << " " << PaddingOpts << "><B>"
|
|
<< NameBuilder.name(F) << "()</B></TD></TR>";
|
|
|
|
// Print connected types in a table
|
|
Out << "<TR><TD><TABLE " << TableOpts << "> ";
|
|
// Header
|
|
Out << "<TR>";
|
|
headerCell(Out, Color, "Prototype");
|
|
Out << "</TR>";
|
|
|
|
// Second row of the inner table (actual types)
|
|
Out << "<TR>";
|
|
|
|
if (const model::TypeDefinition *Prototype = F.prototype())
|
|
paddedCell(Out, NameBuilder.name(*Prototype), /*port=*/0);
|
|
else
|
|
Out << "<TD></TD>";
|
|
|
|
Out << "</TR>";
|
|
|
|
// End of inner table
|
|
Out << "</TABLE></TD></TR>";
|
|
|
|
// End of label
|
|
Out << "</TABLE> >];\n";
|
|
}
|
|
|
|
uint64_t TypeSystemPrinter::print(const model::DynamicFunction &F) {
|
|
// Node corresponding to the function
|
|
uint64_t FunctionNodeID = NextID;
|
|
dumpFunctionNode(F, FunctionNodeID);
|
|
NextID++;
|
|
|
|
// Node of the prototype type, if present
|
|
if (const model::TypeDefinition *Prototype = F.prototype()) {
|
|
print(*Prototype);
|
|
|
|
// Edges
|
|
uint64_t PrototypeNodeID = NodesMap.at(Prototype);
|
|
addEdge(FunctionNodeID, 0, PrototypeNodeID);
|
|
}
|
|
|
|
return FunctionNodeID;
|
|
}
|
|
|
|
void TypeSystemPrinter::dumpSegmentNode(const model::Segment &S, int NodeID) {
|
|
// Print the name of the node
|
|
Out << nodeName(NodeID) << "[";
|
|
|
|
// Choose the node's border color
|
|
llvm::StringRef Color = Orange;
|
|
Out << "color=" << Color << ", ";
|
|
|
|
// Start of HTML-style label
|
|
Out << "label= < <TABLE " << TableOpts << ">";
|
|
|
|
// Print the name of the function on top
|
|
Out << "<TR><TD bgcolor=" << Color << " " << PaddingOpts << "><B>"
|
|
<< NameBuilder.name(Binary, S) << "()</B></TD></TR>";
|
|
|
|
// Print connected types in a table
|
|
Out << "<TR><TD><TABLE " << TableOpts << "> ";
|
|
// Header
|
|
Out << "<TR>";
|
|
headerCell(Out, Color, "Type");
|
|
Out << "</TR>";
|
|
|
|
// Second row of the inner table (actual types)
|
|
Out << "<TR>";
|
|
|
|
if (const model::StructDefinition *Type = S.type())
|
|
paddedCell(Out, NameBuilder.name(*Type), /*port=*/0);
|
|
else
|
|
Out << "<TD></TD>";
|
|
|
|
Out << "</TR>";
|
|
|
|
// End of inner table
|
|
Out << "</TABLE></TD></TR>";
|
|
|
|
// End of label
|
|
Out << "</TABLE> >];\n";
|
|
}
|
|
|
|
uint64_t TypeSystemPrinter::print(const model::Segment &S) {
|
|
// Node corresponding to the function
|
|
uint64_t SegmentNodeID = NextID;
|
|
dumpSegmentNode(S, SegmentNodeID);
|
|
NextID++;
|
|
|
|
// Node of the prototype type, if present
|
|
if (const model::StructDefinition *Type = S.type()) {
|
|
print(*Type);
|
|
|
|
// Edges
|
|
uint64_t PrototypeNodeID = NodesMap.at(Type);
|
|
addEdge(SegmentNodeID, 0, PrototypeNodeID);
|
|
}
|
|
|
|
return SegmentNodeID;
|
|
}
|
|
|
|
void TypeSystemPrinter::print() {
|
|
std::vector<uint64_t> TopLevelIDs;
|
|
// Print all functions and related types
|
|
for (auto &F : Binary.Functions())
|
|
TopLevelIDs.push_back(print(F));
|
|
|
|
// Print all dynamic functions and related types
|
|
for (auto &F : Binary.ImportedDynamicFunctions())
|
|
TopLevelIDs.push_back(print(F));
|
|
|
|
// Print all the segments and related types
|
|
for (auto &S : Binary.Segments())
|
|
TopLevelIDs.push_back(print(S));
|
|
|
|
if (not TopLevelIDs.empty()) {
|
|
Out << "{ rank=source;";
|
|
for (uint64_t I : TopLevelIDs)
|
|
Out << " " << nodeName(I);
|
|
Out << "}\n";
|
|
}
|
|
|
|
// Print remaining types, if any
|
|
for (auto &T : Binary.TypeDefinitions())
|
|
if (!NodesMap.contains(T.get()))
|
|
print(*T);
|
|
}
|