]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdb/dwarf2/cooked-index.c
ppc: sanity check writing relocs
[thirdparty/binutils-gdb.git] / gdb / dwarf2 / cooked-index.c
CommitLineData
51f5a4b8
TT
1/* DIE indexing
2
213516ef 3 Copyright (C) 2022-2023 Free Software Foundation, Inc.
51f5a4b8
TT
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"
7d82b08e 23#include "dwarf2/stringify.h"
52e5e48e 24#include "dwarf2/index-cache.h"
51f5a4b8 25#include "cp-support.h"
55fc1623 26#include "c-lang.h"
51f5a4b8
TT
27#include "ada-lang.h"
28#include "split-name.h"
52e5e48e
TT
29#include "observable.h"
30#include "run-on-main-thread.h"
51f5a4b8 31#include <algorithm>
e0f4b3ec 32#include "gdbsupport/gdb-safe-ctype.h"
ac37b79c 33#include "gdbsupport/selftest.h"
307733cc 34#include <chrono>
52e5e48e 35#include <unordered_set>
f0c3dcc1 36#include "cli/cli-cmds.h"
52e5e48e
TT
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. */
41static std::unordered_set<cooked_index *> active_vectors;
ac37b79c
TT
42
43/* See cooked-index.h. */
44
7d82b08e
SM
45std::string
46to_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 };
55
56 return flags.to_string (mapping);
57}
58
59/* See cooked-index.h. */
60
47fe57c9
TT
61bool
62language_requires_canonicalization (enum language lang)
63{
64 return (lang == language_ada
65 || lang == language_c
66 || lang == language_cplus);
67}
68
69/* See cooked-index.h. */
70
c121e82c 71int
ac37b79c 72cooked_index_entry::compare (const char *stra, const char *strb,
c121e82c 73 comparison_mode mode)
ac37b79c 74{
c121e82c
TT
75 auto munge = [] (char c) -> unsigned char
76 {
77 /* We want to sort '<' before any other printable character.
78 So, rewrite '<' to something just before ' '. */
79 if (c == '<')
80 return '\x1f';
81 return TOLOWER ((unsigned char) c);
82 };
ac37b79c
TT
83
84 while (*stra != '\0'
85 && *strb != '\0'
c121e82c 86 && (munge (*stra) == munge (*strb)))
ac37b79c 87 {
ac37b79c
TT
88 ++stra;
89 ++strb;
90 }
91
c121e82c
TT
92 unsigned char c1 = munge (*stra);
93 unsigned char c2 = munge (*strb);
ac37b79c 94
c121e82c
TT
95 if (c1 == c2)
96 return 0;
97
98 /* When completing, if STRB ends earlier than STRA, consider them as
99 equal. When comparing, if STRB ends earlier and STRA ends with
100 '<', consider them as equal. */
101 if (mode == COMPLETE || (mode == MATCH && c1 == munge ('<')))
ac37b79c 102 {
c121e82c
TT
103 if (c2 == '\0')
104 return 0;
ac37b79c
TT
105 }
106
c121e82c 107 return c1 < c2 ? -1 : 1;
ac37b79c
TT
108}
109
110#if GDB_SELF_TEST
111
112namespace {
113
114void
115test_compare ()
116{
c121e82c
TT
117 /* Convenience aliases. */
118 const auto mode_compare = cooked_index_entry::MATCH;
119 const auto mode_sort = cooked_index_entry::SORT;
120 const auto mode_complete = cooked_index_entry::COMPLETE;
121
122 SELF_CHECK (cooked_index_entry::compare ("abcd", "abcd",
123 mode_compare) == 0);
124 SELF_CHECK (cooked_index_entry::compare ("abcd", "abcd",
125 mode_complete) == 0);
126
127 SELF_CHECK (cooked_index_entry::compare ("abcd", "ABCDE",
128 mode_compare) < 0);
129 SELF_CHECK (cooked_index_entry::compare ("ABCDE", "abcd",
130 mode_compare) > 0);
131 SELF_CHECK (cooked_index_entry::compare ("abcd", "ABCDE",
132 mode_complete) < 0);
133 SELF_CHECK (cooked_index_entry::compare ("ABCDE", "abcd",
134 mode_complete) == 0);
135
136 SELF_CHECK (cooked_index_entry::compare ("name", "name<>",
137 mode_compare) < 0);
138 SELF_CHECK (cooked_index_entry::compare ("name<>", "name",
139 mode_compare) == 0);
140 SELF_CHECK (cooked_index_entry::compare ("name", "name<>",
141 mode_complete) < 0);
142 SELF_CHECK (cooked_index_entry::compare ("name<>", "name",
143 mode_complete) == 0);
144
145 SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>",
146 mode_compare) == 0);
147 SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>",
148 mode_compare) > 0);
149 SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>",
150 mode_complete) == 0);
151 SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>",
152 mode_complete) > 0);
153
154 SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>",
155 "name<arg<more>>",
156 mode_compare) == 0);
157
158 SELF_CHECK (cooked_index_entry::compare ("name", "name<arg<more>>",
159 mode_compare) < 0);
160 SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name",
161 mode_compare) == 0);
162 SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<",
163 mode_compare) > 0);
164 SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<",
165 mode_complete) == 0);
166
167 SELF_CHECK (cooked_index_entry::compare ("", "abcd", mode_compare) < 0);
168 SELF_CHECK (cooked_index_entry::compare ("", "abcd", mode_complete) < 0);
169 SELF_CHECK (cooked_index_entry::compare ("abcd", "", mode_compare) > 0);
170 SELF_CHECK (cooked_index_entry::compare ("abcd", "", mode_complete) == 0);
171
172 SELF_CHECK (cooked_index_entry::compare ("func", "func<type>",
173 mode_sort) < 0);
174 SELF_CHECK (cooked_index_entry::compare ("func<type>", "func1",
175 mode_sort) < 0);
ac37b79c
TT
176}
177
178} /* anonymous namespace */
179
180#endif /* GDB_SELF_TEST */
51f5a4b8 181
51f5a4b8
TT
182/* See cooked-index.h. */
183
184const char *
47fe57c9 185cooked_index_entry::full_name (struct obstack *storage, bool for_main) const
51f5a4b8 186{
47fe57c9
TT
187 const char *local_name = for_main ? name : canonical;
188
72b580b8 189 if ((flags & IS_LINKAGE) != 0 || parent_entry == nullptr)
47fe57c9 190 return local_name;
51f5a4b8
TT
191
192 const char *sep = nullptr;
2c474c46 193 switch (per_cu->lang ())
51f5a4b8
TT
194 {
195 case language_cplus:
196 case language_rust:
197 sep = "::";
198 break;
199
200 case language_go:
201 case language_d:
202 case language_ada:
203 sep = ".";
204 break;
51f5a4b8 205
72b580b8 206 default:
47fe57c9 207 return local_name;
72b580b8 208 }
51f5a4b8 209
47fe57c9
TT
210 parent_entry->write_scope (storage, sep, for_main);
211 obstack_grow0 (storage, local_name, strlen (local_name));
51f5a4b8
TT
212 return (const char *) obstack_finish (storage);
213}
214
215/* See cooked-index.h. */
216
217void
218cooked_index_entry::write_scope (struct obstack *storage,
47fe57c9
TT
219 const char *sep,
220 bool for_main) const
51f5a4b8
TT
221{
222 if (parent_entry != nullptr)
47fe57c9
TT
223 parent_entry->write_scope (storage, sep, for_main);
224 const char *local_name = for_main ? name : canonical;
225 obstack_grow (storage, local_name, strlen (local_name));
51f5a4b8
TT
226 obstack_grow (storage, sep, strlen (sep));
227}
228
229/* See cooked-index.h. */
230
231const cooked_index_entry *
a8dc6718
SM
232cooked_index_shard::add (sect_offset die_offset, enum dwarf_tag tag,
233 cooked_index_flag flags, const char *name,
234 const cooked_index_entry *parent_entry,
235 dwarf2_per_cu_data *per_cu)
51f5a4b8
TT
236{
237 cooked_index_entry *result = create (die_offset, tag, flags, name,
238 parent_entry, per_cu);
239 m_entries.push_back (result);
240
241 /* An explicitly-tagged main program should always override the
242 implicit "main" discovery. */
243 if ((flags & IS_MAIN) != 0)
244 m_main = result;
51f5a4b8
TT
245
246 return result;
247}
248
46114cb7
TT
249/* See cooked-index.h. */
250
20a26f4e 251void
a8dc6718 252cooked_index_shard::finalize ()
46114cb7 253{
20a26f4e 254 m_future = gdb::thread_pool::g_thread_pool->post_task ([this] ()
46114cb7 255 {
20a26f4e
TT
256 do_finalize ();
257 });
51f5a4b8
TT
258}
259
260/* See cooked-index.h. */
261
262gdb::unique_xmalloc_ptr<char>
a8dc6718
SM
263cooked_index_shard::handle_gnat_encoded_entry (cooked_index_entry *entry,
264 htab_t gnat_entries)
51f5a4b8
TT
265{
266 std::string canonical = ada_decode (entry->name, false, false);
267 if (canonical.empty ())
268 return {};
269 std::vector<gdb::string_view> names = split_name (canonical.c_str (),
270 split_style::DOT);
271 gdb::string_view tail = names.back ();
272 names.pop_back ();
273
274 const cooked_index_entry *parent = nullptr;
275 for (const auto &name : names)
276 {
277 uint32_t hashval = dwarf5_djb_hash (name);
278 void **slot = htab_find_slot_with_hash (gnat_entries, &name,
279 hashval, INSERT);
280 /* CUs are processed in order, so we only need to check the most
281 recent entry. */
282 cooked_index_entry *last = (cooked_index_entry *) *slot;
283 if (last == nullptr || last->per_cu != entry->per_cu)
284 {
285 gdb::unique_xmalloc_ptr<char> new_name
286 = make_unique_xstrndup (name.data (), name.length ());
20a26f4e
TT
287 last = create (entry->die_offset, DW_TAG_namespace,
288 0, new_name.get (), parent,
289 entry->per_cu);
51f5a4b8
TT
290 last->canonical = last->name;
291 m_names.push_back (std::move (new_name));
292 *slot = last;
293 }
294
295 parent = last;
296 }
297
298 entry->parent_entry = parent;
299 return make_unique_xstrndup (tail.data (), tail.length ());
300}
301
302/* See cooked-index.h. */
303
304void
a8dc6718 305cooked_index_shard::do_finalize ()
51f5a4b8
TT
306{
307 auto hash_name_ptr = [] (const void *p)
308 {
309 const cooked_index_entry *entry = (const cooked_index_entry *) p;
310 return htab_hash_pointer (entry->name);
311 };
312
313 auto eq_name_ptr = [] (const void *a, const void *b) -> int
314 {
315 const cooked_index_entry *ea = (const cooked_index_entry *) a;
316 const cooked_index_entry *eb = (const cooked_index_entry *) b;
317 return ea->name == eb->name;
318 };
319
320 /* We can use pointer equality here because names come from
321 .debug_str, which will normally be unique-ified by the linker.
322 Also, duplicates are relatively harmless -- they just mean a bit
323 of extra memory is used. */
324 htab_up seen_names (htab_create_alloc (10, hash_name_ptr, eq_name_ptr,
325 nullptr, xcalloc, xfree));
326
5a89072f
TT
327 auto hash_entry = [] (const void *e)
328 {
329 const cooked_index_entry *entry = (const cooked_index_entry *) e;
330 return dwarf5_djb_hash (entry->canonical);
331 };
332
333 auto eq_entry = [] (const void *a, const void *b) -> int
334 {
335 const cooked_index_entry *ae = (const cooked_index_entry *) a;
336 const gdb::string_view *sv = (const gdb::string_view *) b;
337 return (strlen (ae->canonical) == sv->length ()
338 && strncasecmp (ae->canonical, sv->data (), sv->length ()) == 0);
339 };
340
20a26f4e
TT
341 htab_up gnat_entries (htab_create_alloc (10, hash_entry, eq_entry,
342 nullptr, xcalloc, xfree));
46114cb7 343
20a26f4e
TT
344 for (cooked_index_entry *entry : m_entries)
345 {
47fe57c9
TT
346 /* Note that this code must be kept in sync with
347 language_requires_canonicalization. */
20a26f4e 348 gdb_assert (entry->canonical == nullptr);
bed34ce7 349 if ((entry->flags & IS_LINKAGE) != 0)
20a26f4e 350 entry->canonical = entry->name;
bed34ce7 351 else if (entry->per_cu->lang () == language_ada)
51f5a4b8 352 {
bed34ce7
TT
353 gdb::unique_xmalloc_ptr<char> canon_name
354 = handle_gnat_encoded_entry (entry, gnat_entries.get ());
355 if (canon_name == nullptr)
356 entry->canonical = entry->name;
357 else
358 {
359 entry->canonical = canon_name.get ();
360 m_names.push_back (std::move (canon_name));
361 }
362 }
55fc1623
TT
363 else if (entry->per_cu->lang () == language_cplus
364 || entry->per_cu->lang () == language_c)
bed34ce7
TT
365 {
366 void **slot = htab_find_slot (seen_names.get (), entry,
367 INSERT);
368 if (*slot == nullptr)
20a26f4e
TT
369 {
370 gdb::unique_xmalloc_ptr<char> canon_name
55fc1623
TT
371 = (entry->per_cu->lang () == language_cplus
372 ? cp_canonicalize_string (entry->name)
373 : c_canonicalize_name (entry->name));
20a26f4e
TT
374 if (canon_name == nullptr)
375 entry->canonical = entry->name;
376 else
377 {
378 entry->canonical = canon_name.get ();
379 m_names.push_back (std::move (canon_name));
380 }
b6c55de7 381 *slot = entry;
20a26f4e 382 }
51f5a4b8
TT
383 else
384 {
bed34ce7
TT
385 const cooked_index_entry *other
386 = (const cooked_index_entry *) *slot;
387 entry->canonical = other->canonical;
51f5a4b8
TT
388 }
389 }
bed34ce7
TT
390 else
391 entry->canonical = entry->name;
51f5a4b8
TT
392 }
393
394 m_names.shrink_to_fit ();
395 m_entries.shrink_to_fit ();
396 std::sort (m_entries.begin (), m_entries.end (),
397 [] (const cooked_index_entry *a, const cooked_index_entry *b)
398 {
399 return *a < *b;
400 });
401}
20a26f4e
TT
402
403/* See cooked-index.h. */
404
a8dc6718
SM
405cooked_index_shard::range
406cooked_index_shard::find (const std::string &name, bool completing) const
20a26f4e
TT
407{
408 wait ();
409
c121e82c
TT
410 cooked_index_entry::comparison_mode mode = (completing
411 ? cooked_index_entry::COMPLETE
412 : cooked_index_entry::MATCH);
413
35e17631 414 auto lower = std::lower_bound (m_entries.cbegin (), m_entries.cend (), name,
20a26f4e 415 [=] (const cooked_index_entry *entry,
ac37b79c 416 const std::string &n)
20a26f4e 417 {
c121e82c 418 return cooked_index_entry::compare (entry->canonical, n.c_str (), mode) < 0;
20a26f4e
TT
419 });
420
35e17631 421 auto upper = std::upper_bound (m_entries.cbegin (), m_entries.cend (), name,
ac37b79c 422 [=] (const std::string &n,
20a26f4e
TT
423 const cooked_index_entry *entry)
424 {
c121e82c 425 return cooked_index_entry::compare (entry->canonical, n.c_str (), mode) > 0;
20a26f4e
TT
426 });
427
428 return range (lower, upper);
429}
430
307733cc
TT
431/* See cooked-index.h. */
432
433void
434cooked_index_shard::wait (bool allow_quit) const
435{
436 if (allow_quit)
437 {
438 std::chrono::milliseconds duration { 15 };
439 while (m_future.wait_for (duration) == gdb::future_status::timeout)
440 QUIT;
441 }
442 else
443 m_future.wait ();
444}
445
6f214d0f 446cooked_index::cooked_index (vec_type &&vec)
20a26f4e
TT
447 : m_vector (std::move (vec))
448{
449 for (auto &idx : m_vector)
450 idx->finalize ();
52e5e48e 451
52e5e48e
TT
452 /* ACTIVE_VECTORS is not locked, and this assert ensures that this
453 will be caught if ever moved to the background. */
454 gdb_assert (is_main_thread ());
455 active_vectors.insert (this);
456}
457
6f214d0f
TT
458/* See cooked-index.h. */
459
460void
461cooked_index::start_writing_index (dwarf2_per_bfd *per_bfd)
462{
463 /* This must be set after all the finalization tasks have been
464 started, because it may call 'wait'. */
465 m_write_future
466 = gdb::thread_pool::g_thread_pool->post_task ([this, per_bfd] ()
467 {
468 maybe_write_index (per_bfd);
469 });
470}
471
52e5e48e
TT
472cooked_index::~cooked_index ()
473{
474 /* The 'finalize' method may be run in a different thread. If
475 this object is destroyed before this completes, then the method
476 will end up writing to freed memory. Waiting for this to
477 complete avoids this problem; and the cost seems ignorable
478 because creating and immediately destroying the debug info is a
479 relatively rare thing to do. */
480 for (auto &item : m_vector)
481 item->wait (false);
482
483 /* Likewise for the index-creating future, though this one must also
484 waited for by the per-BFD object to ensure the required data
485 remains live. */
486 wait_completely ();
487
488 /* Remove our entry from the global list. See the assert in the
489 constructor to understand this. */
490 gdb_assert (is_main_thread ());
491 active_vectors.erase (this);
20a26f4e
TT
492}
493
494/* See cooked-index.h. */
495
496dwarf2_per_cu_data *
19455ee1 497cooked_index::lookup (CORE_ADDR addr)
20a26f4e
TT
498{
499 for (const auto &index : m_vector)
500 {
501 dwarf2_per_cu_data *result = index->lookup (addr);
502 if (result != nullptr)
503 return result;
504 }
505 return nullptr;
506}
507
508/* See cooked-index.h. */
509
70ca3a6b 510std::vector<const addrmap *>
19455ee1 511cooked_index::get_addrmaps () const
20a26f4e 512{
70ca3a6b 513 std::vector<const addrmap *> result;
20a26f4e
TT
514 for (const auto &index : m_vector)
515 result.push_back (index->m_addrmap);
516 return result;
517}
518
519/* See cooked-index.h. */
520
19455ee1
SM
521cooked_index::range
522cooked_index::find (const std::string &name, bool completing) const
20a26f4e 523{
a8dc6718 524 std::vector<cooked_index_shard::range> result_range;
20a26f4e
TT
525 result_range.reserve (m_vector.size ());
526 for (auto &entry : m_vector)
527 result_range.push_back (entry->find (name, completing));
528 return range (std::move (result_range));
529}
530
531/* See cooked-index.h. */
532
533const cooked_index_entry *
19455ee1 534cooked_index::get_main () const
20a26f4e
TT
535{
536 const cooked_index_entry *result = nullptr;
537
538 for (const auto &index : m_vector)
539 {
540 const cooked_index_entry *entry = index->get_main ();
47fe57c9
TT
541 /* Choose the first "main" we see. The choice among several is
542 arbitrary. See the comment by the sole caller to understand
543 the rationale for filtering by language. */
544 if (entry != nullptr
545 && !language_requires_canonicalization (entry->per_cu->lang ()))
546 {
547 result = entry;
548 break;
549 }
20a26f4e
TT
550 }
551
552 return result;
553}
ac37b79c 554
7d82b08e
SM
555/* See cooked-index.h. */
556
557void
19455ee1 558cooked_index::dump (gdbarch *arch) const
7d82b08e
SM
559{
560 /* Ensure the index is done building. */
561 this->wait ();
562
563 gdb_printf (" entries:\n");
564 gdb_printf ("\n");
565
566 size_t i = 0;
567 for (const cooked_index_entry *entry : this->all_entries ())
568 {
569 QUIT;
570
571 gdb_printf (" [%zu] ((cooked_index_entry *) %p)\n", i++, entry);
572 gdb_printf (" name: %s\n", entry->name);
573 gdb_printf (" canonical: %s\n", entry->canonical);
574 gdb_printf (" DWARF tag: %s\n", dwarf_tag_name (entry->tag));
575 gdb_printf (" flags: %s\n", to_string (entry->flags).c_str ());
902d61e3 576 gdb_printf (" DIE offset: 0x%" PRIx64 "\n",
7d82b08e
SM
577 to_underlying (entry->die_offset));
578
579 if (entry->parent_entry != nullptr)
580 gdb_printf (" parent: ((cooked_index_entry *) %p) [%s]\n",
581 entry->parent_entry, entry->parent_entry->name);
582 else
583 gdb_printf (" parent: ((cooked_index_entry *) 0)\n");
584
585 gdb_printf ("\n");
586 }
587
588 const cooked_index_entry *main_entry = this->get_main ();
589 if (main_entry != nullptr)
590 gdb_printf (" main: ((cooked_index_entry *) %p) [%s]\n", main_entry,
591 main_entry->name);
592 else
593 gdb_printf (" main: ((cooked_index_entry *) 0)\n");
594
595 gdb_printf ("\n");
596 gdb_printf (" address maps:\n");
597 gdb_printf ("\n");
598
599 std::vector<const addrmap *> addrmaps = this->get_addrmaps ();
600 for (i = 0; i < addrmaps.size (); ++i)
601 {
602 const addrmap &addrmap = *addrmaps[i];
603
604 gdb_printf (" [%zu] ((addrmap *) %p)\n", i, &addrmap);
605 gdb_printf ("\n");
606
607 addrmap.foreach ([arch] (CORE_ADDR start_addr, const void *obj)
608 {
609 QUIT;
610
611 const char *start_addr_str = paddress (arch, start_addr);
612
613 if (obj != nullptr)
614 {
615 const dwarf2_per_cu_data *per_cu
616 = static_cast<const dwarf2_per_cu_data *> (obj);
617 gdb_printf (" [%s] ((dwarf2_per_cu_data *) %p)\n",
618 start_addr_str, per_cu);
619 }
620 else
621 gdb_printf (" [%s] ((dwarf2_per_cu_data *) 0)\n",
622 start_addr_str);
623
624 return 0;
625 });
626
627 gdb_printf ("\n");
628 }
629}
630
52e5e48e
TT
631void
632cooked_index::maybe_write_index (dwarf2_per_bfd *per_bfd)
633{
634 /* Wait for finalization. */
635 wait ();
636
637 /* (maybe) store an index in the cache. */
638 global_index_cache.store (per_bfd);
639}
640
641/* Wait for all the index cache entries to be written before gdb
642 exits. */
643static void
644wait_for_index_cache (int)
645{
646 gdb_assert (is_main_thread ());
647 for (cooked_index *item : active_vectors)
648 item->wait_completely ();
649}
650
f0c3dcc1
TT
651/* A maint command to wait for the cache. */
652
653static void
654maintenance_wait_for_index_cache (const char *args, int from_tty)
655{
656 wait_for_index_cache (0);
657}
658
ac37b79c
TT
659void _initialize_cooked_index ();
660void
661_initialize_cooked_index ()
662{
663#if GDB_SELF_TEST
664 selftests::register_test ("cooked_index_entry::compare", test_compare);
665#endif
52e5e48e 666
f0c3dcc1
TT
667 add_cmd ("wait-for-index-cache", class_maintenance,
668 maintenance_wait_for_index_cache, _("\
4779ed97
TT
669Wait until all pending writes to the index cache have completed.\n\
670Usage: maintenance wait-for-index-cache"),
f0c3dcc1
TT
671 &maintenancelist);
672
52e5e48e 673 gdb::observers::gdb_exiting.attach (wait_for_index_cache, "cooked-index");
ac37b79c 674}