084eb999ffe896986ee632b1ae73e0362ea2c8de
[platform/upstream/elfutils.git] / src / strings.c
1 /* Print the strings of printable characters in files.
2    Copyright (C) 2005-2010, 2012 Red Hat, Inc.
3    This file is part of elfutils.
4    Written by Ulrich Drepper <drepper@redhat.com>, 2005.
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 <assert.h>
25 #include <ctype.h>
26 #include <endian.h>
27 #include <errno.h>
28 #include <error.h>
29 #include <fcntl.h>
30 #include <gelf.h>
31 #include <inttypes.h>
32 #include <libintl.h>
33 #include <locale.h>
34 #include <stdbool.h>
35 #include <stdio.h>
36 #include <stdio_ext.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <sys/mman.h>
41 #include <sys/param.h>
42 #include <sys/stat.h>
43
44 #include <system.h>
45
46
47 /* Prototypes of local functions.  */
48 static int read_fd (int fd, const char *fname, off64_t fdlen);
49 static int read_elf (Elf *elf, int fd, const char *fname, off64_t fdlen);
50
51
52 /* Name and version of program.  */
53 static void print_version (FILE *stream, struct argp_state *state);
54 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
55
56 /* Bug report address.  */
57 ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
58
59 /* Definitions of arguments for argp functions.  */
60 static const struct argp_option options[] =
61 {
62   { NULL, 0, NULL, 0, N_("Output Selection:"), 0 },
63   { "all", 'a', NULL, 0, N_("Scan entire file, not only loaded sections"), 0 },
64   { "bytes", 'n', "MIN-LEN", 0,
65     N_("Only NUL-terminated sequences of MIN-LEN characters or more are printed"), 0 },
66   { "encoding", 'e', "SELECTOR", 0, N_("\
67 Select character size and endianess: s = 7-bit, S = 8-bit, {b,l} = 16-bit, {B,L} = 32-bit"),
68     0},
69   { "print-file-name", 'f', NULL, 0,
70     N_("Print name of the file before each string."), 0 },
71   { "radix", 't', "{o,d,x}", 0,
72     N_("Print location of the string in base 8, 10, or 16 respectively."), 0 },
73   { NULL, 'o', NULL, 0, N_("Alias for --radix=o"), 0 },
74
75   { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
76   { NULL, 0, NULL, 0, NULL, 0 }
77 };
78
79 /* Short description of program.  */
80 static const char doc[] = N_("\
81 Print the strings of printable characters in files.");
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 /* Global variables.  */
97
98 /* True if whole file and not only loaded sections are looked at.  */
99 static bool entire_file;
100
101 /* Minimum length of any sequence reported.  */
102 static size_t min_len = 4;
103
104 /* Number of bytes per character.  */
105 static size_t bytes_per_char = 1;
106
107 /* Minimum length of any sequence reported in bytes.  */
108 static size_t min_len_bytes;
109
110 /* True if multibyte characters are in big-endian order.  */
111 static bool big_endian;
112
113 /* True unless 7-bit ASCII are expected.  */
114 static bool char_7bit;
115
116 /* True if file names should be printed before strings.  */
117 static bool print_file_name;
118
119 /* Location print format string.  */
120 static const char *locfmt;
121
122 /* Page size in use.  */
123 static size_t ps;
124
125
126 /* Mapped parts of the ELF file.  */
127 static unsigned char *elfmap;
128 static unsigned char *elfmap_base;
129 static size_t elfmap_size;
130 static off64_t elfmap_off;
131
132
133 int
134 main (int argc, char *argv[])
135 {
136   /* We use no threads.  */
137   __fsetlocking (stdin, FSETLOCKING_BYCALLER);
138   __fsetlocking (stdout, FSETLOCKING_BYCALLER);
139
140   /* Set locale.  */
141   (void) setlocale (LC_ALL, "");
142
143   /* Make sure the message catalog can be found.  */
144   (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
145
146   /* Initialize the message catalog.  */
147   (void) textdomain (PACKAGE_TARNAME);
148
149   /* Parse and process arguments.  */
150   int remaining;
151   (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
152
153   /* Tell the library which version we are expecting.  */
154   elf_version (EV_CURRENT);
155
156   /* Determine the page size.  We will likely need it a couple of times.  */
157   ps = sysconf (_SC_PAGESIZE);
158
159   struct stat64 st;
160   int result = 0;
161   if (remaining == argc)
162     /* We read from standard input.  This we cannot do for a
163        structured file.  */
164     result = read_fd (STDIN_FILENO,
165                       print_file_name ? "{standard input}" : NULL,
166                       (fstat64 (STDIN_FILENO, &st) == 0 && S_ISREG (st.st_mode))
167                       ? st.st_size : INT64_C (0x7fffffffffffffff));
168   else
169     do
170       {
171         int fd = (strcmp (argv[remaining], "-") == 0
172                   ? STDIN_FILENO : open (argv[remaining], O_RDONLY));
173         if (unlikely (fd == -1))
174           {
175             error (0, errno, gettext ("cannot open '%s'"), argv[remaining]);
176             result = 1;
177           }
178         else
179           {
180             const char *fname = print_file_name ? argv[remaining] : NULL;
181             int fstat_fail = fstat64 (fd, &st);
182             off64_t fdlen = (fstat_fail
183                              ? INT64_C (0x7fffffffffffffff) : st.st_size);
184             if (fdlen > (off64_t) min_len_bytes)
185               {
186                 Elf *elf = NULL;
187                 if (entire_file
188                     || fstat_fail
189                     || !S_ISREG (st.st_mode)
190                     || (elf = elf_begin (fd, ELF_C_READ, NULL)) == NULL
191                     || elf_kind (elf) != ELF_K_ELF)
192                   result |= read_fd (fd, fname, fdlen);
193                 else
194                   result |= read_elf (elf, fd, fname, fdlen);
195
196                 /* This call will succeed even if ELF is NULL.  */
197                 elf_end (elf);
198               }
199
200             if (strcmp (argv[remaining], "-") != 0)
201               close (fd);
202           }
203
204         if (elfmap != NULL && elfmap != MAP_FAILED)
205           munmap (elfmap, elfmap_size);
206         elfmap = NULL;
207       }
208     while (++remaining < argc);
209
210   return result;
211 }
212
213
214 /* Print the version information.  */
215 static void
216 print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
217 {
218   fprintf (stream, "strings (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
219   fprintf (stream, gettext ("\
220 Copyright (C) %s Red Hat, Inc.\n\
221 This is free software; see the source for copying conditions.  There is NO\n\
222 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
223 "), "2012");
224   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
225 }
226
227
228 /* Handle program arguments.  */
229 static error_t
230 parse_opt (int key, char *arg,
231            struct argp_state *state __attribute__ ((unused)))
232 {
233   switch (key)
234     {
235     case 'a':
236       entire_file = true;
237       break;
238
239     case 'e':
240       /* We expect a string of one character.  */
241       switch (arg[1] != '\0' ? '\0' : arg[0])
242         {
243         case 's':
244         case 'S':
245           char_7bit = arg[0] == 's';
246           bytes_per_char = 1;
247           break;
248
249         case 'b':
250         case 'B':
251           big_endian = true;
252           /* FALLTHROUGH */
253
254         case 'l':
255         case 'L':
256           bytes_per_char = isupper (arg[0]) ? 4 : 2;
257           break;
258
259         default:
260           error (0, 0, gettext ("invalid value '%s' for %s parameter"),
261                  arg, "-e");
262           argp_help (&argp, stderr, ARGP_HELP_SEE, "strings");
263           return ARGP_ERR_UNKNOWN;
264         }
265       break;
266
267     case 'f':
268       print_file_name = true;
269       break;
270
271     case 'n':
272       min_len = atoi (arg);
273       break;
274
275     case 'o':
276       goto octfmt;
277
278     case 't':
279       switch (arg[0])
280         {
281         case 'd':
282           locfmt = "%7" PRId64 " ";
283           break;
284
285         case 'o':
286         octfmt:
287           locfmt = "%7" PRIo64 " ";
288           break;
289
290         case 'x':
291           locfmt = "%7" PRIx64 " ";
292           break;
293
294         default:
295           error (0, 0, gettext ("invalid value '%s' for %s parameter"),
296                  arg, "-t");
297           argp_help (&argp, stderr, ARGP_HELP_SEE, "strings");
298           return ARGP_ERR_UNKNOWN;
299         }
300       break;
301
302     case ARGP_KEY_FINI:
303       /* Compute the length in bytes of any match.  */
304       if (min_len <= 0 || min_len > INT_MAX / bytes_per_char)
305         error (EXIT_FAILURE, 0,
306                gettext ("invalid minimum length of matched string size"));
307       min_len_bytes = min_len * bytes_per_char;
308       break;
309
310     default:
311       return ARGP_ERR_UNKNOWN;
312     }
313   return 0;
314 }
315
316
317 static void
318 process_chunk_mb (const char *fname, const unsigned char *buf, off64_t to,
319                   size_t len, char **unprinted)
320 {
321   size_t curlen = *unprinted == NULL ? 0 : strlen (*unprinted);
322   const unsigned char *start = buf;
323   while (len >= bytes_per_char)
324     {
325       uint32_t ch;
326
327       if (bytes_per_char == 2)
328         {
329           if (big_endian)
330             ch = buf[0] << 8 | buf[1];
331           else
332             ch = buf[1] << 8 | buf[0];
333         }
334       else
335         {
336           if (big_endian)
337             ch = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
338           else
339             ch = buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0];
340         }
341
342       if (ch <= 255 && (isprint (ch) || ch == '\t'))
343         {
344           ++buf;
345           ++curlen;
346         }
347       else
348         {
349           if (curlen >= min_len)
350             {
351               /* We found a match.  */
352               if (unlikely (fname != NULL))
353                 {
354                   fputs_unlocked (fname, stdout);
355                   fputs_unlocked (": ", stdout);
356                 }
357
358               if (unlikely (locfmt != NULL))
359                 printf (locfmt, (int64_t) to - len - (buf - start));
360
361               if (unlikely (*unprinted != NULL))
362                 {
363                   fputs_unlocked (*unprinted, stdout);
364                   free (*unprinted);
365                   *unprinted = NULL;
366                 }
367
368               /* There is no sane way of printing the string.  If we
369                  assume the file data is encoded in UCS-2/UTF-16 or
370                  UCS-4/UTF-32 respectively we could covert the string.
371                  But there is no such guarantee.  */
372               fwrite_unlocked (start, 1, buf - start, stdout);
373               putc_unlocked ('\n', stdout);
374             }
375
376           start = ++buf;
377           curlen =  0;
378
379           if (len <= min_len)
380             break;
381         }
382
383       --len;
384     }
385
386   if (curlen != 0)
387     *unprinted = xstrndup ((const char *) start, curlen);
388 }
389
390
391 static void
392 process_chunk (const char *fname, const unsigned char *buf, off64_t to,
393                size_t len, char **unprinted)
394 {
395   /* We are not going to slow the check down for the 2- and 4-byte
396      encodings.  Handle them special.  */
397   if (unlikely (bytes_per_char != 1))
398     {
399       process_chunk_mb (fname, buf, to, len, unprinted);
400       return;
401     }
402
403   size_t curlen = *unprinted == NULL ? 0 : strlen (*unprinted);
404   const unsigned char *start = buf;
405   while (len > 0)
406     {
407       if ((isprint (*buf) || *buf == '\t') && (! char_7bit || *buf <= 127))
408         {
409           ++buf;
410           ++curlen;
411         }
412       else
413         {
414           if (curlen >= min_len)
415             {
416               /* We found a match.  */
417               if (likely (fname != NULL))
418                 {
419                   fputs_unlocked (fname, stdout);
420                   fputs_unlocked (": ", stdout);
421                 }
422
423               if (likely (locfmt != NULL))
424                 printf (locfmt, (int64_t) to - len - (buf - start));
425
426               if (unlikely (*unprinted != NULL))
427                 {
428                   fputs_unlocked (*unprinted, stdout);
429                   free (*unprinted);
430                   *unprinted = NULL;
431                 }
432               fwrite_unlocked (start, 1, buf - start, stdout);
433               putc_unlocked ('\n', stdout);
434             }
435
436           start = ++buf;
437           curlen =  0;
438
439           if (len <= min_len)
440             break;
441         }
442
443       --len;
444     }
445
446   if (curlen != 0)
447     *unprinted = xstrndup ((const char *) start, curlen);
448 }
449
450
451 /* Map a file in as large chunks as possible.  */
452 static void *
453 map_file (int fd, off64_t start_off, off64_t fdlen, size_t *map_sizep)
454 {
455 #if _MUDFLAP
456   (void) fd;
457   (void) start_off;
458   (void) fdlen;
459   (void) map_sizep;
460   return MAP_FAILED;
461 #else
462   /* Maximum size we mmap.  We use an #ifdef to avoid overflows on
463      32-bit machines.  64-bit machines these days do not have usable
464      address spaces larger than about 43 bits.  Not that any file
465      should be that large.  */
466 # if SIZE_MAX > 0xffffffff
467   const size_t mmap_max = 0x4000000000lu;
468 # else
469   const size_t mmap_max = 0x40000000lu;
470 # endif
471
472   /* Try to mmap the file.  */
473   size_t map_size = MIN ((off64_t) mmap_max, fdlen);
474   const size_t map_size_min = MAX (MAX (SIZE_MAX / 16, 2 * ps),
475                                    roundup (2 * min_len_bytes + 1, ps));
476   void *mem;
477   while (1)
478     {
479       /* We map the memory for reading only here.  Since we will
480          always look at every byte of the file it makes sense to
481          use MAP_POPULATE.  */
482       mem = mmap64 (NULL, map_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE,
483                     fd, start_off);
484       if (mem != MAP_FAILED)
485         {
486           /* We will go through the mapping sequentially.  */
487           (void) posix_madvise (mem, map_size, POSIX_MADV_SEQUENTIAL);
488           break;
489         }
490       if (errno != EINVAL && errno != ENOMEM)
491         /* This is an error other than the lack of address space.  */
492         break;
493
494       /* Maybe the size of the mapping is too big.  Try again.  */
495       map_size /= 2;
496       if (map_size < map_size_min)
497         /* That size should have fit.  */
498         break;
499     }
500
501   *map_sizep = map_size;
502   return mem;
503 #endif
504 }
505
506
507 /* Read the file without mapping.  */
508 static int
509 read_block_no_mmap (int fd, const char *fname, off64_t from, off64_t fdlen)
510 {
511   char *unprinted = NULL;
512 #define CHUNKSIZE 65536
513   unsigned char *buf = xmalloc (CHUNKSIZE + min_len_bytes
514                                 + bytes_per_char - 1);
515   size_t ntrailer = 0;
516   int result = 0;
517   while (fdlen > 0)
518     {
519       ssize_t n = TEMP_FAILURE_RETRY (read (fd, buf + ntrailer,
520                                             MIN (fdlen, CHUNKSIZE)));
521       if (n == 0)
522         {
523           /* There are less than MIN_LEN+1 bytes left so there cannot be
524              another match.  */
525           assert (unprinted == NULL || ntrailer == 0);
526           break;
527         }
528       if (unlikely (n < 0))
529         {
530           /* Something went wrong.  */
531           result = 1;
532           break;
533         }
534
535       /* Account for the number of bytes read in this round.  */
536       fdlen -= n;
537
538       /* Do not use the signed N value.  Note that the addition cannot
539          overflow.  */
540       size_t nb = (size_t) n + ntrailer;
541       if (nb >= min_len_bytes)
542         {
543           /* We only use complete characters.  */
544           nb &= ~(bytes_per_char - 1);
545
546           process_chunk (fname, buf, from + nb, nb, &unprinted);
547
548           /* If the last bytes of the buffer (modulo the character
549              size) have been printed we are not copying them.  */
550           size_t to_keep = unprinted != NULL ? 0 : min_len_bytes;
551
552           memmove (buf, buf + nb - to_keep, to_keep);
553           ntrailer = to_keep;
554           from += nb;
555         }
556       else
557         ntrailer = nb;
558     }
559
560   free (buf);
561
562   /* Don't print anything we collected so far.  There is no
563      terminating NUL byte.  */
564   free (unprinted);
565
566   return result;
567 }
568
569
570 static int
571 read_block (int fd, const char *fname, off64_t fdlen, off64_t from, off64_t to)
572 {
573   if (elfmap == NULL)
574     {
575       /* We need a completely new mapping.  */
576       elfmap_off = from & ~(ps - 1);
577       elfmap_base = elfmap = map_file (fd, elfmap_off, fdlen, &elfmap_size);
578
579       if (unlikely (elfmap == MAP_FAILED))
580         /* Let the kernel know we are going to read everything in sequence.  */
581         (void) posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL);
582     }
583
584   if (unlikely (elfmap == MAP_FAILED))
585     {
586       /* Read from the file descriptor.  For this we must position the
587          read pointer.  */
588       // XXX Eventually add flag which avoids this if the position
589       // XXX is known to match.
590       if (from != 0 && lseek64 (fd, from, SEEK_SET) != from)
591         error (EXIT_FAILURE, errno, gettext ("lseek64 failed"));
592
593       return read_block_no_mmap (fd, fname, from, to - from);
594     }
595
596   assert ((off64_t) min_len_bytes < fdlen);
597
598   if (to < (off64_t) elfmap_off || from > (off64_t) (elfmap_off + elfmap_size))
599     {
600       /* The existing mapping cannot fit at all.  Map the new area.
601          We always map the full range of ELFMAP_SIZE bytes even if
602          this extend beyond the end of the file.  The Linux kernel
603          handles this OK if the access pages are not touched.  */
604       elfmap_off = from & ~(ps - 1);
605       if (mmap64 (elfmap, elfmap_size, PROT_READ,
606                   MAP_PRIVATE | MAP_POPULATE | MAP_FIXED, fd, from)
607           == MAP_FAILED)
608         error (EXIT_FAILURE, errno, gettext ("re-mmap failed"));
609       elfmap_base = elfmap;
610     }
611
612   char *unprinted = NULL;
613
614   /* Use the existing mapping as much as possible.  If necessary, map
615      new pages.  */
616   if (from >= (off64_t) elfmap_off
617       && from < (off64_t) (elfmap_off + elfmap_size))
618     /* There are at least a few bytes in this mapping which we can
619        use.  */
620     process_chunk (fname, elfmap_base + (from - elfmap_off),
621                    MIN (to, (off64_t) (elfmap_off + elfmap_size)),
622                    MIN (to, (off64_t) (elfmap_off + elfmap_size)) - from,
623                    &unprinted);
624
625   if (to > (off64_t) (elfmap_off + elfmap_size))
626     {
627       unsigned char *remap_base = elfmap_base;
628       size_t read_now = elfmap_size - (elfmap_base - elfmap);
629
630       assert (from >= (off64_t) elfmap_off
631               && from < (off64_t) (elfmap_off + elfmap_size));
632       off64_t handled_to = elfmap_off + elfmap_size;
633       assert (elfmap == elfmap_base
634               || (elfmap_base - elfmap
635                   == (ptrdiff_t) ((min_len_bytes + ps - 1) & ~(ps - 1))));
636       if (elfmap == elfmap_base)
637         {
638           size_t keep_area = (min_len_bytes + ps - 1) & ~(ps - 1);
639           assert (elfmap_size >= keep_area + ps);
640           /* The keep area is used for the content of the previous
641              buffer we have to keep.  This means copying those bytes
642              and for this we have to make the data writable.  */
643           if (unlikely (mprotect (elfmap, keep_area, PROT_READ | PROT_WRITE)
644                         != 0))
645             error (EXIT_FAILURE, errno, gettext ("mprotect failed"));
646
647           elfmap_base = elfmap + keep_area;
648         }
649
650       while (1)
651         {
652           /* Map the rest of the file, eventually again in pieces.
653              We speed things up with a nice Linux feature.  Note
654              that we have at least two pages mapped.  */
655           size_t to_keep = unprinted != NULL ? 0 : min_len_bytes;
656
657           assert (read_now >= to_keep);
658           memmove (elfmap_base - to_keep,
659                    remap_base + read_now - to_keep, to_keep);
660           remap_base = elfmap_base;
661
662           assert ((elfmap_size - (elfmap_base - elfmap)) % bytes_per_char
663                   == 0);
664           read_now = MIN (to - handled_to,
665                           (ptrdiff_t) elfmap_size - (elfmap_base - elfmap));
666
667           assert (handled_to % ps == 0);
668           assert (handled_to % bytes_per_char == 0);
669           if (mmap64 (remap_base, read_now, PROT_READ,
670                       MAP_PRIVATE | MAP_POPULATE | MAP_FIXED, fd, handled_to)
671               == MAP_FAILED)
672             error (EXIT_FAILURE, errno, gettext ("re-mmap failed"));
673           elfmap_off = handled_to;
674
675           process_chunk (fname, remap_base - to_keep,
676                          elfmap_off + (read_now & ~(bytes_per_char - 1)),
677                          to_keep + (read_now & ~(bytes_per_char - 1)),
678                          &unprinted);
679           handled_to += read_now;
680           if (handled_to >= to)
681             break;
682         }
683     }
684
685   /* Don't print anything we collected so far.  There is no
686      terminating NUL byte.  */
687   free (unprinted);
688
689   return 0;
690 }
691
692
693 static int
694 read_fd (int fd, const char *fname, off64_t fdlen)
695 {
696   return read_block (fd, fname, fdlen, 0, fdlen);
697 }
698
699
700 static int
701 read_elf (Elf *elf, int fd, const char *fname, off64_t fdlen)
702 {
703   assert (fdlen >= 0);
704
705   /* We will look at each section separately.  The ELF file is not
706      mmapped.  The libelf implementation will load the needed parts on
707      demand.  Since we only interate over the section header table the
708      memory consumption at this stage is kept minimal.  */
709   Elf_Scn *scn = elf_nextscn (elf, NULL);
710   if (scn == NULL)
711     return read_fd (fd, fname, fdlen);
712
713   int result = 0;
714   do
715     {
716       GElf_Shdr shdr_mem;
717       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
718
719       /* Only look in sections which are loaded at runtime and
720          actually have content.  */
721       if (shdr != NULL && shdr->sh_type != SHT_NOBITS
722           && (shdr->sh_flags & SHF_ALLOC) != 0)
723         result |= read_block (fd, fname, fdlen, shdr->sh_offset,
724                               shdr->sh_offset + shdr->sh_size);
725     }
726   while ((scn = elf_nextscn (elf, scn)) != NULL);
727
728   if (elfmap != NULL && elfmap != MAP_FAILED)
729     munmap (elfmap, elfmap_size);
730   elfmap = NULL;
731
732   return result;
733 }
734
735
736 #include "debugpred.h"