]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp
Merge ^/head r295902 through r296006.
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / lldb / source / Plugins / SymbolFile / DWARF / DWARFDebugInfo.cpp
1 //===-- DWARFDebugInfo.cpp --------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "SymbolFileDWARF.h"
11
12 #include <algorithm>
13 #include <set>
14
15 #include "lldb/Core/RegularExpression.h"
16 #include "lldb/Core/Stream.h"
17 #include "lldb/Symbol/ObjectFile.h"
18
19 #include "DWARFDebugAranges.h"
20 #include "DWARFDebugInfo.h"
21 #include "DWARFCompileUnit.h"
22 #include "DWARFDebugAranges.h"
23 #include "DWARFDebugInfoEntry.h"
24 #include "DWARFFormValue.h"
25 #include "LogChannelDWARF.h"
26
27 using namespace lldb;
28 using namespace lldb_private;
29 using namespace std;
30
31 //----------------------------------------------------------------------
32 // Constructor
33 //----------------------------------------------------------------------
34 DWARFDebugInfo::DWARFDebugInfo() :
35     m_dwarf2Data(NULL),
36     m_compile_units(),
37     m_cu_aranges_ap ()
38 {
39 }
40
41 //----------------------------------------------------------------------
42 // SetDwarfData
43 //----------------------------------------------------------------------
44 void
45 DWARFDebugInfo::SetDwarfData(SymbolFileDWARF* dwarf2Data)
46 {
47     m_dwarf2Data = dwarf2Data;
48     m_compile_units.clear();
49 }
50
51
52 DWARFDebugAranges &
53 DWARFDebugInfo::GetCompileUnitAranges ()
54 {
55     if (m_cu_aranges_ap.get() == NULL && m_dwarf2Data)
56     {
57         Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_ARANGES));
58
59         m_cu_aranges_ap.reset (new DWARFDebugAranges());
60         const DWARFDataExtractor &debug_aranges_data = m_dwarf2Data->get_debug_aranges_data();
61         if (debug_aranges_data.GetByteSize() > 0)
62         {
63             if (log)
64                 log->Printf ("DWARFDebugInfo::GetCompileUnitAranges() for \"%s\" from .debug_aranges",
65                              m_dwarf2Data->GetObjectFile()->GetFileSpec().GetPath().c_str());
66             m_cu_aranges_ap->Extract (debug_aranges_data);
67             
68         }
69
70         // Make a list of all CUs represented by the arange data in the file.
71         std::set<dw_offset_t> cus_with_data;
72         for (size_t n=0;n<m_cu_aranges_ap.get()->GetNumRanges();n++)
73         {
74             dw_offset_t offset = m_cu_aranges_ap.get()->OffsetAtIndex(n);
75             if (offset != DW_INVALID_OFFSET)
76                 cus_with_data.insert (offset);
77         }
78
79         // Manually build arange data for everything that wasn't in the .debug_aranges table.
80         bool printed = false;
81         const size_t num_compile_units = GetNumCompileUnits();
82         for (size_t idx = 0; idx < num_compile_units; ++idx)
83         {
84             DWARFCompileUnit* cu = GetCompileUnitAtIndex(idx);
85
86             dw_offset_t offset = cu->GetOffset();
87             if (cus_with_data.find(offset) == cus_with_data.end())
88             {
89                 if (log)
90                 {
91                     if (!printed)
92                         log->Printf ("DWARFDebugInfo::GetCompileUnitAranges() for \"%s\" by parsing",
93                                      m_dwarf2Data->GetObjectFile()->GetFileSpec().GetPath().c_str());
94                     printed = true;
95                 }
96                 cu->BuildAddressRangeTable (m_dwarf2Data, m_cu_aranges_ap.get());
97             }
98         }
99
100         const bool minimize = true;
101         m_cu_aranges_ap->Sort (minimize);
102     }
103     return *m_cu_aranges_ap.get();
104 }
105
106 void
107 DWARFDebugInfo::ParseCompileUnitHeadersIfNeeded()
108 {
109     if (m_compile_units.empty())
110     {
111         if (m_dwarf2Data != NULL)
112         {
113             lldb::offset_t offset = 0;
114             const DWARFDataExtractor &debug_info_data = m_dwarf2Data->get_debug_info_data();
115             while (debug_info_data.ValidOffset(offset))
116             {
117                 DWARFCompileUnitSP cu_sp(new DWARFCompileUnit(m_dwarf2Data));
118                 // Out of memory?
119                 if (cu_sp.get() == NULL)
120                     break;
121
122                 if (cu_sp->Extract(debug_info_data, &offset) == false)
123                     break;
124
125                 m_compile_units.push_back(cu_sp);
126
127                 offset = cu_sp->GetNextCompileUnitOffset();
128             }
129         }
130     }
131 }
132
133 size_t
134 DWARFDebugInfo::GetNumCompileUnits()
135 {
136     ParseCompileUnitHeadersIfNeeded();
137     return m_compile_units.size();
138 }
139
140 DWARFCompileUnit*
141 DWARFDebugInfo::GetCompileUnitAtIndex(uint32_t idx)
142 {
143     DWARFCompileUnit* cu = NULL;
144     if (idx < GetNumCompileUnits())
145         cu = m_compile_units[idx].get();
146     return cu;
147 }
148
149 bool
150 DWARFDebugInfo::ContainsCompileUnit (const DWARFCompileUnit *cu) const
151 {
152     // Not a verify efficient function, but it is handy for use in assertions
153     // to make sure that a compile unit comes from a debug information file.
154     CompileUnitColl::const_iterator end_pos = m_compile_units.end();
155     CompileUnitColl::const_iterator pos;
156     
157     for (pos = m_compile_units.begin(); pos != end_pos; ++pos)
158     {
159         if (pos->get() == cu)
160             return true;
161     }
162     return false;
163 }
164
165 bool
166 DWARFDebugInfo::OffsetLessThanCompileUnitOffset (dw_offset_t offset, const DWARFCompileUnitSP& cu_sp)
167 {
168     return offset < cu_sp->GetOffset();
169 }
170
171 DWARFCompileUnit *
172 DWARFDebugInfo::GetCompileUnit(dw_offset_t cu_offset, uint32_t* idx_ptr)
173 {
174     DWARFCompileUnitSP cu_sp;
175     uint32_t cu_idx = DW_INVALID_INDEX;
176     if (cu_offset != DW_INVALID_OFFSET)
177     {
178         ParseCompileUnitHeadersIfNeeded();
179
180         // Watch out for single compile unit executable as they are pretty common
181         const size_t num_cus = m_compile_units.size();
182         if (num_cus == 1)
183         {
184             if (m_compile_units[0]->GetOffset() == cu_offset)
185             {
186                 cu_sp = m_compile_units[0];
187                 cu_idx = 0;
188             }
189         }
190         else if (num_cus)
191         {
192             CompileUnitColl::const_iterator end_pos = m_compile_units.end();
193             CompileUnitColl::const_iterator begin_pos = m_compile_units.begin();
194             CompileUnitColl::const_iterator pos = std::upper_bound(begin_pos, end_pos, cu_offset, OffsetLessThanCompileUnitOffset);
195             if (pos != begin_pos)
196             {
197                 --pos;
198                 if ((*pos)->GetOffset() == cu_offset)
199                 {
200                     cu_sp = *pos;
201                     cu_idx = std::distance(begin_pos, pos);
202                 }
203             }
204         }
205     }
206     if (idx_ptr)
207         *idx_ptr = cu_idx;
208     return cu_sp.get();
209 }
210
211 DWARFCompileUnit *
212 DWARFDebugInfo::GetCompileUnitContainingDIE (const DIERef& die_ref)
213 {
214     dw_offset_t search_offset = die_ref.die_offset;
215     bool is_cu_offset = false;
216     if (m_dwarf2Data->GetID() == 0 && die_ref.cu_offset != DW_INVALID_OFFSET)
217     {
218         is_cu_offset = true;
219         search_offset = die_ref.cu_offset;
220     }
221
222     DWARFCompileUnitSP cu_sp;
223     if (search_offset != DW_INVALID_OFFSET)
224     {
225         ParseCompileUnitHeadersIfNeeded();
226
227         // Watch out for single compile unit executable as they are pretty common
228         const size_t num_cus = m_compile_units.size();
229         if (num_cus == 1)
230         {
231             if ((is_cu_offset && m_compile_units[0]->GetOffset() == search_offset) ||
232                 (!is_cu_offset && m_compile_units[0]->ContainsDIEOffset(search_offset)))
233             {
234                 cu_sp = m_compile_units[0];
235             }
236         }
237         else if (num_cus)
238         {
239             CompileUnitColl::const_iterator end_pos = m_compile_units.end();
240             CompileUnitColl::const_iterator begin_pos = m_compile_units.begin();
241             CompileUnitColl::const_iterator pos = std::upper_bound(begin_pos, end_pos, search_offset, OffsetLessThanCompileUnitOffset);
242             if (pos != begin_pos)
243             {
244                 --pos;
245                 if ((is_cu_offset && (*pos)->GetOffset() == search_offset) ||
246                     (!is_cu_offset && (*pos)->ContainsDIEOffset(search_offset)))
247                 {
248                     cu_sp = *pos;
249                 }
250             }
251         }
252     }
253     return cu_sp.get();
254 }
255
256 //----------------------------------------------------------------------
257 // GetDIE()
258 //
259 // Get the DIE (Debug Information Entry) with the specified offset.
260 //----------------------------------------------------------------------
261 DWARFDIE
262 DWARFDebugInfo::GetDIE(const DIERef& die_ref)
263 {
264     DWARFCompileUnit *cu = GetCompileUnitContainingDIE(die_ref);
265     if (cu)
266         return cu->GetDIE (die_ref.die_offset);
267     return DWARFDIE();    // Not found
268 }
269
270 //----------------------------------------------------------------------
271 // Parse
272 //
273 // Parses the .debug_info section and uses the .debug_abbrev section
274 // and various other sections in the SymbolFileDWARF class and calls the
275 // supplied callback function each time a compile unit header, or debug
276 // information entry is successfully parsed. This function can be used
277 // for different tasks such as parsing the file contents into a
278 // structured data, dumping, verifying and much more.
279 //----------------------------------------------------------------------
280 void
281 DWARFDebugInfo::Parse(SymbolFileDWARF* dwarf2Data, Callback callback, void* userData)
282 {
283     if (dwarf2Data)
284     {
285         lldb::offset_t offset = 0;
286         uint32_t depth = 0;
287         DWARFCompileUnitSP cu(new DWARFCompileUnit(dwarf2Data));
288         if (cu.get() == NULL)
289             return;
290         DWARFDebugInfoEntry die;
291
292         while (cu->Extract(dwarf2Data->get_debug_info_data(), &offset))
293         {
294             const dw_offset_t next_cu_offset = cu->GetNextCompileUnitOffset();
295
296             depth = 0;
297             // Call the callback function with no DIE pointer for the compile unit
298             // and get the offset that we are to continue to parse from
299             offset = callback(dwarf2Data, cu.get(), NULL, offset, depth, userData);
300
301             // Make sure we are within our compile unit
302             if (offset < next_cu_offset)
303             {
304                 // We are in our compile unit, parse starting at the offset
305                 // we were told to parse
306                 bool done = false;
307                 while (!done && die.Extract(dwarf2Data, cu.get(), &offset))
308                 {
309                     // Call the callback function with DIE pointer that falls within the compile unit
310                     offset = callback(dwarf2Data, cu.get(), &die, offset, depth, userData);
311
312                     if (die.IsNULL())
313                     {
314                         if (depth)
315                             --depth;
316                         else
317                             done = true;    // We are done with this compile unit!
318                     }
319                     else if (die.HasChildren())
320                         ++depth;
321                 }
322             }
323
324             // Make sure the offset returned is valid, and if not stop parsing.
325             // Returning DW_INVALID_OFFSET from this callback is a good way to end
326             // all parsing
327             if (!dwarf2Data->get_debug_info_data().ValidOffset(offset))
328                 break;
329
330             // See if during the callback anyone retained a copy of the compile
331             // unit other than ourselves and if so, let whomever did own the object
332             // and create a new one for our own use!
333             if (!cu.unique())
334                 cu.reset(new DWARFCompileUnit(dwarf2Data));
335
336
337             // Make sure we start on a proper
338             offset = next_cu_offset;
339         }
340     }
341 }
342
343 typedef struct DumpInfo
344 {
345     DumpInfo(Stream* init_strm, uint32_t off, uint32_t depth) :
346         strm(init_strm),
347         die_offset(off),
348         recurse_depth(depth),
349         found_depth(UINT32_MAX),
350         found_die(false),
351         ancestors()
352     {
353     }
354     Stream* strm;
355     const uint32_t die_offset;
356     const uint32_t recurse_depth;
357     uint32_t found_depth;
358     bool found_die;
359     std::vector<DWARFDebugInfoEntry> ancestors;
360
361     DISALLOW_COPY_AND_ASSIGN(DumpInfo);
362 } DumpInfo;
363
364 //----------------------------------------------------------------------
365 // DumpCallback
366 //
367 // A callback function for the static DWARFDebugInfo::Parse() function
368 // that gets called each time a compile unit header or debug information
369 // entry is successfully parsed.
370 //
371 // This function dump DWARF information and obey recurse depth and
372 // whether a single DIE is to be dumped (or all of the data).
373 //----------------------------------------------------------------------
374 static dw_offset_t DumpCallback
375 (
376     SymbolFileDWARF* dwarf2Data,
377     DWARFCompileUnit* cu,
378     DWARFDebugInfoEntry* die,
379     const dw_offset_t next_offset,
380     const uint32_t curr_depth,
381     void* userData
382 )
383 {
384     DumpInfo* dumpInfo = (DumpInfo*)userData;
385     Stream *s = dumpInfo->strm;
386     bool show_parents = s->GetFlags().Test(DWARFDebugInfo::eDumpFlag_ShowAncestors);
387
388     if (die)
389     {
390         // Are we dumping everything?
391         if (dumpInfo->die_offset == DW_INVALID_OFFSET)
392         {
393             // Yes we are dumping everything. Obey our recurse level though
394             if (curr_depth < dumpInfo->recurse_depth)
395                 die->Dump(dwarf2Data, cu, *s, 0);
396         }
397         else
398         {
399             // We are dumping a specific DIE entry by offset
400             if (dumpInfo->die_offset == die->GetOffset())
401             {
402                 // We found the DIE we were looking for, dump it!
403                 if (show_parents)
404                 {
405                     s->SetIndentLevel(0);
406                     const uint32_t num_ancestors = dumpInfo->ancestors.size();
407                     if (num_ancestors > 0)
408                     {
409                         for (uint32_t i=0; i<num_ancestors-1; ++i)
410                         {
411                             dumpInfo->ancestors[i].Dump(dwarf2Data, cu, *s, 0);
412                             s->IndentMore();
413                         }
414                     }
415                 }
416
417                 dumpInfo->found_depth = curr_depth;
418
419                 die->Dump(dwarf2Data, cu, *s, 0);
420
421                 // Note that we found the DIE we were looking for
422                 dumpInfo->found_die = true;
423
424                 // Since we are dumping a single DIE, if there are no children we are done!
425                 if (!die->HasChildren() || dumpInfo->recurse_depth == 0)
426                     return DW_INVALID_OFFSET;   // Return an invalid address to end parsing
427             }
428             else if (dumpInfo->found_die)
429             {
430                 // Are we done with all the children?
431                 if (curr_depth <= dumpInfo->found_depth)
432                     return DW_INVALID_OFFSET;
433
434                 // We have already found our DIE and are printing it's children. Obey
435                 // our recurse depth and return an invalid offset if we get done
436                 // dumping all of the children
437                 if (dumpInfo->recurse_depth == UINT32_MAX || curr_depth <= dumpInfo->found_depth + dumpInfo->recurse_depth)
438                     die->Dump(dwarf2Data, cu, *s, 0);
439             }
440             else if (dumpInfo->die_offset > die->GetOffset())
441             {
442                 if (show_parents)
443                     dumpInfo->ancestors.back() = *die;
444             }
445         }
446
447         // Keep up with our indent level
448         if (die->IsNULL())
449         {
450             if (show_parents)
451                 dumpInfo->ancestors.pop_back();
452
453             if (curr_depth <= 1)
454                 return cu->GetNextCompileUnitOffset();
455             else
456                 s->IndentLess();
457         }
458         else if (die->HasChildren())
459         {
460             if (show_parents)
461             {
462                 DWARFDebugInfoEntry null_die;
463                 dumpInfo->ancestors.push_back(null_die);
464             }
465             s->IndentMore();
466         }
467     }
468     else
469     {
470         if (cu == NULL)
471             s->PutCString("NULL - cu");
472         // We have a compile unit, reset our indent level to zero just in case
473         s->SetIndentLevel(0);
474
475         // See if we are dumping everything?
476         if (dumpInfo->die_offset == DW_INVALID_OFFSET)
477         {
478             // We are dumping everything
479             if (cu)
480             {
481                 cu->Dump(s);
482                 return cu->GetFirstDIEOffset(); // Return true to parse all DIEs in this Compile Unit
483             }
484             else
485             {
486                 return DW_INVALID_OFFSET;
487             }
488         }
489         else
490         {
491             if (show_parents)
492             {
493                 dumpInfo->ancestors.clear();
494                 dumpInfo->ancestors.resize(1);
495             }
496
497             // We are dumping only a single DIE possibly with it's children and
498             // we must find it's compile unit before we can dump it properly
499             if (cu && dumpInfo->die_offset < cu->GetFirstDIEOffset())
500             {
501                 // Not found, maybe the DIE offset provided wasn't correct?
502             //  *ostrm_ptr << "DIE at offset " << HEX32 << dumpInfo->die_offset << " was not found." << endl;
503                 return DW_INVALID_OFFSET;
504             }
505             else
506             {
507                 // See if the DIE is in this compile unit?
508                 if (cu && dumpInfo->die_offset < cu->GetNextCompileUnitOffset())
509                 {
510                     // This DIE is in this compile unit!
511                     if (s->GetVerbose())
512                         cu->Dump(s); // Dump the compile unit for the DIE in verbose mode
513
514                     return next_offset;
515                 //  // We found our compile unit that contains our DIE, just skip to dumping the requested DIE...
516                 //  return dumpInfo->die_offset;
517                 }
518                 else
519                 {
520                     // Skip to the next compile unit as the DIE isn't in the current one!
521                     if (cu)
522                     {
523                         return cu->GetNextCompileUnitOffset();
524                     }
525                     else
526                     {
527                         return DW_INVALID_OFFSET;
528                     }
529                 }
530             }
531         }
532     }
533
534     // Just return the current offset to parse the next CU or DIE entry
535     return next_offset;
536 }
537
538 //----------------------------------------------------------------------
539 // Dump
540 //
541 // Dump the information in the .debug_info section to the specified
542 // ostream. If die_offset is valid, a single DIE will be dumped. If the
543 // die_offset is invalid, all the DWARF information will be dumped. Both
544 // cases will obey a "recurse_depth" or how deep to traverse into the
545 // children of each DIE entry. A recurse_depth of zero will dump all
546 // compile unit headers. A recurse_depth of 1 will dump all compile unit
547 // headers and the DW_TAG_compile unit tags. A depth of 2 will also
548 // dump all types and functions.
549 //----------------------------------------------------------------------
550 void
551 DWARFDebugInfo::Dump
552 (
553     Stream *s,
554     SymbolFileDWARF* dwarf2Data,
555     const uint32_t die_offset,
556     const uint32_t recurse_depth
557 )
558 {
559     DumpInfo dumpInfo(s, die_offset, recurse_depth);
560     s->PutCString(".debug_info contents");
561     if (dwarf2Data->get_debug_info_data().GetByteSize() > 0)
562     {
563         if (die_offset == DW_INVALID_OFFSET)
564             s->PutCString(":\n");
565         else
566         {
567             s->Printf(" for DIE entry at .debug_info[0x%8.8x]", die_offset);
568             if (recurse_depth != UINT32_MAX)
569                 s->Printf(" recursing %u levels deep.", recurse_depth);
570             s->EOL();
571         }
572     }
573     else
574     {
575         s->PutCString(": < EMPTY >\n");
576         return;
577     }
578     DWARFDebugInfo::Parse(dwarf2Data, DumpCallback, &dumpInfo);
579 }
580
581
582 //----------------------------------------------------------------------
583 // Dump
584 //
585 // Dump the contents of this DWARFDebugInfo object as has been parsed
586 // and/or modified after it has been parsed.
587 //----------------------------------------------------------------------
588 void
589 DWARFDebugInfo::Dump (Stream *s, const uint32_t die_offset, const uint32_t recurse_depth)
590 {
591     DumpInfo dumpInfo(s, die_offset, recurse_depth);
592
593     s->PutCString("Dumping .debug_info section from internal representation\n");
594
595     CompileUnitColl::const_iterator pos;
596     uint32_t curr_depth = 0;
597     ParseCompileUnitHeadersIfNeeded();
598     for (pos = m_compile_units.begin(); pos != m_compile_units.end(); ++pos)
599     {
600         DWARFCompileUnit *cu = pos->get();
601         DumpCallback(m_dwarf2Data, cu, NULL, 0, curr_depth, &dumpInfo);
602         
603         const DWARFDIE die = cu->DIE();
604         if (die)
605             die.Dump(s, recurse_depth);
606     }
607 }
608
609
610 //----------------------------------------------------------------------
611 // FindCallbackString
612 //
613 // A callback function for the static DWARFDebugInfo::Parse() function
614 // that gets called each time a compile unit header or debug information
615 // entry is successfully parsed.
616 //
617 // This function will find the die_offset of any items whose DW_AT_name
618 // matches the given string
619 //----------------------------------------------------------------------
620 typedef struct FindCallbackStringInfoTag
621 {
622     const char* name;
623     bool ignore_case;
624     RegularExpression* regex;
625     vector<dw_offset_t>& die_offsets;
626 } FindCallbackStringInfo;
627
628 static dw_offset_t FindCallbackString
629 (
630     SymbolFileDWARF* dwarf2Data,
631     DWARFCompileUnit* cu,
632     DWARFDebugInfoEntry* die,
633     const dw_offset_t next_offset,
634     const uint32_t curr_depth,
635     void* userData
636 )
637 {
638     FindCallbackStringInfo* info = (FindCallbackStringInfo*)userData;
639
640     if (die)
641     {
642         const char* die_name = die->GetName(dwarf2Data, cu);
643         if (die_name)
644         {
645             if (info->regex)
646             {
647                 if (info->regex->Execute(die_name))
648                     info->die_offsets.push_back(die->GetOffset());
649             }
650             else
651             {
652                 if ((info->ignore_case ? strcasecmp(die_name, info->name) : strcmp(die_name, info->name)) == 0)
653                     info->die_offsets.push_back(die->GetOffset());
654             }
655         }
656     }
657
658     // Just return the current offset to parse the next CU or DIE entry
659     return next_offset;
660 }
661
662 //----------------------------------------------------------------------
663 // Find
664 //
665 // Finds all DIE that have a specific DW_AT_name attribute by manually
666 // searching through the debug information (not using the
667 // .debug_pubnames section). The string must match the entire name
668 // and case sensitive searches are an option.
669 //----------------------------------------------------------------------
670 bool
671 DWARFDebugInfo::Find(const char* name, bool ignore_case, vector<dw_offset_t>& die_offsets) const
672 {
673     die_offsets.clear();
674     if (name && name[0])
675     {
676         FindCallbackStringInfo info = { name, ignore_case, NULL, die_offsets };
677         DWARFDebugInfo::Parse(m_dwarf2Data, FindCallbackString, &info);
678     }
679     return !die_offsets.empty();
680 }
681
682 //----------------------------------------------------------------------
683 // Find
684 //
685 // Finds all DIE that have a specific DW_AT_name attribute by manually
686 // searching through the debug information (not using the
687 // .debug_pubnames section). The string must match the supplied regular
688 // expression.
689 //----------------------------------------------------------------------
690 bool
691 DWARFDebugInfo::Find(RegularExpression& re, vector<dw_offset_t>& die_offsets) const
692 {
693     die_offsets.clear();
694     FindCallbackStringInfo info = { NULL, false, &re, die_offsets };
695     DWARFDebugInfo::Parse(m_dwarf2Data, FindCallbackString, &info);
696     return !die_offsets.empty();
697 }