Hi I am working on a feature to provide crash tool an ability to print function arguments. Currently the best way, architecture independent and scalable, to implement the feature is through dwarf routines. I am facing some challenges with the implementation and need your advice and guidance to take it further from here.
I am facing challenges primarily in these 3 areas: 1. Design of the application: Since I was not able to find a good documentation I was not able to validate the code design. If some one can review and provide me guidance it will help me immensely. Challenges are a. Efficient way of accessing the function/subprogram DIE (Debug Information Entry) in an executable that contains multiple Complier Units (CU) b. Is the sequence of dwarf API calls appropriate c. Scalability of the application, especially architecture dependency I am attaching the code that I have developed please review it and let me know if the design is appropriate. The code is not formatted but has been written purely for understanding purpose.
2. Converting the address mentioned in DW_AT_location to actual address of the local variable/arguments. System tap function literal_stmt_for_local() has an implementation for this, I am going thorough this some help in this direction will help me immensly.
3. Converting the address generated from DW_AT_location to actual address in vmcore dump. I am assuming the address we get from the dwarf tag will be a virtual address and that needs to be mapped to a location at vmocre file. crash tool has a routine readmem(), I am currently looking into it for probable solution.
Please let me know your thoughts and ideas on these and in general about the implementation
Thanks Yeehaw
Sharyathi Nagesh wrote:
Since I was not able to find a good documentation I was not able to validate the code design. If some one can review and provide me guidance it will help me immensely. Challenges are a. Efficient way of accessing the function/subprogram DIE (Debug Information Entry) in an executable that contains multiple Complier Units (CU) b. Is the sequence of dwarf API calls appropriate
I think you shouldn't be using <libdwarf.h>, but rather <libdw.h>.
In fact, you might want to look at libdwfl, which might be a better match for what you do. I know it supports common core files directly, but I don't how and if vmcore files differ from that, so you may end up having to write some support code yourself. I don't know much about this library though, so will stick with <libdw.h>.
Also, use <gelf.h>, it will let you handle 32bit as well as 64bit ELF data structures transparently.
dwarf_begin_elf will give you Dwarf *dbg that you can work with further. You can that use dwarf_nextcu to iterate CUs and dwarf_offdie to get CU DIE of each CU:
Dwarf_Off cu_off = 0, next_off; size_t header_size; while (!dwarf_nextcu (dbg, cu_off, &next_off, &header_size, NULL, NULL, NULL)) { Dwarf_Die cudie; if (!dwarf_offdie (dbg, cu_off + header_size, &cudie)) return 0;
/* per-CU processing here, for example: */ int each_fun (Dwarf_Die *die, void *spaces) { Dwarf_Attribute attr; if (dwarf_attr (die, DW_AT_name, &attr) != NULL) printf ("%s%s\n", spaces, dwarf_formstring (&attr) ?: "???"); return DWARF_CB_OK; } each_fun (&cudie, ""); dwarf_getfuncs (&cudie, each_fun, " ", 0);
cu_off = next_off; }
The example above will list names of all CUs and for each of them give a list of functions defined therein. each_fun is a callback, which I abuse to also print out CU names. You would put your per-function processing there and likely not call it for CU DIE itself.
c. Scalability of the application, especially architecture dependency
The above should work pretty much everywhere.
- Converting the address mentioned in DW_AT_location to actual address
of the local variable/arguments. System tap function literal_stmt_for_local() has an implementation for this, I am going thorough this some help in this direction will help me immensly.
I don't know any deep secrets about this. dwarf_getlocation_addr looks to me like the right function to call to get hands on stream of location operands for given DIE. The address argument that it needs is, I think, the instruction pointer (the location of the variable changes depending on where in code you are; it can be a register here, stack over there, etc.)
dwfl_module_return_value_location from libdwfl looks like an interesting alternative to me.
You would still need to write evaluator for DW_AT_location. I think some things in that are pretty easy, namely general arithmetic, but most of the interesting stuff involves reading register values, and that's pretty specific for your problem domain. That's actually something that peeking in systemtap sources might help with.
- Converting the address generated from DW_AT_location to actual
address in vmcore dump. I am assuming the address we get from the dwarf tag will be a virtual address and that needs to be mapped to a location at vmocre file. crash tool has a routine readmem(), I am currently looking into it for probable solution.
That's something I can't help with at all.
As a sidenote, in your code you seem to be doing a lookup by name. If you are trying to annotate a stack trace (which I don't know if you do), I would think that you would do lookup by address. That is, given stack trace of PCs that you have somehow obtained from the core file, call dwarf_addrdie to get CU DIE that covers given PC, and then use that with dwarf_getscopes to get "stack" of DIEs around that PC. Then find closest function DIE in that stack, iterate its arguments, evaluate their location expressions to find values, etc.
Hope that helps, PM
Peter Thanks for the input, I was able to redesign the code with your suggestions. I was infact looking into dwarfdump code and it uses libdwarf.h header, but this header files doesn't seems to be there any more in elfutils package. This code uses only libdw.h and dwarf.h, Please have a look, I have removed most of the error checking (non existence of dwarf information) , which I will include later. I looked into libdwfl.h, which is used by systemtap, as I understand this will be useful in debugging running machine and not the case I am looking for so I am considering only libdw.h for the time being. Let me know your thoughts Thanks Sharyathi Nagesh
Petr Machata wrote:
Sharyathi Nagesh wrote:
Since I was not able to find a good documentation I was not able to validate the code design. If some one can review and provide me guidance it will help me immensely. Challenges are a. Efficient way of accessing the function/subprogram DIE (Debug Information Entry) in an executable that contains multiple Complier Units (CU) b. Is the sequence of dwarf API calls appropriate
I think you shouldn't be using <libdwarf.h>, but rather <libdw.h>.
In fact, you might want to look at libdwfl, which might be a better match for what you do. I know it supports common core files directly, but I don't how and if vmcore files differ from that, so you may end up having to write some support code yourself. I don't know much about this library though, so will stick with <libdw.h>.
Also, use <gelf.h>, it will let you handle 32bit as well as 64bit ELF data structures transparently.
dwarf_begin_elf will give you Dwarf *dbg that you can work with further. You can that use dwarf_nextcu to iterate CUs and dwarf_offdie to get CU DIE of each CU:
Dwarf_Off cu_off = 0, next_off; size_t header_size; while (!dwarf_nextcu (dbg, cu_off, &next_off, &header_size, NULL, NULL, NULL)) { Dwarf_Die cudie; if (!dwarf_offdie (dbg, cu_off + header_size, &cudie)) return 0; /* per-CU processing here, for example: */ int each_fun (Dwarf_Die *die, void *spaces) { Dwarf_Attribute attr; if (dwarf_attr (die, DW_AT_name, &attr) != NULL) printf ("%s%s\n", spaces, dwarf_formstring (&attr) ?: "???"); return DWARF_CB_OK; } each_fun (&cudie, ""); dwarf_getfuncs (&cudie, each_fun, " ", 0); cu_off = next_off; }
The example above will list names of all CUs and for each of them give a list of functions defined therein. each_fun is a callback, which I abuse to also print out CU names. You would put your per-function processing there and likely not call it for CU DIE itself.
c. Scalability of the application, especially architecture dependency
The above should work pretty much everywhere.
- Converting the address mentioned in DW_AT_location to actual
address of the local variable/arguments. System tap function literal_stmt_for_local() has an implementation for this, I am going thorough this some help in this direction will help me immensly.
I don't know any deep secrets about this. dwarf_getlocation_addr looks to me like the right function to call to get hands on stream of location operands for given DIE. The address argument that it needs is, I think, the instruction pointer (the location of the variable changes depending on where in code you are; it can be a register here, stack over there, etc.)
dwfl_module_return_value_location from libdwfl looks like an interesting alternative to me.
You would still need to write evaluator for DW_AT_location. I think some things in that are pretty easy, namely general arithmetic, but most of the interesting stuff involves reading register values, and that's pretty specific for your problem domain. That's actually something that peeking in systemtap sources might help with.
- Converting the address generated from DW_AT_location to actual
address in vmcore dump. I am assuming the address we get from the dwarf tag will be a virtual address and that needs to be mapped to a location at vmocre file. crash tool has a routine readmem(), I am currently looking into it for probable solution.
That's something I can't help with at all.
As a sidenote, in your code you seem to be doing a lookup by name. If you are trying to annotate a stack trace (which I don't know if you do), I would think that you would do lookup by address. That is, given stack trace of PCs that you have somehow obtained from the core file, call dwarf_addrdie to get CU DIE that covers given PC, and then use that with dwarf_getscopes to get "stack" of DIEs around that PC. Then find closest function DIE in that stack, iterate its arguments, evaluate their location expressions to find values, etc.
Hope that helps, PM
Sharyathi Nagesh wrote:
I looked into libdwfl.h, which is used by systemtap, as I understand this will be useful in debugging running machine and not the case I am looking for so I am considering only libdw.h for the time being.
libdwfl can be useful also for post-mortem debugging. It manages address space, i.e. knows which module is mapped to which address, and has a couple useful features that are related to that. It understands that debuginfo portion of the module can be stored separately from the main ELF file, and supports build ID for debuginfo file lookup. It knows about bias (the difference between where the library gets mapped, and what's its base address as noted in the ELF file; important e.g. when translating symbol table addresses to address space addresses). I think it can do some limited REL file relocation (so that you can map kernel modules into the address space). So it's basically a library of things you would have to implement anyway, I think.
I'm not too familiar with libdwfl myself, so if what I've written above seems vague, that's because it is ;)
Anyway, hope that helps. PM
elfutils-devel@lists.fedorahosted.org