Add basic class type support

This implements the first step at supporting C++: class types. In
particular, this adds a new drgn_type_kind, DRGN_TYPE_CLASS, and support
for parsing DW_TAG_class_type from DWARF. Although classes are not valid
in C, this adds support for pretty printing them, for completeness.
This commit is contained in:
Amlan Nayak 2019-11-14 17:12:47 -08:00 committed by Omar Sandoval
parent b49f773fe6
commit 0df2152307
20 changed files with 527 additions and 96 deletions

View File

@ -1112,6 +1112,10 @@ Types
Union type.
.. attribute:: CLASS
Class type.
.. attribute:: ENUM
Enumerated type.
@ -1274,6 +1278,11 @@ can be used just like types obtained from :meth:`Program.type()`.
Create a new union type. It has kind :attr:`TypeKind.UNION`. Otherwise,
this is the same as :func:`struct_type()`.
.. function:: class_type(tag, size, members, qualifiers=None)
Create a new class type. It has kind :attr:`TypeKind.CLASS`. Otherwise,
this is the same as :func:`struct_type()`.
.. function:: enum_type(tag, type, enumerators, qualifiers=None)
Create a new enumerated type. It has kind :attr:`TypeKind.ENUM`.

View File

@ -67,6 +67,7 @@ from _drgn import (
array_type,
bool_type,
cast,
class_type,
complex_type,
container_of,
enum_type,
@ -110,6 +111,7 @@ __all__ = [
'array_type',
'bool_type',
'cast',
'class_type',
'complex_type',
'container_of',
'enum_type',

View File

@ -291,6 +291,8 @@ enum drgn_type_kind {
DRGN_TYPE_STRUCT,
/** Union type. */
DRGN_TYPE_UNION,
/** Class type. */
DRGN_TYPE_CLASS,
/** Enumerated type. */
DRGN_TYPE_ENUM,
/** Type definition (a.k.a.\ alias) type. */
@ -332,7 +334,7 @@ enum drgn_primitive_type {
*/
} __attribute__((packed));
/** Member of a structure or union type. */
/** Member of a structure, union, or class type. */
struct drgn_type_member {
/**
* Type of the member.
@ -456,7 +458,7 @@ drgn_type_primitive(struct drgn_type *type)
* Get whether a type is complete (i.e., the type definition is known).
*
* This is always @c false for the void type. It may be @c false for structure,
* union, enumerated, and array types, as well as typedef types where the
* union, class, enumerated, and array types, as well as typedef types where the
* underlying type is one of those. Otherwise, it is always @c true.
*/
static inline bool drgn_type_is_complete(struct drgn_type *type)
@ -492,7 +494,7 @@ static inline const char *drgn_type_name(struct drgn_type *type)
/**
* Get whether a kind of type has a size. This is true for integer, boolean,
* floating-point, complex, structure, union, and pointer types.
* floating-point, complex, structure, union, class, and pointer types.
*/
static inline bool drgn_type_kind_has_size(enum drgn_type_kind kind)
{
@ -502,6 +504,7 @@ static inline bool drgn_type_kind_has_size(enum drgn_type_kind kind)
kind == DRGN_TYPE_COMPLEX ||
kind == DRGN_TYPE_STRUCT ||
kind == DRGN_TYPE_UNION ||
kind == DRGN_TYPE_CLASS ||
kind == DRGN_TYPE_POINTER);
}
/** Get whether a type has a size. @sa drgn_type_kind_has_size() */
@ -542,13 +545,14 @@ static inline bool drgn_type_is_signed(struct drgn_type *type)
}
/**
* Get whether a kind of type has a tag. This is true for structure, union, and
* enumerated types.
* Get whether a kind of type has a tag. This is true for structure, union,
* class, and enumerated types.
*/
static inline bool drgn_type_kind_has_tag(enum drgn_type_kind kind)
{
return (kind == DRGN_TYPE_STRUCT ||
kind == DRGN_TYPE_UNION ||
kind == DRGN_TYPE_CLASS ||
kind == DRGN_TYPE_ENUM);
}
/** Get whether a type has a tag. @sa drgn_type_kind_has_tag() */
@ -571,12 +575,14 @@ static inline void *drgn_type_payload(struct drgn_type *type)
}
/**
* Get whether a kind of type has members. This is true for structure and union
* types.
* Get whether a kind of type has members. This is true for structure, union,
* and class types.
*/
static inline bool drgn_type_kind_has_members(enum drgn_type_kind kind)
{
return kind == DRGN_TYPE_STRUCT || kind == DRGN_TYPE_UNION;
return (kind == DRGN_TYPE_STRUCT ||
kind == DRGN_TYPE_UNION ||
kind == DRGN_TYPE_CLASS);
}
/** Get whether a type has members. @sa drgn_type_kind_has_members() */
static inline bool drgn_type_has_members(struct drgn_type *type)
@ -1405,9 +1411,9 @@ enum drgn_object_kind {
/**
* Memory buffer.
*
* This is used for objects with a complex, structure, union, or array
* type. The value is a buffer of the contents of that object's memory
* in the program.
* This is used for objects with a complex, structure, union, class, or
* array type. The value is a buffer of the contents of that object's
* memory in the program.
*/
DRGN_OBJECT_BUFFER,
/**
@ -1440,7 +1446,7 @@ enum drgn_object_kind {
* Incomplete buffer value.
*
* This is used for reference objects with an incomplete structure,
* union, or array type.
* union, class, or array type.
*/
DRGN_OBJECT_INCOMPLETE_BUFFER = -2,
/**
@ -2198,7 +2204,7 @@ drgn_object_dereference(struct drgn_object *res, const struct drgn_object *obj)
}
/**
* Get a member of a structure or union @ref drgn_object (@c .).
* Get a member of a structure, union, or class @ref drgn_object (@c .).
*
* @param[out] res Returned member. May be the same as @p obj.
* @param[in] obj Object.

View File

@ -341,10 +341,11 @@ drgn_base_type_from_dwarf(struct drgn_dwarf_info_cache *dicache, Dwarf_Die *die,
}
/*
* DW_TAG_structure_type, DW_TAG_union_type, and DW_TAG_enumeration_type can be
* incomplete (i.e., have a DW_AT_declaration of true). This tries to find the
* complete type. If it succeeds, it returns NULL. If it can't find a complete
* type, it returns a DRGN_ERROR_STOP error. Otherwise, it returns an error.
* DW_TAG_structure_type, DW_TAG_union_type, DW_TAG_class_type, and
* DW_TAG_enumeration_type can be incomplete (i.e., have a DW_AT_declaration of
* true). This tries to find the complete type. If it succeeds, it returns NULL.
* If it can't find a complete type, it returns a DRGN_ERROR_STOP error.
* Otherwise, it returns an error.
*/
static struct drgn_error *
drgn_dwarf_info_cache_find_complete(struct drgn_dwarf_info_cache *dicache,
@ -543,11 +544,13 @@ static struct drgn_error *parse_member(struct drgn_dwarf_info_cache *dicache,
static struct drgn_error *
drgn_compound_type_from_dwarf(struct drgn_dwarf_info_cache *dicache,
Dwarf_Die *die, bool is_struct,
Dwarf_Die *die, enum drgn_type_kind kind,
struct drgn_type **ret, bool *should_free)
{
struct drgn_error *err;
struct drgn_type *type;
const char *dw_tag_str;
uint64_t dw_tag;
Dwarf_Attribute attr_mem;
Dwarf_Attribute *attr;
const char *tag;
@ -558,28 +561,43 @@ drgn_compound_type_from_dwarf(struct drgn_dwarf_info_cache *dicache,
bool little_endian;
int r;
switch (kind) {
case DRGN_TYPE_STRUCT:
dw_tag_str = "DW_TAG_structure_type";
dw_tag = DW_TAG_structure_type;
break;
case DRGN_TYPE_UNION:
dw_tag_str = "DW_TAG_union_type";
dw_tag = DW_TAG_union_type;
break;
case DRGN_TYPE_CLASS:
dw_tag_str = "DW_TAG_class_type";
dw_tag = DW_TAG_class_type;
break;
default:
DRGN_UNREACHABLE();
}
attr = dwarf_attr_integrate(die, DW_AT_name, &attr_mem);
if (attr) {
tag = dwarf_formstring(attr);
if (!tag)
if (!tag) {
return drgn_error_format(DRGN_ERROR_OTHER,
"DW_TAG_%s_type has invalid DW_AT_name",
is_struct ? "structure" : "union");
"%s has invalid DW_AT_name",
dw_tag_str);
}
} else {
tag = NULL;
}
if (dwarf_flag(die, DW_AT_declaration, &declaration)) {
return drgn_error_format(DRGN_ERROR_OTHER,
"DW_TAG_%s_type has invalid DW_AT_declaration",
is_struct ? "structure" : "union");
"%s has invalid DW_AT_declaration",
dw_tag_str);
}
if (declaration && tag) {
err = drgn_dwarf_info_cache_find_complete(dicache,
is_struct ?
DW_TAG_structure_type :
DW_TAG_union_type,
tag, ret);
dw_tag, tag, ret);
if (!err) {
*should_free = false;
return NULL;
@ -594,10 +612,19 @@ drgn_compound_type_from_dwarf(struct drgn_dwarf_info_cache *dicache,
return &drgn_enomem;
if (declaration) {
if (is_struct)
switch (kind) {
case DRGN_TYPE_STRUCT:
drgn_struct_type_init_incomplete(type, tag);
else
break;
case DRGN_TYPE_UNION:
drgn_union_type_init_incomplete(type, tag);
break;
case DRGN_TYPE_CLASS:
drgn_class_type_init_incomplete(type, tag);
break;
default:
DRGN_UNREACHABLE();
}
*ret = type;
return NULL;
}
@ -605,8 +632,8 @@ drgn_compound_type_from_dwarf(struct drgn_dwarf_info_cache *dicache,
size = dwarf_bytesize(die);
if (size == -1) {
err = drgn_error_format(DRGN_ERROR_OTHER,
"DW_TAG_%s_type has missing or invalid DW_AT_byte_size",
is_struct ? "structure" : "union");
"%s has missing or invalid DW_AT_byte_size",
dw_tag_str);
goto err;
}
@ -645,8 +672,13 @@ drgn_compound_type_from_dwarf(struct drgn_dwarf_info_cache *dicache,
sizeof(struct drgn_type_member));
}
if (is_struct) {
if (kind == DRGN_TYPE_UNION) {
drgn_union_type_init(type, tag, size, num_members);
} else {
if (kind == DRGN_TYPE_STRUCT)
drgn_struct_type_init(type, tag, size, num_members);
else
drgn_class_type_init(type, tag, size, num_members);
/*
* Flexible array members are only allowed as the last member of
* a structure with more than one named member. We defaulted
@ -671,8 +703,6 @@ drgn_compound_type_from_dwarf(struct drgn_dwarf_info_cache *dicache,
thunk->can_be_incomplete_array = true;
}
}
} else {
drgn_union_type_init(type, tag, size, num_members);
}
*ret = type;
return NULL;
@ -1289,13 +1319,19 @@ drgn_type_from_dwarf_internal(struct drgn_dwarf_info_cache *dicache,
err = drgn_base_type_from_dwarf(dicache, die, &ret->type);
break;
case DW_TAG_structure_type:
err = drgn_compound_type_from_dwarf(dicache, die, true,
err = drgn_compound_type_from_dwarf(dicache, die,
DRGN_TYPE_STRUCT,
&ret->type,
&entry.value.should_free);
break;
case DW_TAG_union_type:
err = drgn_compound_type_from_dwarf(dicache, die, false,
&ret->type,
err = drgn_compound_type_from_dwarf(dicache, die,
DRGN_TYPE_UNION, &ret->type,
&entry.value.should_free);
break;
case DW_TAG_class_type:
err = drgn_compound_type_from_dwarf(dicache, die,
DRGN_TYPE_CLASS, &ret->type,
&entry.value.should_free);
break;
case DW_TAG_enumeration_type:
@ -1375,6 +1411,9 @@ struct drgn_error *drgn_dwarf_type_find(enum drgn_type_kind kind,
case DRGN_TYPE_UNION:
tag = DW_TAG_union_type;
break;
case DRGN_TYPE_CLASS:
tag = DW_TAG_class_type;
break;
case DRGN_TYPE_ENUM:
tag = DW_TAG_enumeration_type;
break;

View File

@ -115,6 +115,9 @@ c_append_tagged_name(struct drgn_qualified_type qualified_type, size_t indent,
case DRGN_TYPE_UNION:
keyword = "union";
break;
case DRGN_TYPE_CLASS:
keyword = "class";
break;
case DRGN_TYPE_ENUM:
keyword = "enum";
break;
@ -338,6 +341,7 @@ c_declare_variable(struct drgn_qualified_type qualified_type,
return c_declare_basic(qualified_type, name, indent, sb);
case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
case DRGN_TYPE_ENUM:
return c_declare_tagged(qualified_type, name, indent, sb);
case DRGN_TYPE_POINTER:
@ -488,6 +492,7 @@ c_define_type(struct drgn_qualified_type qualified_type, size_t indent,
return c_declare_basic(qualified_type, NULL, indent, sb);
case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
return c_define_compound(qualified_type, indent, sb);
case DRGN_TYPE_ENUM:
return c_define_enum(qualified_type, indent, sb);
@ -714,10 +719,24 @@ c_pretty_print_compound_object(const struct drgn_object *obj,
struct drgn_object member;
if (!drgn_type_is_complete(underlying_type)) {
const char *keyword;
switch (drgn_type_kind(underlying_type)) {
case DRGN_TYPE_STRUCT:
keyword = "struct";
break;
case DRGN_TYPE_UNION:
keyword = "union";
break;
case DRGN_TYPE_CLASS:
keyword = "class";
break;
default:
DRGN_UNREACHABLE();
}
return drgn_error_format(DRGN_ERROR_TYPE,
"cannot format incomplete %s object",
drgn_type_kind(underlying_type) ==
DRGN_TYPE_STRUCT ? "struct" : "union");
keyword);
}
if (!string_builder_appendc(sb, '{'))
@ -1246,6 +1265,7 @@ c_pretty_print_object_impl(const struct drgn_object *obj, bool cast,
"complex object formatting is not implemented");
case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
return c_pretty_print_compound_object(obj, underlying_type,
indent,
multi_line_columns, sb);

View File

@ -1040,6 +1040,7 @@ struct drgn_error *drgn_object_truthiness(const struct drgn_object *obj,
switch (drgn_type_kind(underlying_type)) {
case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
err = drgn_object_compound_truthiness(obj,
underlying_type);
if (!err || err->code == DRGN_ERROR_STOP) {
@ -1375,7 +1376,7 @@ struct drgn_error *drgn_object_member_dereference(struct drgn_object *res,
underlying_type = drgn_underlying_type(obj->type);
if (drgn_type_kind(underlying_type) != DRGN_TYPE_POINTER) {
return drgn_type_error("'%s' is not a pointer to a structure or union",
return drgn_type_error("'%s' is not a pointer to a structure, union, or class",
obj->type);
}

View File

@ -36,10 +36,10 @@
/**
* Get the truthiness of an object.
*
* For a signed, unsigned, or floating-point value, this is true iff the value
* is non-zero. For structures and unions, this is true iff it is true for any
* of its members. For arrays, this is true iff it is true for any of its
* elements.
* For a signed, unsigned, or floating-point values, this is true iff the value
* is non-zero. For structures, unions, and classes, this is true iff it is true
* for any of its members. For arrays, this is true iff it is true for any of
* its elements.
*/
struct drgn_error *drgn_object_truthiness(const struct drgn_object *obj,
bool *ret);

View File

@ -178,6 +178,7 @@ DrgnType *float_type(PyObject *self, PyObject *args, PyObject *kwds);
DrgnType *complex_type(PyObject *self, PyObject *args, PyObject *kwds);
DrgnType *struct_type(PyObject *self, PyObject *args, PyObject *kwds);
DrgnType *union_type(PyObject *self, PyObject *args, PyObject *kwds);
DrgnType *class_type(PyObject *self, PyObject *args, PyObject *kwds);
DrgnType *enum_type(PyObject *self, PyObject *args, PyObject *kwds);
DrgnType *typedef_type(PyObject *self, PyObject *args, PyObject *kwds);
DrgnType *pointer_type(PyObject *self, PyObject *args, PyObject *kwds);

View File

@ -101,6 +101,8 @@ static PyMethodDef drgn_methods[] = {
drgn_struct_type_DOC},
{"union_type", (PyCFunction)union_type, METH_VARARGS | METH_KEYWORDS,
drgn_union_type_DOC},
{"class_type", (PyCFunction)class_type, METH_VARARGS | METH_KEYWORDS,
drgn_class_type_DOC},
{"enum_type", (PyCFunction)enum_type, METH_VARARGS | METH_KEYWORDS,
drgn_enum_type_DOC},
{"typedef_type", (PyCFunction)typedef_type,

View File

@ -303,6 +303,7 @@ static int serialize_py_object(struct drgn_program *prog, char *buf,
return -1;
case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
return serialize_compound_value(prog, buf, buf_bit_size,
bit_offset, value_obj,
type, little_endian);
@ -587,8 +588,7 @@ static PyObject *DrgnObject_compound_value(struct drgn_object *obj,
if (!drgn_type_is_complete(underlying_type)) {
PyErr_Format(PyExc_TypeError,
"cannot get value of incomplete %s",
drgn_type_kind(underlying_type) ==
DRGN_TYPE_STRUCT ? "struct" : "union");
drgn_type_kind_spelling[drgn_type_kind(underlying_type)]);
return NULL;
}
@ -742,6 +742,7 @@ static PyObject *DrgnObject_value_impl(struct drgn_object *obj)
return NULL;
case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
return DrgnObject_compound_value(obj, underlying_type);
case DRGN_TYPE_ARRAY:
return DrgnObject_array_value(obj, underlying_type);
@ -1424,8 +1425,8 @@ static PyObject *DrgnObject_getattro(DrgnObject *self, PyObject *attr_name)
Py_CLEAR(res);
if (err->code == DRGN_ERROR_TYPE) {
/*
* If the object isn't a structure or union, raise the
* original AttributeError.
* If the object doesn't have a compound type,
* raise the original AttributeError.
*/
PyErr_Restore(exc_type, exc_value, exc_traceback);
return NULL;
@ -1616,7 +1617,7 @@ static PyMethodDef DrgnObject_methods[] = {
{"__floor__", (PyCFunction)DrgnObject_floor, METH_NOARGS},
{"__ceil__", (PyCFunction)DrgnObject_ceil, METH_NOARGS},
{"__dir__", (PyCFunction)DrgnObject_dir, METH_NOARGS,
"dir() implementation which includes structure and union members."},
"dir() implementation which includes structure, union, and class members."},
{"__format__", (PyCFunction)DrgnObject_format, METH_O,
"Object formatter."},
{},

View File

@ -691,10 +691,9 @@ static int append_field(PyObject *parts, bool *first, const char *format, ...)
_Py_IDENTIFIER(DrgnType_Repr);
/*
* We only want to print compound types (structure and union types) one level
* deep in order to avoid very deep recursion. Return 0 if this is the first
* level, 1 if this is a deeper level (and thus we shouldn't print more
* members), and -1 on error.
* We only want to print compound types one level deep in order to avoid very
* deep recursion. Return 0 if this is the first level, 1 if this is a deeper
* level (and thus we shouldn't print more members), and -1 on error.
*/
static int DrgnType_ReprEnter(DrgnType *self)
{
@ -1250,7 +1249,8 @@ out:
static DrgnType *compound_type(PyObject *tag_obj, PyObject *size_obj,
PyObject *members_obj,
enum drgn_qualifiers qualifiers, bool is_struct)
enum drgn_qualifiers qualifiers,
enum drgn_type_kind kind)
{
const char *tag;
DrgnType *type_obj = NULL;
@ -1268,7 +1268,7 @@ static DrgnType *compound_type(PyObject *tag_obj, PyObject *size_obj,
} else {
PyErr_Format(PyExc_TypeError,
"%s_type() tag must be str or None",
is_struct ? "struct" : "union");
drgn_type_kind_spelling[kind]);
return NULL;
}
@ -1276,7 +1276,7 @@ static DrgnType *compound_type(PyObject *tag_obj, PyObject *size_obj,
if (size_obj != Py_None) {
PyErr_Format(PyExc_ValueError,
"incomplete %s type must not have size",
is_struct ? "structure" : "union");
drgn_type_kind_spelling[kind]);
return NULL;
}
type_obj = DrgnType_new(qualifiers, 0, 0);
@ -1290,7 +1290,7 @@ static DrgnType *compound_type(PyObject *tag_obj, PyObject *size_obj,
if (size_obj == Py_None) {
PyErr_Format(PyExc_ValueError, "%s type must have size",
is_struct ? "structure" : "union");
drgn_type_kind_spelling[kind]);
return NULL;
}
@ -1335,17 +1335,35 @@ static DrgnType *compound_type(PyObject *tag_obj, PyObject *size_obj,
goto err;
if (members_obj == Py_None) {
if (is_struct)
switch (kind) {
case DRGN_TYPE_STRUCT:
drgn_struct_type_init_incomplete(type_obj->type, tag);
else
break;
case DRGN_TYPE_UNION:
drgn_union_type_init_incomplete(type_obj->type, tag);
break;
case DRGN_TYPE_CLASS:
drgn_class_type_init_incomplete(type_obj->type, tag);
break;
default:
DRGN_UNREACHABLE();
}
} else {
if (is_struct) {
switch (kind) {
case DRGN_TYPE_STRUCT:
drgn_struct_type_init(type_obj->type, tag, size,
num_members);
} else {
break;
case DRGN_TYPE_UNION:
drgn_union_type_init(type_obj->type, tag, size,
num_members);
break;
case DRGN_TYPE_CLASS:
drgn_class_type_init(type_obj->type, tag, size,
num_members);
break;
default:
DRGN_UNREACHABLE();
}
}
return type_obj;
@ -1371,7 +1389,8 @@ DrgnType *struct_type(PyObject *self, PyObject *args, PyObject *kwds)
&qualifiers))
return NULL;
return compound_type(tag_obj, size_obj, members_obj, qualifiers, true);
return compound_type(tag_obj, size_obj, members_obj, qualifiers,
DRGN_TYPE_STRUCT);
}
DrgnType *union_type(PyObject *self, PyObject *args, PyObject *kwds)
@ -1388,7 +1407,26 @@ DrgnType *union_type(PyObject *self, PyObject *args, PyObject *kwds)
&qualifiers))
return NULL;
return compound_type(tag_obj, size_obj, members_obj, qualifiers, false);
return compound_type(tag_obj, size_obj, members_obj, qualifiers,
DRGN_TYPE_UNION);
}
DrgnType *class_type(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *keywords[] = { "tag", "size", "members", "qualifiers", NULL, };
PyObject *tag_obj;
PyObject *size_obj = Py_None;
PyObject *members_obj = Py_None;
unsigned char qualifiers = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOO&:class_type",
keywords, &tag_obj, &size_obj,
&members_obj, qualifiers_converter,
&qualifiers))
return NULL;
return compound_type(tag_obj, size_obj, members_obj, qualifiers,
DRGN_TYPE_CLASS);
}
static int unpack_enumerator(DrgnType *type_obj, PyObject *enumerators_seq,

View File

@ -14,6 +14,7 @@ const char * const drgn_type_kind_spelling[] = {
[DRGN_TYPE_COMPLEX] = "complex",
[DRGN_TYPE_STRUCT] = "struct",
[DRGN_TYPE_UNION] = "union",
[DRGN_TYPE_CLASS] = "class",
[DRGN_TYPE_ENUM] = "enum",
[DRGN_TYPE_TYPEDEF] = "typedef",
[DRGN_TYPE_POINTER] = "pointer",
@ -297,6 +298,27 @@ void drgn_union_type_init_incomplete(struct drgn_type *type, const char *tag)
type->_private.num_members = 0;
}
void drgn_class_type_init(struct drgn_type *type, const char *tag,
uint64_t size, size_t num_members)
{
type->_private.kind = DRGN_TYPE_CLASS;
type->_private.is_complete = true;
type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE;
type->_private.tag = tag;
type->_private.size = size;
type->_private.num_members = num_members;
}
void drgn_class_type_init_incomplete(struct drgn_type *type, const char *tag)
{
type->_private.kind = DRGN_TYPE_CLASS;
type->_private.is_complete = false;
type->_private.primitive = DRGN_NOT_PRIMITIVE_TYPE;
type->_private.tag = tag;
type->_private.size = 0;
type->_private.num_members = 0;
}
void drgn_enum_type_init(struct drgn_type *type, const char *tag,
struct drgn_type *compatible_type,
size_t num_enumerators)
@ -717,8 +739,14 @@ LIBDRGN_PUBLIC struct drgn_error *drgn_type_sizeof(struct drgn_type *type,
uint64_t *ret)
{
struct drgn_error *err;
enum drgn_type_kind kind = drgn_type_kind(type);
switch (drgn_type_kind(type)) {
if (!drgn_type_is_complete(type)) {
return drgn_error_format(DRGN_ERROR_TYPE,
"cannot get size of incomplete %s type",
drgn_type_kind_spelling[kind]);
}
switch (kind) {
case DRGN_TYPE_INT:
case DRGN_TYPE_BOOL:
case DRGN_TYPE_FLOAT:
@ -727,33 +755,14 @@ LIBDRGN_PUBLIC struct drgn_error *drgn_type_sizeof(struct drgn_type *type,
*ret = drgn_type_size(type);
return NULL;
case DRGN_TYPE_STRUCT:
if (!drgn_type_is_complete(type)) {
return drgn_error_create(DRGN_ERROR_TYPE,
"cannot get size of incomplete structure type");
}
*ret = drgn_type_size(type);
return NULL;
case DRGN_TYPE_UNION:
if (!drgn_type_is_complete(type)) {
return drgn_error_create(DRGN_ERROR_TYPE,
"cannot get size of incomplete union type");
}
case DRGN_TYPE_CLASS:
*ret = drgn_type_size(type);
return NULL;
case DRGN_TYPE_ENUM:
if (!drgn_type_is_complete(type)) {
return drgn_error_create(DRGN_ERROR_TYPE,
"cannot get size of incomplete enumerated type");
}
/* fallthrough */
case DRGN_TYPE_TYPEDEF:
return drgn_type_sizeof(drgn_type_type(type).type, ret);
case DRGN_TYPE_ARRAY:
if (!drgn_type_is_complete(type)) {
return drgn_error_create(DRGN_ERROR_TYPE,
"cannot get size of incomplete array type");
}
err = drgn_type_sizeof(drgn_type_type(type).type, ret);
if (err)
return err;
@ -801,6 +810,7 @@ enum drgn_object_kind drgn_type_object_kind(struct drgn_type *type)
return DRGN_OBJECT_BUFFER;
case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
case DRGN_TYPE_ARRAY:
return (drgn_type_is_complete(type) ? DRGN_OBJECT_BUFFER :
DRGN_OBJECT_INCOMPLETE_BUFFER);
@ -849,6 +859,9 @@ struct drgn_error *drgn_error_incomplete_type(const char *format,
case DRGN_TYPE_UNION:
return drgn_error_format(DRGN_ERROR_TYPE, format,
"incomplete union");
case DRGN_TYPE_CLASS:
return drgn_error_format(DRGN_ERROR_TYPE, format,
"incomplete class");
case DRGN_TYPE_ENUM:
return drgn_error_format(DRGN_ERROR_TYPE, format,
"incomplete enumerated");

View File

@ -34,7 +34,7 @@
* Lazily-evaluated types.
*
* The graph of types in a program can be very deep (and often cyclical), so
* drgn lazily evaluates the types of structure/union members and function
* drgn lazily evaluates the types of compound type members and function
* parameters.
*
* @{
@ -297,6 +297,30 @@ void drgn_union_type_init(struct drgn_type *type, const char *tag,
*/
void drgn_union_type_init_incomplete(struct drgn_type *type, const char *tag);
/**
* Initialize a class type.
* @param[out] type Type to initialize. This must have @c num_members @ref
* drgn_type_memer%s allocated after it. Similar to struct type, the members
* must be initialized with @ref drgn_type_member_init() (either before or after
* this function is called).
* @param[in] tag Name of the type.
* @param[in] size Size of the type in bytes.
* @param[in] num_members The number of members in the type.
*/
void drgn_class_type_init(struct drgn_type *type, const char *tag,
uint64_t size, size_t num_members);
/**
* Initialize an incomplete class type.
*
* @c size and @c num_members are set to zero and @c is_complete is set to @c
* false.
*
* @param[out] type Type to initialize.
* @param[int] tag Name of the type.
*/
void drgn_class_type_init_incomplete(struct drgn_type *type, const char *tag);
/**
* Initialize a signed enumerator of a type.
*
@ -518,14 +542,15 @@ static inline bool drgn_enum_type_is_signed(struct drgn_type *type)
/**
* Get whether a type is anonymous (i.e., the type has no name).
*
* This may be @c false for structure, union, and enum types. Otherwise, it is
* always true.
* This may be @c false for structure, union, class, and enum types. Otherwise,
* it is always true.
*/
static inline bool drgn_type_is_anonymous(struct drgn_type *type)
{
switch (drgn_type_kind(type)) {
case DRGN_TYPE_STRUCT:
case DRGN_TYPE_UNION:
case DRGN_TYPE_CLASS:
case DRGN_TYPE_ENUM:
return !drgn_type_tag(type);
default:

View File

@ -574,14 +574,15 @@ struct drgn_error *drgn_type_index_find_member(struct drgn_type_index *tindex,
/*
* Cache miss. One of the following is true:
*
* 1. The type isn't a structure or union, which is a type error.
* 1. The type isn't a structure, union, or class, which is a type
* error.
* 2. The type hasn't been cached, which means we need to cache it and
* check again.
* 3. The type has already been cached, which means the member doesn't
* exist.
*/
if (!drgn_type_has_members(key.type)) {
return drgn_type_error("'%s' is not a structure or union",
return drgn_type_error("'%s' is not a structure, union, or class",
type);
}
cached_hp = drgn_type_set_hash(&key.type);

View File

@ -178,7 +178,8 @@ drgn_type_index_find_primitive(struct drgn_type_index *tindex,
* drgn_language::find_type().
*
* @param[in] kind Kind of type to find. Must be @ref DRGN_TYPE_STRUCT, @ref
* DRGN_TYPE_UNION, @ref DRGN_TYPE_ENUM, or @ref DRGN_TYPE_TYPEDEF.
* DRGN_TYPE_UNION, @ref DRGN_TYPE_CLASS, @ref DRGN_TYPE_ENUM, or @ref
* DRGN_TYPE_TYPEDEF.
* @param[in] name Name of the type.
* @param[in] name_len Length of @p name in bytes.
* @param[in] filename See @ref drgn_type_index_find().

View File

@ -11,6 +11,7 @@ from drgn import (
Program,
Type,
TypeKind,
class_type,
enum_type,
float_type,
int_type,
@ -20,6 +21,11 @@ from drgn import (
)
coord_type = class_type('coord', 12, (
(int_type('int', 4, True), 'x', 0),
(int_type('int', 4, True), 'y', 32),
(int_type('int', 4, True), 'z', 64),
))
point_type = struct_type('point', 8, (
(int_type('int', 4, True), 'x', 0),
(int_type('int', 4, True), 'y', 32),

View File

@ -8,6 +8,7 @@ from drgn import (
Program,
Qualifiers,
array_type,
class_type,
complex_type,
enum_type,
float_type,
@ -19,7 +20,14 @@ from drgn import (
union_type,
void_type,
)
from tests import ObjectTestCase, color_type, option_type, pid_type, point_type
from tests import (
ObjectTestCase,
color_type,
coord_type,
option_type,
pid_type,
point_type,
)
from tests.dwarf import DW_AT, DW_ATE, DW_FORM, DW_TAG
from tests.dwarfwriter import compile_dwarf, DwarfDie, DwarfAttrib
@ -709,6 +717,109 @@ class TestTypes(unittest.TestCase):
self.type_from_dwarf, dies)
dies[0].attribs.insert(1, size)
def test_class(self):
dies = [
DwarfDie(
DW_TAG.class_type,
[
DwarfAttrib(DW_AT.name, DW_FORM.string, 'coord'),
DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 12),
],
[
DwarfDie(
DW_TAG.member,
[
DwarfAttrib(DW_AT.name, DW_FORM.string, 'x'),
DwarfAttrib(DW_AT.data_member_location, DW_FORM.data1, 0),
DwarfAttrib(DW_AT.type, DW_FORM.ref4, 1),
],
),
DwarfDie(
DW_TAG.member,
[
DwarfAttrib(DW_AT.name, DW_FORM.string, 'y'),
DwarfAttrib(DW_AT.data_member_location, DW_FORM.data1, 4),
DwarfAttrib(DW_AT.type, DW_FORM.ref4, 1),
],
),
DwarfDie(
DW_TAG.member,
[
DwarfAttrib(DW_AT.name, DW_FORM.string, 'z'),
DwarfAttrib(DW_AT.data_member_location, DW_FORM.data1, 8),
DwarfAttrib(DW_AT.type, DW_FORM.ref4, 1),
],
),
],
),
int_die,
]
self.assertFromDwarf(dies, coord_type)
tag = dies[0].attribs.pop(0)
self.assertFromDwarf(
dies, class_type(None, coord_type.size, coord_type.members))
dies[0].attribs.insert(0, tag)
children = list(dies[0].children)
dies[0].children.clear()
self.assertFromDwarf(
dies, class_type('coord', coord_type.size, ()))
size = dies[0].attribs.pop(1)
dies[0].attribs.append(DwarfAttrib(DW_AT.declaration, DW_FORM.flag_present, True))
self.assertFromDwarf(dies, class_type('coord'))
del dies[0].attribs[-1]
dies[0].attribs.insert(1, size)
dies[0].children.extend(children)
name = dies[0].children[0].attribs.pop(0)
self.assertFromDwarf(
dies,
class_type('coord', coord_type.size, (
(int_type('int', 4, True), None, 0),
(int_type('int', 4, True), 'y', 32),
(int_type('int', 4, True), 'z', 64),
)))
dies[0].children[0].attribs.insert(0, name)
tag = dies[0].attribs.pop(0)
dies[0].attribs.insert(0, DwarfAttrib(DW_AT.name, DW_FORM.data1, 0))
self.assertRaisesRegex(Exception,
'DW_TAG_class_type has invalid DW_AT_name',
self.type_from_dwarf, dies)
dies[0].attribs[0] = tag
size = dies[0].attribs.pop(1)
self.assertRaisesRegex(Exception,
'DW_TAG_class_type has missing or invalid DW_AT_byte_size',
self.type_from_dwarf, dies)
dies[0].attribs.insert(1, size)
name = dies[0].children[0].attribs.pop(0)
dies[0].children[0].attribs.insert(0, DwarfAttrib(DW_AT.name, DW_FORM.data1, 0))
self.assertRaisesRegex(Exception,
'DW_TAG_member has invalid DW_AT_name',
self.type_from_dwarf, dies)
dies[0].children[0].attribs[0] = name
location = dies[0].children[0].attribs[1]
dies[0].children[0].attribs[1] = DwarfAttrib(DW_AT.data_member_location, DW_FORM.string, 'foo')
self.assertRaisesRegex(Exception,
'DW_TAG_member has invalid DW_AT_data_member_location',
self.type_from_dwarf, dies)
dies[0].children[0].attribs[1] = location
type_ = dies[0].children[0].attribs.pop(2)
self.assertRaisesRegex(Exception,
'DW_TAG_member is missing DW_AT_type',
self.type_from_dwarf, dies)
dies[0].children[0].attribs.insert(2, DwarfAttrib(DW_AT.type, DW_FORM.string, 'foo'))
self.assertRaisesRegex(Exception,
'DW_TAG_member has invalid DW_AT_type',
self.type_from_dwarf, dies)
dies[0].children[0].attribs[2] = type_
def test_lazy_cycle(self):
dies = [
DwarfDie(

View File

@ -3,21 +3,22 @@ import operator
import unittest
from drgn import (
Qualifiers,
array_type,
bool_type,
class_type,
complex_type,
enum_type,
float_type,
function_type,
int_type,
pointer_type,
Qualifiers,
struct_type,
typedef_type,
union_type,
void_type,
)
from tests import point_type
from tests import coord_type, point_type
from tests.libdrgn import C_TOKEN, drgn_lexer_c, Lexer
@ -78,6 +79,12 @@ class TestPrettyPrintTypeName(unittest.TestCase):
self.assertQualifiedTypeName('union <anonymous>', False, union_type,
None)
def test_class(self):
self.assertQualifiedTypeName('class coord', True, class_type,
'coord')
self.assertQualifiedTypeName('class <anonymous>', False, class_type,
None)
def test_enum(self):
self.assertQualifiedTypeName('enum color', True, enum_type, 'color',
None, None),
@ -279,6 +286,14 @@ const union foo {
unsigned char a[4];
}""")
def test_class(self):
self.assertPrettyPrint(coord_type, """\
class coord {
int x;
int y;
int z;
}""")
def test_enum(self):
t = enum_type('color', int_type('unsigned int', 4, False), (
('RED', 0),

View File

@ -26,6 +26,7 @@ from tests import (
MockMemorySegment,
ObjectTestCase,
color_type,
coord_type,
line_segment_type,
mock_program,
option_type,
@ -1200,7 +1201,7 @@ class TestCOperators(ObjectTestCase):
r'container_of\(\) argument must be a pointer',
container_of, obj[0], point_type, 'x')
self.assertRaisesRegex(TypeError, 'not a structure or union',
self.assertRaisesRegex(TypeError, 'not a structure, union, or class',
container_of, obj, obj.type_, 'x'),
type_ = struct_type('foo', 16, (
@ -1771,7 +1772,7 @@ class TestGenericOperators(ObjectTestCase):
self.assertEqual(obj.y, Object(self.prog, 'int', value=1))
obj = Object(self.prog, 'int', value=1)
self.assertRaisesRegex(TypeError, "'int' is not a structure or union",
self.assertRaisesRegex(TypeError, "'int' is not a structure, union, or class",
obj.member_, 'x')
self.assertRaisesRegex(AttributeError, 'no attribute', getattr, obj,
'x')

View File

@ -6,6 +6,7 @@ from drgn import (
TypeKind,
array_type,
bool_type,
class_type,
complex_type,
enum_type,
float_type,
@ -366,6 +367,144 @@ class TestType(unittest.TestCase):
(int_type('unsigned int', 4, False), 'y', 0, 4),
))
def test_class(self):
t = class_type('coord', 12, (
(int_type('int', 4, True), 'x', 0),
(int_type('int', 4, True), 'y', 32),
(int_type('int', 4, True), 'z', 64),
))
self.assertEqual(t.kind, TypeKind.CLASS)
self.assertIsNone(t.primitive)
self.assertEqual(t.tag, 'coord')
self.assertEqual(t.size, 12)
self.assertEqual(t.members, (
(int_type('int', 4, True), 'x', 0, 0),
(int_type('int', 4, True), 'y', 32, 0),
(int_type('int', 4, True), 'z', 64, 0),
))
self.assertTrue(t.is_complete())
self.assertEqual(t, class_type('coord', 12, (
(int_type('int', 4, True), 'x', 0),
(int_type('int', 4, True), 'y', 32),
(int_type('int', 4, True), 'z', 64),
)))
# Different tag.
self.assertNotEqual(t, class_type('crd', 12, (
(int_type('int', 4, True), 'x', 0),
(int_type('int', 4, True), 'y', 32),
(int_type('int', 4, True), 'z', 64),
)))
# Different size.
self.assertNotEqual(t, class_type('coord', 16, (
(int_type('int', 4, True), 'x', 0),
(int_type('int', 4, True), 'y', 32),
(int_type('int', 4, True), 'z', 64),
)))
# One is anonymous.
self.assertNotEqual(t, class_type(None, 12, (
(int_type('int', 4, True), 'x', 0),
(int_type('int', 4, True), 'y', 32),
(int_type('int', 4, True), 'z', 64),
)))
# Different members.
self.assertNotEqual(t, class_type('coord', 12, (
(int_type('long', 8, True), 'x', 0),
(int_type('long', 8, True), 'y', 64),
(int_type('long', 8, True), 'z', 128),
)))
# Different number of members.
self.assertNotEqual(t, class_type('coord', 12, (
(int_type('int', 4, True), 'x', 0),
(int_type('int', 4, True), 'y', 32),
)))
# One member is anonymous.
self.assertNotEqual(t, class_type('coord', 8, (
(int_type('int', 4, True), 'x', 0, 0),
(int_type('int', 4, True), None, 32, 0),
(int_type('int', 4, True), 'z', 64, 0),
)))
# One is incomplete.
self.assertNotEqual(t, class_type('coord'))
self.assertEqual(repr(t), "class_type(tag='coord', size=12, members=((int_type(name='int', size=4, is_signed=True), 'x', 0, 0), (int_type(name='int', size=4, is_signed=True), 'y', 32, 0), (int_type(name='int', size=4, is_signed=True), 'z', 64, 0)))")
self.assertEqual(sizeof(t), 12)
t = class_type(None, 12, (
(int_type('int', 4, True), 'x', 0),
(int_type('int', 4, True), 'y', 32),
(int_type('int', 4, True), 'z', 64),
))
self.assertEqual(t.kind, TypeKind.CLASS)
self.assertIsNone(t.primitive)
self.assertIsNone(t.tag)
self.assertEqual(t.size, 12)
self.assertEqual(t.members, (
(int_type('int', 4, True), 'x', 0, 0),
(int_type('int', 4, True), 'y', 32, 0),
(int_type('int', 4, True), 'z', 64, 0),
))
self.assertTrue(t.is_complete())
t = class_type('color', 0, ())
self.assertEqual(t.kind, TypeKind.CLASS)
self.assertIsNone(t.primitive)
self.assertEqual(t.tag, 'color')
self.assertEqual(t.size, 0)
self.assertEqual(t.members, ())
self.assertTrue(t.is_complete())
self.assertEqual(repr(t), "class_type(tag='color', size=0, members=())")
t = class_type('color')
self.assertEqual(t.kind, TypeKind.CLASS)
self.assertIsNone(t.primitive)
self.assertEqual(t.tag, 'color')
self.assertIsNone(t.size)
self.assertIsNone(t.members)
self.assertFalse(t.is_complete())
self.assertEqual(repr(t), "class_type(tag='color', size=None, members=None)")
t = class_type(None, None, None)
self.assertEqual(t.kind, TypeKind.CLASS)
self.assertIsNone(t.primitive)
self.assertEqual(t.tag, None)
self.assertIsNone(t.size)
self.assertIsNone(t.members)
self.assertFalse(t.is_complete())
self.assertEqual(repr(t), "class_type(tag=None, size=None, members=None)")
self.assertRaises(TypeError, class_type, 4)
self.assertRaisesRegex(ValueError, 'must not have size', class_type,
'coord', 12, None)
self.assertRaisesRegex(ValueError, 'must have size', class_type,
'coord', None, ())
self.assertRaisesRegex(TypeError, 'must be sequence or None',
class_type, 'coord', 12, 4)
self.assertRaisesRegex(TypeError, 'must be.*sequence', class_type,
'coord', 12, (4))
self.assertRaisesRegex(ValueError, 'must be.*sequence', class_type,
'coord', 12, ((),))
self.assertRaisesRegex(TypeError, 'must be string or None',
class_type, 'coord', 12,
((int_type('int', 4, True), 4, 0),))
self.assertRaisesRegex(TypeError, 'must be integer', class_type,
'coord', 12,
((int_type('int', 4, True), 'x', None),))
self.assertRaisesRegex(TypeError, 'must be Type', class_type, 'coord',
12, ((None, 'x', 0),))
# Bit size.
t = class_type('coord', 12, (
(int_type('int', 4, True), 'x', 0, 4),
(int_type('int', 4, True), 'y', 32, 4),
(int_type('int', 4, True), 'z', 64, 4),
))
self.assertEqual(t.members, (
(int_type('int', 4, True), 'x', 0, 4),
(int_type('int', 4, True), 'y', 32, 4),
(int_type('int', 4, True), 'z', 64, 4),
))
def test_enum(self):
t = enum_type('color', int_type('unsigned int', 4, False),
(('RED', 0), ('GREEN', 1), ('BLUE', 2)))