]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - gdb/dwarf2/cooked-index.c
Update copyright year range in header of all files managed by GDB
[thirdparty/binutils-gdb.git] / gdb / dwarf2 / cooked-index.c
1 /* DIE indexing
2
3 Copyright (C) 2022-2024 Free Software Foundation, Inc.
4
5 This file is part of GDB.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19
20 #include "defs.h"
21 #include "dwarf2/cooked-index.h"
22 #include "dwarf2/read.h"
23 #include "dwarf2/stringify.h"
24 #include "dwarf2/index-cache.h"
25 #include "cp-support.h"
26 #include "c-lang.h"
27 #include "ada-lang.h"
28 #include "split-name.h"
29 #include "observable.h"
30 #include "run-on-main-thread.h"
31 #include <algorithm>
32 #include "gdbsupport/gdb-safe-ctype.h"
33 #include "gdbsupport/selftest.h"
34 #include <chrono>
35 #include <unordered_set>
36 #include "cli/cli-cmds.h"
37
38 /* We don't want gdb to exit while it is in the process of writing to
39 the index cache. So, all live cooked index vectors are stored
40 here, and then these are all waited for before exit proceeds. */
41 static std::unordered_set<cooked_index *> active_vectors;
42
43 /* See cooked-index.h. */
44
45 std::string
46 to_string (cooked_index_flag flags)
47 {
48 static constexpr cooked_index_flag::string_mapping mapping[] = {
49 MAP_ENUM_FLAG (IS_MAIN),
50 MAP_ENUM_FLAG (IS_STATIC),
51 MAP_ENUM_FLAG (IS_ENUM_CLASS),
52 MAP_ENUM_FLAG (IS_LINKAGE),
53 MAP_ENUM_FLAG (IS_TYPE_DECLARATION),
54 MAP_ENUM_FLAG (IS_PARENT_DEFERRED),
55 };
56
57 return flags.to_string (mapping);
58 }
59
60 /* See cooked-index.h. */
61
62 bool
63 language_requires_canonicalization (enum language lang)
64 {
65 return (lang == language_ada
66 || lang == language_c
67 || lang == language_cplus);
68 }
69
70 /* Return true if a plain "main" could be the main program for this
71 language. Languages that are known to use some other mechanism are
72 excluded here. */
73
74 static bool
75 language_may_use_plain_main (enum language lang)
76 {
77 /* No need to handle "unknown" here. */
78 return (lang == language_c
79 || lang == language_objc
80 || lang == language_cplus
81 || lang == language_m2
82 || lang == language_asm
83 || lang == language_opencl
84 || lang == language_minimal);
85 }
86
87 /* See cooked-index.h. */
88
89 int
90 cooked_index_entry::compare (const char *stra, const char *strb,
91 comparison_mode mode)
92 {
93 auto munge = [] (char c) -> unsigned char
94 {
95 /* We want to sort '<' before any other printable character.
96 So, rewrite '<' to something just before ' '. */
97 if (c == '<')
98 return '\x1f';
99 return TOLOWER ((unsigned char) c);
100 };
101
102 while (*stra != '\0'
103 && *strb != '\0'
104 && (munge (*stra) == munge (*strb)))
105 {
106 ++stra;
107 ++strb;
108 }
109
110 unsigned char c1 = munge (*stra);
111 unsigned char c2 = munge (*strb);
112
113 if (c1 == c2)
114 return 0;
115
116 /* When completing, if STRB ends earlier than STRA, consider them as
117 equal. When comparing, if STRB ends earlier and STRA ends with
118 '<', consider them as equal. */
119 if (mode == COMPLETE || (mode == MATCH && c1 == munge ('<')))
120 {
121 if (c2 == '\0')
122 return 0;
123 }
124
125 return c1 < c2 ? -1 : 1;
126 }
127
128 #if GDB_SELF_TEST
129
130 namespace {
131
132 void
133 test_compare ()
134 {
135 /* Convenience aliases. */
136 const auto mode_compare = cooked_index_entry::MATCH;
137 const auto mode_sort = cooked_index_entry::SORT;
138 const auto mode_complete = cooked_index_entry::COMPLETE;
139
140 SELF_CHECK (cooked_index_entry::compare ("abcd", "abcd",
141 mode_compare) == 0);
142 SELF_CHECK (cooked_index_entry::compare ("abcd", "abcd",
143 mode_complete) == 0);
144
145 SELF_CHECK (cooked_index_entry::compare ("abcd", "ABCDE",
146 mode_compare) < 0);
147 SELF_CHECK (cooked_index_entry::compare ("ABCDE", "abcd",
148 mode_compare) > 0);
149 SELF_CHECK (cooked_index_entry::compare ("abcd", "ABCDE",
150 mode_complete) < 0);
151 SELF_CHECK (cooked_index_entry::compare ("ABCDE", "abcd",
152 mode_complete) == 0);
153
154 SELF_CHECK (cooked_index_entry::compare ("name", "name<>",
155 mode_compare) < 0);
156 SELF_CHECK (cooked_index_entry::compare ("name<>", "name",
157 mode_compare) == 0);
158 SELF_CHECK (cooked_index_entry::compare ("name", "name<>",
159 mode_complete) < 0);
160 SELF_CHECK (cooked_index_entry::compare ("name<>", "name",
161 mode_complete) == 0);
162
163 SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>",
164 mode_compare) == 0);
165 SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>",
166 mode_compare) > 0);
167 SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>",
168 mode_complete) == 0);
169 SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>",
170 mode_complete) > 0);
171
172 SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>",
173 "name<arg<more>>",
174 mode_compare) == 0);
175
176 SELF_CHECK (cooked_index_entry::compare ("name", "name<arg<more>>",
177 mode_compare) < 0);
178 SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name",
179 mode_compare) == 0);
180 SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<",
181 mode_compare) > 0);
182 SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<",
183 mode_complete) == 0);
184
185 SELF_CHECK (cooked_index_entry::compare ("", "abcd", mode_compare) < 0);
186 SELF_CHECK (cooked_index_entry::compare ("", "abcd", mode_complete) < 0);
187 SELF_CHECK (cooked_index_entry::compare ("abcd", "", mode_compare) > 0);
188 SELF_CHECK (cooked_index_entry::compare ("abcd", "", mode_complete) == 0);
189
190 SELF_CHECK (cooked_index_entry::compare ("func", "func<type>",
191 mode_sort) < 0);
192 SELF_CHECK (cooked_index_entry::compare ("func<type>", "func1",
193 mode_sort) < 0);
194 }
195
196 } /* anonymous namespace */
197
198 #endif /* GDB_SELF_TEST */
199
200 /* See cooked-index.h. */
201
202 const char *
203 cooked_index_entry::full_name (struct obstack *storage, bool for_main) const
204 {
205 const char *local_name = for_main ? name : canonical;
206
207 if ((flags & IS_LINKAGE) != 0 || get_parent () == nullptr)
208 return local_name;
209
210 const char *sep = nullptr;
211 switch (per_cu->lang ())
212 {
213 case language_cplus:
214 case language_rust:
215 sep = "::";
216 break;
217
218 case language_go:
219 case language_d:
220 case language_ada:
221 sep = ".";
222 break;
223
224 default:
225 return local_name;
226 }
227
228 get_parent ()->write_scope (storage, sep, for_main);
229 obstack_grow0 (storage, local_name, strlen (local_name));
230 return (const char *) obstack_finish (storage);
231 }
232
233 /* See cooked-index.h. */
234
235 void
236 cooked_index_entry::write_scope (struct obstack *storage,
237 const char *sep,
238 bool for_main) const
239 {
240 if (get_parent () != nullptr)
241 get_parent ()->write_scope (storage, sep, for_main);
242 const char *local_name = for_main ? name : canonical;
243 obstack_grow (storage, local_name, strlen (local_name));
244 obstack_grow (storage, sep, strlen (sep));
245 }
246
247 /* See cooked-index.h. */
248
249 cooked_index_entry *
250 cooked_index_shard::add (sect_offset die_offset, enum dwarf_tag tag,
251 cooked_index_flag flags, const char *name,
252 cooked_index_entry_ref parent_entry,
253 dwarf2_per_cu_data *per_cu)
254 {
255 cooked_index_entry *result = create (die_offset, tag, flags, name,
256 parent_entry, per_cu);
257 m_entries.push_back (result);
258
259 /* An explicitly-tagged main program should always override the
260 implicit "main" discovery. */
261 if ((flags & IS_MAIN) != 0)
262 m_main = result;
263 else if ((flags & IS_PARENT_DEFERRED) == 0
264 && parent_entry.resolved == nullptr
265 && m_main == nullptr
266 && language_may_use_plain_main (per_cu->lang ())
267 && strcmp (name, "main") == 0)
268 m_main = result;
269
270 return result;
271 }
272
273 /* See cooked-index.h. */
274
275 gdb::unique_xmalloc_ptr<char>
276 cooked_index_shard::handle_gnat_encoded_entry (cooked_index_entry *entry,
277 htab_t gnat_entries)
278 {
279 /* We decode Ada names in a particular way: operators and wide
280 characters are left as-is. This is done to make name matching a
281 bit simpler; and for wide characters, it means the choice of Ada
282 source charset does not affect the indexer directly. */
283 std::string canonical = ada_decode (entry->name, false, false, false);
284 if (canonical.empty ())
285 return {};
286 std::vector<std::string_view> names = split_name (canonical.c_str (),
287 split_style::DOT_STYLE);
288 std::string_view tail = names.back ();
289 names.pop_back ();
290
291 const cooked_index_entry *parent = nullptr;
292 for (const auto &name : names)
293 {
294 uint32_t hashval = dwarf5_djb_hash (name);
295 void **slot = htab_find_slot_with_hash (gnat_entries, &name,
296 hashval, INSERT);
297 /* CUs are processed in order, so we only need to check the most
298 recent entry. */
299 cooked_index_entry *last = (cooked_index_entry *) *slot;
300 if (last == nullptr || last->per_cu != entry->per_cu)
301 {
302 gdb::unique_xmalloc_ptr<char> new_name
303 = make_unique_xstrndup (name.data (), name.length ());
304 last = create (entry->die_offset, DW_TAG_namespace,
305 0, new_name.get (), parent,
306 entry->per_cu);
307 last->canonical = last->name;
308 m_names.push_back (std::move (new_name));
309 *slot = last;
310 }
311
312 parent = last;
313 }
314
315 entry->set_parent (parent);
316 return make_unique_xstrndup (tail.data (), tail.length ());
317 }
318
319 /* See cooked-index.h. */
320
321 void
322 cooked_index_shard::finalize ()
323 {
324 auto hash_name_ptr = [] (const void *p)
325 {
326 const cooked_index_entry *entry = (const cooked_index_entry *) p;
327 return htab_hash_pointer (entry->name);
328 };
329
330 auto eq_name_ptr = [] (const void *a, const void *b) -> int
331 {
332 const cooked_index_entry *ea = (const cooked_index_entry *) a;
333 const cooked_index_entry *eb = (const cooked_index_entry *) b;
334 return ea->name == eb->name;
335 };
336
337 /* We can use pointer equality here because names come from
338 .debug_str, which will normally be unique-ified by the linker.
339 Also, duplicates are relatively harmless -- they just mean a bit
340 of extra memory is used. */
341 htab_up seen_names (htab_create_alloc (10, hash_name_ptr, eq_name_ptr,
342 nullptr, xcalloc, xfree));
343
344 auto hash_entry = [] (const void *e)
345 {
346 const cooked_index_entry *entry = (const cooked_index_entry *) e;
347 return dwarf5_djb_hash (entry->canonical);
348 };
349
350 auto eq_entry = [] (const void *a, const void *b) -> int
351 {
352 const cooked_index_entry *ae = (const cooked_index_entry *) a;
353 const std::string_view *sv = (const std::string_view *) b;
354 return (strlen (ae->canonical) == sv->length ()
355 && strncasecmp (ae->canonical, sv->data (), sv->length ()) == 0);
356 };
357
358 htab_up gnat_entries (htab_create_alloc (10, hash_entry, eq_entry,
359 nullptr, xcalloc, xfree));
360
361 for (cooked_index_entry *entry : m_entries)
362 {
363 /* Note that this code must be kept in sync with
364 language_requires_canonicalization. */
365 gdb_assert (entry->canonical == nullptr);
366 if ((entry->flags & IS_LINKAGE) != 0)
367 entry->canonical = entry->name;
368 else if (entry->per_cu->lang () == language_ada)
369 {
370 gdb::unique_xmalloc_ptr<char> canon_name
371 = handle_gnat_encoded_entry (entry, gnat_entries.get ());
372 if (canon_name == nullptr)
373 entry->canonical = entry->name;
374 else
375 {
376 entry->canonical = canon_name.get ();
377 m_names.push_back (std::move (canon_name));
378 }
379 }
380 else if (entry->per_cu->lang () == language_cplus
381 || entry->per_cu->lang () == language_c)
382 {
383 void **slot = htab_find_slot (seen_names.get (), entry,
384 INSERT);
385 if (*slot == nullptr)
386 {
387 gdb::unique_xmalloc_ptr<char> canon_name
388 = (entry->per_cu->lang () == language_cplus
389 ? cp_canonicalize_string (entry->name)
390 : c_canonicalize_name (entry->name));
391 if (canon_name == nullptr)
392 entry->canonical = entry->name;
393 else
394 {
395 entry->canonical = canon_name.get ();
396 m_names.push_back (std::move (canon_name));
397 }
398 *slot = entry;
399 }
400 else
401 {
402 const cooked_index_entry *other
403 = (const cooked_index_entry *) *slot;
404 entry->canonical = other->canonical;
405 }
406 }
407 else
408 entry->canonical = entry->name;
409 }
410
411 m_names.shrink_to_fit ();
412 m_entries.shrink_to_fit ();
413 std::sort (m_entries.begin (), m_entries.end (),
414 [] (const cooked_index_entry *a, const cooked_index_entry *b)
415 {
416 return *a < *b;
417 });
418 }
419
420 /* See cooked-index.h. */
421
422 cooked_index_shard::range
423 cooked_index_shard::find (const std::string &name, bool completing) const
424 {
425 cooked_index_entry::comparison_mode mode = (completing
426 ? cooked_index_entry::COMPLETE
427 : cooked_index_entry::MATCH);
428
429 auto lower = std::lower_bound (m_entries.cbegin (), m_entries.cend (), name,
430 [=] (const cooked_index_entry *entry,
431 const std::string &n)
432 {
433 return cooked_index_entry::compare (entry->canonical, n.c_str (), mode) < 0;
434 });
435
436 auto upper = std::upper_bound (m_entries.cbegin (), m_entries.cend (), name,
437 [=] (const std::string &n,
438 const cooked_index_entry *entry)
439 {
440 return cooked_index_entry::compare (entry->canonical, n.c_str (), mode) > 0;
441 });
442
443 return range (lower, upper);
444 }
445
446
447 cooked_index::cooked_index (dwarf2_per_objfile *per_objfile)
448 : m_state (std::make_unique<cooked_index_worker> (per_objfile)),
449 m_per_bfd (per_objfile->per_bfd)
450 {
451 /* ACTIVE_VECTORS is not locked, and this assert ensures that this
452 will be caught if ever moved to the background. */
453 gdb_assert (is_main_thread ());
454 active_vectors.insert (this);
455 }
456
457 void
458 cooked_index::start_reading ()
459 {
460 m_state->start ();
461 }
462
463 void
464 cooked_index::wait (cooked_state desired_state, bool allow_quit)
465 {
466 gdb_assert (desired_state != cooked_state::INITIAL);
467
468 /* If the state object has been deleted, then that means waiting is
469 completely done. */
470 if (m_state == nullptr)
471 return;
472
473 if (m_state->wait (desired_state, allow_quit))
474 {
475 /* Only the main thread can modify this. */
476 gdb_assert (is_main_thread ());
477 m_state.reset (nullptr);
478 }
479 }
480
481 void
482 cooked_index::set_contents (vec_type &&vec)
483 {
484 gdb_assert (m_vector.empty ());
485 m_vector = std::move (vec);
486
487 m_state->set (cooked_state::MAIN_AVAILABLE);
488
489 index_cache_store_context ctx (global_index_cache, m_per_bfd);
490
491 /* This is run after finalization is done -- but not before. If
492 this task were submitted earlier, it would have to wait for
493 finalization. However, that would take a slot in the global
494 thread pool, and if enough such tasks were submitted at once, it
495 would cause a livelock. */
496 gdb::task_group finalizers ([this, ctx = std::move (ctx)] ()
497 {
498 m_state->set (cooked_state::FINALIZED);
499 maybe_write_index (m_per_bfd, ctx);
500 });
501
502 for (auto &idx : m_vector)
503 {
504 auto this_index = idx.get ();
505 finalizers.add_task ([=] () { this_index->finalize (); });
506 }
507
508 finalizers.start ();
509 }
510
511 cooked_index::~cooked_index ()
512 {
513 /* Wait for index-creation to be done, though this one must also
514 waited for by the per-BFD object to ensure the required data
515 remains live. */
516 wait (cooked_state::CACHE_DONE);
517
518 /* Remove our entry from the global list. See the assert in the
519 constructor to understand this. */
520 gdb_assert (is_main_thread ());
521 active_vectors.erase (this);
522 }
523
524 /* See cooked-index.h. */
525
526 dwarf2_per_cu_data *
527 cooked_index::lookup (unrelocated_addr addr)
528 {
529 /* Ensure that the address maps are ready. */
530 wait (cooked_state::MAIN_AVAILABLE, true);
531 for (const auto &index : m_vector)
532 {
533 dwarf2_per_cu_data *result = index->lookup (addr);
534 if (result != nullptr)
535 return result;
536 }
537 return nullptr;
538 }
539
540 /* See cooked-index.h. */
541
542 std::vector<const addrmap *>
543 cooked_index::get_addrmaps ()
544 {
545 /* Ensure that the address maps are ready. */
546 wait (cooked_state::MAIN_AVAILABLE, true);
547 std::vector<const addrmap *> result;
548 for (const auto &index : m_vector)
549 result.push_back (index->m_addrmap);
550 return result;
551 }
552
553 /* See cooked-index.h. */
554
555 cooked_index::range
556 cooked_index::find (const std::string &name, bool completing)
557 {
558 wait (cooked_state::FINALIZED, true);
559 std::vector<cooked_index_shard::range> result_range;
560 result_range.reserve (m_vector.size ());
561 for (auto &entry : m_vector)
562 result_range.push_back (entry->find (name, completing));
563 return range (std::move (result_range));
564 }
565
566 /* See cooked-index.h. */
567
568 const char *
569 cooked_index::get_main_name (struct obstack *obstack, enum language *lang)
570 const
571 {
572 const cooked_index_entry *entry = get_main ();
573 if (entry == nullptr)
574 return nullptr;
575
576 *lang = entry->per_cu->lang ();
577 return entry->full_name (obstack, true);
578 }
579
580 /* See cooked_index.h. */
581
582 const cooked_index_entry *
583 cooked_index::get_main () const
584 {
585 const cooked_index_entry *best_entry = nullptr;
586 for (const auto &index : m_vector)
587 {
588 const cooked_index_entry *entry = index->get_main ();
589 /* Choose the first "main" we see. We only do this for names
590 not requiring canonicalization. At this point in the process
591 names might not have been canonicalized. However, currently,
592 languages that require this step also do not use
593 DW_AT_main_subprogram. An assert is appropriate here because
594 this filtering is done in get_main. */
595 if (entry != nullptr)
596 {
597 if ((entry->flags & IS_MAIN) != 0)
598 {
599 if (!language_requires_canonicalization (entry->per_cu->lang ()))
600 {
601 /* There won't be one better than this. */
602 return entry;
603 }
604 }
605 else
606 {
607 /* This is one that is named "main". Here we don't care
608 if the language requires canonicalization, due to how
609 the entry is detected. Entries like this have worse
610 priority than IS_MAIN entries. */
611 if (best_entry == nullptr)
612 best_entry = entry;
613 }
614 }
615 }
616
617 return best_entry;
618 }
619
620 /* See cooked-index.h. */
621
622 void
623 cooked_index::dump (gdbarch *arch)
624 {
625 auto_obstack temp_storage;
626
627 gdb_printf (" entries:\n");
628 gdb_printf ("\n");
629
630 size_t i = 0;
631 for (const cooked_index_entry *entry : this->all_entries ())
632 {
633 QUIT;
634
635 gdb_printf (" [%zu] ((cooked_index_entry *) %p)\n", i++, entry);
636 gdb_printf (" name: %s\n", entry->name);
637 gdb_printf (" canonical: %s\n", entry->canonical);
638 gdb_printf (" qualified: %s\n", entry->full_name (&temp_storage, false));
639 gdb_printf (" DWARF tag: %s\n", dwarf_tag_name (entry->tag));
640 gdb_printf (" flags: %s\n", to_string (entry->flags).c_str ());
641 gdb_printf (" DIE offset: %s\n", sect_offset_str (entry->die_offset));
642
643 if ((entry->flags & IS_PARENT_DEFERRED) != 0)
644 gdb_printf (" parent: deferred (%" PRIx64 ")\n",
645 entry->get_deferred_parent ());
646 else if (entry->get_parent () != nullptr)
647 gdb_printf (" parent: ((cooked_index_entry *) %p) [%s]\n",
648 entry->get_parent (), entry->get_parent ()->name);
649 else
650 gdb_printf (" parent: ((cooked_index_entry *) 0)\n");
651
652 gdb_printf ("\n");
653 }
654
655 const cooked_index_entry *main_entry = this->get_main ();
656 if (main_entry != nullptr)
657 gdb_printf (" main: ((cooked_index_entry *) %p) [%s]\n", main_entry,
658 main_entry->name);
659 else
660 gdb_printf (" main: ((cooked_index_entry *) 0)\n");
661
662 gdb_printf ("\n");
663 gdb_printf (" address maps:\n");
664 gdb_printf ("\n");
665
666 std::vector<const addrmap *> addrmaps = this->get_addrmaps ();
667 for (i = 0; i < addrmaps.size (); ++i)
668 {
669 const addrmap &addrmap = *addrmaps[i];
670
671 gdb_printf (" [%zu] ((addrmap *) %p)\n", i, &addrmap);
672 gdb_printf ("\n");
673
674 addrmap.foreach ([arch] (CORE_ADDR start_addr, const void *obj)
675 {
676 QUIT;
677
678 const char *start_addr_str = paddress (arch, start_addr);
679
680 if (obj != nullptr)
681 {
682 const dwarf2_per_cu_data *per_cu
683 = static_cast<const dwarf2_per_cu_data *> (obj);
684 gdb_printf (" [%s] ((dwarf2_per_cu_data *) %p)\n",
685 start_addr_str, per_cu);
686 }
687 else
688 gdb_printf (" [%s] ((dwarf2_per_cu_data *) 0)\n",
689 start_addr_str);
690
691 return 0;
692 });
693
694 gdb_printf ("\n");
695 }
696 }
697
698 void
699 cooked_index::maybe_write_index (dwarf2_per_bfd *per_bfd,
700 const index_cache_store_context &ctx)
701 {
702 /* (maybe) store an index in the cache. */
703 global_index_cache.store (m_per_bfd, ctx);
704 m_state->set (cooked_state::CACHE_DONE);
705 }
706
707 /* Wait for all the index cache entries to be written before gdb
708 exits. */
709 static void
710 wait_for_index_cache (int)
711 {
712 gdb_assert (is_main_thread ());
713 for (cooked_index *item : active_vectors)
714 item->wait_completely ();
715 }
716
717 /* A maint command to wait for the cache. */
718
719 static void
720 maintenance_wait_for_index_cache (const char *args, int from_tty)
721 {
722 wait_for_index_cache (0);
723 }
724
725 void _initialize_cooked_index ();
726 void
727 _initialize_cooked_index ()
728 {
729 #if GDB_SELF_TEST
730 selftests::register_test ("cooked_index_entry::compare", test_compare);
731 #endif
732
733 add_cmd ("wait-for-index-cache", class_maintenance,
734 maintenance_wait_for_index_cache, _("\
735 Wait until all pending writes to the index cache have completed.\n\
736 Usage: maintenance wait-for-index-cache"),
737 &maintenancelist);
738
739 gdb::observers::gdb_exiting.attach (wait_for_index_cache, "cooked-index");
740 }