mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-16 13:01:21 +08:00
compiler: add new debugging helper function debug_go_type()
Add a new debugging utility routine debug_go_type(), intended to display the contents of a Type object in a way useful to debugging a run of the compiler. Prior to this the only useful alternative for debugging types was invoking the mangled_name() method, which has problems (for example, won't work on interface types prior to finalizing of methods). This is a "deep" dump, meaning that all types reachable from the type passed to debug_go_type() will be printed out. Example: (gdb) print debug_go_type(t1) T0 0x535f300 'net/http.Header' -> T1 T1 0x535d3d0 map ['string' -> string] T4 T2 0x5304bb0 'string' -> string T3 0x331f900 string T4 0x535d370 array [] 'string' -> string Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/166637 From-SVN: r269633
This commit is contained in:
parent
50e021a590
commit
f4390da0c5
@ -1,4 +1,4 @@
|
||||
3106ec19626d75d8275be16c86421132548fa13e
|
||||
565b5cd0f49a00ca20941ea042c07ebe6ddf3553
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the gofrontend repository.
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include "gogo.h"
|
||||
#include "expressions.h"
|
||||
@ -580,3 +581,382 @@ debug_go_block_deep(const Block* block)
|
||||
Block* ncblock = const_cast<Block*>(block);
|
||||
adc.dump_block(ncblock);
|
||||
}
|
||||
|
||||
class Type_dumper
|
||||
{
|
||||
typedef Unordered_map(const Type*, unsigned) idx_map;
|
||||
public:
|
||||
Type_dumper(const Type* type)
|
||||
: top_(type), ntypes_(0)
|
||||
{
|
||||
this->worklist_.push_back(type);
|
||||
}
|
||||
|
||||
void visit();
|
||||
|
||||
std::string stringResult() { return ss_.str(); }
|
||||
|
||||
private:
|
||||
void emitpre(unsigned tag, const Type* addr);
|
||||
void typeref(const char*, const Type*, const char *);
|
||||
void visit_forward_declaration_type(const Forward_declaration_type* fdt);
|
||||
void visit_function_type(const Function_type* ft);
|
||||
void visit_struct_type(const Struct_type* st);
|
||||
void visit_array_type(const Array_type* at);
|
||||
void visit_map_type(const Map_type* mt);
|
||||
void visit_channel_type(const Channel_type* mt);
|
||||
void visit_interface_type(const Interface_type* mt);
|
||||
void visit_methods(const Typed_identifier_list* methods,
|
||||
const char *tag);
|
||||
std::pair<bool, unsigned> lookup(const Type*);
|
||||
|
||||
static constexpr unsigned notag = 0xffffffff;
|
||||
|
||||
private:
|
||||
const Type* top_;
|
||||
idx_map types_;
|
||||
unsigned ntypes_;
|
||||
std::list<const Type*> worklist_;
|
||||
std::ostringstream ss_;
|
||||
};
|
||||
|
||||
// Look up a type, installing it in 'types_'. Return is <found, N>
|
||||
// where 'found' is true if type had been previously recorded, and N
|
||||
// is the index/tag assigned to N. The input argument is appended to
|
||||
// the work list if this is the first time we've seen it.
|
||||
|
||||
std::pair<bool, unsigned> Type_dumper::lookup(const Type* t)
|
||||
{
|
||||
std::pair<const Type*, unsigned> entry = std::make_pair(t, this->ntypes_);
|
||||
std::pair<idx_map::iterator, bool> ins = this->types_.insert(entry);
|
||||
if (ins.second)
|
||||
{
|
||||
this->ntypes_++;
|
||||
if (t != this->top_)
|
||||
this->worklist_.push_back(t);
|
||||
}
|
||||
return std::make_pair(ins.second, ins.first->second);
|
||||
}
|
||||
|
||||
// Emit preamble prior to dumping a type, including the type
|
||||
// pointer itself and the tag we've assigned it. If no
|
||||
// tag is specified (via special "notag" value) and/or the
|
||||
// pointer is null, then just emit an equivalent amount
|
||||
// of spaces.
|
||||
|
||||
void Type_dumper::emitpre(unsigned tag, const Type* ptr)
|
||||
{
|
||||
char tbuf[50], pbuf[50], buf[200];
|
||||
|
||||
tbuf[0] = '\0';
|
||||
if (tag != notag)
|
||||
snprintf(tbuf, sizeof tbuf, "T%u", tag);
|
||||
|
||||
pbuf[0] = '\0';
|
||||
if (ptr != NULL)
|
||||
snprintf(pbuf, sizeof pbuf, "%p", (const void*) ptr);
|
||||
|
||||
snprintf(buf, sizeof buf, "%8s %16s ", tbuf, pbuf);
|
||||
this->ss_ << buf;
|
||||
}
|
||||
|
||||
// Emit a reference to a type into the dump buffer. In most cases this means
|
||||
// just the type tag, but for named types we also emit the name, and for
|
||||
// simple/primitive types (ex: int64) we emit the type itself. If "pref" is
|
||||
// non-NULL, emit the string prior to the reference, and if "suf" is non-NULL,
|
||||
// emit it following the reference.
|
||||
|
||||
void Type_dumper::typeref(const char* pref, const Type* t, const char* suf)
|
||||
{
|
||||
if (pref != NULL)
|
||||
this->ss_ << pref;
|
||||
std::pair<bool, unsigned> p = this->lookup(t);
|
||||
unsigned tag = p.second;
|
||||
switch (t->classification())
|
||||
{
|
||||
case Type::TYPE_NAMED:
|
||||
{
|
||||
const Named_type* nt = t->named_type();
|
||||
const Named_object* no = nt->named_object();
|
||||
this->ss_ << "'" << no->message_name() << "' -> ";
|
||||
const Type* underlying = nt->real_type();
|
||||
this->typeref(NULL, underlying, NULL);
|
||||
break;
|
||||
}
|
||||
case Type::TYPE_POINTER:
|
||||
this->typeref("*", t->points_to(), NULL);
|
||||
break;
|
||||
case Type::TYPE_ERROR:
|
||||
this->ss_ << "error_type";
|
||||
break;
|
||||
case Type::TYPE_INTEGER:
|
||||
{
|
||||
const Integer_type* it = t->integer_type();
|
||||
if (it->is_abstract())
|
||||
this->ss_ << "abstract_int";
|
||||
else
|
||||
this->ss_ << (it->is_unsigned() ? "u" : "") << "int" << it->bits();
|
||||
break;
|
||||
}
|
||||
case Type::TYPE_FLOAT:
|
||||
{
|
||||
const Float_type* ft = t->float_type();
|
||||
if (ft->is_abstract())
|
||||
this->ss_ << "abstract_float";
|
||||
else
|
||||
this->ss_ << "float" << ft->bits();
|
||||
break;
|
||||
}
|
||||
case Type::TYPE_COMPLEX:
|
||||
{
|
||||
const Complex_type* ct = t->complex_type();
|
||||
if (ct->is_abstract())
|
||||
this->ss_ << "abstract_complex";
|
||||
else
|
||||
this->ss_ << "complex" << ct->bits();
|
||||
break;
|
||||
}
|
||||
case Type::TYPE_BOOLEAN:
|
||||
this->ss_ << "bool";
|
||||
break;
|
||||
case Type::TYPE_STRING:
|
||||
this->ss_ << "string";
|
||||
break;
|
||||
case Type::TYPE_NIL:
|
||||
this->ss_ << "nil_type";
|
||||
break;
|
||||
case Type::TYPE_VOID:
|
||||
this->ss_ << "void_type";
|
||||
break;
|
||||
case Type::TYPE_FUNCTION:
|
||||
case Type::TYPE_STRUCT:
|
||||
case Type::TYPE_ARRAY:
|
||||
case Type::TYPE_MAP:
|
||||
case Type::TYPE_CHANNEL:
|
||||
case Type::TYPE_FORWARD:
|
||||
case Type::TYPE_INTERFACE:
|
||||
this->ss_ << "T" << tag;
|
||||
break;
|
||||
|
||||
default:
|
||||
// This is a debugging routine, so instead of a go_unreachable()
|
||||
// issue a warning/error, to allow for the possibility that the
|
||||
// compiler we're debugging is in a bad state.
|
||||
this->ss_ << "<??? " << ((unsigned)t->classification()) << "> "
|
||||
<< "T" << tag;
|
||||
}
|
||||
if (suf != NULL)
|
||||
this->ss_ << suf;
|
||||
}
|
||||
|
||||
void Type_dumper::visit_forward_declaration_type(const Forward_declaration_type* fdt)
|
||||
{
|
||||
this->ss_ << "forward_declaration_type ";
|
||||
if (fdt->is_defined())
|
||||
this->typeref("-> ", fdt->real_type(), NULL);
|
||||
else
|
||||
this->ss_ << "'" << fdt->name() << "'";
|
||||
this->ss_ << "\n";
|
||||
}
|
||||
|
||||
void Type_dumper::visit_function_type(const Function_type* ft)
|
||||
{
|
||||
this->ss_ << "function\n";
|
||||
const Typed_identifier* rec = ft->receiver();
|
||||
if (rec != NULL)
|
||||
{
|
||||
this->emitpre(notag, NULL);
|
||||
this->typeref("receiver ", rec->type(), NULL);
|
||||
}
|
||||
const Typed_identifier_list* parameters = ft->parameters();
|
||||
if (parameters != NULL)
|
||||
{
|
||||
for (Typed_identifier_list::const_iterator p = parameters->begin();
|
||||
p != parameters->end();
|
||||
++p)
|
||||
{
|
||||
this->emitpre(notag, NULL);
|
||||
this->typeref(" param ", p->type(), "\n");
|
||||
}
|
||||
}
|
||||
const Typed_identifier_list* results = ft->results();
|
||||
if (results != NULL)
|
||||
{
|
||||
for (Typed_identifier_list::const_iterator p = results->begin();
|
||||
p != results->end();
|
||||
++p)
|
||||
{
|
||||
this->emitpre(notag, NULL);
|
||||
this->typeref(" result ", p->type(), "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Type_dumper::visit_struct_type(const Struct_type* st)
|
||||
{
|
||||
this->ss_ << "struct\n";
|
||||
const Struct_field_list* fields = st->fields();
|
||||
if (fields != NULL)
|
||||
{
|
||||
for (Struct_field_list::const_iterator p = fields->begin();
|
||||
p != fields->end();
|
||||
++p)
|
||||
{
|
||||
this->emitpre(notag, NULL);
|
||||
this->typeref(" field ", p->type(), "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Type_dumper::visit_array_type(const Array_type* at)
|
||||
{
|
||||
this->ss_ << "array [";
|
||||
if (at->length() != NULL)
|
||||
{
|
||||
int64_t len = 0;
|
||||
if (at->int_length(&len))
|
||||
this->ss_ << len;
|
||||
}
|
||||
this->typeref("] ", at->element_type(), "\n");
|
||||
}
|
||||
|
||||
void Type_dumper::visit_map_type(const Map_type* mt)
|
||||
{
|
||||
this->ss_ << "map [";
|
||||
this->typeref(NULL, mt->key_type(), NULL);
|
||||
this->typeref("] ", mt->val_type(), "\n");
|
||||
}
|
||||
|
||||
void Type_dumper::visit_methods(const Typed_identifier_list* methods,
|
||||
const char *tag)
|
||||
{
|
||||
if (tag != NULL)
|
||||
{
|
||||
this->emitpre(notag, NULL);
|
||||
this->ss_ << tag << "\n";
|
||||
}
|
||||
for (Typed_identifier_list::const_iterator p = methods->begin();
|
||||
p != methods->end();
|
||||
++p)
|
||||
{
|
||||
this->emitpre(notag, NULL);
|
||||
if (p->name().empty())
|
||||
this->typeref(" embedded method ", p->type(), "\n");
|
||||
else
|
||||
{
|
||||
this->ss_ << " method '" << p->name() << "' ";
|
||||
this->typeref(NULL, p->type(), "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Type_dumper::visit_interface_type(const Interface_type* it)
|
||||
{
|
||||
const Typed_identifier_list* methods =
|
||||
(it->methods_are_finalized() ? it->methods() : it->local_methods());
|
||||
if (methods == NULL)
|
||||
{
|
||||
this->ss_ << "empty_interface\n";
|
||||
return;
|
||||
}
|
||||
this->ss_ << "interface";
|
||||
if (! it->methods_are_finalized())
|
||||
{
|
||||
this->ss_ << " [unfinalized]\n";
|
||||
visit_methods(it->local_methods(), NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->ss_ << "\n";
|
||||
visit_methods(it->local_methods(), "[parse_methods]");
|
||||
visit_methods(it->methods(), "[all_methods]");
|
||||
}
|
||||
}
|
||||
|
||||
void Type_dumper::visit_channel_type(const Channel_type* ct)
|
||||
{
|
||||
this->ss_ << "channel {";
|
||||
if (ct->may_send())
|
||||
this->ss_ << " send";
|
||||
if (ct->may_receive())
|
||||
this->ss_ << " receive";
|
||||
this->typeref(" } ", ct->element_type(), "\n");
|
||||
}
|
||||
|
||||
void Type_dumper::visit()
|
||||
{
|
||||
while (! this->worklist_.empty()) {
|
||||
const Type* t = this->worklist_.front();
|
||||
this->worklist_.pop_front();
|
||||
|
||||
std::pair<bool, unsigned> p = this->lookup(t);
|
||||
unsigned tag = p.second;
|
||||
this->emitpre(tag, t);
|
||||
|
||||
switch(t->classification())
|
||||
{
|
||||
case Type::TYPE_ERROR:
|
||||
case Type::TYPE_INTEGER:
|
||||
case Type::TYPE_FLOAT:
|
||||
case Type::TYPE_COMPLEX:
|
||||
case Type::TYPE_BOOLEAN:
|
||||
case Type::TYPE_STRING:
|
||||
case Type::TYPE_VOID:
|
||||
case Type::TYPE_POINTER:
|
||||
case Type::TYPE_NIL:
|
||||
case Type::TYPE_NAMED:
|
||||
this->typeref(NULL, t, "\n");
|
||||
break;
|
||||
case Type::TYPE_FORWARD:
|
||||
this->visit_forward_declaration_type(t->forward_declaration_type());
|
||||
break;
|
||||
|
||||
case Type::TYPE_FUNCTION:
|
||||
this->visit_function_type(t->function_type());
|
||||
break;
|
||||
case Type::TYPE_STRUCT:
|
||||
this->visit_struct_type(t->struct_type());
|
||||
break;
|
||||
case Type::TYPE_ARRAY:
|
||||
this->visit_array_type(t->array_type());
|
||||
break;
|
||||
case Type::TYPE_MAP:
|
||||
this->visit_map_type(t->map_type());
|
||||
break;
|
||||
case Type::TYPE_CHANNEL:
|
||||
this->visit_channel_type(t->channel_type());
|
||||
break;
|
||||
case Type::TYPE_INTERFACE:
|
||||
this->visit_interface_type(t->interface_type());
|
||||
break;
|
||||
default:
|
||||
// This is a debugging routine, so instead of a go_unreachable()
|
||||
// issue a warning/error, to allow for the possibility that the
|
||||
// compiler we're debugging is in a bad state.
|
||||
this->ss_ << "<unknown/unrecognized classification "
|
||||
<< ((unsigned)t->classification()) << ">\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dump a Go type for debugging purposes. This is a deep as opposed
|
||||
// to shallow dump; all of the types reachable from the specified
|
||||
// type will be dumped in addition to the type itself.
|
||||
|
||||
void debug_go_type(const Type* type)
|
||||
{
|
||||
if (type == NULL)
|
||||
{
|
||||
std::cerr << "<NULL type>\n";
|
||||
return;
|
||||
}
|
||||
Type_dumper dumper(type);
|
||||
dumper.visit();
|
||||
std::cerr << dumper.stringResult();
|
||||
}
|
||||
|
||||
void debug_go_type(Type* type)
|
||||
{
|
||||
const Type* ctype = type;
|
||||
debug_go_type(ctype);
|
||||
}
|
||||
|
@ -6951,7 +6951,7 @@ Type::make_struct_type(Struct_field_list* fields,
|
||||
// called for a slice.
|
||||
|
||||
bool
|
||||
Array_type::int_length(int64_t* plen)
|
||||
Array_type::int_length(int64_t* plen) const
|
||||
{
|
||||
go_assert(this->length_ != NULL);
|
||||
Numeric_constant nc;
|
||||
|
@ -2706,7 +2706,7 @@ class Array_type : public Type
|
||||
// length can not be determined. This will assert if called for a
|
||||
// slice.
|
||||
bool
|
||||
int_length(int64_t* plen);
|
||||
int_length(int64_t* plen) const;
|
||||
|
||||
// Whether this type is identical with T.
|
||||
bool
|
||||
@ -3160,6 +3160,11 @@ class Interface_type : public Type
|
||||
static Type*
|
||||
make_interface_type_descriptor_type();
|
||||
|
||||
// Return whether methods are finalized for this interface.
|
||||
bool
|
||||
methods_are_finalized() const
|
||||
{ return this->methods_are_finalized_; }
|
||||
|
||||
protected:
|
||||
int
|
||||
do_traverse(Traverse*);
|
||||
|
Loading…
x
Reference in New Issue
Block a user