xref: /netbsd-src/external/gpl3/binutils/dist/gprofng/src/gp-archive.cc (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
1 /* Copyright (C) 2021-2024 Free Software Foundation, Inc.
2    Contributed by Oracle.
3 
4    This file is part of GNU Binutils.
5 
6    This program 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, or (at your option)
9    any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but 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, write to the Free Software
18    Foundation, 51 Franklin Street - Fifth Floor, Boston,
19    MA 02110-1301, USA.  */
20 
21 #include "config.h"
22 #include <ctype.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <getopt.h>
26 
27 #include "util.h"
28 #include "StringMap.h"
29 #include "LoadObject.h"
30 #include "DbeSession.h"
31 #include "DbeFile.h"
32 #include "SourceFile.h"
33 #include "Elf.h"
34 #include "gp-archive.h"
35 #include "ArchiveExp.h"
36 #include "Print.h"
37 #include "Module.h"
38 
er_archive(int argc,char * argv[])39 er_archive::er_archive (int argc, char *argv[]) : DbeApplication (argc, argv)
40 {
41   force = 0;
42   common_archive_dir = NULL;
43   quiet = 0;
44   descendant = 1;
45   use_relative_path = 0;
46   s_option = ARCH_EXE_ONLY;
47   mask = NULL;
48 }
49 
~er_archive()50 er_archive::~er_archive ()
51 {
52   if (mask)
53     {
54       for (long i = 0, sz = mask->size (); i < sz; i++)
55 	{
56 	  regex_t *regex_desc = mask->get (i);
57 	  regfree (regex_desc);
58 	  delete regex_desc;
59 	}
60       delete mask;
61     }
62   delete common_archive_dir;
63 }
64 
65 int
mask_is_on(const char * str)66 er_archive::mask_is_on (const char *str)
67 {
68   if (mask == NULL)
69     return 1;
70   for (long i = 0, sz = mask->size (); i < sz; i++)
71     {
72       regex_t *regex_desc = mask->get (i);
73       if (regexec (regex_desc, str, 0, NULL, 0) == 0)
74 	return 1;
75     }
76   return 0;
77 }
78 
79 void
usage()80 er_archive::usage ()
81 {
82 /*
83   fprintf (stderr, GTXT ("Usage: %s [-nqFV] [-a on|ldobjects|src|usedldobjects|usedsrc|off] [-m regexp] experiment\n"), whoami);
84 */
85 
86 /*
87   Ruud - Isolate this line because it has an argument.  Otherwise it would be at the
88   end of this long list.
89 */
90   printf ( GTXT (
91     "Usage: gprofng archive [OPTION(S)] EXPERIMENT\n"));
92 
93   printf ( GTXT (
94     "\n"
95     "Archive the associated application binaries and source files in a gprofng\n"
96     "experiment to make it self contained and portable.\n"
97     "\n"
98     "Options:\n"
99     "\n"
100     " --version           print the version number and exit.\n"
101     " --help              print usage information and exit.\n"
102     " --verbose {on|off}  enable (on) or disable (off) verbose mode; the default is \"off\".\n"
103     "\n"
104     " -a {off|on|ldobjects|src|usedldobjects|usedsrc}  specify archiving of binaries and other files;\n"
105     "                    in addition to disable this feature (off), or enable archiving off all\n"
106     "                    loadobjects and sources (on), the other options support a more\n"
107     "                    refined selection. All of these options enable archiving, but the\n"
108     "                    keyword controls what exactly is selected: all load objects (ldobjects),\n"
109     "                    all source files (src), the loadobjects asscoiated with a program counter\n"
110     "                    (usedldobjects), or the source files associated with a program counter\n"
111     "                    (usedsrc); the default is \"-a ldobjects\".\n"
112     "\n"
113     " -n                 archive the named experiment only, not any of its descendants.\n"
114     "\n"
115     " -q                 do not write any warnings to stderr; messages are archived and\n"
116     "                    can be retrieved later.\n"
117     "\n"
118     " -F                 force writing or rewriting of the archive; ignored with the -n\n"
119     "                    or -m options, or if this is a subexperiment.\n"
120     "\n"
121     " -d <path>          specifies the location of a common archive; this is a directory that\n"
122     "                    contains archived files.\n"
123     "\n"
124     " -m <regex>         archive only those source, object, and debug info files whose full\n"
125     "                    path name matches the given POSIX compliant regular expression.\n"
126     "\n"
127     "Limitations:\n"
128     "\n"
129     "Default archiving does not occur in case the application profiled terminates prematurely,\n"
130     "or if archiving is disabled when collecting the performance data. In such cases, this\n"
131     "tool can be used to afterwards archive the information, but it has to run on the same \n"
132     "system where the profiling data was recorded.\n"
133     "\n"
134     "Documentation:\n"
135     "\n"
136     "A getting started guide for gprofng is maintained as a Texinfo manual. If the info and\n"
137     "gprofng programs are properly installed at your site, the command \"info gprofng\"\n"
138     "should give you access to this document.\n"
139     "\n"
140     "See also:\n"
141     "\n"
142     "gprofng(1), gp-collect-app(1), gp-display-html(1), gp-display-src(1), gp-display-text(1)\n"));
143 // Ruud
144 /*
145   fprintf (stderr, GTXT ("GNU %s version %s\n"), get_basename (prog_name), VERSION);
146 */
147   exit (1);
148 }
149 
150 Vector <LoadObject*> *
get_loadObjs()151 er_archive::get_loadObjs ()
152 {
153   Vector <LoadObject*> *objs = new Vector<LoadObject*>();
154   Vector <LoadObject*> *loadObjs = dbeSession->get_text_segments ();
155   if (s_option != ARCH_NOTHING)
156     {
157       for (long i = 0, sz = VecSize(loadObjs); i < sz; i++)
158 	{
159 	  LoadObject *lo = loadObjs->get (i);
160 	  if ((lo->flags & SEG_FLAG_DYNAMIC) != 0)
161 	    continue;
162 	  DbeFile *df = lo->dbeFile;
163 	  if (df && ((df->filetype & DbeFile::F_FICTION) != 0))
164 	    continue;
165 	  if (!lo->isUsed && ((s_option & (ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY)) != 0))
166 	    continue;
167 	  objs->append (lo);
168 	}
169     }
170   if (DEBUG_ARCHIVE)
171     {
172       Dprintf (DEBUG_ARCHIVE, NTXT ("get_text_segments(): %d\n"),
173 	       (int) (loadObjs ? loadObjs->size () : -1));
174       for (long i = 0, sz = loadObjs ? loadObjs->size () : 0; i < sz; i++)
175 	{
176 	  LoadObject *lo = loadObjs->get (i);
177 	  Dprintf (DEBUG_ARCHIVE, NTXT ("%s:%d  [%2ld] %s\n"),
178 		   get_basename (__FILE__), (int) __LINE__, i, STR (lo->dump ()));
179 	}
180       Dprintf (DEBUG_ARCHIVE, NTXT ("\nget_loadObjs(): %d\n"),
181 	       (int) (objs ? objs->size () : -1));
182       for (long i = 0, sz = VecSize(objs); i < sz; i++)
183 	{
184 	  LoadObject *lo = objs->get (i);
185 	  Dprintf (DEBUG_ARCHIVE, NTXT ("%s:%d  [%2ld] %s\n"),
186 		   get_basename (__FILE__), (int) __LINE__, i, STR (lo->dump ()));
187 	}
188     }
189   delete loadObjs;
190   return objs;
191 }
192 
193 /**
194  * Clean old archive
195  * Except the following cases:
196  * 1. Founder experiment is an MPI experiment
197  * 2. "-n" option is passed (do not archive descendants)
198  * 3. "-m" option is passed (partial archiving)
199  * 4. Experiment name is not the founder experiment (it is a sub-experiment)
200  * @param expname
201  * @param founder_exp
202  * @return 0 - success
203  */
204 int
clean_old_archive(char * expname,ArchiveExp * founder_exp)205 er_archive::clean_old_archive (char *expname, ArchiveExp *founder_exp)
206 {
207   if (0 == descendant)
208     { // do not archive descendants
209       fprintf (stderr, GTXT ("Warning: Option -F is ignored because -n option is specified (do not archive descendants)\n"));
210       return 1;
211     }
212   if (NULL != mask)
213     { // partial archiving
214       fprintf (stderr, GTXT ("Warning: Option -F is ignored because -m option is specified\n"));
215       return 1;
216     }
217   // Check if the experiment is the founder
218   char *s1 = dbe_strdup (expname);
219   char *s2 = dbe_strdup (founder_exp->get_expt_name ());
220   if (!s1 || !s2)
221     {
222       fprintf (stderr, GTXT ("Cannot allocate memory\n"));
223       exit (1);
224     }
225   // remove trailing slashes
226   for (int n = strlen (s1); n > 0; n--)
227     {
228       if ('/' != s1[n - 1])
229 	break;
230       s1[n - 1] = 0;
231     }
232   for (int n = strlen (s2); n > 0; n--)
233     {
234       if ('/' != s2[n - 1])
235 	break;
236       s2[n - 1] = 0;
237     }
238   if (strcmp (s1, s2) != 0)
239     { // not founder
240       fprintf (stderr, GTXT ("Warning: Option -F is ignored because specified experiment name %s does not match founder experiment name %s\n"), s1, s2);
241       free (s1);
242       free (s2);
243       return 1;
244     }
245   // Remove old "archives"
246   char *arch = founder_exp->get_arch_name ();
247   fprintf (stderr, GTXT ("INFO: removing existing archive: %s\n"), arch);
248   if (dbe_stat (arch, NULL) == 0)
249     {
250       char *cmd = dbe_sprintf ("/bin/rm -rf %s", arch);
251       system (cmd);
252       free (cmd);
253       if (dbe_stat (arch, NULL) != 0)
254 	{ // create "archives"
255 	  if (!founder_exp->create_dir (founder_exp->get_arch_name ()))
256 	    {
257 	      fprintf (stderr, GTXT ("Unable to create directory `%s'\n"), founder_exp->get_arch_name ());
258 	      exit (1);
259 	    }
260 	}
261     }
262   free (s1);
263   free (s2);
264   return 0;
265 } // clean_old_archive_if_necessary
266 
267 void
start(int argc,char * argv[])268 er_archive::start (int argc, char *argv[])
269 {
270   int last = argc - 1;
271   if (check_args (argc, argv) != last)
272     usage ();
273   check_env_var ();
274   if (s_option == ARCH_NOTHING)
275     return;
276 
277   ArchiveExp *founder_exp = new ArchiveExp (argv[last]);
278   if (founder_exp->get_status () == Experiment::FAILURE)
279     {
280       if (!quiet)
281 	fprintf (stderr, GTXT ("gp-archive: %s: %s\n"), argv[last],
282 		 pr_mesgs (founder_exp->fetch_errors (), NTXT (""), NTXT ("")));
283       exit (1);
284     }
285   if (!founder_exp->create_dir (founder_exp->get_arch_name ()))
286     {
287       fprintf (stderr, GTXT ("Unable to create directory `%s'\n"), founder_exp->get_arch_name ());
288       exit (1);
289     }
290   if (!common_archive_dir)
291     common_archive_dir = dbe_strdup (getenv ("GPROFNG_ARCHIVE_COMMON_DIR"));
292   if (common_archive_dir)
293     {
294       if (!founder_exp->create_dir (common_archive_dir))
295 	if (dbe_stat (common_archive_dir, NULL) != 0)
296 	  {
297 	    fprintf (stderr, GTXT ("Unable to create directory for common archive `%s'\n"), common_archive_dir);
298 	    exit (1);
299 	  }
300     }
301   // Clean old archives if necessary
302   if (force)
303     clean_old_archive (argv[last], founder_exp);
304   Vector<ArchiveExp*> *exps = new Vector<ArchiveExp*>();
305   exps->append (founder_exp);
306   if (descendant)
307     {
308       Vector<char*> *exp_names = founder_exp->get_descendants_names ();
309       if (exp_names)
310 	{
311 	  for (long i = 0, sz = exp_names->size (); i < sz; i++)
312 	    {
313 	      char *exp_path = exp_names->get (i);
314 	      ArchiveExp *exp = new ArchiveExp (exp_path);
315 	      if (exp->get_status () == Experiment::FAILURE)
316 		{
317 		  if (!quiet)
318 		    fprintf (stderr, GTXT ("gp-archive: %s: %s\n"), exp_path,
319 			     pr_mesgs (exp->fetch_errors (), "", ""));
320 		  delete exp;
321 		  continue;
322 		}
323 	      exps->append (exp);
324 	    }
325 	  exp_names->destroy ();
326 	  delete exp_names;
327 	}
328     }
329   for (long i = 0, sz = exps->size (); i < sz; i++)
330     {
331       ArchiveExp *exp = exps->get (i);
332       exp->read_data (s_option);
333     }
334 
335   Vector <DbeFile*> *copy_files = new Vector<DbeFile*>();
336   Vector <LoadObject*> *loadObjs = get_loadObjs ();
337   for (long i = 0, sz = VecSize(loadObjs); i < sz; i++)
338     {
339       LoadObject *lo = loadObjs->get (i);
340       if (strcmp (lo->get_pathname (), "LinuxKernel") == 0)
341 	continue;
342       DbeFile *df = lo->dbeFile;
343       if ((df->filetype & DbeFile::F_FICTION) != 0)
344 	continue;
345       if (df->get_location () == NULL)
346 	{
347 	  copy_files->append (df);
348 	  continue;
349 	}
350       if ((df->filetype & DbeFile::F_JAVACLASS) != 0)
351 	{
352 	  if (df->container)
353 	    { // Found in .jar file
354 	      copy_files->append (df->container);
355 	    }
356 	  copy_files->append (df);
357 	  if ((s_option & ARCH_EXE_ONLY) != 0)
358 	    continue;
359 	}
360       lo->sync_read_stabs ();
361       Elf *elf = lo->get_elf ();
362       if (elf && (lo->checksum != 0) && (lo->checksum != elf->elf_checksum ()))
363 	{
364 	  if (!quiet)
365 	    fprintf (stderr, GTXT ("gp-archive: '%s' has an unexpected checksum value; perhaps it was rebuilt. File ignored\n"),
366 		       df->get_location ());
367 	  continue;
368 	}
369       copy_files->append (df);
370       if (elf)
371 	{
372 	  Elf *f = elf->find_ancillary_files (lo->get_pathname ());
373 	  if (f)
374 	    copy_files->append (f->dbeFile);
375 	  for (long i1 = 0, sz1 = VecSize(elf->ancillary_files); i1 < sz1; i1++)
376 	    {
377 	      Elf *ancElf = elf->ancillary_files->get (i1);
378 	      copy_files->append (ancElf->dbeFile);
379 	    }
380 	}
381       Vector<Module*> *modules = lo->seg_modules;
382       for (long i1 = 0, sz1 = VecSize(modules); i1 < sz1; i1++)
383 	{
384 	  Module *mod = modules->get (i1);
385 	  if ((mod->flags & MOD_FLAG_UNKNOWN) != 0)
386 	    continue;
387 	  else if ((s_option & (ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY)) != 0 &&
388 		   !mod->isUsed)
389 	    continue;
390 	  if ((s_option & ARCH_ALL) != 0)
391 	    mod->read_stabs (false); // Find all Sources
392 	  if (mod->dot_o_file && mod->dot_o_file->dbeFile)
393 	    copy_files->append (mod->dot_o_file->dbeFile);
394 	}
395     }
396   delete loadObjs;
397 
398   int bmask = DbeFile::F_LOADOBJ | DbeFile::F_JAVACLASS | DbeFile::F_JAR_FILE |
399 	  DbeFile::F_DOT_O | DbeFile::F_DEBUG_FILE;
400   if ((s_option & (ARCH_USED_SRC_ONLY | ARCH_ALL)) != 0)
401     {
402       bmask |= DbeFile::F_JAVA_SOURCE | DbeFile::F_SOURCE;
403       Vector<SourceFile*> *sources = dbeSession->get_sources ();
404       for (long i = 0, sz = VecSize(sources); i < sz; i++)
405 	{
406 	  SourceFile *src = sources->get (i);
407 	  if ((src->flags & SOURCE_FLAG_UNKNOWN) != 0)
408 	    continue;
409 	  else if ((s_option & ARCH_USED_SRC_ONLY) != 0)
410 	    {
411 	      if ((src->dbeFile->filetype & DbeFile::F_JAVA_SOURCE) != 0 &&
412 		  !src->isUsed)
413 		continue;
414 	    }
415 	  if (src->dbeFile)
416 	    copy_files->append (src->dbeFile);
417 	}
418     }
419 
420   Vector <DbeFile*> *notfound_files = new Vector<DbeFile*>();
421   for (long i = 0, sz = VecSize(copy_files); i < sz; i++)
422     {
423       DbeFile *df = copy_files->get (i);
424       char *fnm = df->get_location ();
425       char *nm = df->get_name ();
426       Dprintf (DEBUG_ARCHIVE,
427 	       "%s::%d copy_files[%ld] filetype=%4d inArchive=%d '%s' --> '%s'\n",
428 	       get_basename (__FILE__), (int) __LINE__, i,
429 	       df->filetype, df->inArchive ? 1 : 0, STR (nm), STR (fnm));
430       Dprintf (DEBUG_ARCHIVE && df->container,
431 	       "    copy_files[%ld]: Found '%s' in '%s'\n",
432 	       i, STR (nm), STR (df->container->get_name ()));
433       if (fnm == NULL)
434 	{
435 	  if (!quiet)
436 	    notfound_files->append (df);
437 	  continue;
438 	}
439       else if (df->inArchive)
440 	{
441 	  Dprintf (DEBUG_ARCHIVE,
442 		   "  NOT COPIED: copy_files[%ld]: inArchive=1 '%s'\n",
443 		   i, STR (nm));
444 	  continue;
445 	}
446       else if ((df->filetype & bmask) == 0)
447 	{
448 	  Dprintf (DEBUG_ARCHIVE,
449 		   "  NOT COPIED: copy_files[%ld]: container=%p filetype=%d bmask=%d '%s'\n",
450 		   i, df->container, df->filetype, bmask, STR (nm));
451 	  continue;
452 	}
453       else if (df->container &&
454 	       (df->filetype & (DbeFile::F_JAVA_SOURCE | DbeFile::F_SOURCE)) == 0)
455 	{
456 	  Dprintf (DEBUG_ARCHIVE,
457 		   "  NOT COPIED: copy_files[%ld]: container=%p filetype=%d bmask=%d '%s'\n",
458 		   i, df->container, df->filetype, bmask, STR (nm));
459 	  continue;
460 	}
461       else if (!mask_is_on (df->get_name ()))
462 	{
463 	  Dprintf (DEBUG_ARCHIVE,
464 		   "  NOT COPIED: copy_files[%ld]: mask is off for '%s'\n",
465 		   i, STR (nm));
466 	  continue;
467 	}
468       char *anm = founder_exp->getNameInArchive (nm, false);
469       if (force)
470 	unlink (anm);
471       int res = founder_exp->copy_file (fnm, anm, quiet, common_archive_dir, use_relative_path);
472       if (0 == res)  // file successfully archived
473 	df->inArchive = 1;
474       delete anm;
475     }
476   delete copy_files;
477 
478   if (notfound_files->size () > 0)
479     {
480       for (long i = 0, sz = notfound_files->size (); i < sz; i++)
481 	{
482 	  DbeFile *df = notfound_files->get (i);
483 	  fprintf (stderr, GTXT ("gp-archive: Cannot find file: `%s'\n"), df->get_name ());
484 	}
485       fprintf (stderr,
486         GTXT ("\n If you know the correct location of the missing file(s)"
487 	      " you can help gp-archive to find them by manually editing"
488 	      " the .gprofng.rc file."
489 	      " See the gp-archive man page for more details.\n"));
490     }
491   delete notfound_files;
492 }
493 
494 int
check_args(int argc,char * argv[])495 er_archive::check_args (int argc, char *argv[])
496 {
497   int opt;
498   int rseen = 0;
499   int dseen = 0;
500   // Parsing the command line
501   opterr = 0;
502   optind = 1;
503   static struct option long_options[] = {
504     {"help",    no_argument,        0, 'h'},
505     {"version", no_argument,        0, 'V'},
506     {"whoami",  required_argument,  0, 'w'},
507     {"outfile", required_argument,  0, 'O'},
508     {NULL, 0, 0, 0}
509   };
510   while (1)
511     {
512       int option_index = 0;
513       opt = getopt_long (argc, argv, NTXT (":VFa:d:qnr:m:"),
514 			 long_options, &option_index);
515       if (opt == EOF)
516 	break;
517       switch (opt)
518 	{
519 	case 'F':
520 	  force = 1;
521 	  break;
522 	case 'd': // Common archive directory (absolute path)
523 	  if (rseen)
524 	    {
525 	      fprintf (stderr, GTXT ("Error: invalid combination of options: -r and -d are in conflict.\n"));
526 	      return -1;
527 	    }
528 	  if (dseen)
529 	    fprintf (stderr, GTXT ("Warning: option -d was specified several times. Last value is used.\n"));
530 	  free (common_archive_dir);
531 	  common_archive_dir = strdup (optarg);
532 	  dseen = 1;
533 	  break;
534 	case 'q':
535 	  quiet = 1;
536 	  break;
537 	case 'n':
538 	  descendant = 0;
539 	  break;
540 	case 'r': // Common archive directory (relative path)
541 	  if (dseen)
542 	    {
543 	      fprintf (stderr, GTXT ("Error: invalid combination of options: -d and -r are in conflict.\n"));
544 	      return -1;
545 	    }
546 	  if (rseen)
547 	    fprintf (stderr, GTXT ("Warning: option -r was specified several times. Last value is used.\n"));
548 	  free (common_archive_dir);
549 	  common_archive_dir = strdup (optarg);
550 	  use_relative_path = 1;
551 	  rseen = 1;
552 	  break;
553 	case 'a':
554 	  if (strcmp (optarg, "off") == 0)
555 	    s_option = ARCH_NOTHING;
556 	  else if (strcmp (optarg, "on") == 0 ||
557 		   strcmp (optarg, "ldobjects") == 0)
558 	    s_option = ARCH_EXE_ONLY;
559 	  else if (strcmp (optarg, "usedldobjects") == 0)
560 	    s_option = ARCH_USED_EXE_ONLY;
561 	  else if (strcmp (optarg, "usedsrc") == 0)
562 	    s_option = ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY;
563 	  else if (strcmp (optarg, "all") == 0 || strcmp (optarg, "src") == 0)
564 	    s_option = ARCH_ALL;
565 	  else
566 	    {
567 	      fprintf (stderr, GTXT ("Error: invalid option: `-%c %s'\n"),
568 		       optopt, optarg);
569 	      return -1;
570 	    }
571 	  break;
572 	case 'm':
573 	  {
574 	    regex_t *regex_desc = new regex_t ();
575 	    if (regcomp (regex_desc, optarg, REG_EXTENDED | REG_NOSUB | REG_NEWLINE))
576 	      {
577 		delete regex_desc;
578 		fprintf (stderr, GTXT ("Error: invalid option: `-%c %s'\n"),
579 			 optopt, optarg);
580 		return -1;
581 	      }
582 	    if (mask == NULL)
583 	      mask = new Vector<regex_t *>();
584 	    mask->append (regex_desc);
585 	    break;
586 	  }
587 	case 'O':
588 	  {
589 	    int fd = open (optarg, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
590 	    if (fd == -1)
591 	      {
592 		fprintf (stderr, GTXT ("gp-archive: Can't open %s: %s\n"),
593 			 optarg, strerror (errno));
594 		break;
595 	      }
596 	    if (dup2 (fd, 2) == -1)
597 	      {
598 		close (fd);
599 		fprintf (stderr, GTXT ("gp-archive: Can't divert stderr: %s\n"),
600 			 strerror (errno));
601 		break;
602 	      }
603 	    if (dup2 (fd, 1) == -1)
604 	      {
605 		close (fd);
606 		fprintf (stderr, GTXT ("gp-archive: Can't divert stdout: %s\n"),
607 			 strerror (errno));
608 		break;
609 	      }
610 	    close (fd);
611 	    struct timeval tp;
612 	    gettimeofday (&tp, NULL);
613 	    fprintf (stderr, "### Start %s#", ctime (&tp.tv_sec));
614 	    for (int i = 0; i < argc; i++)
615 	      fprintf (stderr, " %s", argv[i]);
616 	    fprintf (stderr, "\n");
617 	    break;
618 	  }
619 	case 'V':
620 // Ruud
621 	  Application::print_version_info ();
622 /*
623 	  printf (GTXT ("GNU %s version %s\n"), get_basename (prog_name), VERSION);
624 */
625 	  exit (0);
626 	case 'w':
627 	  whoami = optarg;
628 	  break;
629 	case 'h':
630 	  usage ();
631 	  exit (0);
632 	case ':': // -s -m without operand
633 	  fprintf (stderr, GTXT ("Option -%c requires an operand\n"), optopt);
634 	  return -1;
635 	case '?':
636 	default:
637 	  fprintf (stderr, GTXT ("Unrecognized option: -%c\n"), optopt);
638 	  return -1;
639 	}
640     }
641   return optind;
642 }
643 
644 void
check_env_var()645 er_archive::check_env_var ()
646 {
647   char *ename = NTXT ("GPROFNG_ARCHIVE");
648   char *var = getenv (ename);
649   if (var == NULL)
650     return;
651   var = dbe_strdup (var);
652   Vector<char*> *opts = new Vector<char*>();
653   opts->append (ename);
654   for (char *s = var;;)
655     {
656       while (*s && isblank (*s))
657 	s++;
658       if (*s == 0)
659 	break;
660       opts->append (s);
661       while (*s && !isblank (*s))
662 	s++;
663       if (*s == 0)
664 	break;
665       *s = 0;
666       s++;
667     }
668   if (opts->size () > 0)
669     {
670       char **arr = (char **) malloc (sizeof (char *) *opts->size ());
671       for (long i = 0; i < opts->size (); i++)
672 	arr[i] = opts->get (i);
673       if (-1 == check_args (opts->size (), arr))
674 	fprintf (stderr, GTXT ("Error: Wrong SP_ER_ARCHIVE: '%s'\n"), var);
675       free (arr);
676     }
677   delete opts;
678   free (var);
679 }
680 
681 static int
real_main(int argc,char * argv[])682 real_main (int argc, char *argv[])
683 {
684   er_archive *archive = new er_archive (argc, argv);
685   dbeSession->archive_mode = 1;
686   archive->start (argc, argv);
687   dbeSession->unlink_tmp_files ();
688   return 0;
689 }
690 
691 /**
692  * Call catch_out_of_memory(int (*real_main)(int, char*[]), int argc, char *argv[]) which will call real_main()
693  * @param argc
694  * @param argv
695  * @return
696  */
697 int
main(int argc,char * argv[])698 main (int argc, char *argv[])
699 {
700   return catch_out_of_memory (real_main, argc, argv);
701 }
702