diff options
author | Vladimir Mezentsev <vladimir.mezentsev@oracle.com> | 2022-03-11 08:58:31 +0000 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2022-03-11 08:58:31 +0000 |
commit | bb368aad297fe3ad40cf397e6fc85aa471429a28 (patch) | |
tree | 0ab25909b8fe789d676bbdb00d501d4d485e4afe /gprofng/src/checks.cc | |
parent | a655f19af95eb685ba64f48ee8fc2b3b7a3d886a (diff) | |
download | gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.zip gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.gz gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.bz2 |
gprofng: a new GNU profiler
top-level
* Makefile.def: Add gprofng module.
* configure.ac: Add --enable-gprofng option.
* src-release.sh: Add gprofng.
* Makefile.in: Regenerate.
* configure: Regenerate.
* gprofng: New directory.
binutils
* MAINTAINERS: Add gprofng maintainer.
* README-how-to-make-a-release: Add gprofng.
include.
* collectorAPI.h: New file.
* libcollector.h: New file.
* libfcollector.h: New file.
Diffstat (limited to 'gprofng/src/checks.cc')
-rw-r--r-- | gprofng/src/checks.cc | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/gprofng/src/checks.cc b/gprofng/src/checks.cc new file mode 100644 index 0000000..105821e --- /dev/null +++ b/gprofng/src/checks.cc @@ -0,0 +1,516 @@ +/* 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); +} |