[Ltrace-devel] Getting prototypes from debug information

Petr Machata pmachata at redhat.com
Wed Apr 23 23:48:43 UTC 2014


Dima Kogan <lists at dima.secretsauce.net> writes:

> - added a disabled stub for complex float support. ltrace itself doesn't
>   have complex lenses yet. I'd like to get the core of this new DWARF
>   functionality working, then build extra things on it. Thus I'd like to
>   punt on this one until later

Sure, no problem.  I believe all ABI's essentially treat complex as a
struct of two floating point values, so the support is present, even
though we don't have a lens to format these in a fancy manner.

>> I looked into this, and GCC currently doesn't seem to emit DW_AT_type
>> for enums with specified base.  Instead it denotes signedness by
>> encoding enumerators with DW_FORM_udata or DW_FORM_sdata.  Size of the
>> underlying type is encoded in DW_TAG_enumeration_type's DW_AT_byte_size,
>> which might be important for enums in structs (I don't think it will
>> matter in naked parameters, unless in weird cases like __int128, which
>> ltrace doesn't handle anyway).
>
> I ran some tests. Indeed, there's no DW_AT_type, but I always see
> DW_FORM_sdata, even if it's supposedly unsigned. This is gcc 4.8.2. In
> any case, the current code in my tree assumes signed. I think this is
> fine for the first pass.

You're right, I'm not sure what I was looking at.  I don't see it as
udata now either.

>> What you end up inserting is an incomplete type.
>>
> This was done intentionally to support recursive types. Let's say you
> have struct S { struct S* s; };. The code

[...]

> I THINK (correct me if I'm wrong) that if implemented the way you
> suggest, the getType() call would never complete.

Right, I see.  Good catch.

>> Later in process_die_compileunit:
>>
>> +			const char* function_name = dwarf_diename(&die);
>>
>> This could concievably return NULL for DW_AT_name-less DIE.
>
> As part of another change I'm no longer reading functions that have no
> name. Makes sense?

It does.  We couldn't assign them a prototype anyway.

>> +static bool import( struct protolib* plib, struct library* lib, Dwfl* dwfl )
>> +{
>> +	dict_init(&type_hash, sizeof(Dwarf_Off), sizeof(struct arg_type_info*),
>> +			  dwarf_die_hash, dwarf_die_eq, NULL );
>>
>> type_hash should be a local variable in import and the pointer should be
>> passed to other functions.  Is there a reason for making it global?
>
> Only that it makes the code simpler. It's a static global, so its scope
> is limited to dwarf_prototypes.c. In my opinion this is an acceptable
> tradeoff. If you really want it to be local to some function, and pass
> the pointer around instead, tell me and I'll do it that way.

Yes please.

> OK. I did this, but I'm not certain it's right, so please check. In
> library_get_prototype() I now do the import if needed. The
> import_DWARF_prototypes() function itself calls protolib_cache_default()
> to init the protolib, then reads the DWARF data on top of that. Is this
> right? I DO want to read the config files first to have pieces take
> precedence over the DWARF, so overall this should be good, but the
> details are likely wrong.

It's been some time that I wrote this code, so I don't recall details
either.  I'll take a look at your code and let you know.

>> -e traces PLT calls (i.e. inter-library calls), -x traces symbol entry
>> points.  -l traces PLT calls done to a symbol defined by a library in
>> -l.
>
> Can we add this to the manpage? I don't feel qualified to do this myself
> yet.

How about this?

diff --git a/ltrace.1 b/ltrace.1
index f683844..93032f2 100644
--- a/ltrace.1
+++ b/ltrace.1
@@ -122,11 +122,13 @@ describing which debug messages should be displayed.  Use the option
 \-Dh to see what can be used, but note that currently the only
 reliable debugmask is 77, which shows all debug messages.
 .IP "\-e \fIfilter"
-A qualifying expression which modifies which library calls to trace.
-The format of the filter expression is described in the section
-\fBFILTER EXPRESSIONS\fR.  If more than one \-e option appears on the
-command line, the library calls that match any of them are traced.  If
-no \-e is given, \fB at MAIN\fR is assumed as a default.
+A qualifying expression which modifies which library calls (i.e. calls
+done through PLT slots, which are typically calls from the main binary
+to a library, or inter-library calls) to trace.  The format of the
+filter expression is described in the section \fBFILTER
+EXPRESSIONS\fR.  If more than one \-e option appears on the command
+line, the library calls that match any of them are traced.  If no \-e
+is given, \fB at MAIN\fR is assumed as a default.
 .IP \-f
 Trace child processes as they are created by
 currently traced processes as a result of the fork(2)
@@ -145,6 +147,9 @@ Print the instruction pointer at the time of the library call.
 .IP "\-l, \-\-library \fIlibrary_pattern"
 Display only calls to functions implemented by libraries that match
 .I library_pattern.
+This is as if you specified one \-e for every symbol implemented in a
+library specified by
+.I library_pattern.
 Multiple library patters can be specified with several instances of
 this option.  Syntax of library_pattern is described in section
 \fBFILTER EXPRESSIONS\fR.
@@ -200,10 +205,12 @@ option enabled only if elfutils or libunwind support was enabled at compile
 time.
 .IP "\-x \fIfilter"
 A qualifying expression which modifies which symbol table entry points
-to trace.  The format of the filter expression is described in the
-section \fBFILTER EXPRESSIONS\fR.  If more than one \-x option appears
-on the command line, the symbols that match any of them are traced.
-No entry points are traced if no \-x is given.
+to trace (those are typically calls inside a library or main binary,
+though PLT calls, traced by \-e, land on entry points as well).  The
+format of the filter expression is described in the section \fBFILTER
+EXPRESSIONS\fR.  If more than one \-x option appears on the command
+line, the symbols that match any of them are traced.  No entry points
+are traced if no \-x is given.
 .IP "\-V, \-\-version"
 Show the version number of ltrace and exit.
 

> Relatedly, say I have the following 1-file program:
>
>  int f(double x) { return (int)x; }
>  int main(void)
>  {
>      return f(5.0);
>  }
>
> Can ltrace be used to see the f() call? I can't figure out the right
> -l/-e/-x to make that work. This also related to static functions. If we
> can't instrument this, than we probably can't do static functions
> either.

-x can currently trace f, if it is in a symbol table.  If you implement
a Dwarf-based symbol discovery, -x is the filter that shoud use those
symbols.  Both -e and -l are PLT-oriented.  (Of course prototypes can be
deduced from Dwarf for PLT symbols as well.)

> OK. My near-term todo list:
>
> - Try to get prototypes from both DWARF and conf files.
>
> - Go through the code to make sure error checking, memory allocation is
>   handled properly

Cool.  I'll look at your updated patches this week.

Thanks,
PM



More information about the Ltrace-devel mailing list