[Ltrace-devel] Getting prototypes from debug information

Petr Machata pmachata at redhat.com
Sun May 11 12:21:06 UTC 2014


The following review is about cb3f6d450f0fcc2c2d.

You addressed the *-at-variable in function prototypes, but this should
apply everywhere.  I see a number of cases like this:
+	struct arg_type_info* argument_type = NULL;

This should look like:
+	struct arg_type_info *argument_type = NULL;

+static bool get_die_numeric(uint64_t *result,
+			    Dwarf_Die *die, unsigned int attr_name)
+{
+	Dwarf_Attribute attr ;
[... much later ...]
+	if ( array_type == NULL ) {
[... later still ...]
+	if ( function_name_dup == NULL ) {

Please drop the extra spaces.

+static bool get_integer_base_type(enum arg_type *type, int byte_size,
+				  bool is_signed)
+{
+	switch (byte_size) {
+	case sizeof(char):
+		*type = ARGTYPE_CHAR;
+		return true;
+
+	case sizeof(short):
+		*type = is_signed ? ARGTYPE_SHORT : ARGTYPE_USHORT;
+		return true;
+
+	case sizeof(int):
+		*type = is_signed ? ARGTYPE_INT : ARGTYPE_UINT;
+		return true;
+
+	case sizeof(long):
+		*type = is_signed ? ARGTYPE_LONG : ARGTYPE_ULONG;
+		return true;
+
+	default:
+		return false;
+	}
+}

This won't work on 32-bit arches, where int and long are both 4 bytes
wide.  Additionally, and I didn't realize this before, this test also
assumes that the traced binary comes from the same architecture as
ltrace, which generally doesn't hold (you can trace an i386 binary with
a x86_64 ltrace).  Unfortunately, ltrace's type system doesn't make it
very easy to do this the right way.

What we would very much like to have is ARGTYPE_I32, ARGTYPE_U64, and
the like (or likely just ARGTYPE_INTEGRAL with an attribute indicating a
width, encoding, and the like).  TODO has this to say about the issue:

** Support fixed-width types
   Really we should keep everything as {u,}int{8,16,32,64} internally,
   and have long, short and others be translated to one of those
   according to architecture rules.  Maybe this could be achieved by a
   per-arch config file with typedefs such as:

   | typedef ulong = uint8_t; |

Alas, that's not how this is done currently.  So in the interim, I think
you can assume that ARGTYPE_{U,}SHORT is 2 bytes and ARGTYPE_{U,}INT is
4 bytes--that's so on all arches that ltrace supports.  The only iffy
type is the long.  Essentially for now, you can assume that 8-bytes are
ARGTYPE_LONG.  That will be wrong for long long on 32-bit arches, but
what can we do.

+	while ((die = dwfl_nextcu(dwfl, die, &bias)) != NULL) {

This doesn't seem right.  dwfl_nextcu should go through all CU's of the
whole Dwfl--i.e. if you add two Dwarf-based libraries to a process, it
would iterate the former one twice.  Or maybe I don't quite grasp how
your code works.  Anyway, it looks as if you should keep around
(probably in struct library) the Dwfl_Module that dwfl_report_elf
returns.  Then you can use either dwfl_module_nextcu to iterate just the
bits that you care about.

Alternatively, instead of dwfl_module_nextcu, you could use
dwfl_module_getdwarf and then dwarf_nextcu.  The advantage is that the
latter call tells you address_size that you can use to distinguish
between 32-bit and 64-bit processes, and determine ARGTYPE_LONG's size.
But that would only serve to determine whether we can support 8-byte
integral types.  But that's assuming ARGTYPE_LONG really is 8-byte on an
arch that has 64-bit addresses.  I'm not sure, but x32 I think is a
4-byte address architecture with 8-byte longs, so it's not a silver
bullet anyway.  This really needs the fixes cited above in TODO to be
fully correct.

+	int64_t encoding;
+	if (!get_die_numeric((uint64_t*)&encoding, die, DW_AT_encoding))

Is there a reason not to just use uint64_t for encoding?

+	if (encoding == DW_ATE_float) {
+		switch (byte_size) {
+		case sizeof(float):
+			return ARGTYPE_FLOAT;
+
+		case sizeof(double):
+			return ARGTYPE_DOUBLE;

Hm, this is similar to the above.  Again, we'll have to assume that
ARGTYPE_FLOAT is 4 bytes and ARGTYPE_DOUBLE is 8 bytes.  Luckily I think
this is the case on all arches that ltrace supports.

+		complain(&die, "member: 0x%02x", dwarf_tag(&die));

Just a tip: %#02x includes the initial 0x.

+		type_struct_add(result, member_type,
+				newly_allocated_member_type);

This can fail.

+static struct arg_type_info *get_type(int *newly_allocated_result,
+				      Dwarf_Die *type_die,
+				      struct protolib *plib,
+				      struct dict *type_dieoffset_hash)
[...]
+		dict_insert(type_dieoffset_hash, &die_offset, &result);

This as well.  I understand there's no point checking this in
CLEANUP_AND_RETURN_ERROR and when just storing an ARGTYPE_VOID, but
otherwise please do.

The biggest issue right now is the switch over sizeofs, as I very much
suspect that would break compilation on 32-bit machines.  Otherwise it's
solid and with the few nits addressed, good to go.

Thanks,
PM



More information about the Ltrace-devel mailing list