mirror of
https://github.com/autc04/Retro68.git
synced 2024-06-08 04:29:46 +00:00
517 lines
14 KiB
C++
517 lines
14 KiB
C++
/* Copyright (C) 2021 Free Software Foundation, Inc.
|
|
Contributed by Oracle.
|
|
|
|
This file is part of GNU Binutils.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, 51 Franklin Street - Fifth Floor, Boston,
|
|
MA 02110-1301, USA. */
|
|
|
|
#include "config.h"
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include <sys/utsname.h>
|
|
#include <sys/param.h>
|
|
|
|
#include "gp-defs.h"
|
|
#include "Elf.h"
|
|
#include "collctrl.h"
|
|
#include "i18n.h"
|
|
#include "util.h"
|
|
#include "collect.h"
|
|
|
|
void
|
|
collect::check_target (int argc, char **argv)
|
|
{
|
|
char *next;
|
|
char *last = 0;
|
|
char *a;
|
|
char *ccret;
|
|
char **lasts = &last;
|
|
int tindex = targ_index;
|
|
int ret;
|
|
char *basename;
|
|
is_64 = false;
|
|
|
|
/* now check the executable */
|
|
nargs = argc - targ_index;
|
|
Exec_status rv = check_executable (argv[targ_index]);
|
|
switch (rv)
|
|
{
|
|
case EXEC_OK:
|
|
njargs = cc->get_java_arg_cnt ();
|
|
arglist = (char **) calloc (nargs + 5 + njargs, sizeof (char *));
|
|
jargs = cc->get_java_args ();
|
|
|
|
// store the first argument -- target name
|
|
ret = 0;
|
|
arglist[ret++] = argv[tindex++];
|
|
if (cc->get_java_mode () == 1)
|
|
{
|
|
// add any user-specified -J (Java) arguments
|
|
int length = (int) strlen (argv[targ_index]);
|
|
int is_java = 0;
|
|
if ((length >= 6) && strcmp (&argv[targ_index][length - 5], NTXT ("/java")) == 0)
|
|
is_java = 1;
|
|
else if ((length == 4) && strcmp (&argv[targ_index][0], NTXT ("java")) == 0)
|
|
is_java = 1;
|
|
if (njargs != 0 && is_java)
|
|
{
|
|
next = strtok_r (jargs, NTXT (" \t"), lasts);
|
|
arglist[ret++] = next;
|
|
for (;;)
|
|
{
|
|
next = strtok_r (NULL, NTXT (" \t"), lasts);
|
|
if (next == NULL)
|
|
break;
|
|
arglist[ret++] = next;
|
|
}
|
|
}
|
|
}
|
|
|
|
// copy the rest of the arguments
|
|
for (int i = 1; i < nargs; i++)
|
|
arglist[ret++] = argv[tindex++];
|
|
nargs = ret;
|
|
break;
|
|
case EXEC_IS_JAR:
|
|
// Preface the user-supplied argument list with
|
|
// the path to the java, the collector invocation,
|
|
// any -J java arguments provided, and "-jar".
|
|
ccret = cc->set_java_mode (NTXT ("on"));
|
|
if (ccret != NULL)
|
|
{
|
|
writeStr (2, ccret);
|
|
exit (1);
|
|
}
|
|
njargs = cc->get_java_arg_cnt ();
|
|
arglist = (char **) calloc (nargs + 5 + njargs, sizeof (char *));
|
|
jargs = cc->get_java_args ();
|
|
|
|
a = find_java ();
|
|
if (a == NULL)
|
|
exit (1); // message was written
|
|
ret = 0;
|
|
arglist[ret++] = a;
|
|
// add any user-specified Java arguments
|
|
if (njargs != 0)
|
|
{
|
|
next = strtok_r (jargs, NTXT (" \t"), lasts);
|
|
arglist[ret++] = next;
|
|
for (;;)
|
|
{
|
|
next = strtok_r (NULL, NTXT (" \t"), lasts);
|
|
if (next == NULL)
|
|
break;
|
|
arglist[ret++] = next;
|
|
}
|
|
}
|
|
arglist[ret++] = NTXT ("-jar");
|
|
for (int i = 0; i < nargs; i++)
|
|
arglist[ret++] = argv[tindex++];
|
|
nargs = ret;
|
|
break;
|
|
case EXEC_IS_CLASSCLASS:
|
|
// remove the .class from the name
|
|
ret = (int) strlen (argv[targ_index]);
|
|
argv[targ_index][ret - 6] = 0;
|
|
|
|
// now fall through to the EXEC_IS_CLASS case
|
|
case EXEC_IS_CLASS:
|
|
// Preface the user-supplied argument list with
|
|
// the path to the java, the collector invocation,
|
|
// and any -J java arguments provided.
|
|
ccret = cc->set_java_mode (NTXT ("on"));
|
|
if (ccret != NULL)
|
|
{
|
|
writeStr (2, ccret);
|
|
exit (1);
|
|
}
|
|
jargs = cc->get_java_args ();
|
|
njargs = cc->get_java_arg_cnt ();
|
|
arglist = (char **) calloc (nargs + 4 + njargs, sizeof (char *));
|
|
|
|
a = find_java ();
|
|
if (a == NULL)
|
|
exit (1); // message was written
|
|
ret = 0;
|
|
arglist[ret++] = a;
|
|
// add any user-specified Java arguments
|
|
if (njargs != 0)
|
|
{
|
|
next = strtok_r (jargs, NTXT (" \t"), lasts);
|
|
arglist[ret++] = next;
|
|
for (;;)
|
|
{
|
|
next = strtok_r (NULL, NTXT (" \t"), lasts);
|
|
if (next == NULL)
|
|
break;
|
|
arglist[ret++] = next;
|
|
}
|
|
}
|
|
|
|
// copy the remaining arguments to the new list
|
|
for (int i = 0; i < nargs; i++)
|
|
arglist[ret++] = argv[tindex++];
|
|
nargs = ret;
|
|
break;
|
|
case EXEC_ELF_NOSHARE:
|
|
case EXEC_OPEN_FAIL:
|
|
case EXEC_ELF_LIB:
|
|
case EXEC_ELF_HEADER:
|
|
case EXEC_ELF_ARCH:
|
|
case EXEC_ISDIR:
|
|
case EXEC_NOT_EXEC:
|
|
case EXEC_NOT_FOUND:
|
|
default:
|
|
/* something wrong; write a message */
|
|
char *errstr = status_str (rv, argv[targ_index]);
|
|
if (errstr)
|
|
{
|
|
dbe_write (2, "%s", errstr);
|
|
free (errstr);
|
|
}
|
|
exit (1);
|
|
}
|
|
cc->set_target (arglist[0]);
|
|
|
|
/* check the experiment */
|
|
char *ccwarn;
|
|
ccret = cc->check_expt (&ccwarn);
|
|
if (ccwarn != NULL)
|
|
{
|
|
writeStr (2, ccwarn);
|
|
free (ccwarn);
|
|
}
|
|
if (ccret != NULL)
|
|
{
|
|
writeStr (2, ccret);
|
|
exit (1);
|
|
}
|
|
/* check if java, to see if -j flag was given */
|
|
if ((basename = strrchr (arglist[0], '/')) == NULL)
|
|
basename = arglist[0];
|
|
else
|
|
basename++;
|
|
if (strcmp (basename, NTXT ("java")) == 0)
|
|
{
|
|
/* the target's name is java; was java flag set? */
|
|
if ((jseen_global == 0) && (cc->get_java_mode () == 0))
|
|
{
|
|
char *cret = cc->set_java_mode (NTXT ("on"));
|
|
if (cret != NULL)
|
|
{
|
|
writeStr (2, cret);
|
|
exit (1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
collect::Exec_status
|
|
collect::check_executable (char *target_name)
|
|
{
|
|
char target_path[MAXPATHLEN];
|
|
struct stat64 statbuf;
|
|
if (target_name == NULL) // not set, but assume caller knows what it's doing
|
|
return EXEC_OK;
|
|
if (getenv ("GPROFNG_SKIP_VALIDATION")) // don't check target
|
|
return EXEC_OK;
|
|
|
|
// see if target exists and is not a directory
|
|
if ((dbe_stat (target_name, &statbuf) == 0) && ((statbuf.st_mode & S_IFMT) != S_IFDIR))
|
|
{
|
|
// target is found, check for access as executable
|
|
if (access (target_name, X_OK) != 0)
|
|
{
|
|
// not an executable, check for jar or class file
|
|
int i = (int) strlen (target_name);
|
|
if ((i >= 5) && strcmp (&target_name[i - 4], NTXT (".jar")) == 0)
|
|
{
|
|
// could be a jar file
|
|
// XXXX -- need better check for real jar file
|
|
cc->set_java_mode ("on");
|
|
return EXEC_IS_JAR;
|
|
}
|
|
if ((i >= 7) && strcmp (&target_name[i - 6], NTXT (".class")) == 0)
|
|
{
|
|
// could be a class file
|
|
// XXXX -- need better check for real class file
|
|
cc->set_java_mode (NTXT ("on"));
|
|
return EXEC_IS_CLASSCLASS;
|
|
}
|
|
// not a jar or class file, return not an executable
|
|
return EXEC_NOT_EXEC;
|
|
}
|
|
else // found, and it is executable. set the path to it
|
|
snprintf (target_path, sizeof (target_path), NTXT ("%s"), target_name);
|
|
}
|
|
else
|
|
{
|
|
// not found, look on path
|
|
char *exe_name = get_realpath (target_name);
|
|
if (access (exe_name, X_OK) == 0)
|
|
{
|
|
// target can't be located
|
|
// one last attempt: append .class to name, and see if we can find it
|
|
snprintf (target_path, sizeof (target_path), NTXT ("%s.class"), target_name);
|
|
if (dbe_stat (target_path, &statbuf) == 0)
|
|
{
|
|
// the file exists
|
|
if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
|
|
{
|
|
// this is a directory; that won't do.
|
|
return EXEC_ISDIR;
|
|
}
|
|
// say it's a class file
|
|
cc->set_java_mode (NTXT ("on"));
|
|
return EXEC_IS_CLASS;
|
|
}
|
|
return EXEC_NOT_FOUND;
|
|
}
|
|
snprintf (target_path, sizeof (target_path), NTXT ("%s"), exe_name);
|
|
delete exe_name;
|
|
}
|
|
|
|
// target_path is now the purported executable
|
|
// check for ELF library out of date
|
|
if (Elf::elf_version (EV_CURRENT) == EV_NONE)
|
|
return EXEC_ELF_LIB;
|
|
Elf *elf = Elf::elf_begin (target_path);
|
|
if (elf == NULL)
|
|
return EXEC_OK;
|
|
// do not by pass checking architectural match
|
|
collect::Exec_status exec_stat = check_executable_arch (elf);
|
|
if (exec_stat != EXEC_OK)
|
|
{
|
|
delete elf;
|
|
return exec_stat;
|
|
}
|
|
delete elf;
|
|
return EXEC_OK;
|
|
}
|
|
|
|
collect::Exec_status
|
|
collect::check_executable_arch (Elf *elf)
|
|
{
|
|
Elf_Internal_Ehdr *ehdrp = elf->elf_getehdr ();
|
|
if (ehdrp == NULL)
|
|
return EXEC_ELF_HEADER;
|
|
unsigned short machine = ehdrp->e_machine;
|
|
|
|
switch (machine)
|
|
{
|
|
#if ARCH(SPARC)
|
|
case EM_SPARC:
|
|
case EM_SPARC32PLUS:
|
|
break;
|
|
case EM_SPARCV9:
|
|
is_64 = true;
|
|
break;
|
|
#elif ARCH(Intel)
|
|
case EM_X86_64:
|
|
{
|
|
is_64 = true;
|
|
// now figure out if the platform can run it
|
|
struct utsname unbuf;
|
|
int r = uname (&unbuf);
|
|
if (r == 0 && unbuf.machine && strstr (unbuf.machine, "_64") == NULL)
|
|
// machine can not run 64 bits, but this code is 64-bit
|
|
return EXEC_ELF_ARCH;
|
|
}
|
|
break;
|
|
case EM_386:
|
|
break;
|
|
#elif ARCH(Aarch64)
|
|
case EM_AARCH64:
|
|
is_64 = true;
|
|
break;
|
|
#endif
|
|
default:
|
|
return EXEC_ELF_ARCH;
|
|
}
|
|
|
|
// now check if target was built with shared libraries
|
|
int dynamic = 0;
|
|
for (unsigned cnt = 0; cnt < ehdrp->e_phnum; cnt++)
|
|
{
|
|
Elf_Internal_Phdr *phdrp = elf->get_phdr (cnt);
|
|
if (phdrp && phdrp->p_type == PT_DYNAMIC)
|
|
{
|
|
dynamic = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (dynamic == 0)
|
|
{
|
|
// target is not a dynamic executable or shared object;
|
|
// can't record data
|
|
return EXEC_ELF_NOSHARE;
|
|
}
|
|
return EXEC_OK;
|
|
}
|
|
|
|
char *
|
|
collect::status_str (Exec_status rv, char *target_name)
|
|
{
|
|
switch (rv)
|
|
{
|
|
case EXEC_OK:
|
|
case EXEC_IS_JAR:
|
|
case EXEC_IS_CLASS:
|
|
case EXEC_IS_CLASSCLASS:
|
|
// supported flavors -- no error message
|
|
return NULL;
|
|
case EXEC_ELF_NOSHARE:
|
|
return dbe_sprintf (GTXT ("Target executable `%s' must be built with shared libraries\n"), target_name);
|
|
case EXEC_OPEN_FAIL:
|
|
return dbe_sprintf (GTXT ("Can't open target executable `%s'\n"), target_name);
|
|
case EXEC_ELF_LIB:
|
|
return strdup (GTXT ("Internal error: Not a working version of ELF library\n"));
|
|
case EXEC_ELF_HEADER:
|
|
return dbe_sprintf (GTXT ("Target `%s' is not a valid ELF executable\n"), target_name);
|
|
case EXEC_ELF_ARCH:
|
|
return dbe_sprintf (GTXT ("Target architecture of executable `%s' is not supported on this machine\n"), target_name);
|
|
case EXEC_ISDIR:
|
|
return dbe_sprintf (GTXT ("Target `%s' is a directory, not an executable\n"), target_name);
|
|
case EXEC_NOT_EXEC:
|
|
return dbe_sprintf (GTXT ("Target `%s' is not executable\n"), target_name);
|
|
case EXEC_NOT_FOUND:
|
|
return dbe_sprintf (GTXT ("Target `%s' not found\n"), target_name);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
char *
|
|
collect::find_java (void)
|
|
{
|
|
char buf[MAXPATHLEN];
|
|
char *var = NULL;
|
|
Exec_status rv = EXEC_OK;
|
|
|
|
// first see if the user entered a -j argument
|
|
var = cc->get_java_path ();
|
|
if (var != NULL)
|
|
{
|
|
snprintf (buf, sizeof (buf), NTXT ("%s/bin/java"), var);
|
|
java_how = NTXT ("-j");
|
|
rv = check_executable (buf);
|
|
}
|
|
// then try JDK_HOME
|
|
if (java_how == NULL)
|
|
{
|
|
var = getenv (NTXT ("JDK_HOME"));
|
|
if ((var != NULL) && (strlen (var) > 0))
|
|
{
|
|
snprintf (buf, sizeof (buf), NTXT ("%s/bin/java"), var);
|
|
java_how = NTXT ("JDK_HOME");
|
|
rv = check_executable (buf);
|
|
}
|
|
}
|
|
// then try JAVA_PATH
|
|
if (java_how == NULL)
|
|
{
|
|
var = getenv (NTXT ("JAVA_PATH"));
|
|
if ((var != NULL) && (strlen (var) > 0))
|
|
{
|
|
snprintf (buf, sizeof (buf), NTXT ("%s/bin/java"), var);
|
|
java_how = NTXT ("JAVA_PATH");
|
|
rv = check_executable (buf);
|
|
}
|
|
}
|
|
// try the user's path
|
|
if (java_how == NULL)
|
|
{
|
|
snprintf (buf, sizeof (buf), NTXT ("java"));
|
|
rv = check_executable (buf);
|
|
if (rv == EXEC_OK)
|
|
java_how = NTXT ("PATH");
|
|
}
|
|
// finally, just try /usr/java -- system default
|
|
if (java_how == NULL)
|
|
{
|
|
snprintf (buf, sizeof (buf), NTXT ("/usr/java/bin/java"));
|
|
rv = check_executable (buf);
|
|
java_how = NTXT ("/usr/java/bin/java");
|
|
}
|
|
|
|
// we now have a nominal path to java, and how we chose it
|
|
// and we have rv set to the check_executable return
|
|
switch (rv)
|
|
{
|
|
case EXEC_OK:
|
|
java_path = strdup (buf);
|
|
if (verbose == 1)
|
|
dbe_write (2, GTXT ("Path to `%s' (set from %s) used for Java profiling\n"),
|
|
java_path, java_how);
|
|
return ( strdup (buf));
|
|
default:
|
|
dbe_write (2, GTXT ("Path to `%s' (set from %s) does not point to a JVM executable\n"),
|
|
buf, java_how);
|
|
break;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
collect::validate_config (int how)
|
|
{
|
|
if (getenv (NTXT ("GPROFNG_SKIP_VALIDATION")) != NULL)
|
|
return;
|
|
char *cmd = dbe_sprintf (NTXT ("%s/perftools_validate"), run_dir);
|
|
if (access (cmd, X_OK) != 0)
|
|
{
|
|
if (how)
|
|
dbe_write (2, GTXT ("WARNING: Unable to validate system: `%s' could not be executed\n"), cmd);
|
|
return;
|
|
}
|
|
char *quiet = how == 0 ? NTXT ("") : NTXT ("-q"); // check collection, verbosely
|
|
char *buf;
|
|
if (cc->get_java_default () == 0 && java_path)
|
|
buf = dbe_sprintf (NTXT ("%s -c -j %s -H \"%s\" %s"), cmd, java_path, java_how, quiet);
|
|
else // not java mode -- don't check the java version
|
|
buf = dbe_sprintf (NTXT ("%s -c %s"), cmd, quiet);
|
|
free (cmd);
|
|
|
|
/* now run the command */
|
|
int ret = system (buf);
|
|
int status = WEXITSTATUS (ret);
|
|
if ((status & 0x1) != 0)
|
|
dbe_write (2, GTXT ("WARNING: Data collection may fail: system is not properly configured or is unsupported.\n"));
|
|
if ((status & 0x2) != 0)
|
|
dbe_write (2, GTXT ("WARNING: Java data collection may fail: J2SE[tm] version is unsupported.\n"));
|
|
free (buf);
|
|
}
|
|
|
|
void
|
|
collect::validate_java (const char *jvm, const char *jhow, int q)
|
|
{
|
|
char *cmd = dbe_sprintf (NTXT ("%s/perftools_ckjava"), run_dir);
|
|
if (access (cmd, X_OK) != 0)
|
|
{
|
|
dbe_write (2, GTXT ("WARNING: Unable to validate Java: `%s' could not be executed\n"), cmd);
|
|
return;
|
|
}
|
|
char *buf = dbe_sprintf (NTXT ("%s -j %s -H \"%s\" %s"), cmd, jvm, jhow,
|
|
(q == 1 ? "-q" : ""));
|
|
free (cmd);
|
|
|
|
/* now run the command */
|
|
int ret = system (buf);
|
|
int status = WEXITSTATUS (ret);
|
|
if (status != 0)
|
|
dbe_write (2, GTXT ("WARNING: Java data collection may fail: J2SE[tm] version is unsupported.\n"));
|
|
free (buf);
|
|
}
|