This is an archive of the discontinued LLVM Phabricator instance.

Make a DWARF generator so we can unit test DWARF APIs with gtest.
ClosedPublic

Authored by clayborg on Dec 1 2016, 5:44 PM.

Details

Summary

The only tests we have for the DWARF parser are the tests that use llvm-dwarfdump and expect output from textual dumps.

More DWARF parser modification are coming in the next few weeks and I wanted to add tests that can verify that we can encode and decode all form types, as well as test some other basic DWARF APIs where we ask DIE objects for their children and siblings.

DwarfGenerator.cpp was added in the lib/CodeGen directory. This file contains the code necessary to easily create DWARF for tests:

dwarfgen::Generator DG;
Triple Triple("x86_64--");
bool success = DG.init(Triple, Version);
if (!success)
  return;
dwarfgen::CompileUnit &CU = DG.addCompileUnit();
dwarfgen::DIE CUDie = CU.getUnitDIE();

CUDie.addAttribute(DW_AT_name, DW_FORM_strp, "/tmp/main.c");
CUDie.addAttribute(DW_AT_language, DW_FORM_data2, DW_LANG_C);

dwarfgen::DIE SubprogramDie = CUDie.addChild(DW_TAG_subprogram);
SubprogramDie.addAttribute(DW_AT_name, DW_FORM_strp, "main");
SubprogramDie.addAttribute(DW_AT_low_pc, DW_FORM_addr, 0x1000U);
SubprogramDie.addAttribute(DW_AT_high_pc, DW_FORM_addr, 0x2000U);

dwarfgen::DIE IntDie = CUDie.addChild(DW_TAG_base_type);
IntDie.addAttribute(DW_AT_name, DW_FORM_strp, "int");
IntDie.addAttribute(DW_AT_encoding, DW_FORM_data1, DW_ATE_signed);
IntDie.addAttribute(DW_AT_byte_size, DW_FORM_data1, 4);

dwarfgen::DIE ArgcDie = SubprogramDie.addChild(DW_TAG_formal_parameter);
ArgcDie.addAttribute(DW_AT_name, DW_FORM_strp, "argc");
// ArgcDie.addAttribute(DW_AT_type, DW_FORM_ref4, IntDie);
ArgcDie.addAttribute(DW_AT_type, DW_FORM_ref_addr, IntDie);

StringRef FileBytes = DG.generate();
MemoryBufferRef FileBuffer(FileBytes, "dwarf");
auto Obj = object::ObjectFile::createObjectFile(FileBuffer);
EXPECT_TRUE((bool)Obj);
DWARFContextInMemory DwarfContext(*Obj.get());

This code is backed by the AsmPrinter code that emits DWARF for the actual compiler.

While adding unit tests it was discovered that DIEValue that used DIEEntry as their values had bugs where DW_FORM_ref1, DW_FORM_ref2, DW_FORM_ref8, and DW_FORM_ref_udata forms were not supported. These are all now supported. Added support for DW_FORM_string so we can emit inlined C strings.

Centralized the code to unique abbreviations into a new DIEAbbrevSet class and made both the dwarfgen::Generator and the llvm::DwarfFile classes use the new class.

Fixed comments in the llvm::DIE class so that the Offset is known to be the compile/type unit offset.

DIEInteger now supports more DW_FORM values.

There are also unit tests that cover:

  • Encoding and decoding all form types and values
  • Encoding and decoding all reference types (DW_FORM_ref1, DW_FORM_ref2, DW_FORM_ref4, DW_FORM_ref8, DW_FORM_ref_udata, DW_FORM_ref_addr) including cross compile unit references with that go forward one compile unit and backward on compile unit.

Diff Detail

Repository
rL LLVM

Event Timeline

clayborg updated this revision to Diff 80002.Dec 1 2016, 5:44 PM
clayborg retitled this revision from to Make a DWARF generator so we can unit test DWARF APIs with gtest..
clayborg updated this object.

Added an inline comment about triples required to created the dwarfgen::Generator since it requires us to use the triple to get all the right classes for the AsmPrinter and all the associated classes that go with it.

unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp
63–66 ↗(On Diff #80002)

Is there a better way to ensure we get a 32 and 64 bit target we can use for generating DWARF? This is my only issue with the patch that I know of. This triple code is repeated 3 times in this file. What happens if we aren't able to get these architectures?

aprantl edited edge metadata.Dec 5 2016, 10:12 AM

Couple of inline comments.

include/llvm/CodeGen/DIE.h
122 ↗(On Diff #80002)

The extra newline could defeat doxygen..

129 ↗(On Diff #80002)

///

lib/CodeGen/AsmPrinter/DIE.cpp
388 ↗(On Diff #80002)

LLVM_FALLTHROUGH *and* break?

389 ↗(On Diff #80002)

I think we might as well move the
Asm->OutStreamer->EmitIntValue(Integer, Size);
up here and return since it is only reachable from this case?

437 ↗(On Diff #80002)

maybe add a getPointerSize method somewhere and hardcode it there?

593 ↗(On Diff #80002)

better write
assert(Form == dwarf::DW_FORM_string && "Expected valid string form")`
and get rid of the extra indentation. I don't think there's a good reason for being defensive here, is there?

603 ↗(On Diff #80002)

-Wpedantic: I think the coding guide lines prefer the commont on a separate line.

620 ↗(On Diff #80002)

move to bottom to match other switch-stmts in this file?

674 ↗(On Diff #80002)

see my other comment

lib/CodeGen/DwarfGenerator.cpp
122 ↗(On Diff #80002)

inline in the header?

124 ↗(On Diff #80002)

should we use any of the new error handling mechanisms here instead of returning a bool?

lib/CodeGen/DwarfGenerator.h
170 ↗(On Diff #80002)

can this be a nullptr? Should it be a reference?

unittests/DebugInfo/DWARF/CMakeLists.txt
9 ↗(On Diff #80002)

Are we 100% sure that all users of libDebugInfoDWARF want to link agains all of these extra libraries all the time? If not, we may need to layer this differently.

unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp
63–66 ↗(On Diff #80002)

This definitely looks like it would break on the bots that build only an arm backend.

774 ↗(On Diff #80002)

Thanks for add all these tests!

clayborg updated this revision to Diff 80340.Dec 5 2016, 3:59 PM
clayborg edited edge metadata.

Fixed Adrian's issues. Big changes include:

  • Added llvm::Expected support to the dwarfgen::Generator
  • Handle errors if any arise
  • Get host triple and get its 32 and 64 bits variants for 4 byte and 8 byte addresses and bail out if targets are not available without breaking the gtest.
clayborg marked 10 inline comments as done.Dec 5 2016, 4:02 PM

Marked things as done and commented where things weren't changed.

lib/CodeGen/AsmPrinter/DIE.cpp
593 ↗(On Diff #80340)

If we use an assert and they aren't enabled, we want to crash, so I used llvm_unreachable below.

lib/CodeGen/DwarfGenerator.cpp
123 ↗(On Diff #80340)

Can't inline the destructor as the headers must be included so that the std::unique_ptr member variables and properly destruct themselves.

lib/CodeGen/DwarfGenerator.h
170 ↗(On Diff #80002)

It is returned as a pointer from the place we get it from (TheTarget->createMCAsmBackend(...)) so this should stay a pointer.

unittests/DebugInfo/DWARF/CMakeLists.txt
4–9 ↗(On Diff #80002)

This is only for this unit test executable, not for libDebugInfoDWARF.

Friendly ping.

Would love to get this in if anyone has time for a review.

aprantl accepted this revision.Dec 7 2016, 4:04 PM
aprantl edited edge metadata.

Thanks, I think this looks good now!
Please be sure to test this with '-DLLVM_TARGETS_TO_BUILD=ARM' to make sure this works even if the X86 target isn't present before committing.

lib/CodeGen/DwarfGenerator.h
52 ↗(On Diff #80340)

I would remove this line.

59 ↗(On Diff #80340)

please remove this line, I think it conflicts with Doxygen's parser.

This revision is now accepted and ready to land.Dec 7 2016, 4:04 PM
This revision was automatically updated to reflect the committed changes.