• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /* Print size information from ELF file.
2     Copyright (C) 2000-2007,2009,2012,2014 Red Hat, Inc.
3     This file is part of elfutils.
4     Written by Ulrich Drepper <drepper@redhat.com>, 2000.
5  
6     This file is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 3 of the License, or
9     (at your option) any later version.
10  
11     elfutils is distributed in the hope that it will be useful, but
12     WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15  
16     You should have received a copy of the GNU General Public License
17     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18  
19  #ifdef HAVE_CONFIG_H
20  # include <config.h>
21  #endif
22  
23  #include <argp.h>
24  #include <error.h>
25  #include <fcntl.h>
26  #include <gelf.h>
27  #include <inttypes.h>
28  #include <libelf.h>
29  #include <libintl.h>
30  #include <locale.h>
31  #include <mcheck.h>
32  #include <stdbool.h>
33  #include <stdio.h>
34  #include <stdio_ext.h>
35  #include <stdlib.h>
36  #include <string.h>
37  #include <unistd.h>
38  #include <sys/param.h>
39  
40  #include <system.h>
41  
42  
43  /* Name and version of program.  */
44  static void print_version (FILE *stream, struct argp_state *state);
45  ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
46  
47  /* Bug report address.  */
48  ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
49  
50  
51  /* Values for the parameters which have no short form.  */
52  #define OPT_FORMAT	0x100
53  #define OPT_RADIX	0x101
54  
55  /* Definitions of arguments for argp functions.  */
56  static const struct argp_option options[] =
57  {
58    { NULL, 0, NULL, 0, N_("Output format:"), 0 },
59    { "format", OPT_FORMAT, "FORMAT", 0,
60      N_("Use the output format FORMAT.  FORMAT can be `bsd' or `sysv'.  "
61         "The default is `bsd'"), 0 },
62    { NULL, 'A', NULL, 0, N_("Same as `--format=sysv'"), 0 },
63    { NULL, 'B', NULL, 0, N_("Same as `--format=bsd'"), 0 },
64    { "radix", OPT_RADIX, "RADIX", 0, N_("Use RADIX for printing symbol values"),
65      0},
66    { NULL, 'd', NULL, 0, N_("Same as `--radix=10'"), 0 },
67    { NULL, 'o', NULL, 0, N_("Same as `--radix=8'"), 0 },
68    { NULL, 'x', NULL, 0, N_("Same as `--radix=16'"), 0 },
69    { NULL, 'f', NULL, 0,
70      N_("Similar to `--format=sysv' output but in one line"), 0 },
71  
72    { NULL, 0, NULL, 0, N_("Output options:"), 0 },
73    { NULL, 'F', NULL, 0,
74      N_("Print size and permission flags for loadable segments"), 0 },
75    { "totals", 't', NULL, 0, N_("Display the total sizes (bsd only)"), 0 },
76    { NULL, 0, NULL, 0, NULL, 0 }
77  };
78  
79  /* Short description of program.  */
80  static const char doc[] = N_("\
81  List section sizes of FILEs (a.out by default).");
82  
83  /* Strings for arguments in help texts.  */
84  static const char args_doc[] = N_("[FILE...]");
85  
86  /* Prototype for option handler.  */
87  static error_t parse_opt (int key, char *arg, struct argp_state *state);
88  
89  /* Data structure to communicate with argp functions.  */
90  static struct argp argp =
91  {
92    options, parse_opt, args_doc, doc, NULL, NULL, NULL
93  };
94  
95  
96  /* Print symbols in file named FNAME.  */
97  static int process_file (const char *fname);
98  
99  /* Handle content of archive.  */
100  static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname);
101  
102  /* Handle ELF file.  */
103  static void handle_elf (Elf *elf, const char *fullname, const char *fname);
104  
105  /* Show total size.  */
106  static void show_bsd_totals (void);
107  
108  #define INTERNAL_ERROR(fname) \
109    error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s-%s): %s"),      \
110  	 fname, __LINE__, PACKAGE_VERSION, __DATE__, elf_errmsg (-1))
111  
112  
113  /* User-selectable options.  */
114  
115  /* The selected output format.  */
116  static enum
117  {
118    format_bsd = 0,
119    format_sysv,
120    format_sysv_one_line,
121    format_segments
122  } format;
123  
124  /* Radix for printed numbers.  */
125  static enum
126  {
127    radix_decimal = 0,
128    radix_hex,
129    radix_octal
130  } radix;
131  
132  
133  /* Mapping of radix and binary class to length.  */
134  static const int length_map[2][3] =
135  {
136    [ELFCLASS32 - 1] =
137    {
138      [radix_hex] = 8,
139      [radix_decimal] = 10,
140      [radix_octal] = 11
141    },
142    [ELFCLASS64 - 1] =
143    {
144      [radix_hex] = 16,
145      [radix_decimal] = 20,
146      [radix_octal] = 22
147    }
148  };
149  
150  /* True if total sizes should be printed.  */
151  static bool totals;
152  /* To print the total sizes in a reasonable format remember the higest
153     "class" of ELF binaries processed.  */
154  static int totals_class;
155  
156  
157  int
main(int argc,char * argv[])158  main (int argc, char *argv[])
159  {
160    int remaining;
161    int result = 0;
162  
163    /* Make memory leak detection possible.  */
164    mtrace ();
165  
166    /* We use no threads here which can interfere with handling a stream.  */
167    __fsetlocking (stdin, FSETLOCKING_BYCALLER);
168    __fsetlocking (stdout, FSETLOCKING_BYCALLER);
169    __fsetlocking (stderr, FSETLOCKING_BYCALLER);
170  
171    /* Set locale.  */
172    setlocale (LC_ALL, "");
173  
174    /* Make sure the message catalog can be found.  */
175    bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
176  
177    /* Initialize the message catalog.  */
178    textdomain (PACKAGE_TARNAME);
179  
180    /* Parse and process arguments.  */
181    argp_parse (&argp, argc, argv, 0, &remaining, NULL);
182  
183  
184    /* Tell the library which version we are expecting.  */
185    elf_version (EV_CURRENT);
186  
187    if (remaining == argc)
188      /* The user didn't specify a name so we use a.out.  */
189      result = process_file ("a.out");
190    else
191      /* Process all the remaining files.  */
192      do
193        result |= process_file (argv[remaining]);
194      while (++remaining < argc);
195  
196    /* Print the total sizes but only if the output format is BSD and at
197       least one file has been correctly read (i.e., we recognized the
198       class).  */
199    if (totals && format == format_bsd && totals_class != 0)
200      show_bsd_totals ();
201  
202    return result;
203  }
204  
205  
206  /* Print the version information.  */
207  static void
print_version(FILE * stream,struct argp_state * state)208  print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
209  {
210    fprintf (stream, "size (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
211    fprintf (stream, gettext ("\
212  Copyright (C) %s Red Hat, Inc.\n\
213  This is free software; see the source for copying conditions.  There is NO\n\
214  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
215  "), "2012");
216    fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
217  }
218  
219  
220  /* Handle program arguments.  */
221  static error_t
parse_opt(int key,char * arg,struct argp_state * state)222  parse_opt (int key, char *arg,
223  	   struct argp_state *state __attribute__ ((unused)))
224  {
225    switch (key)
226      {
227      case 'd':
228        radix = radix_decimal;
229        break;
230  
231      case 'f':
232        format = format_sysv_one_line;
233        break;
234  
235      case 'o':
236        radix = radix_octal;
237        break;
238  
239      case 'x':
240        radix = radix_hex;
241        break;
242  
243      case 'A':
244        format = format_sysv;
245        break;
246  
247      case 'B':
248        format = format_bsd;
249        break;
250  
251      case 'F':
252        format = format_segments;
253        break;
254  
255      case OPT_FORMAT:
256        if (strcmp (arg, "bsd") == 0 || strcmp (arg, "berkeley") == 0)
257  	format = format_bsd;
258        else if (likely (strcmp (arg, "sysv") == 0))
259  	format = format_sysv;
260        else
261  	error (EXIT_FAILURE, 0, gettext ("Invalid format: %s"), arg);
262        break;
263  
264      case OPT_RADIX:
265        if (strcmp (arg, "x") == 0 || strcmp (arg, "16") == 0)
266  	radix = radix_hex;
267        else if (strcmp (arg, "d") == 0 || strcmp (arg, "10") == 0)
268  	radix = radix_decimal;
269        else if (strcmp (arg, "o") == 0 || strcmp (arg, "8") == 0)
270  	radix = radix_octal;
271        else
272  	error (EXIT_FAILURE, 0, gettext ("Invalid radix: %s"), arg);
273        break;
274  
275      case 't':
276        totals = true;
277        break;
278  
279      default:
280        return ARGP_ERR_UNKNOWN;
281      }
282    return 0;
283  }
284  
285  
286  /* Open the file and determine the type.  */
287  static int
process_file(const char * fname)288  process_file (const char *fname)
289  {
290    int fd = open (fname, O_RDONLY);
291    if (unlikely (fd == -1))
292      {
293        error (0, errno, gettext ("cannot open '%s'"), fname);
294        return 1;
295      }
296  
297    /* Now get the ELF descriptor.  */
298    Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
299    if (likely (elf != NULL))
300      {
301        if (elf_kind (elf) == ELF_K_ELF)
302  	{
303  	  handle_elf (elf, NULL, fname);
304  
305  	  if (unlikely (elf_end (elf) != 0))
306  	    INTERNAL_ERROR (fname);
307  
308  	  if (unlikely (close (fd) != 0))
309  	    error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
310  
311  	  return 0;
312  	}
313        else if (likely (elf_kind (elf) == ELF_K_AR))
314  	{
315  	  int result = handle_ar (fd, elf, NULL, fname);
316  
317  	  if (unlikely  (close (fd) != 0))
318  	    error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
319  
320  	  return result;
321  	}
322  
323        /* We cannot handle this type.  Close the descriptor anyway.  */
324        if (unlikely (elf_end (elf) != 0))
325  	INTERNAL_ERROR (fname);
326      }
327  
328    if (unlikely (close (fd) != 0))
329      error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
330  
331    error (0, 0, gettext ("%s: file format not recognized"), fname);
332  
333    return 1;
334  }
335  
336  
337  /* Print the BSD-style header.  This is done exactly once.  */
338  static void
print_header(Elf * elf)339  print_header (Elf *elf)
340  {
341    static int done;
342  
343    if (! done)
344      {
345        int ddigits = length_map[gelf_getclass (elf) - 1][radix_decimal];
346        int xdigits = length_map[gelf_getclass (elf) - 1][radix_hex];
347  
348        printf ("%*s %*s %*s %*s %*s %s\n",
349  	      ddigits - 2, sgettext ("bsd|text"),
350  	      ddigits - 2, sgettext ("bsd|data"),
351  	      ddigits - 2, sgettext ("bsd|bss"),
352  	      ddigits - 2, sgettext ("bsd|dec"),
353  	      xdigits - 2, sgettext ("bsd|hex"),
354  	      sgettext ("bsd|filename"));
355  
356        done = 1;
357      }
358  }
359  
360  
361  static int
handle_ar(int fd,Elf * elf,const char * prefix,const char * fname)362  handle_ar (int fd, Elf *elf, const char *prefix, const char *fname)
363  {
364    size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
365    size_t fname_len = strlen (fname) + 1;
366    char new_prefix[prefix_len + 1 + fname_len];
367    char *cp = new_prefix;
368  
369    /* Create the full name of the file.  */
370    if (prefix != NULL)
371      {
372        cp = mempcpy (cp, prefix, prefix_len);
373        *cp++ = ':';
374      }
375    memcpy (cp, fname, fname_len);
376  
377    /* Process all the files contained in the archive.  */
378    int result = 0;
379    Elf *subelf;
380    Elf_Cmd cmd = ELF_C_READ_MMAP;
381    while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
382      {
383        /* The the header for this element.  */
384        Elf_Arhdr *arhdr = elf_getarhdr (subelf);
385  
386        if (elf_kind (subelf) == ELF_K_ELF)
387  	handle_elf (subelf, new_prefix, arhdr->ar_name);
388        else if (likely (elf_kind (subelf) == ELF_K_AR))
389  	result |= handle_ar (fd, subelf, new_prefix, arhdr->ar_name);
390        /* else signal error??? */
391  
392        /* Get next archive element.  */
393        cmd = elf_next (subelf);
394        if (unlikely (elf_end (subelf) != 0))
395  	INTERNAL_ERROR (fname);
396      }
397  
398    if (unlikely (elf_end (elf) != 0))
399      INTERNAL_ERROR (fname);
400  
401    return result;
402  }
403  
404  
405  /* Show sizes in SysV format.  */
406  static void
show_sysv(Elf * elf,const char * prefix,const char * fname,const char * fullname)407  show_sysv (Elf *elf, const char *prefix, const char *fname,
408  	   const char *fullname)
409  {
410    int maxlen = 10;
411    const int digits = length_map[gelf_getclass (elf) - 1][radix];
412  
413    /* Get the section header string table index.  */
414    size_t shstrndx;
415    if (unlikely (elf_getshdrstrndx (elf, &shstrndx) < 0))
416      error (EXIT_FAILURE, 0,
417  	   gettext ("cannot get section header string table index"));
418  
419    /* First round over the sections: determine the longest section name.  */
420    Elf_Scn *scn = NULL;
421    while ((scn = elf_nextscn (elf, scn)) != NULL)
422      {
423        GElf_Shdr shdr_mem;
424        GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
425  
426        if (shdr == NULL)
427  	INTERNAL_ERROR (fullname);
428  
429        /* Ignore all sections which are not used at runtime.  */
430        const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
431        if (name != NULL && (shdr->sh_flags & SHF_ALLOC) != 0)
432  	maxlen = MAX (maxlen, (int) strlen (name));
433      }
434  
435    fputs_unlocked (fname, stdout);
436    if (prefix != NULL)
437      printf (gettext (" (ex %s)"), prefix);
438    printf (":\n%-*s %*s %*s\n",
439  	  maxlen, sgettext ("sysv|section"),
440  	  digits - 2, sgettext ("sysv|size"),
441  	  digits, sgettext ("sysv|addr"));
442  
443    /* Iterate over all sections.  */
444    GElf_Off total = 0;
445    while ((scn = elf_nextscn (elf, scn)) != NULL)
446      {
447        GElf_Shdr shdr_mem;
448        GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
449  
450        /* Ignore all sections which are not used at runtime.  */
451        if ((shdr->sh_flags & SHF_ALLOC) != 0)
452  	{
453  	  printf ((radix == radix_hex
454  		   ? "%-*s %*" PRIx64 " %*" PRIx64 "\n"
455  		   : (radix == radix_decimal
456  		      ? "%-*s %*" PRId64 " %*" PRId64 "\n"
457  		      : "%-*s %*" PRIo64 " %*" PRIo64 "\n")),
458  		  maxlen, elf_strptr (elf, shstrndx, shdr->sh_name),
459  		  digits - 2, shdr->sh_size,
460  		  digits, shdr->sh_addr);
461  
462  	  total += shdr->sh_size;
463  	}
464      }
465  
466    if (radix == radix_hex)
467      printf ("%-*s %*" PRIx64 "\n\n\n", maxlen, sgettext ("sysv|Total"),
468  	    digits - 2, total);
469    else if (radix == radix_decimal)
470      printf ("%-*s %*" PRId64 "\n\n\n", maxlen, sgettext ("sysv|Total"),
471  	    digits - 2, total);
472    else
473      printf ("%-*s %*" PRIo64 "\n\n\n", maxlen, sgettext ("sysv|Total"),
474  	    digits - 2, total);
475  }
476  
477  
478  /* Show sizes in SysV format in one line.  */
479  static void
show_sysv_one_line(Elf * elf)480  show_sysv_one_line (Elf *elf)
481  {
482    /* Get the section header string table index.  */
483    size_t shstrndx;
484    if (unlikely (elf_getshdrstrndx (elf, &shstrndx) < 0))
485      error (EXIT_FAILURE, 0,
486  	   gettext ("cannot get section header string table index"));
487  
488    /* Iterate over all sections.  */
489    GElf_Off total = 0;
490    bool first = true;
491    Elf_Scn *scn = NULL;
492    while ((scn = elf_nextscn (elf, scn)) != NULL)
493      {
494        GElf_Shdr shdr_mem;
495        GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
496  
497        /* Ignore all sections which are not used at runtime.  */
498        if ((shdr->sh_flags & SHF_ALLOC) == 0)
499  	continue;
500  
501        if (! first)
502  	fputs_unlocked (" + ", stdout);
503        first = false;
504  
505        printf ((radix == radix_hex ? "%" PRIx64 "(%s)"
506  	       : (radix == radix_decimal ? "%" PRId64 "(%s)"
507  		  : "%" PRIo64 "(%s)")),
508  	      shdr->sh_size, elf_strptr (elf, shstrndx, shdr->sh_name));
509  
510        total += shdr->sh_size;
511      }
512  
513    if (radix == radix_hex)
514      printf (" = %#" PRIx64 "\n", total);
515    else if (radix == radix_decimal)
516      printf (" = %" PRId64 "\n", total);
517    else
518      printf (" = %" PRIo64 "\n", total);
519  }
520  
521  
522  /* Variables to add up the sizes of all files.  */
523  static uintmax_t total_textsize;
524  static uintmax_t total_datasize;
525  static uintmax_t total_bsssize;
526  
527  
528  /* Show sizes in BSD format.  */
529  static void
show_bsd(Elf * elf,const char * prefix,const char * fname,const char * fullname)530  show_bsd (Elf *elf, const char *prefix, const char *fname,
531  	  const char *fullname)
532  {
533    GElf_Off textsize = 0;
534    GElf_Off datasize = 0;
535    GElf_Off bsssize = 0;
536    const int ddigits = length_map[gelf_getclass (elf) - 1][radix_decimal];
537    const int xdigits = length_map[gelf_getclass (elf) - 1][radix_hex];
538  
539    /* Iterate over all sections.  */
540    Elf_Scn *scn = NULL;
541    while ((scn = elf_nextscn (elf, scn)) != NULL)
542      {
543        GElf_Shdr shdr_mem;
544        GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
545  
546        if (shdr == NULL)
547  	INTERNAL_ERROR (fullname);
548  
549        /* Ignore all sections which are not marked as loaded.  */
550        if ((shdr->sh_flags & SHF_ALLOC) == 0)
551  	continue;
552  
553        if ((shdr->sh_flags & SHF_WRITE) == 0)
554  	textsize += shdr->sh_size;
555        else if (shdr->sh_type == SHT_NOBITS)
556  	bsssize += shdr->sh_size;
557        else
558  	datasize += shdr->sh_size;
559      }
560  
561    printf ("%*" PRId64 " %*" PRId64 " %*" PRId64 " %*" PRId64 " %*"
562  	  PRIx64 " %s",
563  	  ddigits - 2, textsize,
564  	  ddigits - 2, datasize,
565  	  ddigits - 2, bsssize,
566  	  ddigits - 2, textsize + datasize + bsssize,
567  	  xdigits - 2, textsize + datasize + bsssize,
568  	  fname);
569    if (prefix != NULL)
570      printf (gettext (" (ex %s)"), prefix);
571    fputs_unlocked ("\n", stdout);
572  
573    total_textsize += textsize;
574    total_datasize += datasize;
575    total_bsssize += bsssize;
576  
577    totals_class = MAX (totals_class, gelf_getclass (elf));
578  }
579  
580  
581  /* Show total size.  */
582  static void
show_bsd_totals(void)583  show_bsd_totals (void)
584  {
585    int ddigits = length_map[totals_class - 1][radix_decimal];
586    int xdigits = length_map[totals_class - 1][radix_hex];
587  
588    printf ("%*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*"
589  	  PRIxMAX " %s",
590  	  ddigits - 2, total_textsize,
591  	  ddigits - 2, total_datasize,
592  	  ddigits - 2, total_bsssize,
593  	  ddigits - 2, total_textsize + total_datasize + total_bsssize,
594  	  xdigits - 2, total_textsize + total_datasize + total_bsssize,
595  	  gettext ("(TOTALS)\n"));
596  }
597  
598  
599  /* Show size and permission of loadable segments.  */
600  static void
show_segments(Elf * elf,const char * fullname)601  show_segments (Elf *elf, const char *fullname)
602  {
603    size_t phnum;
604    if (elf_getphdrnum (elf, &phnum) != 0)
605      INTERNAL_ERROR (fullname);
606  
607    GElf_Off total = 0;
608    bool first = true;
609    for (size_t cnt = 0; cnt < phnum; ++cnt)
610      {
611        GElf_Phdr phdr_mem;
612        GElf_Phdr *phdr;
613  
614        phdr = gelf_getphdr (elf, cnt, &phdr_mem);
615        if (phdr == NULL)
616  	INTERNAL_ERROR (fullname);
617  
618        if (phdr->p_type != PT_LOAD)
619  	/* Only load segments.  */
620  	continue;
621  
622        if (! first)
623  	fputs_unlocked (" + ", stdout);
624        first = false;
625  
626        printf (radix == radix_hex ? "%" PRIx64 "(%c%c%c)"
627  	      : (radix == radix_decimal ? "%" PRId64 "(%c%c%c)"
628  		 : "%" PRIo64 "(%c%c%c)"),
629  	      phdr->p_memsz,
630  	      (phdr->p_flags & PF_R) == 0 ? '-' : 'r',
631  	      (phdr->p_flags & PF_W) == 0 ? '-' : 'w',
632  	      (phdr->p_flags & PF_X) == 0 ? '-' : 'x');
633  
634        total += phdr->p_memsz;
635      }
636  
637    if (radix == radix_hex)
638      printf (" = %#" PRIx64 "\n", total);
639    else if (radix == radix_decimal)
640      printf (" = %" PRId64 "\n", total);
641    else
642      printf (" = %" PRIo64 "\n", total);
643  }
644  
645  
646  static void
handle_elf(Elf * elf,const char * prefix,const char * fname)647  handle_elf (Elf *elf, const char *prefix, const char *fname)
648  {
649    size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
650    size_t fname_len = strlen (fname) + 1;
651    char fullname[prefix_len + 1 + fname_len];
652    char *cp = fullname;
653  
654    /* Create the full name of the file.  */
655    if (prefix != NULL)
656      {
657        cp = mempcpy (cp, prefix, prefix_len);
658        *cp++ = ':';
659      }
660    memcpy (cp, fname, fname_len);
661  
662    if (format == format_sysv)
663      show_sysv (elf, prefix, fname, fullname);
664    else if (format == format_sysv_one_line)
665      show_sysv_one_line (elf);
666    else if (format == format_segments)
667      show_segments (elf, fullname);
668    else
669      {
670        print_header (elf);
671  
672        show_bsd (elf, prefix, fname, fullname);
673      }
674  }
675  
676  
677  #include "debugpred.h"
678