@@ -30,32 +30,70 @@ using namespace llvm::object;
3030// / Pair of file index and line number representing a source location.
3131typedef std::pair<uint16_t , size_t > SourceLocation;
3232
33- // / Returns the set of source lines covered by a variable's debug information,
34- // / computed by intersecting the variable's location ranges and the containing
35- // / scope's address ranges.
36- static DenseSet<SourceLocation>
37- computeVariableCoverage (DWARFContext &DICtx, DWARFDie VariableDIE,
38- const DWARFDebugLine::LineTable *const LineTable) {
39- // / Adds source locations to the set that correspond to an address range.
40- auto addLines = [](const DWARFDebugLine::LineTable *LineTable,
33+ // / Adds source locations to the line set that correspond to an address range.
34+ static void addLines (const DWARFDebugLine::LineTable *LineTable,
4135 DenseSet<SourceLocation> &Lines, DWARFAddressRange Range) {
42- std::vector<uint32_t > Rows;
43- if (LineTable->lookupAddressRange ({Range.LowPC , Range.SectionIndex },
44- Range.HighPC - Range.LowPC , Rows)) {
45- for (const auto &RowI : Rows) {
46- const auto Row = LineTable->Rows [RowI];
47- // Lookup can return addresses below the LowPC - filter these out.
48- if (Row.Address .Address < Range.LowPC )
49- continue ;
50- const auto FileIndex = Row.File ;
36+ std::vector<uint32_t > Rows;
37+ if (LineTable->lookupAddressRange ({Range.LowPC , Range.SectionIndex },
38+ Range.HighPC - Range.LowPC , Rows)) {
39+ for (const auto &RowI : Rows) {
40+ const auto Row = LineTable->Rows [RowI];
41+ // Lookup can return addresses below the LowPC - filter these out.
42+ if (Row.Address .Address < Range.LowPC )
43+ continue ;
44+
45+ if (Row.Line ) // Ignore zero lines.
46+ Lines.insert ({Row.File , Row.Line });
47+ }
48+ }
49+ }
5150
52- const auto Line = Row.Line ;
53- if (Line) // Ignore zero lines.
54- Lines.insert ({FileIndex, Line});
51+ // Converts the file index of each line in the set to use our own internal
52+ // file index. This is required for a reliable comparison as the DWARF index may
53+ // differ across compilations.
54+ static DenseSet<SourceLocation>
55+ convertFileIndices (DenseSet<SourceLocation> Lines,
56+ const DWARFDebugLine::LineTable *const LineTable,
57+ DenseMap<uint16_t , uint16_t > &FileIndexMap,
58+ StringMap<uint16_t > &FileNameMap) {
59+ DenseSet<SourceLocation> ResultLines;
60+ for (const auto &L : Lines) {
61+ uint16_t Index;
62+ const auto IndexIt = FileIndexMap.find (L.first );
63+ if (IndexIt != FileIndexMap.end ()) {
64+ Index = IndexIt->second ;
65+ } else {
66+ std::string Name;
67+ [[maybe_unused]] bool ValidIndex = LineTable->getFileNameByIndex (
68+ L.first , " " , DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath,
69+ Name);
70+ assert (ValidIndex && " File index was not valid for its own line table" );
71+
72+ auto NameIt = FileNameMap.find (Name);
73+ if (NameIt != FileNameMap.end ()) {
74+ Index = NameIt->second ;
75+ } else {
76+ Index = FileNameMap.size ();
77+ FileNameMap.insert ({Name, Index});
5578 }
79+
80+ FileIndexMap.insert ({L.first , Index});
5681 }
57- };
5882
83+ ResultLines.insert ({Index, L.second });
84+ }
85+
86+ return ResultLines;
87+ }
88+
89+ // / Returns the set of source lines covered by a variable's debug information,
90+ // / computed by intersecting the variable's location ranges and the containing
91+ // / scope's address ranges.
92+ static DenseSet<SourceLocation>
93+ computeVariableCoverage (DWARFDie VariableDIE,
94+ const DWARFDebugLine::LineTable *const LineTable,
95+ DenseMap<uint16_t , uint16_t > &FileIndexMap,
96+ StringMap<uint16_t > &FileNameMap) {
5997 // The optionals below will be empty if no address ranges were found, and
6098 // present (but containing an empty set) if ranges were found but contained no
6199 // source locations, in order to distinguish the two cases.
@@ -92,9 +130,55 @@ computeVariableCoverage(DWARFContext &DICtx, DWARFDie VariableDIE,
92130 if (!Lines && ParentLines)
93131 Lines = std::move (ParentLines);
94132 else if (ParentLines)
95- llvm:: set_intersect (*Lines, *ParentLines);
133+ set_intersect (*Lines, *ParentLines);
96134
97- return Lines.value_or (DenseSet<SourceLocation>());
135+ if (!Lines)
136+ return {};
137+
138+ return convertFileIndices (Lines.value_or (DenseSet<SourceLocation>()),
139+ LineTable, FileIndexMap, FileNameMap);
140+ }
141+
142+ // / Adds source locations to the line set that are within an inlined subroutine.
143+ static void getInlinedLines (DWARFDie SubroutineDIE,
144+ DenseSet<SourceLocation> &Lines,
145+ const DWARFDebugLine::LineTable *const LineTable) {
146+ for (const auto &ChildDIE : SubroutineDIE.children ()) {
147+ if (ChildDIE.getTag () == DW_TAG_inlined_subroutine) {
148+ auto Ranges = ChildDIE.getAddressRanges ();
149+ if (Ranges) {
150+ for (const auto &R : Ranges.get ())
151+ addLines (LineTable, Lines, R);
152+ } else {
153+ consumeError (Ranges.takeError ());
154+ }
155+ } else {
156+ getInlinedLines (ChildDIE, Lines, LineTable);
157+ }
158+ }
159+ }
160+
161+ // / Returns the set of source lines present in the line table for a subroutine.
162+ static DenseSet<SourceLocation>
163+ computeSubroutineCoverage (DWARFDie SubroutineDIE,
164+ const DWARFDebugLine::LineTable *const LineTable,
165+ DenseMap<uint16_t , uint16_t > &FileIndexMap,
166+ StringMap<uint16_t > &FileNameMap) {
167+ auto Ranges = SubroutineDIE.getAddressRanges ();
168+ DenseSet<SourceLocation> Lines;
169+ if (Ranges) {
170+ for (const auto &R : Ranges.get ())
171+ addLines (LineTable, Lines, R);
172+ } else {
173+ consumeError (Ranges.takeError ());
174+ }
175+
176+ // Exclude lines from any subroutines inlined into this one.
177+ DenseSet<SourceLocation> InlinedLines;
178+ getInlinedLines (SubroutineDIE, InlinedLines, LineTable);
179+ set_subtract (Lines, InlinedLines);
180+
181+ return convertFileIndices (Lines, LineTable, FileIndexMap, FileNameMap);
98182}
99183
100184static const SmallVector<DWARFDie> getParentSubroutines (DWARFDie DIE) {
@@ -140,19 +224,27 @@ struct VarKey {
140224struct VarCoverage {
141225 SmallVector<DWARFDie> Parents;
142226 size_t Cov;
227+ size_t BaselineCov;
228+ size_t LTCov;
229+ size_t Missing;
143230 size_t Instances;
231+ bool MissingBaseline;
144232};
145233
146234typedef std::multimap<VarKey, VarCoverage, std::less<>> VarMap;
235+ typedef std::map<VarKey, DenseSet<SourceLocation>, std::less<>> BaselineVarMap;
147236
148- static std::optional<const VarKey> getVarKey (DWARFDie Die, DWARFDie Parent) {
149- const auto *const DieName = Die.getName (DINameKind::LinkageName);
150- const auto DieFile =
151- Die.getDeclFile (DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath);
152- const auto *const ParentName = Parent.getName (DINameKind::LinkageName);
153- if (!DieName || !ParentName)
237+ static std::optional<const VarKey> getVarKey (DWARFDie VariableDIE,
238+ DWARFDie SubroutineDIE) {
239+ const auto *const VariableName = VariableDIE.getName (DINameKind::LinkageName);
240+ const auto DeclFile = VariableDIE.getDeclFile (
241+ DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath);
242+ const auto *const SubroutineName =
243+ SubroutineDIE.getName (DINameKind::LinkageName);
244+ if (!VariableName || !SubroutineName)
154245 return std::nullopt ;
155- return VarKey{ParentName, DieName, DieFile, Die.getDeclLine ()};
246+ return VarKey{SubroutineName, VariableName, DeclFile,
247+ VariableDIE.getDeclLine ()};
156248}
157249
158250static void displayParents (SmallVector<DWARFDie> Parents, raw_ostream &OS) {
@@ -182,34 +274,105 @@ static void displayVariableCoverage(const VarKey &Key, const VarCoverage &Var,
182274 displayParents (Var.Parents , OS);
183275 OS << " \t " ;
184276 WithColor (OS, HighlightColor::String) << Key.Name ;
185- OS << " \t " << Key.DeclFile << " :" << Key.DeclLine ;
277+ OS << " \t " ;
278+ if (!Key.DeclFile .empty ())
279+ OS << Key.DeclFile << " :" << Key.DeclLine ;
186280 OS << " \t " << format (" %.3g" , ((float )Var.Cov / Var.Instances ));
281+ if (Var.BaselineCov )
282+ OS << " \t " << format (" %.3g" , ((float )Var.BaselineCov / Var.Instances ))
283+ << " \t " << format (" %.3g" , ((float )Var.Cov / Var.BaselineCov )) << " \t "
284+ << format (" %.3g" , ((float )Var.LTCov / Var.Instances )) << " \t "
285+ << format (" %.3g" , ((float )Var.LTCov / Var.BaselineCov ));
187286 OS << " \n " ;
287+ if (Var.MissingBaseline )
288+ WithColor (errs (), HighlightColor::Warning).warning ()
289+ << " DIE not found in baseline\n " ;
290+ if (Var.Missing )
291+ WithColor (errs (), HighlightColor::Warning).warning ()
292+ << Var.Missing << " lines not found in baseline\n " ;
188293}
189294
190295bool dwarfdump::showVariableCoverage (ObjectFile &Obj, DWARFContext &DICtx,
296+ ObjectFile *BaselineObj,
297+ DWARFContext *BaselineCtx,
191298 bool CombineInstances, raw_ostream &OS) {
299+ BaselineVarMap BaselineVars;
300+ StringMap<uint16_t > FileNameMap;
301+
302+ if (BaselineCtx) {
303+ for (const auto &U : BaselineCtx->info_section_units ()) {
304+ const auto *const LT = BaselineCtx->getLineTableForUnit (U.get ());
305+ DenseMap<uint16_t , uint16_t > FileIndexMap;
306+ for (const auto &Entry : U->dies ()) {
307+ DWARFDie VariableDIE = {U.get (), &Entry};
308+ if (VariableDIE.getTag () != DW_TAG_variable &&
309+ VariableDIE.getTag () != DW_TAG_formal_parameter)
310+ continue ;
311+
312+ const auto Parents = getParentSubroutines (VariableDIE);
313+ if (!Parents.size ())
314+ continue ;
315+ const auto SubroutineDIE = Parents.front ();
316+ auto Key = getVarKey (VariableDIE, SubroutineDIE);
317+ if (!Key)
318+ continue ;
319+
320+ auto Cov =
321+ computeVariableCoverage (VariableDIE, LT, FileIndexMap, FileNameMap);
322+ const auto SubroutineCov = computeSubroutineCoverage (
323+ SubroutineDIE, LT, FileIndexMap, FileNameMap);
324+ set_intersect (Cov, SubroutineCov);
325+
326+ auto Result = BaselineVars.insert ({*Key, Cov});
327+ if (!Result.second )
328+ Result.first ->second .insert_range (Cov);
329+ }
330+ }
331+ }
332+
192333 VarMap Vars;
193334
194335 for (const auto &U : DICtx.info_section_units ()) {
195336 const auto *const LT = DICtx.getLineTableForUnit (U.get ());
337+ DenseMap<uint16_t , uint16_t > FileIndexMap;
196338 for (const auto &Entry : U->dies ()) {
197- DWARFDie Die = {U.get (), &Entry};
198- if (Die .getTag () != DW_TAG_variable &&
199- Die .getTag () != DW_TAG_formal_parameter)
339+ DWARFDie VariableDIE = {U.get (), &Entry};
340+ if (VariableDIE .getTag () != DW_TAG_variable &&
341+ VariableDIE .getTag () != DW_TAG_formal_parameter)
200342 continue ;
201343
202- const auto Parents = getParentSubroutines (Die );
344+ const auto Parents = getParentSubroutines (VariableDIE );
203345 if (!Parents.size ())
204346 continue ;
205- const auto Parent = Parents.front ();
206- auto Key = getVarKey (Die, Parent );
347+ const auto SubroutineDIE = Parents.front ();
348+ auto Key = getVarKey (VariableDIE, SubroutineDIE );
207349 if (!Key)
208350 continue ;
209351
210- const auto Cov = computeVariableCoverage (DICtx, Die, LT);
352+ auto Cov =
353+ computeVariableCoverage (VariableDIE, LT, FileIndexMap, FileNameMap);
354+ const auto SubroutineCov = computeSubroutineCoverage (
355+ SubroutineDIE, LT, FileIndexMap, FileNameMap);
356+ set_intersect (Cov, SubroutineCov);
211357
212- VarCoverage VarCov = {Parents, Cov.size (), 1 };
358+ VarCoverage VarCov = {Parents, Cov.size (), 0 , 0 , 0 , 1 , false };
359+
360+ if (BaselineCtx) {
361+ BaselineVarMap::iterator Var = BaselineVars.find (*Key);
362+
363+ if (Var != BaselineVars.end ()) {
364+ const auto BCov = Var->second ;
365+ VarCov.BaselineCov = BCov.size ();
366+
367+ for (const auto &L : Cov)
368+ VarCov.Missing += (1 - BCov.count (L));
369+
370+ for (const auto &L : BCov)
371+ VarCov.LTCov += SubroutineCov.count (L);
372+ } else {
373+ VarCov.MissingBaseline = true ;
374+ }
375+ }
213376
214377 Vars.insert ({*Key, VarCov});
215378 }
@@ -219,16 +382,23 @@ bool dwarfdump::showVariableCoverage(ObjectFile &Obj, DWARFContext &DICtx,
219382
220383 OS << " \n Variable coverage statistics:\n Function\t "
221384 << (CombineInstances ? " InstanceCount" : " InlChain" )
222- << " \t Variable\t Decl\t LinesCovered\n " ;
385+ << " \t Variable\t Decl\t LinesCovered" ;
386+ if (BaselineCtx)
387+ OS << " \t Baseline\t CoveredRatio\t LinesPresent\t LinesPresentRatio" ;
388+ OS << " \n " ;
223389
224390 if (CombineInstances) {
225391 for (auto FirstVar = Vars.begin (); FirstVar != Vars.end ();
226392 FirstVar = Range.second ) {
227393 Range = Vars.equal_range (FirstVar->first );
228- VarCoverage CombinedCov = {{}, 0 , 0 };
394+ VarCoverage CombinedCov = {{}, 0 , 0 , 0 , 0 , 0 , false };
229395 for (auto Var = Range.first ; Var != Range.second ; ++Var) {
230396 ++CombinedCov.Instances ;
231397 CombinedCov.Cov += Var->second .Cov ;
398+ CombinedCov.BaselineCov += Var->second .BaselineCov ;
399+ CombinedCov.LTCov += Var->second .LTCov ;
400+ CombinedCov.Missing += Var->second .Missing ;
401+ CombinedCov.MissingBaseline |= Var->second .MissingBaseline ;
232402 }
233403 displayVariableCoverage (FirstVar->first , CombinedCov, true , OS);
234404 }
0 commit comments