replace_typedefs: handle templates in namespaces
[deliverable/binutils-gdb.git] / gdb / memattr.c
CommitLineData
80629b1b 1/* Memory attributes support, for GDB.
14a5e767 2
b811d2c2 3 Copyright (C) 2001-2020 Free Software Foundation, Inc.
80629b1b
EZ
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
a9762ec7 9 the Free Software Foundation; either version 3 of the License, or
80629b1b
EZ
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
a9762ec7 18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
80629b1b 19
29e57380
C
20#include "defs.h"
21#include "command.h"
22#include "gdbcmd.h"
23#include "memattr.h"
24#include "target.h"
68c765e2 25#include "target-dcache.h"
29e57380
C
26#include "value.h"
27#include "language.h"
fbcb778d 28#include "breakpoint.h"
197f0a60 29#include "cli/cli-utils.h"
a664f67e 30#include <algorithm>
0d12e84c 31#include "gdbarch.h"
29e57380 32
a664f67e
SM
33static std::vector<mem_region> user_mem_region_list, target_mem_region_list;
34static std::vector<mem_region> *mem_region_list = &target_mem_region_list;
f4d650ec 35static int mem_number = 0;
29e57380 36
fd79ecee
DJ
37/* If this flag is set, the memory region list should be automatically
38 updated from the target. If it is clear, the list is user-controlled
39 and should be left alone. */
a664f67e
SM
40
41static bool
42mem_use_target ()
43{
44 return mem_region_list == &target_mem_region_list;
45}
fd79ecee
DJ
46
47/* If this flag is set, we have tried to fetch the target memory regions
48 since the last time it was invalidated. If that list is still
49 empty, then the target can't supply memory regions. */
a664f67e 50static bool target_mem_regions_valid;
fd79ecee 51
4b5752d0
VP
52/* If this flag is set, gdb will assume that memory ranges not
53 specified by the memory map have type MEM_NONE, and will
54 emit errors on all accesses to that memory. */
491144b5 55static bool inaccessible_by_default = true;
4b5752d0
VP
56
57static void
58show_inaccessible_by_default (struct ui_file *file, int from_tty,
59 struct cmd_list_element *c,
60 const char *value)
61{
62 if (inaccessible_by_default)
3e43a32a
MS
63 fprintf_filtered (file, _("Unknown memory addresses will "
64 "be treated as inaccessible.\n"));
4b5752d0 65 else
3e43a32a
MS
66 fprintf_filtered (file, _("Unknown memory addresses "
67 "will be treated as RAM.\n"));
4b5752d0
VP
68}
69
fd79ecee
DJ
70/* This function should be called before any command which would
71 modify the memory region list. It will handle switching from
72 a target-provided list to a local list, if necessary. */
73
74static void
75require_user_regions (int from_tty)
76{
fd79ecee 77 /* If we're already using a user-provided list, nothing to do. */
a664f67e 78 if (!mem_use_target ())
fd79ecee
DJ
79 return;
80
81 /* Switch to a user-provided list (possibly a copy of the current
82 one). */
a664f67e 83 mem_region_list = &user_mem_region_list;
fd79ecee
DJ
84
85 /* If we don't have a target-provided region list yet, then
86 no need to warn. */
a664f67e 87 if (target_mem_region_list.empty ())
fd79ecee
DJ
88 return;
89
90 /* Otherwise, let the user know how to get back. */
91 if (from_tty)
92 warning (_("Switching to manual control of memory regions; use "
93 "\"mem auto\" to fetch regions from the target again."));
94
a664f67e
SM
95 /* And create a new list (copy of the target-supplied regions) for the user
96 to modify. */
97 user_mem_region_list = target_mem_region_list;
fd79ecee
DJ
98}
99
100/* This function should be called before any command which would
101 read the memory region list, other than those which call
102 require_user_regions. It will handle fetching the
103 target-provided list, if necessary. */
104
105static void
106require_target_regions (void)
107{
a664f67e 108 if (mem_use_target () && !target_mem_regions_valid)
fd79ecee 109 {
a664f67e 110 target_mem_regions_valid = true;
fd79ecee 111 target_mem_region_list = target_memory_map ();
fd79ecee
DJ
112 }
113}
114
a664f67e
SM
115/* Create a new user-defined memory region. */
116
c96fc75e 117static void
a664f67e
SM
118create_user_mem_region (CORE_ADDR lo, CORE_ADDR hi,
119 const mem_attrib &attrib)
29e57380 120{
025bb325 121 /* lo == hi is a useless empty region. */
2b236d82 122 if (lo >= hi && hi != 0)
29e57380 123 {
a3f17187 124 printf_unfiltered (_("invalid memory region: low >= high\n"));
c96fc75e 125 return;
29e57380
C
126 }
127
a664f67e 128 mem_region newobj (lo, hi, attrib);
c96fc75e 129
a664f67e
SM
130 auto it = std::lower_bound (user_mem_region_list.begin (),
131 user_mem_region_list.end (),
132 newobj);
133 int ix = std::distance (user_mem_region_list.begin (), it);
c96fc75e
DJ
134
135 /* Check for an overlapping memory region. We only need to check
30baf67b 136 in the vincinity - at most one before and one after the
c96fc75e 137 insertion point. */
a664f67e 138 for (int i = ix - 1; i < ix + 1; i++)
29e57380 139 {
c96fc75e
DJ
140 if (i < 0)
141 continue;
a664f67e 142 if (i >= user_mem_region_list.size ())
c96fc75e
DJ
143 continue;
144
a664f67e 145 mem_region &n = user_mem_region_list[i];
c96fc75e 146
a664f67e
SM
147 if ((lo >= n.lo && (lo < n.hi || n.hi == 0))
148 || (hi > n.lo && (hi <= n.hi || n.hi == 0))
149 || (lo <= n.lo && ((hi >= n.hi && n.hi != 0) || hi == 0)))
29e57380 150 {
a3f17187 151 printf_unfiltered (_("overlapping memory region\n"));
c96fc75e 152 return;
29e57380
C
153 }
154 }
155
fe978cb0 156 newobj.number = ++mem_number;
a664f67e 157 user_mem_region_list.insert (it, newobj);
29e57380
C
158}
159
a664f67e
SM
160/* Look up the memory region corresponding to ADDR. */
161
29e57380
C
162struct mem_region *
163lookup_mem_region (CORE_ADDR addr)
164{
a664f67e 165 static struct mem_region region (0, 0);
29e57380
C
166 CORE_ADDR lo;
167 CORE_ADDR hi;
168
fd79ecee
DJ
169 require_target_regions ();
170
29e57380
C
171 /* First we initialize LO and HI so that they describe the entire
172 memory space. As we process the memory region chain, they are
173 redefined to describe the minimal region containing ADDR. LO
174 and HI are used in the case where no memory region is defined
175 that contains ADDR. If a memory region is disabled, it is
a76d924d
DJ
176 treated as if it does not exist. The initial values for LO
177 and HI represent the bottom and top of memory. */
29e57380 178
a76d924d
DJ
179 lo = 0;
180 hi = 0;
29e57380 181
a664f67e 182 /* Either find memory range containing ADDR, or set LO and HI
4b5752d0
VP
183 to the nearest boundaries of an existing memory range.
184
185 If we ever want to support a huge list of memory regions, this
c96fc75e
DJ
186 check should be replaced with a binary search (probably using
187 VEC_lower_bound). */
a664f67e 188 for (mem_region &m : *mem_region_list)
29e57380 189 {
a664f67e 190 if (m.enabled_p == 1)
29e57380 191 {
3e43a32a
MS
192 /* If the address is in the memory region, return that
193 memory range. */
a664f67e
SM
194 if (addr >= m.lo && (addr < m.hi || m.hi == 0))
195 return &m;
29e57380 196
a76d924d
DJ
197 /* This (correctly) won't match if m->hi == 0, representing
198 the top of the address space, because CORE_ADDR is unsigned;
199 no value of LO is less than zero. */
a664f67e
SM
200 if (addr >= m.hi && lo < m.hi)
201 lo = m.hi;
29e57380 202
a76d924d
DJ
203 /* This will never set HI to zero; if we're here and ADDR
204 is at or below M, and the region starts at zero, then ADDR
205 would have been in the region. */
a664f67e
SM
206 if (addr <= m.lo && (hi == 0 || hi > m.lo))
207 hi = m.lo;
29e57380
C
208 }
209 }
210
211 /* Because no region was found, we must cons up one based on what
212 was learned above. */
213 region.lo = lo;
214 region.hi = hi;
4b5752d0
VP
215
216 /* When no memory map is defined at all, we always return
217 'default_mem_attrib', so that we do not make all memory
218 inaccessible for targets that don't provide a memory map. */
a664f67e
SM
219 if (inaccessible_by_default && !mem_region_list->empty ())
220 region.attrib = mem_attrib::unknown ();
4b5752d0 221 else
a664f67e 222 region.attrib = mem_attrib ();
4b5752d0 223
29e57380
C
224 return &region;
225}
fd79ecee
DJ
226
227/* Invalidate any memory regions fetched from the target. */
228
229void
230invalidate_target_mem_regions (void)
231{
fd79ecee
DJ
232 if (!target_mem_regions_valid)
233 return;
234
a664f67e
SM
235 target_mem_regions_valid = false;
236 target_mem_region_list.clear ();
fd79ecee
DJ
237}
238
a664f67e 239/* Clear user-defined memory region list. */
fd79ecee
DJ
240
241static void
a664f67e 242user_mem_clear (void)
fd79ecee 243{
a664f67e 244 user_mem_region_list.clear ();
fd79ecee 245}
29e57380
C
246\f
247
248static void
0b39b52e 249mem_command (const char *args, int from_tty)
29e57380
C
250{
251 CORE_ADDR lo, hi;
29e57380
C
252
253 if (!args)
e2e0b3e5 254 error_no_arg (_("No mem"));
29e57380 255
fd79ecee
DJ
256 /* For "mem auto", switch back to using a target provided list. */
257 if (strcmp (args, "auto") == 0)
258 {
a664f67e 259 if (mem_use_target ())
fd79ecee
DJ
260 return;
261
a664f67e
SM
262 user_mem_clear ();
263 mem_region_list = &target_mem_region_list;
fd79ecee 264
fd79ecee
DJ
265 return;
266 }
267
268 require_user_regions (from_tty);
269
0b39b52e
TT
270 std::string tok = extract_arg (&args);
271 if (tok == "")
8a3fe4f8 272 error (_("no lo address"));
0b39b52e 273 lo = parse_and_eval_address (tok.c_str ());
29e57380 274
0b39b52e
TT
275 tok = extract_arg (&args);
276 if (tok == "")
8a3fe4f8 277 error (_("no hi address"));
0b39b52e 278 hi = parse_and_eval_address (tok.c_str ());
29e57380 279
a664f67e 280 mem_attrib attrib;
0b39b52e 281 while ((tok = extract_arg (&args)) != "")
29e57380 282 {
0b39b52e 283 if (tok == "rw")
29e57380 284 attrib.mode = MEM_RW;
0b39b52e 285 else if (tok == "ro")
29e57380 286 attrib.mode = MEM_RO;
0b39b52e 287 else if (tok == "wo")
29e57380
C
288 attrib.mode = MEM_WO;
289
0b39b52e 290 else if (tok == "8")
29e57380 291 attrib.width = MEM_WIDTH_8;
0b39b52e 292 else if (tok == "16")
29e57380
C
293 {
294 if ((lo % 2 != 0) || (hi % 2 != 0))
8a3fe4f8 295 error (_("region bounds not 16 bit aligned"));
29e57380
C
296 attrib.width = MEM_WIDTH_16;
297 }
0b39b52e 298 else if (tok == "32")
29e57380
C
299 {
300 if ((lo % 4 != 0) || (hi % 4 != 0))
8a3fe4f8 301 error (_("region bounds not 32 bit aligned"));
29e57380
C
302 attrib.width = MEM_WIDTH_32;
303 }
0b39b52e 304 else if (tok == "64")
29e57380
C
305 {
306 if ((lo % 8 != 0) || (hi % 8 != 0))
8a3fe4f8 307 error (_("region bounds not 64 bit aligned"));
29e57380
C
308 attrib.width = MEM_WIDTH_64;
309 }
310
311#if 0
0b39b52e 312 else if (tok == "hwbreak")
81a9a963 313 attrib.hwbreak = 1;
0b39b52e 314 else if (tok == "swbreak")
81a9a963 315 attrib.hwbreak = 0;
29e57380
C
316#endif
317
0b39b52e 318 else if (tok == "cache")
81a9a963 319 attrib.cache = 1;
0b39b52e 320 else if (tok == "nocache")
81a9a963 321 attrib.cache = 0;
29e57380
C
322
323#if 0
0b39b52e 324 else if (tok == "verify")
81a9a963 325 attrib.verify = 1;
0b39b52e 326 else if (tok == "noverify")
81a9a963 327 attrib.verify = 0;
29e57380
C
328#endif
329
330 else
0b39b52e 331 error (_("unknown attribute: %s"), tok.c_str ());
29e57380
C
332 }
333
a664f67e 334 create_user_mem_region (lo, hi, attrib);
29e57380
C
335}
336\f
337
338static void
1d12d88f 339info_mem_command (const char *args, int from_tty)
29e57380 340{
a664f67e 341 if (mem_use_target ())
fd79ecee
DJ
342 printf_filtered (_("Using memory regions provided by the target.\n"));
343 else
344 printf_filtered (_("Using user-defined memory regions.\n"));
345
346 require_target_regions ();
347
a664f67e 348 if (mem_region_list->empty ())
29e57380 349 {
a3f17187 350 printf_unfiltered (_("There are no memory regions defined.\n"));
29e57380
C
351 return;
352 }
353
ab35b611
EZ
354 printf_filtered ("Num ");
355 printf_filtered ("Enb ");
356 printf_filtered ("Low Addr ");
f5656ead 357 if (gdbarch_addr_bit (target_gdbarch ()) > 32)
ab35b611
EZ
358 printf_filtered (" ");
359 printf_filtered ("High Addr ");
f5656ead 360 if (gdbarch_addr_bit (target_gdbarch ()) > 32)
ab35b611
EZ
361 printf_filtered (" ");
362 printf_filtered ("Attrs ");
363 printf_filtered ("\n");
364
a664f67e 365 for (const mem_region &m : *mem_region_list)
29e57380 366 {
a121b7c1 367 const char *tmp;
b8d56208 368
ab35b611 369 printf_filtered ("%-3d %-3c\t",
a664f67e
SM
370 m.number,
371 m.enabled_p ? 'y' : 'n');
f5656ead 372 if (gdbarch_addr_bit (target_gdbarch ()) <= 32)
a664f67e 373 tmp = hex_string_custom (m.lo, 8);
ab35b611 374 else
a664f67e 375 tmp = hex_string_custom (m.lo, 16);
ab35b611
EZ
376
377 printf_filtered ("%s ", tmp);
2b236d82 378
f5656ead 379 if (gdbarch_addr_bit (target_gdbarch ()) <= 32)
2163ab9d 380 {
a664f67e 381 if (m.hi == 0)
b8d56208
MS
382 tmp = "0x100000000";
383 else
a664f67e 384 tmp = hex_string_custom (m.hi, 8);
2163ab9d 385 }
ab35b611 386 else
2163ab9d 387 {
a664f67e 388 if (m.hi == 0)
b8d56208
MS
389 tmp = "0x10000000000000000";
390 else
a664f67e 391 tmp = hex_string_custom (m.hi, 16);
2163ab9d
DH
392 }
393
ab35b611 394 printf_filtered ("%s ", tmp);
29e57380
C
395
396 /* Print a token for each attribute.
397
398 * FIXME: Should we output a comma after each token? It may
399 * make it easier for users to read, but we'd lose the ability
400 * to cut-and-paste the list of attributes when defining a new
401 * region. Perhaps that is not important.
402 *
403 * FIXME: If more attributes are added to GDB, the output may
404 * become cluttered and difficult for users to read. At that
405 * time, we may want to consider printing tokens only if they
406 * are different from the default attribute. */
407
a664f67e 408 switch (m.attrib.mode)
29e57380
C
409 {
410 case MEM_RW:
411 printf_filtered ("rw ");
412 break;
413 case MEM_RO:
414 printf_filtered ("ro ");
415 break;
416 case MEM_WO:
417 printf_filtered ("wo ");
418 break;
fd79ecee 419 case MEM_FLASH:
a664f67e 420 printf_filtered ("flash blocksize 0x%x ", m.attrib.blocksize);
fd79ecee 421 break;
29e57380
C
422 }
423
a664f67e 424 switch (m.attrib.width)
29e57380
C
425 {
426 case MEM_WIDTH_8:
427 printf_filtered ("8 ");
428 break;
429 case MEM_WIDTH_16:
430 printf_filtered ("16 ");
431 break;
432 case MEM_WIDTH_32:
433 printf_filtered ("32 ");
434 break;
435 case MEM_WIDTH_64:
436 printf_filtered ("64 ");
437 break;
438 case MEM_WIDTH_UNSPECIFIED:
439 break;
440 }
441
442#if 0
443 if (attrib->hwbreak)
444 printf_filtered ("hwbreak");
445 else
446 printf_filtered ("swbreak");
447#endif
448
a664f67e 449 if (m.attrib.cache)
29e57380
C
450 printf_filtered ("cache ");
451 else
452 printf_filtered ("nocache ");
453
454#if 0
455 if (attrib->verify)
456 printf_filtered ("verify ");
457 else
458 printf_filtered ("noverify ");
459#endif
460
461 printf_filtered ("\n");
29e57380
C
462 }
463}
464\f
465
025bb325 466/* Enable the memory region number NUM. */
29e57380
C
467
468static void
469mem_enable (int num)
470{
a664f67e
SM
471 for (mem_region &m : *mem_region_list)
472 if (m.number == num)
29e57380 473 {
a664f67e 474 m.enabled_p = 1;
29e57380
C
475 return;
476 }
a3f17187 477 printf_unfiltered (_("No memory region number %d.\n"), num);
29e57380
C
478}
479
480static void
4465d9db 481enable_mem_command (const char *args, int from_tty)
29e57380 482{
fd79ecee
DJ
483 require_user_regions (from_tty);
484
4e5d721f 485 target_dcache_invalidate ();
29e57380 486
fbcb778d
MS
487 if (args == NULL || *args == '\0')
488 { /* Enable all mem regions. */
a664f67e
SM
489 for (mem_region &m : *mem_region_list)
490 m.enabled_p = 1;
29e57380
C
491 }
492 else
197f0a60 493 {
bfd28288
PA
494 number_or_range_parser parser (args);
495 while (!parser.finished ())
197f0a60 496 {
a664f67e 497 int num = parser.get_number ();
197f0a60
TT
498 mem_enable (num);
499 }
500 }
29e57380
C
501}
502\f
503
025bb325 504/* Disable the memory region number NUM. */
29e57380
C
505
506static void
507mem_disable (int num)
508{
a664f67e
SM
509 for (mem_region &m : *mem_region_list)
510 if (m.number == num)
29e57380 511 {
a664f67e 512 m.enabled_p = 0;
29e57380
C
513 return;
514 }
a3f17187 515 printf_unfiltered (_("No memory region number %d.\n"), num);
29e57380
C
516}
517
518static void
4465d9db 519disable_mem_command (const char *args, int from_tty)
29e57380 520{
fd79ecee
DJ
521 require_user_regions (from_tty);
522
4e5d721f 523 target_dcache_invalidate ();
29e57380 524
fbcb778d 525 if (args == NULL || *args == '\0')
29e57380 526 {
a664f67e
SM
527 for (mem_region &m : *mem_region_list)
528 m.enabled_p = false;
29e57380
C
529 }
530 else
197f0a60 531 {
bfd28288
PA
532 number_or_range_parser parser (args);
533 while (!parser.finished ())
197f0a60 534 {
bfd28288 535 int num = parser.get_number ();
197f0a60
TT
536 mem_disable (num);
537 }
538 }
29e57380
C
539}
540
025bb325 541/* Delete the memory region number NUM. */
29e57380
C
542
543static void
544mem_delete (int num)
545{
c96fc75e 546 if (!mem_region_list)
29e57380 547 {
a3f17187 548 printf_unfiltered (_("No memory region number %d.\n"), num);
29e57380
C
549 return;
550 }
551
a664f67e
SM
552 auto it = std::remove_if (mem_region_list->begin (), mem_region_list->end (),
553 [num] (const mem_region &m)
29e57380 554 {
a664f67e
SM
555 return m.number == num;
556 });
c96fc75e 557
a664f67e
SM
558 if (it != mem_region_list->end ())
559 mem_region_list->erase (it);
560 else
561 printf_unfiltered (_("No memory region number %d.\n"), num);
29e57380
C
562}
563
564static void
4465d9db 565delete_mem_command (const char *args, int from_tty)
29e57380 566{
fd79ecee
DJ
567 require_user_regions (from_tty);
568
4e5d721f 569 target_dcache_invalidate ();
29e57380 570
fbcb778d 571 if (args == NULL || *args == '\0')
29e57380 572 {
9e2f0ad4 573 if (query (_("Delete all memory regions? ")))
a664f67e 574 user_mem_clear ();
29e57380
C
575 dont_repeat ();
576 return;
577 }
578
bfd28288
PA
579 number_or_range_parser parser (args);
580 while (!parser.finished ())
29e57380 581 {
bfd28288 582 int num = parser.get_number ();
29e57380 583 mem_delete (num);
29e57380
C
584 }
585
586 dont_repeat ();
587}
4b5752d0 588
4b5752d0
VP
589static struct cmd_list_element *mem_set_cmdlist;
590static struct cmd_list_element *mem_show_cmdlist;
591
6c265988 592void _initialize_mem ();
29e57380 593void
6c265988 594_initialize_mem ()
29e57380 595{
1bedd215 596 add_com ("mem", class_vars, mem_command, _("\
590042fc
PW
597Define attributes for memory region or reset memory region handling to "
598"target-based.\n\
fd79ecee 599Usage: mem auto\n\
99806209
TT
600 mem LOW HIGH [MODE WIDTH CACHE],\n\
601where MODE may be rw (read/write), ro (read-only) or wo (write-only),\n\
602 WIDTH may be 8, 16, 32, or 64, and\n\
603 CACHE may be cache or nocache"));
29e57380 604
ae3b3f34 605 add_cmd ("mem", class_vars, enable_mem_command, _("\
1a966eab 606Enable memory region.\n\
99806209
TT
607Arguments are the IDs of the memory regions to enable.\n\
608Usage: enable mem [ID]...\n\
609Do \"info mem\" to see current list of IDs."), &enablelist);
29e57380 610
ae3b3f34 611 add_cmd ("mem", class_vars, disable_mem_command, _("\
1a966eab 612Disable memory region.\n\
99806209
TT
613Arguments are the IDs of the memory regions to disable.\n\
614Usage: disable mem [ID]...\n\
615Do \"info mem\" to see current list of IDs."), &disablelist);
29e57380 616
ae3b3f34 617 add_cmd ("mem", class_vars, delete_mem_command, _("\
1a966eab 618Delete memory region.\n\
99806209
TT
619Arguments are the IDs of the memory regions to delete.\n\
620Usage: delete mem [ID]...\n\
621Do \"info mem\" to see current list of IDs."), &deletelist);
29e57380 622
ae3b3f34 623 add_info ("mem", info_mem_command,
590042fc 624 _("Memory region attributes."));
4b5752d0 625
3b6acaee 626 add_basic_prefix_cmd ("mem", class_vars, _("\
590042fc 627Memory regions settings."),
3b6acaee
TT
628 &mem_set_cmdlist, "set mem ",
629 0/* allow-unknown */, &setlist);
630 add_show_prefix_cmd ("mem", class_vars, _("\
590042fc 631Memory regions settings."),
3b6acaee
TT
632 &mem_show_cmdlist, "show mem ",
633 0/* allow-unknown */, &showlist);
4b5752d0
VP
634
635 add_setshow_boolean_cmd ("inaccessible-by-default", no_class,
636 &inaccessible_by_default, _("\
637Set handling of unknown memory regions."), _("\
638Show handling of unknown memory regions."), _("\
639If on, and some memory map is defined, debugger will emit errors on\n\
640accesses to memory not defined in the memory map. If off, accesses to all\n\
641memory addresses will be allowed."),
642 NULL,
643 show_inaccessible_by_default,
644 &mem_set_cmdlist,
645 &mem_show_cmdlist);
29e57380 646}
This page took 1.71398 seconds and 4 git commands to generate.