2012-04-27 Sergio Durigan Junior <sergiodj@redhat.com>
[deliverable/binutils-gdb.git] / gdb / probe.c
1 /* Generic static probe support for GDB.
2
3 Copyright (C) 2012 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 "probe.h"
22 #include "command.h"
23 #include "cli/cli-cmds.h"
24 #include "cli/cli-utils.h"
25 #include "objfiles.h"
26 #include "symtab.h"
27 #include "progspace.h"
28 #include "filenames.h"
29 #include "exceptions.h"
30 #include "linespec.h"
31 #include "gdb_regex.h"
32 #include "frame.h"
33 #include "arch-utils.h"
34 #include <ctype.h>
35
36 \f
37
38 /* See definition in probe.h. */
39
40 struct symtabs_and_lines
41 parse_probes (char **argptr, struct linespec_result *canonical)
42 {
43 char *arg_start, *arg_end, *arg;
44 char *objfile_name = NULL, *provider = NULL, *name, *p;
45 struct cleanup *cleanup;
46 struct symtabs_and_lines result;
47 struct objfile *objfile;
48 struct program_space *pspace;
49 const struct probe_ops *probe_ops;
50 const char *cs;
51
52 result.sals = NULL;
53 result.nelts = 0;
54
55 arg_start = *argptr;
56
57 cs = *argptr;
58 probe_ops = probe_linespec_to_ops (&cs);
59 gdb_assert (probe_ops != NULL);
60
61 arg = (char *) cs;
62 arg = skip_spaces (arg);
63 if (!*arg)
64 error (_("argument to `%s' missing"), arg_start);
65
66 arg_end = skip_to_space (arg);
67
68 /* We make a copy here so we can write over parts with impunity. */
69 arg = savestring (arg, arg_end - arg);
70 cleanup = make_cleanup (xfree, arg);
71
72 /* Extract each word from the argument, separated by ":"s. */
73 p = strchr (arg, ':');
74 if (p == NULL)
75 {
76 /* This is `-p name'. */
77 name = arg;
78 }
79 else
80 {
81 char *hold = p + 1;
82
83 *p = '\0';
84 p = strchr (hold, ':');
85 if (p == NULL)
86 {
87 /* This is `-p provider:name'. */
88 provider = arg;
89 name = hold;
90 }
91 else
92 {
93 /* This is `-p objfile:provider:name'. */
94 *p = '\0';
95 objfile_name = arg;
96 provider = hold;
97 name = p + 1;
98 }
99 }
100
101 if (*name == '\0')
102 error (_("no probe name specified"));
103 if (provider && *provider == '\0')
104 error (_("invalid provider name"));
105 if (objfile_name && *objfile_name == '\0')
106 error (_("invalid objfile name"));
107
108 ALL_PSPACES (pspace)
109 ALL_PSPACE_OBJFILES (pspace, objfile)
110 {
111 VEC (probe_p) *probes;
112 struct probe *probe;
113 int ix;
114
115 if (!objfile->sf || !objfile->sf->sym_probe_fns)
116 continue;
117
118 if (objfile_name
119 && FILENAME_CMP (objfile->name, objfile_name) != 0
120 && FILENAME_CMP (lbasename (objfile->name), objfile_name) != 0)
121 continue;
122
123 if (objfile->separate_debug_objfile_backlink != NULL)
124 continue;
125
126 probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile);
127
128 for (ix = 0; VEC_iterate (probe_p, probes, ix, probe); ix++)
129 {
130 struct symtab_and_line *sal;
131
132 if (probe_ops != &probe_ops_any && probe->pops != probe_ops)
133 continue;
134
135 if (provider && strcmp (probe->provider, provider) != 0)
136 continue;
137
138 if (strcmp (probe->name, name) != 0)
139 continue;
140
141 ++result.nelts;
142 result.sals = xrealloc (result.sals,
143 result.nelts
144 * sizeof (struct symtab_and_line));
145 sal = &result.sals[result.nelts - 1];
146
147 init_sal (sal);
148
149 sal->pc = probe->address;
150 sal->explicit_pc = 1;
151 sal->section = find_pc_overlay (sal->pc);
152 sal->pspace = pspace;
153 sal->probe = probe;
154 }
155 }
156
157 if (result.nelts == 0)
158 {
159 throw_error (NOT_FOUND_ERROR,
160 _("No probe matching objfile=`%s', provider=`%s', name=`%s'"),
161 objfile_name ? objfile_name : _("<any>"),
162 provider ? provider : _("<any>"),
163 name);
164 }
165
166 if (canonical)
167 {
168 canonical->special_display = 1;
169 canonical->pre_expanded = 1;
170 canonical->addr_string = savestring (*argptr, arg_end - *argptr);
171 }
172
173 *argptr = arg_end;
174 do_cleanups (cleanup);
175
176 return result;
177 }
178
179 /* See definition in probe.h. */
180
181 VEC (probe_p) *
182 find_probes_in_objfile (struct objfile *objfile, const char *provider,
183 const char *name)
184 {
185 VEC (probe_p) *probes, *result = NULL;
186 int ix;
187 struct probe *probe;
188
189 if (!objfile->sf || !objfile->sf->sym_probe_fns)
190 return NULL;
191
192 probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile);
193 for (ix = 0; VEC_iterate (probe_p, probes, ix, probe); ix++)
194 {
195 if (strcmp (probe->provider, provider) != 0)
196 continue;
197
198 if (strcmp (probe->name, name) != 0)
199 continue;
200
201 VEC_safe_push (probe_p, result, probe);
202 }
203
204 return result;
205 }
206
207 /* See definition in probe.h. */
208
209 struct probe *
210 find_probe_by_pc (CORE_ADDR pc, struct objfile **objfile_out)
211 {
212 struct objfile *objfile;
213
214 ALL_OBJFILES (objfile)
215 {
216 VEC (probe_p) *probes;
217 int ix;
218 struct probe *probe;
219
220 if (!objfile->sf || !objfile->sf->sym_probe_fns)
221 continue;
222
223 /* If this proves too inefficient, we can replace with a hash. */
224 probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile);
225 for (ix = 0; VEC_iterate (probe_p, probes, ix, probe); ix++)
226 if (probe->address == pc)
227 {
228 *objfile_out = objfile;
229 return probe;
230 }
231 }
232
233 return NULL;
234 }
235
236 \f
237
238 /* A utility structure. A VEC of these is built when handling "info
239 probes". */
240
241 struct probe_and_objfile
242 {
243 /* The probe. */
244 struct probe *probe;
245
246 /* The probe's objfile. */
247 struct objfile *objfile;
248 };
249
250 typedef struct probe_and_objfile probe_and_objfile_s;
251 DEF_VEC_O (probe_and_objfile_s);
252
253 /* A helper function for collect_probes that compiles a regexp and
254 throws an exception on error. This installs a cleanup to free the
255 resulting pattern on success. If RX is NULL, this does nothing. */
256
257 static void
258 compile_rx_or_error (regex_t *pattern, const char *rx, const char *message)
259 {
260 int code;
261
262 if (!rx)
263 return;
264
265 code = regcomp (pattern, rx, REG_NOSUB);
266 if (code == 0)
267 make_regfree_cleanup (pattern);
268 else
269 {
270 char *err = get_regcomp_error (code, pattern);
271
272 make_cleanup (xfree, err);
273 error ("%s: %s", message, err);
274 }
275 }
276
277 /* Make a vector of probes matching OBJNAME, PROVIDER, and PROBE_NAME.
278 If POPS is not NULL, only probes of this certain probe_ops will match.
279 Each argument is a regexp, or NULL, which matches anything. */
280
281 static VEC (probe_and_objfile_s) *
282 collect_probes (char *objname, char *provider, char *probe_name,
283 const struct probe_ops *pops)
284 {
285 struct objfile *objfile;
286 VEC (probe_and_objfile_s) *result = NULL;
287 struct cleanup *cleanup, *cleanup_temps;
288 regex_t obj_pat, prov_pat, probe_pat;
289
290 cleanup = make_cleanup (VEC_cleanup (probe_and_objfile_s), &result);
291
292 cleanup_temps = make_cleanup (null_cleanup, NULL);
293 compile_rx_or_error (&prov_pat, provider, _("Invalid provider regexp"));
294 compile_rx_or_error (&probe_pat, probe_name, _("Invalid probe regexp"));
295 compile_rx_or_error (&obj_pat, objname, _("Invalid object file regexp"));
296
297 ALL_OBJFILES (objfile)
298 {
299 VEC (probe_p) *probes;
300 struct probe *probe;
301 int ix;
302
303 if (! objfile->sf || ! objfile->sf->sym_probe_fns)
304 continue;
305
306 if (objname)
307 {
308 if (regexec (&obj_pat, objfile->name, 0, NULL, 0) != 0)
309 continue;
310 }
311
312 probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile);
313
314 for (ix = 0; VEC_iterate (probe_p, probes, ix, probe); ix++)
315 {
316 probe_and_objfile_s entry;
317
318 if (pops != NULL && probe->pops != pops)
319 continue;
320
321 if (provider
322 && regexec (&prov_pat, probe->provider, 0, NULL, 0) != 0)
323 continue;
324
325 if (probe_name
326 && regexec (&probe_pat, probe->name, 0, NULL, 0) != 0)
327 continue;
328
329 entry.probe = probe;
330 entry.objfile = objfile;
331 VEC_safe_push (probe_and_objfile_s, result, &entry);
332 }
333 }
334
335 do_cleanups (cleanup_temps);
336 discard_cleanups (cleanup);
337 return result;
338 }
339
340 /* A qsort comparison function for probe_and_objfile_s objects. */
341
342 static int
343 compare_entries (const void *a, const void *b)
344 {
345 const probe_and_objfile_s *ea = a;
346 const probe_and_objfile_s *eb = b;
347 int v;
348
349 v = strcmp (ea->probe->provider, eb->probe->provider);
350 if (v)
351 return v;
352
353 v = strcmp (ea->probe->name, eb->probe->name);
354 if (v)
355 return v;
356
357 if (ea->probe->address < eb->probe->address)
358 return -1;
359 if (ea->probe->address > eb->probe->address)
360 return 1;
361
362 return strcmp (ea->objfile->name, eb->objfile->name);
363 }
364
365 /* Helper function that generate entries in the ui_out table being
366 crafted by `info_probes_for_ops'. */
367
368 static void
369 gen_ui_out_table_header_info (VEC (probe_and_objfile_s) *probes,
370 const struct probe_ops *p)
371 {
372 /* `headings' refers to the names of the columns when printing `info
373 probes'. */
374 VEC (info_probe_column_s) *headings = NULL;
375 struct cleanup *c;
376 info_probe_column_s *column;
377 size_t headings_size;
378 int ix;
379
380 gdb_assert (p != NULL);
381
382 if (p->gen_info_probes_table_header == NULL
383 && p->gen_info_probes_table_values == NULL)
384 return;
385
386 gdb_assert (p->gen_info_probes_table_header != NULL
387 && p->gen_info_probes_table_values != NULL);
388
389 c = make_cleanup (VEC_cleanup (info_probe_column_s), &headings);
390 p->gen_info_probes_table_header (&headings);
391
392 headings_size = VEC_length (info_probe_column_s, headings);
393
394 for (ix = 0;
395 VEC_iterate (info_probe_column_s, headings, ix, column);
396 ++ix)
397 {
398 probe_and_objfile_s *entry;
399 int jx;
400 size_t size_max = strlen (column->print_name);
401
402 for (jx = 0; VEC_iterate (probe_and_objfile_s, probes, jx, entry); ++jx)
403 {
404 /* `probe_fields' refers to the values of each new field that this
405 probe will display. */
406 VEC (const_char_ptr) *probe_fields = NULL;
407 struct cleanup *c2;
408 const char *val;
409 int kx;
410
411 if (entry->probe->pops != p)
412 continue;
413
414 c2 = make_cleanup (VEC_cleanup (const_char_ptr), &probe_fields);
415 p->gen_info_probes_table_values (entry->probe, entry->objfile,
416 &probe_fields);
417
418 gdb_assert (VEC_length (const_char_ptr, probe_fields)
419 == headings_size);
420
421 for (kx = 0; VEC_iterate (const_char_ptr, probe_fields, kx, val);
422 ++kx)
423 {
424 /* It is valid to have a NULL value here, which means that the
425 backend does not have something to write and this particular
426 field should be skipped. */
427 if (val == NULL)
428 continue;
429
430 size_max = max (strlen (val), size_max);
431 }
432 do_cleanups (c2);
433 }
434
435 ui_out_table_header (current_uiout, size_max, ui_left,
436 column->field_name, column->print_name);
437 }
438
439 do_cleanups (c);
440 }
441
442 /* Helper function to print extra information about a probe and an objfile
443 represented by ENTRY. */
444
445 static void
446 print_ui_out_info (probe_and_objfile_s *entry)
447 {
448 int ix;
449 int j = 0;
450 /* `values' refers to the actual values of each new field in the output
451 of `info probe'. `headings' refers to the names of each new field. */
452 VEC (const_char_ptr) *values = NULL;
453 VEC (info_probe_column_s) *headings = NULL;
454 info_probe_column_s *column;
455 struct cleanup *c;
456
457 gdb_assert (entry != NULL);
458 gdb_assert (entry->probe != NULL);
459 gdb_assert (entry->probe->pops != NULL);
460
461 if (entry->probe->pops->gen_info_probes_table_header == NULL
462 && entry->probe->pops->gen_info_probes_table_values == NULL)
463 return;
464
465 gdb_assert (entry->probe->pops->gen_info_probes_table_header != NULL
466 && entry->probe->pops->gen_info_probes_table_values != NULL);
467
468 c = make_cleanup (VEC_cleanup (info_probe_column_s), &headings);
469 make_cleanup (VEC_cleanup (const_char_ptr), &values);
470
471 entry->probe->pops->gen_info_probes_table_header (&headings);
472 entry->probe->pops->gen_info_probes_table_values (entry->probe,
473 entry->objfile, &values);
474
475 gdb_assert (VEC_length (info_probe_column_s, headings)
476 == VEC_length (const_char_ptr, values));
477
478 for (ix = 0;
479 VEC_iterate (info_probe_column_s, headings, ix, column);
480 ++ix)
481 {
482 const char *val = VEC_index (const_char_ptr, values, j++);
483
484 if (val == NULL)
485 ui_out_field_skip (current_uiout, column->field_name);
486 else
487 ui_out_field_string (current_uiout, column->field_name, val);
488 }
489
490 do_cleanups (c);
491 }
492
493 /* Helper function that returns the number of extra fields which POPS will
494 need. */
495
496 static int
497 get_number_extra_fields (const struct probe_ops *pops)
498 {
499 VEC (info_probe_column_s) *headings = NULL;
500 struct cleanup *c;
501 int n;
502
503 if (pops->gen_info_probes_table_header == NULL)
504 return 0;
505
506 c = make_cleanup (VEC_cleanup (info_probe_column_s), &headings);
507 pops->gen_info_probes_table_header (&headings);
508
509 n = VEC_length (info_probe_column_s, headings);
510
511 do_cleanups (c);
512
513 return n;
514 }
515
516 /* See comment in probe.h. */
517
518 void
519 info_probes_for_ops (char *arg, int from_tty, const struct probe_ops *pops)
520 {
521 char *provider, *probe = NULL, *objname = NULL;
522 struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
523 VEC (probe_and_objfile_s) *items;
524 int i, any_found;
525 int ui_out_extra_fields = 0;
526 size_t size_addr;
527 size_t size_name = strlen ("Name");
528 size_t size_objname = strlen ("Object");
529 size_t size_provider = strlen ("Provider");
530 probe_and_objfile_s *entry;
531 struct gdbarch *gdbarch = get_current_arch ();
532
533 /* Do we have a `provider:probe:objfile' style of linespec? */
534 provider = extract_arg (&arg);
535 if (provider)
536 {
537 make_cleanup (xfree, provider);
538
539 probe = extract_arg (&arg);
540 if (probe)
541 {
542 make_cleanup (xfree, probe);
543
544 objname = extract_arg (&arg);
545 if (objname)
546 make_cleanup (xfree, objname);
547 }
548 }
549
550 if (pops == NULL)
551 {
552 const struct probe_ops *po;
553 int ix;
554
555 /* If the probe_ops is NULL, it means the user has requested a "simple"
556 `info probes', i.e., she wants to print all information about all
557 probes. For that, we have to identify how many extra fields we will
558 need to add in the ui_out table.
559
560 To do that, we iterate over all probe_ops, querying each one about
561 its extra fields, and incrementing `ui_out_extra_fields' to reflect
562 that number. */
563
564 for (ix = 0; VEC_iterate (probe_ops_cp, all_probe_ops, ix, po); ++ix)
565 ui_out_extra_fields += get_number_extra_fields (po);
566 }
567 else
568 ui_out_extra_fields = get_number_extra_fields (pops);
569
570 items = collect_probes (objname, provider, probe, pops);
571 make_cleanup (VEC_cleanup (probe_and_objfile_s), &items);
572 make_cleanup_ui_out_table_begin_end (current_uiout,
573 4 + ui_out_extra_fields,
574 VEC_length (probe_and_objfile_s, items),
575 "StaticProbes");
576
577 if (!VEC_empty (probe_and_objfile_s, items))
578 qsort (VEC_address (probe_and_objfile_s, items),
579 VEC_length (probe_and_objfile_s, items),
580 sizeof (probe_and_objfile_s), compare_entries);
581
582 /* What's the size of an address in our architecture? */
583 size_addr = gdbarch_addr_bit (gdbarch) == 64 ? 18 : 10;
584
585 /* Determining the maximum size of each field (`provider', `name' and
586 `objname'). */
587 for (i = 0; VEC_iterate (probe_and_objfile_s, items, i, entry); ++i)
588 {
589 size_name = max (strlen (entry->probe->name), size_name);
590 size_provider = max (strlen (entry->probe->provider), size_provider);
591 size_objname = max (strlen (entry->objfile->name), size_objname);
592 }
593
594 ui_out_table_header (current_uiout, size_provider, ui_left, "provider",
595 _("Provider"));
596 ui_out_table_header (current_uiout, size_name, ui_left, "name", _("Name"));
597 ui_out_table_header (current_uiout, size_addr, ui_left, "addr", _("Where"));
598
599 if (pops == NULL)
600 {
601 const struct probe_ops *po;
602 int ix;
603
604 /* We have to generate the table header for each new probe type that we
605 will print. */
606 for (ix = 0; VEC_iterate (probe_ops_cp, all_probe_ops, ix, po); ++ix)
607 gen_ui_out_table_header_info (items, po);
608 }
609 else
610 gen_ui_out_table_header_info (items, pops);
611
612 ui_out_table_header (current_uiout, size_objname, ui_left, "object",
613 _("Object"));
614 ui_out_table_body (current_uiout);
615
616 for (i = 0; VEC_iterate (probe_and_objfile_s, items, i, entry); ++i)
617 {
618 struct cleanup *inner;
619
620 inner = make_cleanup_ui_out_tuple_begin_end (current_uiout, "probe");
621
622 ui_out_field_string (current_uiout, "provider", entry->probe->provider);
623 ui_out_field_string (current_uiout, "name", entry->probe->name);
624 ui_out_field_core_addr (current_uiout, "addr",
625 get_objfile_arch (entry->objfile),
626 entry->probe->address);
627
628 if (pops == NULL)
629 {
630 const struct probe_ops *po;
631 int ix;
632
633 for (ix = 0; VEC_iterate (probe_ops_cp, all_probe_ops, ix, po);
634 ++ix)
635 if (entry->probe->pops == po)
636 print_ui_out_info (entry);
637 }
638 else
639 print_ui_out_info (entry);
640
641 ui_out_field_string (current_uiout, "object", entry->objfile->name);
642 ui_out_text (current_uiout, "\n");
643
644 do_cleanups (inner);
645 }
646
647 any_found = !VEC_empty (probe_and_objfile_s, items);
648 do_cleanups (cleanup);
649
650 if (!any_found)
651 ui_out_message (current_uiout, 0, _("No probes matched.\n"));
652 }
653
654 /* Implementation of the `info probes' command. */
655
656 static void
657 info_probes_command (char *arg, int from_tty)
658 {
659 info_probes_for_ops (arg, from_tty, NULL);
660 }
661
662 /* See comments in probe.h. */
663
664 struct value *
665 probe_safe_evaluate_at_pc (struct frame_info *frame, unsigned n)
666 {
667 struct probe *probe;
668 struct objfile *objfile;
669 unsigned n_probes;
670
671 probe = find_probe_by_pc (get_frame_pc (frame), &objfile);
672 if (!probe)
673 return NULL;
674 gdb_assert (objfile->sf && objfile->sf->sym_probe_fns);
675
676 n_probes
677 = objfile->sf->sym_probe_fns->sym_get_probe_argument_count (objfile,
678 probe);
679 if (n >= n_probes)
680 return NULL;
681
682 return objfile->sf->sym_probe_fns->sym_evaluate_probe_argument (objfile,
683 probe,
684 n);
685 }
686
687 /* See comment in probe.h. */
688
689 const struct probe_ops *
690 probe_linespec_to_ops (const char **linespecp)
691 {
692 int ix;
693 const struct probe_ops *probe_ops;
694
695 for (ix = 0; VEC_iterate (probe_ops_cp, all_probe_ops, ix, probe_ops); ix++)
696 if (probe_ops->is_linespec (linespecp))
697 return probe_ops;
698
699 return NULL;
700 }
701
702 /* See comment in probe.h. */
703
704 int
705 probe_is_linespec_by_keyword (const char **linespecp, const char *const *keywords)
706 {
707 const char *s = *linespecp;
708 const char *const *csp;
709
710 for (csp = keywords; *csp; csp++)
711 {
712 const char *keyword = *csp;
713 size_t len = strlen (keyword);
714
715 if (strncmp (s, keyword, len) == 0 && isspace (s[len]))
716 {
717 *linespecp += len + 1;
718 return 1;
719 }
720 }
721
722 return 0;
723 }
724
725 /* Implementation of `is_linespec' method for `struct probe_ops'. */
726
727 static int
728 probe_any_is_linespec (const char **linespecp)
729 {
730 static const char *const keywords[] = { "-p", "-probe", NULL };
731
732 return probe_is_linespec_by_keyword (linespecp, keywords);
733 }
734
735 /* Dummy method used for `probe_ops_any'. */
736
737 static void
738 probe_any_get_probes (VEC (probe_p) **probesp, struct objfile *objfile)
739 {
740 /* No probes can be provided by this dummy backend. */
741 }
742
743 /* Operations associated with a generic probe. */
744
745 const struct probe_ops probe_ops_any =
746 {
747 probe_any_is_linespec,
748 probe_any_get_probes,
749 };
750
751 /* See comments in probe.h. */
752
753 struct cmd_list_element **
754 info_probes_cmdlist_get (void)
755 {
756 static struct cmd_list_element *info_probes_cmdlist;
757
758 if (info_probes_cmdlist == NULL)
759 add_prefix_cmd ("probes", class_info, info_probes_command,
760 _("\
761 Show available static probes.\n\
762 Usage: info probes [all|TYPE [ARGS]]\n\
763 TYPE specifies the type of the probe, and can be one of the following:\n\
764 - stap\n\
765 If you specify TYPE, there may be additional arguments needed by the\n\
766 subcommand.\n\
767 If you do not specify any argument, or specify `all', then the command\n\
768 will show information about all types of probes."),
769 &info_probes_cmdlist, "info probes ",
770 0/*allow-unknown*/, &infolist);
771
772 return &info_probes_cmdlist;
773 }
774
775 VEC (probe_ops_cp) *all_probe_ops;
776
777 void _initialize_probe (void);
778
779 void
780 _initialize_probe (void)
781 {
782 VEC_safe_push (probe_ops_cp, all_probe_ops, &probe_ops_any);
783
784 add_cmd ("all", class_info, info_probes_command,
785 _("\
786 Show information about all type of probes."),
787 info_probes_cmdlist_get ());
788 }
This page took 0.057297 seconds and 4 git commands to generate.