1 /* Copyright (C) 2021 Free Software Foundation, Inc.
4 This file is part of GNU Binutils.
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)
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.
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. */
28 #include "StringMap.h"
29 #include "LoadObject.h"
30 #include "DbeSession.h"
32 #include "SourceFile.h"
34 #include "gp-archive.h"
35 #include "ArchiveExp.h"
39 er_archive::er_archive (int argc, char *argv[]) : DbeApplication (argc, argv)
42 common_archive_dir = NULL;
45 use_relative_path = 0;
46 s_option = ARCH_EXE_ONLY;
50 er_archive::~er_archive ()
54 for (long i = 0, sz = mask->size (); i < sz; i++)
56 regex_t *regex_desc = mask->get (i);
62 delete common_archive_dir;
66 er_archive::mask_is_on (const char *str)
70 for (long i = 0, sz = mask->size (); i < sz; i++)
72 regex_t *regex_desc = mask->get (i);
73 if (regexec (regex_desc, str, 0, NULL, 0) == 0)
83 fprintf (stderr, GTXT ("Usage: %s [-nqFV] [-a on|ldobjects|src|usedldobjects|usedsrc|off] [-m regexp] experiment\n"), whoami);
87 Ruud - Isolate this line because it has an argument. Otherwise it would be at the
88 end of this long list.
91 "Usage: gprofng archive [OPTION(S)] EXPERIMENT\n"));
95 "Archive the associated application binaries and source files in a gprofng\n"
96 "experiment to make it self contained and portable.\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"
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"
113 " -n archive the named experiment only, not any of its descendants.\n"
115 " -q do not write any warnings to stderr; messages are archived and\n"
116 " can be retrieved later.\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"
121 " -d <path> specifies the location of a common archive; this is a directory that\n"
122 " contains archived files.\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"
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"
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"
142 "gprofng(1), gp-collect-app(1), gp-display-html(1), gp-display-src(1), gp-display-text(1)\n"));
145 fprintf (stderr, GTXT ("GNU %s version %s\n"), get_basename (prog_name), VERSION);
150 Vector <LoadObject*> *
151 er_archive::get_loadObjs ()
153 Vector <LoadObject*> *objs = new Vector<LoadObject*>();
154 Vector <LoadObject*> *loadObjs = dbeSession->get_text_segments ();
155 if (s_option != ARCH_NOTHING)
157 for (long i = 0, sz = VecSize(loadObjs); i < sz; i++)
159 LoadObject *lo = loadObjs->get (i);
160 if ((lo->flags & SEG_FLAG_DYNAMIC) != 0)
162 DbeFile *df = lo->dbeFile;
163 if (df && ((df->filetype & DbeFile::F_FICTION) != 0))
165 if (!lo->isUsed && ((s_option & (ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY)) != 0))
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++)
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 ()));
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++)
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 ()));
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)
202 * @return 0 - success
205 er_archive::clean_old_archive (char *expname, ArchiveExp *founder_exp)
208 { // do not archive descendants
209 fprintf (stderr, GTXT ("Warning: Option -F is ignored because -n option is specified (do not archive descendants)\n"));
213 { // partial archiving
214 fprintf (stderr, GTXT ("Warning: Option -F is ignored because -m option is specified\n"));
217 // Check if the experiment is the founder
218 char *s1 = dbe_strdup (expname);
219 char *s2 = dbe_strdup (founder_exp->get_expt_name ());
222 fprintf (stderr, GTXT ("Cannot allocate memory\n"));
225 // remove trailing slashes
226 for (int n = strlen (s1); n > 0; n--)
228 if ('/' != s1[n - 1])
232 for (int n = strlen (s2); n > 0; n--)
234 if ('/' != s2[n - 1])
238 if (strcmp (s1, s2) != 0)
240 fprintf (stderr, GTXT ("Warning: Option -F is ignored because specified experiment name %s does not match founder experiment name %s\n"), s1, s2);
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)
250 char *cmd = dbe_sprintf ("/bin/rm -rf %s", arch);
253 if (dbe_stat (arch, NULL) != 0)
254 { // create "archives"
255 if (!founder_exp->create_dir (founder_exp->get_arch_name ()))
257 fprintf (stderr, GTXT ("Unable to create directory `%s'\n"), founder_exp->get_arch_name ());
265 } // clean_old_archive_if_necessary
268 er_archive::start (int argc, char *argv[])
271 if (check_args (argc, argv) != last)
274 if (s_option == ARCH_NOTHING)
277 ArchiveExp *founder_exp = new ArchiveExp (argv[last]);
278 if (founder_exp->get_status () == Experiment::FAILURE)
281 fprintf (stderr, GTXT ("er_archive: %s: %s\n"), argv[last],
282 pr_mesgs (founder_exp->fetch_errors (), NTXT (""), NTXT ("")));
285 if (!founder_exp->create_dir (founder_exp->get_arch_name ()))
287 fprintf (stderr, GTXT ("Unable to create directory `%s'\n"), founder_exp->get_arch_name ());
290 if (!common_archive_dir)
291 common_archive_dir = dbe_strdup (getenv ("GPROFNG_ARCHIVE_COMMON_DIR"));
292 if (common_archive_dir)
294 if (!founder_exp->create_dir (common_archive_dir))
295 if (dbe_stat (common_archive_dir, NULL) != 0)
297 fprintf (stderr, GTXT ("Unable to create directory for common archive `%s'\n"), common_archive_dir);
301 // Clean old archives if necessary
303 clean_old_archive (argv[last], founder_exp);
304 Vector<ArchiveExp*> *exps = new Vector<ArchiveExp*>();
305 exps->append (founder_exp);
308 Vector<char*> *exp_names = founder_exp->get_descendants_names ();
311 for (long i = 0, sz = exp_names->size (); i < sz; i++)
313 char *exp_path = exp_names->get (i);
314 ArchiveExp *exp = new ArchiveExp (exp_path);
315 if (exp->get_status () == Experiment::FAILURE)
318 fprintf (stderr, GTXT ("er_archive: %s: %s\n"), exp_path,
319 pr_mesgs (exp->fetch_errors (), NTXT (""), NTXT ("")));
325 exp_names->destroy ();
329 for (long i = 0, sz = exps->size (); i < sz; i++)
331 ArchiveExp *exp = exps->get (i);
332 exp->read_data (s_option);
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++)
339 LoadObject *lo = loadObjs->get (i);
340 if (strcmp (lo->get_pathname (), "LinuxKernel") == 0)
342 DbeFile *df = lo->dbeFile;
343 if ((df->filetype & DbeFile::F_FICTION) != 0)
345 if (df->get_location () == NULL)
347 copy_files->append (df);
350 if ((df->filetype & DbeFile::F_JAVACLASS) != 0)
353 { // Found in .jar file
354 copy_files->append (df->container);
356 copy_files->append (df);
357 if ((s_option & ARCH_EXE_ONLY) != 0)
360 lo->sync_read_stabs ();
361 Elf *elf = lo->get_elf ();
362 if (elf && (lo->checksum != 0) && (lo->checksum != elf->elf_checksum ()))
365 fprintf (stderr, GTXT ("er_archive: '%s' has an unexpected checksum value; perhaps it was rebuilt. File ignored\n"),
366 df->get_location ());
369 copy_files->append (df);
372 Elf *f = elf->find_ancillary_files (lo->get_pathname ());
374 copy_files->append (f->dbeFile);
375 for (long i1 = 0, sz1 = VecSize(elf->ancillary_files); i1 < sz1; i1++)
377 Elf *ancElf = elf->ancillary_files->get (i1);
378 copy_files->append (ancElf->dbeFile);
381 Vector<Module*> *modules = lo->seg_modules;
382 for (long i1 = 0, sz1 = VecSize(modules); i1 < sz1; i1++)
384 Module *mod = modules->get (i1);
385 if ((mod->flags & MOD_FLAG_UNKNOWN) != 0)
387 else if ((s_option & (ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY)) != 0 &&
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);
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)
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++)
406 SourceFile *src = sources->get (i);
407 if ((src->flags & SOURCE_FLAG_UNKNOWN) != 0)
409 else if ((s_option & ARCH_USED_SRC_ONLY) != 0)
411 if ((src->dbeFile->filetype & DbeFile::F_JAVA_SOURCE) != 0 &&
416 copy_files->append (src->dbeFile);
420 Vector <DbeFile*> *notfound_files = new Vector<DbeFile*>();
421 for (long i = 0, sz = VecSize(copy_files); i < sz; i++)
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 ()));
436 notfound_files->append (df);
439 else if (df->inArchive)
441 Dprintf (DEBUG_ARCHIVE,
442 " NOT COPIED: copy_files[%ld]: inArchive=1 '%s'\n",
446 else if ((df->filetype & bmask) == 0)
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));
453 else if (df->container &&
454 (df->filetype & (DbeFile::F_JAVA_SOURCE | DbeFile::F_SOURCE)) == 0)
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));
461 else if (!mask_is_on (df->get_name ()))
463 Dprintf (DEBUG_ARCHIVE,
464 " NOT COPIED: copy_files[%ld]: mask is off for '%s'\n",
468 char *anm = founder_exp->getNameInArchive (nm, false);
471 int res = founder_exp->copy_file (fnm, anm, quiet, common_archive_dir, use_relative_path);
472 if (0 == res) // file successfully archived
478 if (notfound_files->size () > 0)
480 for (long i = 0, sz = notfound_files->size (); i < sz; i++)
482 DbeFile *df = notfound_files->get (i);
483 fprintf (stderr, GTXT ("er_archive: Cannot find file: `%s'\n"), df->get_name ());
485 fprintf (stderr, GTXT ("\n If you know the correct location of the missing file(s)"
486 " you can help %s to find them by manually editing the .gprofng.rc file."
487 " See %s man pages for more details.\n"),
490 delete notfound_files;
494 er_archive::check_args (int argc, char *argv[])
499 // Parsing the command line
502 static struct option long_options[] = {
503 {"help", no_argument, 0, 'h'},
504 {"version", no_argument, 0, 'V'},
505 {"whoami", required_argument, 0, 'w'},
506 {"outfile", required_argument, 0, 'O'},
511 int option_index = 0;
512 opt = getopt_long (argc, argv, NTXT (":VFa:d:qnr:m:"),
513 long_options, &option_index);
521 case 'd': // Common archive directory (absolute path)
524 fprintf (stderr, GTXT ("Error: invalid combination of options: -r and -d are in conflict.\n"));
528 fprintf (stderr, GTXT ("Warning: option -d was specified several times. Last value is used.\n"));
529 free (common_archive_dir);
530 common_archive_dir = strdup (optarg);
539 case 'r': // Common archive directory (relative path)
542 fprintf (stderr, GTXT ("Error: invalid combination of options: -d and -r are in conflict.\n"));
546 fprintf (stderr, GTXT ("Warning: option -r was specified several times. Last value is used.\n"));
547 free (common_archive_dir);
548 common_archive_dir = strdup (optarg);
549 use_relative_path = 1;
553 if (strcmp (optarg, "off") == 0)
554 s_option = ARCH_NOTHING;
555 else if (strcmp (optarg, "on") == 0 ||
556 strcmp (optarg, "ldobjects") == 0)
557 s_option = ARCH_EXE_ONLY;
558 else if (strcmp (optarg, "usedldobjects") == 0)
559 s_option = ARCH_USED_EXE_ONLY;
560 else if (strcmp (optarg, "usedsrc") == 0)
561 s_option = ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY;
562 else if (strcmp (optarg, "all") == 0 || strcmp (optarg, "src") == 0)
566 fprintf (stderr, GTXT ("Error: invalid option: `-%c %s'\n"),
573 regex_t *regex_desc = new regex_t ();
574 if (regcomp (regex_desc, optarg, REG_EXTENDED | REG_NOSUB | REG_NEWLINE))
577 fprintf (stderr, GTXT ("Error: invalid option: `-%c %s'\n"),
582 mask = new Vector<regex_t *>();
583 mask->append (regex_desc);
588 int fd = open (optarg, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
591 fprintf (stderr, GTXT ("er_archive: Can't open %s: %s\n"),
592 optarg, strerror (errno));
595 if (dup2 (fd, 2) == -1)
598 fprintf (stderr, GTXT ("er_archive: Can't divert stderr: %s\n"),
602 if (dup2 (fd, 1) == -1)
605 fprintf (stderr, GTXT ("er_archive: Can't divert stdout: %s\n"),
611 gettimeofday (&tp, NULL);
612 fprintf (stderr, "### Start %s#", ctime (&tp.tv_sec));
613 for (int i = 0; i < argc; i++)
614 fprintf (stderr, " %s", argv[i]);
615 fprintf (stderr, "\n");
620 Application::print_version_info ();
622 printf (GTXT ("GNU %s version %s\n"), get_basename (prog_name), VERSION);
631 case ':': // -s -m without operand
632 fprintf (stderr, GTXT ("Option -%c requires an operand\n"), optopt);
636 fprintf (stderr, GTXT ("Unrecognized option: -%c\n"), optopt);
644 er_archive::check_env_var ()
646 char *ename = NTXT ("GPROFNG_ARCHIVE");
647 char *var = getenv (ename);
650 var = dbe_strdup (var);
651 Vector<char*> *opts = new Vector<char*>();
652 opts->append (ename);
653 for (char *s = var;;)
655 while (*s && isblank (*s))
660 while (*s && !isblank (*s))
667 if (opts->size () > 0)
669 char **arr = (char **) malloc (sizeof (char *) *opts->size ());
670 for (long i = 0; i < opts->size (); i++)
671 arr[i] = opts->get (i);
672 if (-1 == check_args (opts->size (), arr))
673 fprintf (stderr, GTXT ("Error: Wrong SP_ER_ARCHIVE: '%s'\n"), var);
681 real_main (int argc, char *argv[])
683 er_archive *archive = new er_archive (argc, argv);
684 dbeSession->archive_mode = 1;
685 archive->start (argc, argv);
686 dbeSession->unlink_tmp_files ();
691 * Call catch_out_of_memory(int (*real_main)(int, char*[]), int argc, char *argv[]) which will call real_main()
697 main (int argc, char *argv[])
699 return catch_out_of_memory (real_main, argc, argv);