libdrgn: add DRGN_FORMAT_OBJECT_CHAR

This commit is contained in:
Omar Sandoval 2019-12-06 11:17:43 -08:00
parent 7cee597fff
commit 89307c532a
6 changed files with 145 additions and 90 deletions

View File

@ -797,6 +797,10 @@ Objects
For C, this applies to pointers to and arrays of ``char``, ``signed
char``, and ``unsigned char``. Defaults to ``True``.
:type string: bool or None
:param char: Format objects with character type as character literals.
For C, this applies to ``char``, ``signed char``, and ``unsigned
char``. Defaults to ``False``.
:type char: bool or None
:rtype: str
.. function:: NULL(prog, type)

View File

@ -2005,11 +2005,12 @@ enum drgn_format_object_flags {
DRGN_FORMAT_OBJECT_DEREFERENCE = 1 << 0,
DRGN_FORMAT_OBJECT_SYMBOLIZE = 1 << 1,
DRGN_FORMAT_OBJECT_STRING = 1 << 2,
DRGN_FORMAT_OBJECT_CHAR = 1 << 3,
/** Default "pretty" flags. */
DRGN_FORMAT_OBJECT_PRETTY = (DRGN_FORMAT_OBJECT_DEREFERENCE |
DRGN_FORMAT_OBJECT_SYMBOLIZE |
DRGN_FORMAT_OBJECT_STRING),
DRGN_FORMAT_OBJECT_VALID_FLAGS = (1 << 3) - 1,
DRGN_FORMAT_OBJECT_VALID_FLAGS = (1 << 4) - 1,
};
/**

View File

@ -127,7 +127,8 @@ static inline enum drgn_format_object_flags
drgn_passthrough_format_object_flags(enum drgn_format_object_flags flags)
{
return (flags & (DRGN_FORMAT_OBJECT_SYMBOLIZE |
DRGN_FORMAT_OBJECT_STRING));
DRGN_FORMAT_OBJECT_STRING |
DRGN_FORMAT_OBJECT_CHAR));
}
/** @} */

View File

@ -584,11 +584,126 @@ c_format_object_impl(const struct drgn_object *obj, bool cast,
enum drgn_format_object_flags flags,
struct string_builder *sb);
static bool is_character_type(struct drgn_type *type)
{
switch (drgn_type_primitive(type)) {
case DRGN_C_TYPE_CHAR:
case DRGN_C_TYPE_SIGNED_CHAR:
case DRGN_C_TYPE_UNSIGNED_CHAR:
return true;
default:
return false;
}
}
static struct drgn_error *
c_format_int_object(const struct drgn_object *obj, struct string_builder *sb)
c_format_character(unsigned char c, bool escape_single_quote,
bool escape_double_quote, struct string_builder *sb)
{
bool ret;
switch (c) {
case '\0':
ret = string_builder_append(sb, "\\0");
break;
case '\a':
ret = string_builder_append(sb, "\\a");
break;
case '\b':
ret = string_builder_append(sb, "\\b");
break;
case '\t':
ret = string_builder_append(sb, "\\t");
break;
case '\n':
ret = string_builder_append(sb, "\\n");
break;
case '\v':
ret = string_builder_append(sb, "\\v");
break;
case '\f':
ret = string_builder_append(sb, "\\f");
break;
case '\r':
ret = string_builder_append(sb, "\\r");
break;
case '"':
if (!escape_double_quote)
goto no_escape;
ret = string_builder_append(sb, "\\\"");
break;
case '\'':
if (!escape_single_quote)
goto no_escape;
ret = string_builder_append(sb, "\\'");
break;
case '\\':
ret = string_builder_append(sb, "\\\\");
break;
default:
if (c <= '\x1f' || c >= '\x7f') {
ret = string_builder_appendf(sb, "\\x%02x", c);
} else {
no_escape:
ret = string_builder_appendc(sb, c);
}
break;
}
return ret ? NULL : &drgn_enomem;
}
static struct drgn_error *
c_format_string(struct drgn_memory_reader *reader, uint64_t address,
uint64_t length, struct string_builder *sb)
{
struct drgn_error *err;
if (!string_builder_appendc(sb, '"'))
return &drgn_enomem;
while (length) {
unsigned char c;
err = drgn_memory_reader_read(reader, &c, address++, 1, false);
if (err)
return err;
if (c == '\0') {
break;
} else {
err = c_format_character(c, false, true, sb);
if (err)
return err;
}
length--;
}
if (!string_builder_appendc(sb, '"'))
return &drgn_enomem;
return NULL;
}
static struct drgn_error *
c_format_int_object(const struct drgn_object *obj,
enum drgn_format_object_flags flags,
struct string_builder *sb)
{
struct drgn_error *err;
if ((flags & DRGN_FORMAT_OBJECT_CHAR) && is_character_type(obj->type)) {
union drgn_value value;
if (!string_builder_appendc(sb, '\''))
return &drgn_enomem;
err = drgn_object_read_integer(obj, &value);
if (err)
return err;
err = c_format_character(value.uvalue, true, false, sb);
if (err)
return err;
if (!string_builder_appendc(sb, '\''))
return &drgn_enomem;
return NULL;
}
switch (obj->kind) {
case DRGN_OBJECT_SIGNED: {
int64_t svalue;
@ -813,90 +928,6 @@ c_format_enum_object(const struct drgn_object *obj,
}
}
static bool is_character_type(struct drgn_type *type)
{
switch (drgn_type_primitive(type)) {
case DRGN_C_TYPE_CHAR:
case DRGN_C_TYPE_SIGNED_CHAR:
case DRGN_C_TYPE_UNSIGNED_CHAR:
return true;
default:
return false;
}
}
static struct drgn_error *
c_format_character(unsigned char c, struct string_builder *sb)
{
bool ret;
switch (c) {
case '\a':
ret = string_builder_append(sb, "\\a");
break;
case '\b':
ret = string_builder_append(sb, "\\b");
break;
case '\t':
ret = string_builder_append(sb, "\\t");
break;
case '\n':
ret = string_builder_append(sb, "\\n");
break;
case '\v':
ret = string_builder_append(sb, "\\v");
break;
case '\f':
ret = string_builder_append(sb, "\\f");
break;
case '\r':
ret = string_builder_append(sb, "\\r");
break;
case '"':
ret = string_builder_append(sb, "\\\"");
break;
case '\\':
ret = string_builder_append(sb, "\\\\");
break;
default:
if (c <= '\x1f' || c >= '\x7f')
ret = string_builder_appendf(sb, "\\x%02x", c);
else
ret = string_builder_appendc(sb, c);
break;
}
return ret ? NULL : &drgn_enomem;
}
static struct drgn_error *
c_format_string(struct drgn_memory_reader *reader, uint64_t address,
uint64_t length, struct string_builder *sb)
{
struct drgn_error *err;
if (!string_builder_appendc(sb, '"'))
return &drgn_enomem;
while (length) {
unsigned char c;
err = drgn_memory_reader_read(reader, &c, address++, 1, false);
if (err)
return err;
if (c == '\0') {
break;
} else {
err = c_format_character(c, sb);
if (err)
return err;
}
length--;
}
if (!string_builder_appendc(sb, '"'))
return &drgn_enomem;
return NULL;
}
static struct drgn_error *
c_format_pointer_object(const struct drgn_object *obj,
struct drgn_type *underlying_type, bool cast,
@ -1042,7 +1073,8 @@ c_format_array_object(const struct drgn_object *obj,
for (i = 0; i < size; i++) {
if (buf[i] == '\0')
break;
err = c_format_character(buf[i], sb);
err = c_format_character(buf[i], false, true,
sb);
if (err)
return err;
}
@ -1279,7 +1311,7 @@ c_format_object_impl(const struct drgn_object *obj, bool cast, size_t indent,
"cannot format void object");
case DRGN_TYPE_INT:
case DRGN_TYPE_BOOL:
return c_format_int_object(obj, sb);
return c_format_int_object(obj, flags, sb);
case DRGN_TYPE_FLOAT:
return c_format_float_object(obj, sb);
case DRGN_TYPE_COMPLEX:

View File

@ -972,7 +972,8 @@ static PyObject *DrgnObject_format(DrgnObject *self, PyObject *args,
#define FLAGS \
X(dereference, DRGN_FORMAT_OBJECT_DEREFERENCE) \
X(symbolize, DRGN_FORMAT_OBJECT_SYMBOLIZE) \
X(string, DRGN_FORMAT_OBJECT_STRING)
X(string, DRGN_FORMAT_OBJECT_STRING) \
X(char, DRGN_FORMAT_OBJECT_CHAR)
static char *keywords[] = {
#define X(name, value) #name,

View File

@ -1228,6 +1228,22 @@ class TestCPretty(ObjectTestCase):
self.assertEqual(str(Object(self.prog, 'const int', value=-99)),
'(const int)-99')
def test_char(self):
obj = Object(self.prog, 'char', value=65)
self.assertEqual(str(obj), '(char)65')
self.assertEqual(obj.format_(char=True), "(char)'A'")
self.assertEqual(
Object(self.prog, 'signed char', value=65).format_(char=True),
"(signed char)'A'")
self.assertEqual(
Object(self.prog, 'unsigned char', value=65).format_(char=True),
"(unsigned char)'A'")
self.assertEqual(
Object(self.prog,
typedef_type('uint8_t', self.prog.type('unsigned char')),
value=65).format_(char=True),
"(uint8_t)65")
def test_bool(self):
self.assertEqual(str(Object(self.prog, '_Bool', value=False)),
'(_Bool)0')