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 | |
parent | a655f19af95eb685ba64f48ee8fc2b3b7a3d886a (diff) | |
download | binutils-bb368aad297fe3ad40cf397e6fc85aa471429a28.zip binutils-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.gz binutils-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')
196 files changed, 115604 insertions, 0 deletions
diff --git a/gprofng/src/ABS.h b/gprofng/src/ABS.h new file mode 100644 index 0000000..a5bcbea --- /dev/null +++ b/gprofng/src/ABS.h @@ -0,0 +1,62 @@ +/* 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. */ + +#ifndef _ABS_H +#define _ABS_H + +/* + * Apropos Backtracking Scheme definitions. + * Class: com_sun_forte_st_mpmt_timeline_HWCEvent + */ + +/* ABS failure codes */ +typedef enum +{ + ABS_NULL = 0x00, /* undefined/disabled/inactive */ + ABS_UNSUPPORTED = 0x01, /* inappropriate HWC event type */ + ABS_BLOCKED = 0x02, /* runtime backtrack blocker reached */ + ABS_INCOMPLETE = 0x03, /* runtime backtrack limit reached */ + ABS_REG_LOSS = 0x04, /* address register contaminated */ + ABS_INVALID_EA = 0x05, /* invalid effective address value */ + ABS_NO_CTI_INFO = 0x10, /* no AnalyzerInfo for validation */ + ABS_INFO_FAILED = 0x20, /* info failed to validate backtrack */ + ABS_CTI_TARGET = 0x30, /* CTI target invalidated backtrack */ + ABS_CODE_RANGE = 0xFF /* reserved ABS code range in Vaddr */ +} ABS_code; + +enum { + NUM_ABS_RT_CODES = 7, + NUM_ABS_PP_CODES = 5 +}; + +extern const char *ABS_RT_CODES[NUM_ABS_RT_CODES]; +extern char *ABS_PP_CODES[NUM_ABS_PP_CODES]; + +/* libcollector will mark HWC overflow values that appear to be invalid */ +/* dbe should check HWC values for errors */ +#define HWCVAL_ERR_FLAG (1ULL<<63) +#define HWCVAL_SET_ERR(ctr) ((ctr) | HWCVAL_ERR_FLAG) +#define HWCVAL_HAS_ERR(ctr) ((ctr) & HWCVAL_ERR_FLAG ? 1 : 0) +#define HWCVAL_CLR_ERR(ctr) ((ctr) & ~HWCVAL_ERR_FLAG) + +#define ABS_GET_RT_CODE(EA) ((EA) & 0x0FLL) +#define ABS_GET_PP_CODE(EA) (((EA) & 0xF0LL) / 0xF) + +#endif /* _ABS_H */ diff --git a/gprofng/src/Application.cc b/gprofng/src/Application.cc new file mode 100644 index 0000000..e28956c --- /dev/null +++ b/gprofng/src/Application.cc @@ -0,0 +1,259 @@ +/* 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 <stdlib.h> +#include <strings.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "Application.h" +#include "Settings.h" +#include "i18n.h" +#include "util.h" + +Application::ProgressFunc Application::progress_func = NULL; +Application *theApplication; + +Application::Application (int argc, char *argv[], char *fdhome) +{ + theApplication = this; + cur_dir = NULL; + prog_version = dbe_strdup (VERSION); + set_name (strchr (argv[0], '/') ? argv[0] : NULL); + whoami = get_basename (get_name ()); + + // set up a queue for comments + commentq = new Emsgqueue (NTXT ("app_commentq")); + + // Locate where the binaries are installed + set_run_dir (fdhome); + + // Initialize I18N + init_locale (run_dir); + + // Initialize licensing data + lic_found = 0; + lic_err = NULL; + + // Initialize worker threads + number_of_worker_threads = 1; +#if DEBUG + char *use_worker_threads = getenv (NTXT ("SP_USE_WORKER_THREADS")); + if ((NULL != use_worker_threads) && (0 == strcasecmp (use_worker_threads, NTXT ("no")))) + { + number_of_worker_threads = 0; + } +#endif /* DEBUG */ + settings = new Settings (this); +} + +Application::~Application () +{ + delete commentq; + delete settings; + free (prog_version); + free (cur_dir); + free (prog_name); + free (run_dir); +} + +// Set the name of the application (for messages) +void +Application::set_name (const char *_name) +{ + prog_name = get_realpath (_name); +} + +char * +Application::get_realpath (const char *_name) +{ + if (_name == NULL) + _name = "/proc/self/exe"; + char *exe_name = realpath (_name, NULL); + if (exe_name) + return exe_name; + if (strchr (_name, '/') == NULL) + { + char *path = getenv ("PATH"); + if (path) + for (char *s = path;; s++) + if (*s == ':' || *s == 0) + { + if (path != s) + { + char *nm = dbe_sprintf (NTXT ("%.*s/%s"), (int) (path - s - 1), path, _name); + exe_name = realpath (nm, NULL); + free (nm); + if (exe_name) + return exe_name; + } + if (*s == 0) + break; + path = s + 1; + } + } + return strdup (_name); +} + +// Set the directory where all binaries are found +void +Application::set_run_dir (char *fdhome) +{ + run_dir_with_spaces = NULL; + if (fdhome) + { + char *path = dbe_sprintf ("%s/bin", fdhome); + struct stat sbuf; + if (stat (path, &sbuf) != -1) + run_dir = path; + else + { + free (path); + run_dir = dbe_strdup (fdhome); + } + } + else + { + run_dir = realpath (prog_name, NULL); + if (run_dir == NULL) + { + fprintf (stderr, // I18N won't work here -- not catopen yet. + GTXT ("Can't find location of %s\n"), prog_name); + run_dir = dbe_strdup (get_cur_dir ()); + } + else + { + char *d = strrchr (run_dir, '/'); + if (d) + *d = 0; + // Check if the installation path contains spaces + if (strchr (run_dir, ' ') != NULL) + { + // Create a symbolic link without spaces + const char *dir = NTXT ("/tmp/.gprofngLinks"); + char *symbolic_link = dbe_create_symlink_to_path (run_dir, dir); + if (NULL != symbolic_link) + { + // Save old path to avoid memory leak + run_dir_with_spaces = run_dir; + // Use the path through symbolic link + run_dir = symbolic_link; + } + } + } + } +} + +char * +Application::get_cur_dir () +{ + if (cur_dir == NULL) + { + char cwd[MAXPATHLEN]; + if (getcwd (cwd, sizeof (cwd)) == NULL) + { + perror (prog_name); + exit (1); + } + cur_dir = dbe_strdup (canonical_path (cwd)); + } + return cur_dir; +} + +/** + * Get number of worker threads + * This is used to decide if it is ok to use worker threads for stat() + * and other actions that can hang for a long time + * @return number_of_worker_threads + */ +int +Application::get_number_of_worker_threads () +{ + return number_of_worker_threads; +} + +int +Application::check_args (int argc, char *argv[]) +{ + int opt; + // Parsing the command line + opterr = 0; + while ((opt = getopt (argc, argv, "V")) != EOF) + switch (opt) + { + case 'V': +// Ruud + Application::print_version_info (); +/* + printf (NTXT ("GNU %s version %s\n"), get_basename (prog_name), VERSION); +*/ + exit (0); + default: + usage (); + } + return optind; +} + +Emsg * +Application::fetch_comments () +{ + if (commentq == NULL) + return NULL; + return commentq->fetch (); +} + +void +Application::queue_comment (Emsg *m) +{ + commentq->append (m); +} + +void +Application::delete_comments () +{ + if (commentq != NULL) + { + delete commentq; + commentq = new Emsgqueue (NTXT ("app_commentq")); + } +} + +int +Application::set_progress (int percentage, const char *proc_str) +{ + if (progress_func != NULL) + return progress_func (percentage, proc_str); + return 0; +} + +// Ruud +void +Application::print_version_info () +{ + printf ( GTXT ( + "GNU %s binutils version %s\n" + "Copyright (C) 2021 Free Software Foundation, Inc.\n" + "License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n"), + get_basename (prog_name), VERSION); +} diff --git a/gprofng/src/Application.h b/gprofng/src/Application.h new file mode 100644 index 0000000..404383b --- /dev/null +++ b/gprofng/src/Application.h @@ -0,0 +1,108 @@ +/* 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. */ + +/* + * The Application class is the base class for all C++ executables + * in the Performance Tools Suite + * + * It determines the directory from which the running binary came, + * sets up the I18N catalog, the program name, and initializes + * an instance of the Settings class to manage all user preferences + * and settings. It also manages usage tracking. + * + * Applications which read experiments are derived from a subclass + * named DbeApplication (q.v.) + */ + +#ifndef _APPLICATION_H +#define _APPLICATION_H + +#include "dbe_types.h" + +class Settings; +class Emsg; +class Emsgqueue; + +// Application object +class Application +{ +public: + Application (int argc, char *argv[], char *_run_dir = NULL); + virtual ~Application (); + void set_name (const char *_name); + char *get_cur_dir (); + + // Control the settings of a progress bar, used for GUI applications + // this function also detects cancel requests and returns 1 + // if yes, 0 otherwise + static int set_progress (int percentage, const char *proc_str); + static char *get_realpath (const char *_name); + + // queue for messages (from reading er.rc files, ...) + void queue_comment (Emsg *m); // queue for messages + Emsg *fetch_comments (void); // fetch the queue of comment messages + void delete_comments (void); // delete the queue of comment messages + + // worker threads (currently used in dbe_stat() for stat() calls) + int get_number_of_worker_threads (); + + char *get_version () { return prog_version; } + char *get_name () { return prog_name; } + char *get_run_dir () { return run_dir; } + Emsgqueue *get_comments_queue () { return commentq; }; + +protected: // methods + void set_run_dir (char *fdhome = NULL); + typedef int (*ProgressFunc)(int, const char *); + + // Write a usage message; to be defined in derived class + virtual void usage () = 0; + +// Ruud + // Write a version message; to be defined in derived class + void print_version_info (); + + // Can be overridden in derived class + virtual int check_args (int argc, char *argv[]); + + void read_rc (); + static void set_progress_func (ProgressFunc func) { progress_func = func; } + +protected: + Emsgqueue *commentq; + Settings *settings; + char *prog_version; + char *prog_name; + char *whoami; + char *run_dir; + char *run_dir_with_spaces; // used in case there are spaces + char *cur_dir; + int lic_found; + char *lic_err; + +private: + void set_ut_email (int argc, char *argv[]); + int number_of_worker_threads; + static ProgressFunc progress_func; +}; + +extern Application *theApplication; + +#endif /* _APPLICATION_H */ diff --git a/gprofng/src/ArchiveExp.cc b/gprofng/src/ArchiveExp.cc new file mode 100644 index 0000000..e7ebfa9 --- /dev/null +++ b/gprofng/src/ArchiveExp.cc @@ -0,0 +1,149 @@ +/* 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 <stdio.h> +#include <unistd.h> + +#include "util.h" +#include "DbeSession.h" +#include "LoadObject.h" +#include "ArchiveExp.h" +#include "DbeFile.h" +#include "CallStack.h" +#include "gp-archive.h" +#include "Function.h" +#include "Module.h" + +ArchiveExp::ArchiveExp (char *path) : Experiment () +{ + force_flag = false; + copyso_flag = false; + use_fndr_archives = true; + status = find_expdir (path); + if (status == SUCCESS) + read_log_file (); +} + +ArchiveExp::~ArchiveExp () { } + +void +ArchiveExp::read_data (int s_option) +{ + read_archives (); + read_map_file (); + if (read_java_classes_file () == SUCCESS) + { + for (int i = 0, sz = loadObjs ? loadObjs->size () : 0; i < sz; i++) + { + LoadObject *lo = loadObjs->get (i); + Dprintf (DEBUG_ARCHIVE, NTXT ("%s:%d loadObjs[%d]=%-25s %s\n"), + get_basename (__FILE__), (int) __LINE__, i, + STR (lo->get_name ()), STR (lo->get_pathname ())); + if ((lo->dbeFile->filetype & DbeFile::F_JAVACLASS) == 0) + continue; + lo->isUsed = true; + if ((s_option & ARCH_EXE_ONLY) != 0) + continue; + lo->sync_read_stabs (); + } + } + if ((s_option & (ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY)) != 0) + { + read_frameinfo_file (); + resolveFrameInfo = true; + Vector<DataDescriptor*> *ddscr = getDataDescriptors (); + delete ddscr; // getDataDescriptors() forces reading of experiment data + CallStack *callStack = callTree (); + if (callStack) + { + if (DEBUG_ARCHIVE) + { + Dprintf (DEBUG_ARCHIVE, NTXT ("stacks=%p\n"), callStack); + callStack->print (NULL); + } + for (int n = 0;; n++) + { + CallStackNode *node = callStack->get_node (n); + if (node == NULL) + break; + do + { + Histable *h = node->get_instr (); + Histable::Type t = h->get_type (); + if (t == Histable::INSTR) + { + DbeInstr *dbeInstr = (DbeInstr *) h; + if (!dbeInstr->isUsed) + { + Function *func = (Function *) dbeInstr->convertto (Histable::FUNCTION); + if (!func->isUsed) + { + func->isUsed = true; + func->module->isUsed = true; + func->module->loadobject->isUsed = true; + } + DbeLine *dbeLine = (DbeLine *) dbeInstr->convertto (Histable::LINE); + if (dbeLine) + dbeLine->sourceFile->isUsed = true; + } + } + else if (t == Histable::LINE) + { + DbeLine * dbeLine = (DbeLine *) h; + dbeLine->sourceFile->isUsed = true; + } + node = node->ancestor; + } + while (node); + } + } + } +} + +char * +ArchiveExp::createLinkToFndrArchive (LoadObject *lo, int /* hide_msg */) +{ + // For example, archives of libc.so will be: + // <exp>/archives/<libc.so_check_sum> + // <exp>/M_r0.er/archives/libc.so_<hash> -> ../../archives/<libc.so_check_sum> + if (!create_dir (get_fndr_arch_name ())) + { + fprintf (stderr, GTXT ("Unable to create directory `%s'\n"), get_fndr_arch_name ()); + return NULL; + } + uint32_t checksum = lo->get_checksum (); + char *linkName = dbe_sprintf (NTXT ("../../%s/%u"), SP_ARCHIVES_DIR, checksum); + char *nm = lo->get_pathname (); + char *symLinkName = getNameInArchive (nm, false); + if (symlink (linkName, symLinkName) != 0) + { + fprintf (stderr, GTXT ("Unable to create link `%s' -> `%s'\n"), + symLinkName, linkName); + free (linkName); + free (symLinkName); + return NULL; + } + free (linkName); + free (symLinkName); + + // Return a full path inside founder archive: + return dbe_sprintf (NTXT ("%s/%u"), get_fndr_arch_name (), checksum); +} diff --git a/gprofng/src/ArchiveExp.h b/gprofng/src/ArchiveExp.h new file mode 100644 index 0000000..809ed58 --- /dev/null +++ b/gprofng/src/ArchiveExp.h @@ -0,0 +1,41 @@ +/* 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. */ + +#ifndef _ARCHIVE_EXP_H +#define _ARCHIVE_EXP_H + +#include "Experiment.h" +class LoadObject; + +class ArchiveExp : public Experiment +{ +public: + ArchiveExp (char *path); + ~ArchiveExp (); + char *createLinkToFndrArchive (LoadObject *lo, int hide_msg); + void read_data (int s_option); + +private: + bool force_flag; + bool copyso_flag; + bool use_fndr_archives; +}; + +#endif /* _ARCHIVE_EXP_H */ diff --git a/gprofng/src/BaseMetric.cc b/gprofng/src/BaseMetric.cc new file mode 100644 index 0000000..bb51ddf --- /dev/null +++ b/gprofng/src/BaseMetric.cc @@ -0,0 +1,975 @@ +/* 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 <strings.h> +#include <stdlib.h> + +#include "util.h" +#include "BaseMetric.h" +#include "DbeSession.h" +#include "Expression.h" + +int BaseMetric::last_id = 0; + +void +BaseMetric::init (Type t) +{ + id = last_id++; + type = t; + aux = NULL; + cmd = NULL; + username = NULL; + hw_ctr = NULL; + cond = NULL; + val = NULL; + expr = NULL; + cond_spec = NULL; + val_spec = NULL; + expr_spec = NULL; + legend = NULL; + definition = NULL; + dependent_bm = NULL; + zeroThreshold = 0; + clock_unit = (Presentation_clock_unit) 0; + for (int ii = 0; ii < NSUBTYPES; ii++) + default_visbits[ii] = VAL_NA; + valtype = VT_DOUBLE; + precision = METRIC_HR_PRECISION; + flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED; + value_styles = VAL_TIMEVAL | VAL_PERCENT; +} + +BaseMetric::BaseMetric (Type t) +{ + init (t); + switch (t) + { + case CP_LMS_USER: + case CP_LMS_SYSTEM: + case CP_LMS_WAIT_CPU: + case CP_LMS_USER_LOCK: + case CP_LMS_TFAULT: + case CP_LMS_DFAULT: + case OMP_MASTER_THREAD: + case CP_TOTAL: + case CP_TOTAL_CPU: + case CP_LMS_TRAP: + case CP_LMS_KFAULT: + case CP_LMS_SLEEP: + case CP_LMS_STOPPED: + case OMP_NONE: + case OMP_OVHD: + case OMP_WORK: + case OMP_IBAR: + case OMP_EBAR: + case OMP_WAIT: + case OMP_SERL: + case OMP_RDUC: + case OMP_LKWT: + case OMP_CTWT: + case OMP_ODWT: + case OMP_MSTR: + case OMP_SNGL: + case OMP_ORDD: + case CP_KERNEL_CPU: + // all of these are floating point, precision = clock profile tick + valtype = VT_DOUBLE; + precision = METRIC_SIG_PRECISION; + flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED; + value_styles = VAL_TIMEVAL | VAL_PERCENT; + break; + case SYNC_WAIT_TIME: + case IO_READ_TIME: + case IO_WRITE_TIME: + case IO_OTHER_TIME: + case IO_ERROR_TIME: + // all of these are floating point, precision = hrtime tick + valtype = VT_DOUBLE; + precision = METRIC_HR_PRECISION; + flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED; + value_styles = VAL_TIMEVAL | VAL_PERCENT; + break; + case SYNC_WAIT_COUNT: + case HEAP_ALLOC_CNT: + case HEAP_LEAK_CNT: + case IO_READ_CNT: + case IO_WRITE_CNT: + case IO_OTHER_CNT: + case IO_ERROR_CNT: + valtype = VT_LLONG; + precision = 1; + flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED; + value_styles = VAL_VALUE | VAL_PERCENT; + break; + case RACCESS: + case DEADLOCKS: + // all of these are integer + valtype = VT_LLONG; + precision = 1; + flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED; + value_styles = VAL_VALUE | VAL_PERCENT; + zeroThreshold = 1; + break; + case HEAP_ALLOC_BYTES: + case HEAP_LEAK_BYTES: + case IO_READ_BYTES: + case IO_WRITE_BYTES: + // all of these are longlong + valtype = VT_ULLONG; + precision = 1; + flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED; + value_styles = VAL_VALUE | VAL_PERCENT; + break; + case SIZES: + valtype = VT_LLONG; + precision = 1; + flavors = STATIC; + value_styles = VAL_VALUE; + break; + case ADDRESS: + valtype = VT_ADDRESS; + precision = 1; + flavors = STATIC; + value_styles = VAL_VALUE; + break; + case ONAME: + valtype = VT_LABEL; + precision = 0; + flavors = STATIC; + value_styles = VAL_VALUE; + break; + case HWCNTR: // We should call the other constructor for hwc metric + default: + abort (); + } + specify (); +} + +// Constructor for linked HW counters (base counter) +BaseMetric::BaseMetric (Hwcentry *ctr, const char* _aux, const char* _username, + int v_styles, BaseMetric* _dependent_bm) +{ + hwc_init (ctr, _aux, _aux, _username, v_styles); + dependent_bm = _dependent_bm; +} + +// Constructor for linked HW counters (derived counter) + +BaseMetric::BaseMetric (Hwcentry *ctr, const char *_aux, const char *_cmdname, + const char *_username, int v_styles) +{ + hwc_init (ctr, _aux, _cmdname, _username, v_styles); +} + +void +BaseMetric::hwc_init (Hwcentry *ctr, const char* _aux, const char* _cmdname, + const char* _username, int v_styles) +{ + init (HWCNTR); + aux = dbe_strdup (_aux); // HWC identifier + cmd = dbe_strdup (_cmdname); // may differ from _aux for cycles->time hwcs + username = dbe_strdup (_username); + flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED; + value_styles = v_styles | VAL_PERCENT; + if ((value_styles & (VAL_TIMEVAL | VAL_VALUE)) == VAL_TIMEVAL) + valtype = VT_DOUBLE; + else + valtype = VT_ULLONG; + if (ABST_MEMSPACE_ENABLED (ctr->memop)) + flavors |= DATASPACE; // only for ctrs with memop definitions + hw_ctr = ctr; + specify (); +} + +// Constructor for derived metrics +BaseMetric::BaseMetric (const char *_cmd, const char *_username, + Definition *def) +{ + init (DERIVED); + cmd = dbe_strdup (_cmd); + username = dbe_strdup (_username); + aux = dbe_strdup (_cmd); + definition = def; + flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED; + clock_unit = CUNIT_NULL; // should it be CUNIT_TIME or 0 or something? + + /* we're not going to process packets for derived metrics */ + packet_type = (ProfData_type) (-1); + value_styles = VAL_VALUE; + valtype = VT_DOUBLE; + precision = 1000; +} + +// Copy constructor +BaseMetric::BaseMetric (const BaseMetric& m) +{ + id = m.id; + type = m.type; + aux = dbe_strdup (m.aux); + cmd = dbe_strdup (m.cmd); + username = dbe_strdup (m.username); + flavors = m.flavors; + value_styles = m.value_styles; + valtype = m.valtype; + precision = m.precision; + hw_ctr = m.hw_ctr; + packet_type = m.packet_type; + zeroThreshold = m.zeroThreshold; + clock_unit = m.clock_unit; + for (int ii = 0; ii < NSUBTYPES; ii++) + default_visbits[ii] = m.default_visbits[ii]; + if (m.cond_spec) + { + cond_spec = strdup (m.cond_spec); + cond = m.cond->copy (); + } + else + { + cond = NULL; + cond_spec = NULL; + } + if (m.val_spec) + { + val_spec = strdup (m.val_spec); + val = m.val->copy (); + } + else + { + val = NULL; + val_spec = NULL; + } + if (m.expr_spec) + { + expr_spec = strdup (m.expr_spec); + expr = m.expr->copy (); + } + else + { + expr = NULL; + expr_spec = NULL; + } + legend = dbe_strdup (m.legend); + definition = NULL; + if (m.definition) + definition = Definition::add_definition (m.definition->def); + dependent_bm = m.dependent_bm; +} + +BaseMetric::~BaseMetric () +{ + free (aux); + free (cmd); + free (cond_spec); + free (val_spec); + free (expr_spec); + free (legend); + free (username); + delete cond; + delete val; + delete expr; + delete definition; +} + +bool +BaseMetric::is_internal () +{ + return (get_value_styles () & VAL_INTERNAL) != 0; +} + +int +BaseMetric::get_default_visbits (SubType subtype) +{ + int rc = VAL_NA; + switch (subtype) + { + case STATIC: + case EXCLUSIVE: + rc = default_visbits[0]; + break; + case INCLUSIVE: + rc = default_visbits[1]; + break; + default: + break; + } + return rc; +} + +void +BaseMetric::set_default_visbits (SubType subtype, int _visbits) +{ + switch (subtype) + { + case STATIC: + case EXCLUSIVE: + default_visbits[0] = _visbits; + break; + case INCLUSIVE: + default_visbits[1] = _visbits; + break; + default: + break; + } +} + +void +BaseMetric::set_cond_spec (char *_cond_spec) +{ + if (cond_spec) + { + free (cond_spec); + delete cond; + cond_spec = NULL; + cond = NULL; + } + if (_cond_spec) + { + cond = dbeSession->ql_parse (_cond_spec); + if (cond == NULL) + { + fprintf (stderr, GTXT ("Invalid expression in metric specification `%s'\n"), _cond_spec); + abort (); + } + cond_spec = dbe_strdup (_cond_spec); + } +} + +void +BaseMetric::set_val_spec (char *_val_spec) +{ + if (val_spec) + { + free (val_spec); + delete val; + val_spec = NULL; + val = NULL; + } + if (_val_spec) + { + val = dbeSession->ql_parse (_val_spec); + if (val == NULL) + { + fprintf (stderr, GTXT ("Invalid expression in metric specification `%s'\n"), _val_spec); + abort (); + } + val_spec = dbe_strdup (_val_spec); + } +} + +void +BaseMetric::set_expr_spec (char *_expr_spec) +{ + id = last_id++; + if (expr_spec) + { + free (expr_spec); + delete expr; + expr_spec = NULL; + expr = NULL; + } + if (_expr_spec) + { + expr = dbeSession->ql_parse (_expr_spec); + if (expr == NULL) + { + fprintf (stderr, GTXT ("Invalid expression in metric specification `%s'\n"), _expr_spec); + return; + } + expr_spec = dbe_strdup (_expr_spec); + } +} + +void +BaseMetric::specify_mstate_metric (int st) +{ + char buf[128]; + snprintf (buf, sizeof (buf), NTXT ("MSTATE==%d"), st); + specify_prof_metric (buf); +} + +void +BaseMetric::specify_ompstate_metric (int st) +{ + char buf[128]; + snprintf (buf, sizeof (buf), NTXT ("OMPSTATE==%d"), st); + specify_prof_metric (buf); +} + +void +BaseMetric::specify_prof_metric (char *_cond_spec) +{ + packet_type = DATA_CLOCK; + specify_metric (_cond_spec, NTXT ("NTICK_USEC")); // microseconds +} + +void +BaseMetric::specify_metric (char *_cond_spec, char *_val_spec) +{ + set_cond_spec (_cond_spec); + set_val_spec (_val_spec); +} + +void +BaseMetric::specify () +{ + enum + { + IDLE_STATE_BITS = + (1 << OMP_IDLE_STATE) | (1 << OMP_IBAR_STATE) | (1 << OMP_EBAR_STATE) | + (1 << OMP_LKWT_STATE) | (1 << OMP_CTWT_STATE) | (1 << OMP_ODWT_STATE) | + (1 << OMP_ATWT_STATE) | (1 << OMP_TSKWT_STATE), + LMS_USER_BITS = + (1 << OMP_NO_STATE) | (1 << OMP_WORK_STATE) | (1 << OMP_SERL_STATE) | + (1 << OMP_RDUC_STATE) + }; + + char buf[256]; + char buf2[256]; + packet_type = (ProfData_type) - 1; // illegal value + clock_unit = CUNIT_TIME; + switch (type) + { + case SIZES: + username = dbe_strdup (GTXT ("Size")); + clock_unit = CUNIT_BYTES; + cmd = dbe_strdup (NTXT ("size")); + break; + case ADDRESS: + username = dbe_strdup (GTXT ("PC Address")); + cmd = dbe_strdup (NTXT ("address")); + break; + case ONAME: + username = dbe_strdup (GTXT ("Name")); + cmd = dbe_strdup (NTXT ("name")); + break; + case CP_LMS_SYSTEM: + username = dbe_strdup (GTXT ("System CPU Time")); + specify_mstate_metric (LMS_SYSTEM); + cmd = dbe_strdup (NTXT ("system")); + break; + case CP_TOTAL_CPU: + username = dbe_strdup (GTXT ("Total CPU Time")); + snprintf (buf, sizeof (buf), + "(MSTATE==%d)||(MSTATE==%d)||(MSTATE==%d)||(MSTATE==%d)", + LMS_USER, LMS_SYSTEM, LMS_TRAP, LMS_LINUX_CPU); + specify_prof_metric (buf); + cmd = dbe_strdup (NTXT ("totalcpu")); + break; + case CP_TOTAL: + username = dbe_strdup (GTXT ("Total Thread Time")); + snprintf (buf, sizeof (buf), NTXT ("(MSTATE!=%d)&&(MSTATE!=%d)"), + LMS_KERNEL_CPU, LMS_LINUX_CPU); + specify_prof_metric (buf); + cmd = dbe_strdup (NTXT ("total")); + break; + case CP_KERNEL_CPU: + username = dbe_strdup (GTXT ("Kernel CPU Time")); + specify_mstate_metric (LMS_KERNEL_CPU); + cmd = dbe_strdup (NTXT ("kcpu")); + break; + case OMP_MASTER_THREAD: + username = dbe_strdup (GTXT ("Master Thread Time")); + specify_prof_metric (NTXT ("LWPID==1")); + cmd = dbe_strdup (NTXT ("masterthread")); + break; + case CP_LMS_USER: + username = dbe_strdup (GTXT ("User CPU Time")); + specify_mstate_metric (LMS_USER); + cmd = dbe_strdup (NTXT ("user")); + break; + case CP_LMS_WAIT_CPU: + username = dbe_strdup (GTXT ("Wait CPU Time")); + specify_mstate_metric (LMS_WAIT_CPU); + cmd = dbe_strdup (NTXT ("wait")); + break; + case CP_LMS_USER_LOCK: + username = dbe_strdup (GTXT ("User Lock Time")); + specify_mstate_metric (LMS_USER_LOCK); + cmd = dbe_strdup (NTXT ("lock")); + break; + case CP_LMS_TFAULT: + username = dbe_strdup (GTXT ("Text Page Fault Time")); + specify_mstate_metric (LMS_TFAULT); + cmd = dbe_strdup (NTXT ("textpfault")); + break; + case CP_LMS_DFAULT: + username = dbe_strdup (GTXT ("Data Page Fault Time")); + specify_mstate_metric (LMS_DFAULT); + cmd = dbe_strdup (NTXT ("datapfault")); + break; + case CP_LMS_TRAP: + username = dbe_strdup (GTXT ("Trap CPU Time")); + specify_mstate_metric (LMS_TRAP); + cmd = dbe_strdup (NTXT ("trap")); + break; + case CP_LMS_KFAULT: + username = dbe_strdup (GTXT ("Kernel Page Fault Time")); + specify_mstate_metric (LMS_KFAULT); + cmd = dbe_strdup (NTXT ("kernelpfault")); + break; + case CP_LMS_SLEEP: + username = dbe_strdup (GTXT ("Sleep Time")); + specify_mstate_metric (LMS_SLEEP); + cmd = dbe_strdup (NTXT ("sleep")); + break; + case CP_LMS_STOPPED: + username = dbe_strdup (GTXT ("Stopped Time")); + specify_mstate_metric (LMS_STOPPED); + cmd = dbe_strdup (NTXT ("stop")); + break; + case OMP_OVHD: + username = dbe_strdup (GTXT ("OpenMP Overhead Time")); + specify_ompstate_metric (OMP_OVHD_STATE); + cmd = dbe_strdup (NTXT ("ompovhd")); + break; + case OMP_WORK: + username = dbe_strdup (GTXT ("OpenMP Work Time")); + snprintf (buf, sizeof (buf), + NTXT ("(OMPSTATE>=0) && (MSTATE==%d) && ((1<<OMPSTATE) & %d)"), + LMS_USER, LMS_USER_BITS); + specify_prof_metric (buf); + cmd = dbe_strdup (NTXT ("ompwork")); + break; + case OMP_WAIT: + username = dbe_strdup (GTXT ("OpenMP Wait Time")); + snprintf (buf, sizeof (buf), + "OMPSTATE>=0 && ((1<<OMPSTATE) & ((MSTATE!=%d) ? %d : %d))", + LMS_USER, (LMS_USER_BITS | IDLE_STATE_BITS), IDLE_STATE_BITS); + specify_prof_metric (buf); + cmd = dbe_strdup (NTXT ("ompwait")); + break; + case OMP_IBAR: + username = dbe_strdup (GTXT ("OpenMP Implicit Barrier Time")); + specify_ompstate_metric (OMP_IBAR_STATE); + cmd = dbe_strdup (NTXT ("ompibar")); + break; + case OMP_EBAR: + username = dbe_strdup (GTXT ("OpenMP Explicit Barrier Time")); + specify_ompstate_metric (OMP_EBAR_STATE); + cmd = dbe_strdup (NTXT ("ompebar")); + break; + case OMP_SERL: + username = dbe_strdup (GTXT ("OpenMP Serial Time")); + specify_ompstate_metric (OMP_SERL_STATE); + cmd = dbe_strdup (NTXT ("ompserl")); + break; + case OMP_RDUC: + username = dbe_strdup (GTXT ("OpenMP Reduction Time")); + specify_ompstate_metric (OMP_RDUC_STATE); + cmd = dbe_strdup (NTXT ("omprduc")); + break; + case OMP_LKWT: + username = dbe_strdup (GTXT ("OpenMP Lock Wait Time")); + specify_ompstate_metric (OMP_LKWT_STATE); + cmd = dbe_strdup (NTXT ("omplkwt")); + break; + case OMP_CTWT: + username = dbe_strdup (GTXT ("OpenMP Critical Section Wait Time")); + specify_ompstate_metric (OMP_CTWT_STATE); + cmd = dbe_strdup (NTXT ("ompctwt")); + break; + case OMP_ODWT: + username = dbe_strdup (GTXT ("OpenMP Ordered Section Wait Time")); + specify_ompstate_metric (OMP_ODWT_STATE); + cmd = dbe_strdup (NTXT ("ompodwt")); + break; + case SYNC_WAIT_TIME: + packet_type = DATA_SYNCH; + username = dbe_strdup (GTXT ("Sync Wait Time")); + snprintf (buf, sizeof (buf), NTXT ("(EVT_TIME)/%lld"), + (long long) (NANOSEC / METRIC_HR_PRECISION)); + specify_metric (NULL, buf); + cmd = dbe_strdup (NTXT ("sync")); + break; + case SYNC_WAIT_COUNT: + packet_type = DATA_SYNCH; + username = dbe_strdup (GTXT ("Sync Wait Count")); + specify_metric (NULL, NTXT ("1")); + cmd = dbe_strdup (NTXT ("syncn")); + break; + case HEAP_ALLOC_CNT: + packet_type = DATA_HEAP; + username = dbe_strdup (GTXT ("Allocations")); + snprintf (buf, sizeof (buf), NTXT ("(HTYPE!=%d)&&(HTYPE!=%d)&&HVADDR"), + FREE_TRACE, MUNMAP_TRACE); + specify_metric (buf, NTXT ("1")); + cmd = dbe_strdup (NTXT ("heapalloccnt")); + break; + case HEAP_ALLOC_BYTES: + packet_type = DATA_HEAP; + username = dbe_strdup (GTXT ("Bytes Allocated")); + snprintf (buf, sizeof (buf), NTXT ("(HTYPE!=%d)&&(HTYPE!=%d)&&HVADDR"), + FREE_TRACE, MUNMAP_TRACE); + specify_metric (buf, NTXT ("HSIZE")); + cmd = dbe_strdup (NTXT ("heapallocbytes")); + break; + case HEAP_LEAK_CNT: + packet_type = DATA_HEAP; + username = dbe_strdup (GTXT ("Leaks")); + snprintf (buf, sizeof (buf), "(HTYPE!=%d)&&(HTYPE!=%d)&&HVADDR&&HLEAKED", + FREE_TRACE, MUNMAP_TRACE); + specify_metric (buf, NTXT ("1")); + cmd = dbe_strdup (NTXT ("heapleakcnt")); + break; + case HEAP_LEAK_BYTES: + packet_type = DATA_HEAP; + username = dbe_strdup (GTXT ("Bytes Leaked")); + snprintf (buf, sizeof (buf), NTXT ("(HTYPE!=%d)&&(HTYPE!=%d)&&HVADDR"), + FREE_TRACE, MUNMAP_TRACE); + specify_metric (buf, NTXT ("HLEAKED")); + cmd = dbe_strdup (NTXT ("heapleakbytes")); + break; + + case IO_READ_CNT: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("Read Count")); + snprintf (buf, sizeof (buf), "(IOTYPE==%d)", READ_TRACE); + specify_metric (buf, NTXT ("1")); + cmd = dbe_strdup (NTXT ("ioreadcnt")); + break; + case IO_WRITE_CNT: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("Write Count")); + snprintf (buf, sizeof (buf), "(IOTYPE==%d)", WRITE_TRACE); + specify_metric (buf, NTXT ("1")); + cmd = dbe_strdup (NTXT ("iowritecnt")); + break; + case IO_OTHER_CNT: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("Other I/O Count")); + snprintf (buf, sizeof (buf), "(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)", + OPEN_TRACE, CLOSE_TRACE, OTHERIO_TRACE); + specify_metric (buf, NTXT ("1")); + cmd = dbe_strdup (NTXT ("ioothercnt")); + break; + case IO_ERROR_CNT: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("I/O Error Count")); + snprintf (buf, sizeof (buf), + "(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)", + READ_TRACE_ERROR, WRITE_TRACE_ERROR, OPEN_TRACE_ERROR, + CLOSE_TRACE_ERROR, OTHERIO_TRACE_ERROR); + specify_metric (buf, NTXT ("1")); + cmd = dbe_strdup (NTXT ("ioerrorcnt")); + break; + case IO_READ_BYTES: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("Read Bytes")); + snprintf (buf, sizeof (buf), NTXT ("(IOTYPE==%d)&&IONBYTE"), + READ_TRACE); + specify_metric (buf, NTXT ("IONBYTE")); + cmd = dbe_strdup (NTXT ("ioreadbytes")); + break; + case IO_WRITE_BYTES: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("Write Bytes")); + snprintf (buf, sizeof (buf), "(IOTYPE==%d)&&IONBYTE", WRITE_TRACE); + specify_metric (buf, NTXT ("IONBYTE")); + cmd = dbe_strdup (NTXT ("iowritebytes")); + break; + case IO_READ_TIME: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("Read Time")); + snprintf (buf, sizeof (buf), "(IOTYPE==%d)&&EVT_TIME", READ_TRACE); + snprintf (buf2, sizeof (buf2), NTXT ("(EVT_TIME)/%lld"), + (long long) (NANOSEC / METRIC_HR_PRECISION)); + specify_metric (buf, buf2); + cmd = dbe_strdup (NTXT ("ioreadtime")); + break; + case IO_WRITE_TIME: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("Write Time")); + snprintf (buf, sizeof (buf), NTXT ("(IOTYPE==%d)&&EVT_TIME"), + WRITE_TRACE); + snprintf (buf2, sizeof (buf2), NTXT ("(EVT_TIME)/%lld"), + (long long) (NANOSEC / METRIC_HR_PRECISION)); + specify_metric (buf, buf2); + cmd = dbe_strdup (NTXT ("iowritetime")); + break; + case IO_OTHER_TIME: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("Other I/O Time")); + snprintf (buf, sizeof (buf), + "(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)&&EVT_TIME", + OPEN_TRACE, CLOSE_TRACE, OTHERIO_TRACE); + snprintf (buf2, sizeof (buf2), NTXT ("(EVT_TIME)/%lld"), + (long long) (NANOSEC / METRIC_HR_PRECISION)); + specify_metric (buf, buf2); + cmd = dbe_strdup (NTXT ("ioothertime")); + break; + case IO_ERROR_TIME: + packet_type = DATA_IOTRACE; + username = dbe_strdup (GTXT ("I/O Error Time")); + snprintf (buf, sizeof (buf), + "(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)&&EVT_TIME", + READ_TRACE_ERROR, WRITE_TRACE_ERROR, OPEN_TRACE_ERROR, + CLOSE_TRACE_ERROR, OTHERIO_TRACE_ERROR); + snprintf (buf2, sizeof (buf2), NTXT ("(EVT_TIME)/%lld"), + (long long) (NANOSEC / METRIC_HR_PRECISION)); + specify_metric (buf, buf2); + cmd = dbe_strdup (NTXT ("ioerrortime")); + break; + case RACCESS: + packet_type = DATA_RACE; + username = dbe_strdup (GTXT ("Race Accesses")); + specify_metric (NULL, NTXT ("RCNT")); + cmd = dbe_strdup (NTXT ("raccess")); + break; + case DEADLOCKS: + packet_type = DATA_DLCK; + username = dbe_strdup (GTXT ("Deadlocks")); + specify_metric (NULL, NTXT ("1")); + cmd = dbe_strdup (NTXT ("deadlocks")); + break; + case HWCNTR: + packet_type = DATA_HWC; + // username, cmd, and aux set by hwc constructor + if (valtype == VT_DOUBLE) + { + if (hw_ctr->timecvt > 0) // CPU cycles + specify_metric (NULL, NTXT ("((HWCINT*1000000)/FREQ_MHZ)")); + else if (hw_ctr->timecvt < 0) + { // reference clock (frequency is -timecvt MHz) + snprintf (buf, sizeof (buf), NTXT ("((HWCINT*1000000)/%d)"), -hw_ctr->timecvt); + specify_metric (NULL, buf); + } + else // shouldn't happen + specify_metric (NULL, NTXT ("0")); + // resulting unit: seconds * 1e12 + precision = 1000000LL * 1000000LL; // Seconds * 1e12 + } + else + { + specify_metric (NULL, NTXT ("HWCINT")); + precision = 1; + } + break; + case OMP_MSTR: + case OMP_SNGL: + case OMP_ORDD: + case OMP_NONE: + default: + username = dbe_strdup (GTXT ("****")); + fprintf (stderr, "BaseMetric::init Undefined basemetric %s\n", + get_basetype_name ()); + } +} + +#define CASE_S(x) case x: s = (char *) #x; break +char * +BaseMetric::get_basetype_name () +{ + static char buf[128]; + char *s; + switch (type) + { + CASE_S (CP_LMS_SYSTEM); + CASE_S (CP_TOTAL_CPU); + CASE_S (CP_TOTAL); + CASE_S (OMP_MASTER_THREAD); + CASE_S (CP_LMS_USER); + CASE_S (CP_LMS_WAIT_CPU); + CASE_S (CP_LMS_USER_LOCK); + CASE_S (CP_LMS_TFAULT); + CASE_S (CP_LMS_DFAULT); + CASE_S (CP_LMS_TRAP); + CASE_S (CP_LMS_KFAULT); + CASE_S (CP_LMS_SLEEP); + CASE_S (CP_LMS_STOPPED); + CASE_S (OMP_NONE); + CASE_S (OMP_OVHD); + CASE_S (OMP_WORK); + CASE_S (OMP_IBAR); + CASE_S (OMP_EBAR); + CASE_S (OMP_WAIT); + CASE_S (OMP_SERL); + CASE_S (OMP_RDUC); + CASE_S (OMP_LKWT); + CASE_S (OMP_CTWT); + CASE_S (OMP_ODWT); + CASE_S (OMP_MSTR); + CASE_S (OMP_SNGL); + CASE_S (OMP_ORDD); + CASE_S (CP_KERNEL_CPU); + CASE_S (SYNC_WAIT_TIME); + CASE_S (IO_READ_TIME); + CASE_S (IO_WRITE_TIME); + CASE_S (IO_OTHER_TIME); + CASE_S (IO_ERROR_TIME); + CASE_S (HWCNTR); + CASE_S (SYNC_WAIT_COUNT); + CASE_S (HEAP_ALLOC_CNT); + CASE_S (HEAP_LEAK_CNT); + CASE_S (IO_READ_CNT); + CASE_S (IO_WRITE_CNT); + CASE_S (IO_OTHER_CNT); + CASE_S (IO_ERROR_CNT); + CASE_S (RACCESS); + CASE_S (DEADLOCKS); + CASE_S (HEAP_ALLOC_BYTES); + CASE_S (HEAP_LEAK_BYTES); + CASE_S (IO_READ_BYTES); + CASE_S (IO_WRITE_BYTES); + CASE_S (SIZES); + CASE_S (ADDRESS); + CASE_S (ONAME); + CASE_S (DERIVED); + default: + s = NTXT ("???"); + break; + } + snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), s, type); + buf[sizeof (buf) - 1] = 0; + return buf; +} + +char * +BaseMetric::dump () +{ + int len = 4; + char *msg = dbe_sprintf (NTXT ("id=%d %s aux='%s' cmd='%s' user_name='%s' expr_spec='%s'\n" + "%*c cond_spec='%s' val_spec='%s'"), + id, get_basetype_name (), STR (aux), STR (cmd), + STR (username), STR (expr_spec), + len, ' ', STR (cond_spec), STR (val_spec)); + return msg; +} + +Histable * +BaseMetric::get_comparable_obj (Histable *obj) +{ + if (obj == NULL || expr == NULL) + return obj; + if (strncmp (expr_spec, NTXT ("EXPGRID=="), 9) == 0) + { + int n = atoi (expr_spec + 9); + Vector<Histable *> *cmpObjs = obj->get_comparable_objs (); + if (cmpObjs && cmpObjs->size () >= n) + return cmpObjs->get (n - 1); + return NULL; + } + return obj; +} + +Definition::Definition (opType _op) +{ + op = _op; + bm = NULL; + arg1 = NULL; + arg2 = NULL; + def = NULL; + dependencies = NULL; + map = NULL; + index = 0; +} + +Definition::~Definition () +{ + delete arg1; + delete arg2; + delete dependencies; + delete[] map; +} + +Vector<BaseMetric *> * +Definition::get_dependencies () +{ + if (dependencies == NULL) + { + if (arg1 && arg1->bm && arg2 && arg2->bm) + { + dependencies = new Vector<BaseMetric *>(2); + arg1->index = dependencies->size (); + dependencies->append (arg1->bm); + arg2->index = dependencies->size (); + dependencies->append (arg2->bm); + map = new long[2]; + } + } + return dependencies; +} + +long * +Definition::get_map () +{ + get_dependencies (); + return map; +} + +Definition * +Definition::add_definition (char *_def) +{ + // parse the definition + char *op_ptr = strchr (_def, '/'); + if (op_ptr == NULL) + { + // it's a primitive metric + BaseMetric *bm = dbeSession->find_base_reg_metric (_def); + if (bm) + { + Definition *p = new Definition (opPrimitive); + p->bm = bm; + return p; + } + return NULL; // BaseMetric is not yet specified + } + Definition *p2 = add_definition (op_ptr + 1); + if (p2 == NULL) + return NULL; + _def = dbe_strdup (_def); + op_ptr = strchr (_def, '/'); + *op_ptr = 0; + Definition *p1 = add_definition (_def); + if (p1) + { + *op_ptr = '/'; + Definition *p = new Definition (opDivide); + p->arg1 = p1; + p->arg2 = p2; + p->def = _def; + return p; + } + free (_def); + delete p1; + delete p2; + return NULL; +} + +double +Definition::eval (long *indexes, TValue *values) +{ + switch (op) + { + case opPrimitive: + return values[indexes[index]].to_double (); + case opDivide: + { + double x2 = arg2->eval (indexes, values); + if (x2 == 0) + return 0.; + double x1 = arg1->eval (indexes, values); + return x1 / x2; + } + default: + fprintf (stderr, GTXT ("unknown expression\n")); + return 0.; + } +} diff --git a/gprofng/src/BaseMetric.h b/gprofng/src/BaseMetric.h new file mode 100644 index 0000000..e056993 --- /dev/null +++ b/gprofng/src/BaseMetric.h @@ -0,0 +1,246 @@ +/* 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. */ + +#ifndef _BASEMETRIC_H +#define _BASEMETRIC_H + +#include "dbe_structs.h" +#include "hwcentry.h" +#include "Table.h" + +// METRIC_*_PRECISION determine the least threshold value +// for time measured metrics. Any event that counts for less +// than 1sec/METRIC_PRECISION is discarded. +#define METRIC_SIG_PRECISION MICROSEC +#define METRIC_HR_PRECISION MICROSEC + +class Expression; +class Definition; +class Histable; +template <class ITEM> class Vector; + +class BaseMetric +{ +public: + // sync enum changes with AnMetric.java + enum Type + { // Subtype==STATIC metrics: + ONAME = 1, //ONAME must be 1 + SIZES, + ADDRESS, + // Clock Profiling, Derived Metrics: + CP_TOTAL, + CP_TOTAL_CPU, + // Clock profiling, Solaris Microstates (LMS_* defines) + CP_LMS_USER, + CP_LMS_SYSTEM, + CP_LMS_TRAP, + CP_LMS_TFAULT, + CP_LMS_DFAULT, + CP_LMS_KFAULT, + CP_LMS_USER_LOCK, + CP_LMS_SLEEP, + CP_LMS_WAIT_CPU, + CP_LMS_STOPPED, + // Kernel clock profiling + CP_KERNEL_CPU, + // Sync Tracing + SYNC_WAIT_TIME, + SYNC_WAIT_COUNT, + // HWC + HWCNTR, + // Heap Tracing: + HEAP_ALLOC_CNT, + HEAP_ALLOC_BYTES, + HEAP_LEAK_CNT, + HEAP_LEAK_BYTES, + // I/O Tracing: + IO_READ_BYTES, + IO_READ_CNT, + IO_READ_TIME, + IO_WRITE_BYTES, + IO_WRITE_CNT, + IO_WRITE_TIME, + IO_OTHER_CNT, + IO_OTHER_TIME, + IO_ERROR_CNT, + IO_ERROR_TIME, + // MPI Tracing: + MPI_TIME, + MPI_SEND, + MPI_BYTES_SENT, + MPI_RCV, + MPI_BYTES_RCVD, + MPI_OTHER, + // OMP states: + OMP_NONE, + OMP_OVHD, + OMP_WORK, + OMP_IBAR, + OMP_EBAR, + OMP_WAIT, + OMP_SERL, + OMP_RDUC, + OMP_LKWT, + OMP_CTWT, + OMP_ODWT, + OMP_MSTR, + OMP_SNGL, + OMP_ORDD, + OMP_MASTER_THREAD, + // MPI states: + MPI_WORK, + MPI_WAIT, + // Races and Deadlocks + RACCESS, + DEADLOCKS, + // Derived Metrics + DERIVED + }; + + // sync enum changes with AnMetric.java + enum SubType + { + STATIC = 1, // Type==SIZES, ADDRESS, ONAME + EXCLUSIVE = 2, + INCLUSIVE = 4, + ATTRIBUTED = 8, + DATASPACE = 16 // Can be accessed in dataspace views + }; + + BaseMetric (Type t); + BaseMetric (Hwcentry *ctr, const char* _aux, const char* _cmdname, + const char* _username, int v_styles); // depended bm + BaseMetric (Hwcentry *ctr, const char* _aux, const char* _username, + int v_styles, BaseMetric* _depended_bm = NULL); // master bm + BaseMetric (const char *_cmd, const char *_username, Definition *def); // derived metrics + BaseMetric (const BaseMetric& m); + virtual ~BaseMetric (); + + int get_id () { return id; } + Type get_type () { return type; } + Hwcentry *get_hw_ctr () { return hw_ctr; } + char *get_aux () { return aux; } + char *get_username () { return username; } + char *get_cmd () { return cmd; } + int get_flavors () { return flavors; } + int get_clock_unit () { return clock_unit; } + long long get_precision () { return precision; } + ValueTag get_vtype () { return valtype; } + int get_value_styles () { return value_styles; } + bool is_zeroThreshold () { return zeroThreshold; } + ProfData_type get_packet_type () { return packet_type; } + Expression *get_cond () { return cond; } + Expression *get_val () { return val; } + Expression *get_expr () { return expr; } + char *get_expr_spec () { return expr_spec; } + Definition *get_definition () { return definition; }; + BaseMetric *get_dependent_bm () { return dependent_bm; }; + + bool + comparable () + { + return val_spec != NULL || type == DERIVED || type == SIZES || type == ADDRESS; + } + + // setters.. + void set_default_visbits (SubType subtype, int _visbits); + void set_id (int _id) { id = _id; } //TBR, if possible + // For comparison, set which packets to eval: + void set_expr_spec (char *_expr_spec); + void set_cond_spec (char *_cond_spec); + int get_default_visbits (SubType subtype); + char *dump (); + Histable *get_comparable_obj (Histable *obj); + bool is_internal (); // Invisible for users + + char *legend; // for comparison: add'l column text + +private: + BaseMetric *dependent_bm; // for HWCs only: a link to the timecvt metric + Expression *cond; // determines which packets to evaluate + char *cond_spec; // used to generate "cond" + Expression *val; // determines the numeric value for packet + char *val_spec; // used to generate "val" + Expression *expr; // for comparison: an additional expression to determine + // which packets to eval. Should be NULL otherwise. + char *expr_spec; // used to generate "expr" + int id; // unique id (assigned to last_id @ "new") + Type type; // e.g. HWCNTR + char *aux; // for HWCs only: Hwcentry ctr->name + char *cmd; // the .rc metric command, e.g. "total" + char *username; // e.g. "GTXT("Total Wait Time")" + int flavors; // bitmask of SubType capabilities + int value_styles; // bitmask of ValueType capabilities + static const int NSUBTYPES = 2; // STATIC/EXCLUSIVE, INCLUSIVE + int default_visbits[NSUBTYPES]; // ValueType, e.g. VAL_VALUE|VAL_TIMEVAL + ValueTag valtype; // e.g. VT_LLONG + long long precision; // e.g. METRIC_SIG_PRECISION, 1, etc. + Hwcentry *hw_ctr; // HWC definition + ProfData_type packet_type; // e.g. DATA_HWC, or -1 for N/A + bool zeroThreshold; // deadlock stuff + Presentation_clock_unit clock_unit; + + static int last_id; // incremented by 1 w/ every "new". Not MT-safe + Definition *definition; + + void hwc_init (Hwcentry *ctr, const char* _aux, const char* _cmdname, const char* _username, int v_styles); + void init (Type t); + char *get_basetype_name (); + void specify (); + void specify_metric (char *_cond_spec, char *_val_spec); + void set_val_spec (char *_val_spec); + void specify_mstate_metric (int st); + void specify_ompstate_metric (int st); + void specify_prof_metric (char *_cond_spec); +}; + +class Definition +{ +public: + + enum opType + { + opNULL, + opPrimitive, + opDivide + }; + + Definition (opType _op); + ~Definition (); + static Definition *add_definition (char *_def); + Vector<BaseMetric *> *get_dependencies (); + long *get_map (); + double eval (long *indexes, TValue *values); + + opType op; + Definition *arg1; + Definition *arg2; + char *def; + +private: + BaseMetric *bm; + long *map; + Vector<BaseMetric *> *dependencies; + long index; +}; + +#endif /* _BASEMETRIC_H */ + diff --git a/gprofng/src/BaseMetricTreeNode.cc b/gprofng/src/BaseMetricTreeNode.cc new file mode 100644 index 0000000..2d1db99 --- /dev/null +++ b/gprofng/src/BaseMetricTreeNode.cc @@ -0,0 +1,329 @@ +/* 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 <stdio.h> +#include <strings.h> +#include <limits.h> +#include <sys/param.h> + +#include "hwcentry.h" +#include "DbeSession.h" +#include "Experiment.h" +#include "Expression.h" +#include "Metric.h" +#include "Table.h" +#include "i18n.h" +#include "debug.h" + +BaseMetricTreeNode::BaseMetricTreeNode () +{ + init_vars (); + build_basic_tree (); +} + +BaseMetricTreeNode::BaseMetricTreeNode (BaseMetric *item) +{ + init_vars (); + bm = item; + name = dbe_strdup (bm->get_cmd ()); + uname = dbe_strdup (bm->get_username ()); + unit = NULL; //YXXX populate from base_metric (requires updating base_metric) + unit_uname = NULL; +} + +BaseMetricTreeNode::BaseMetricTreeNode (const char *_name, const char *_uname, + const char *_unit, const char *_unit_uname) +{ + init_vars (); + name = dbe_strdup (_name); + uname = dbe_strdup (_uname); + unit = dbe_strdup (_unit); + unit_uname = dbe_strdup (_unit_uname); +} + +void +BaseMetricTreeNode::init_vars () +{ + name = NULL; + uname = NULL; + unit = NULL; + unit_uname = NULL; + root = this; + parent = NULL; + children = new Vector<BaseMetricTreeNode*>; + isCompositeMetric = false; + bm = NULL; + registered = false; + num_registered_descendents = 0; +} + +BaseMetricTreeNode::~BaseMetricTreeNode () +{ + children->destroy (); + delete children; + free (name); + free (uname); + free (unit); + free (unit_uname); +} + +BaseMetricTreeNode * +BaseMetricTreeNode::register_metric (BaseMetric *item) +{ + BaseMetricTreeNode *found = root->find (item->get_cmd ()); + if (!found) + { + switch (item->get_type ()) + { + case BaseMetric::CP_TOTAL: + found = root->find (L_CP_TOTAL); + break; + case BaseMetric::CP_TOTAL_CPU: + found = root->find (L_CP_TOTAL_CPU); + break; + } + if (found && found->bm == NULL) + found->bm = item; + } + if (!found) + { + switch (item->get_type ()) + { + case BaseMetric::HEAP_ALLOC_BYTES: + case BaseMetric::HEAP_ALLOC_CNT: + case BaseMetric::HEAP_LEAK_BYTES: + case BaseMetric::HEAP_LEAK_CNT: + found = root->find (get_prof_data_type_name (DATA_HEAP)); + break; + case BaseMetric::CP_KERNEL_CPU: + case BaseMetric::CP_TOTAL: + found = root->find (get_prof_data_type_name (DATA_CLOCK)); + break; + case BaseMetric::CP_LMS_DFAULT: + case BaseMetric::CP_LMS_TFAULT: + case BaseMetric::CP_LMS_KFAULT: + case BaseMetric::CP_LMS_STOPPED: + case BaseMetric::CP_LMS_WAIT_CPU: + case BaseMetric::CP_LMS_SLEEP: + case BaseMetric::CP_LMS_USER_LOCK: + case BaseMetric::CP_TOTAL_CPU: + found = root->find (L_CP_TOTAL); + break; + case BaseMetric::CP_LMS_USER: + case BaseMetric::CP_LMS_SYSTEM: + case BaseMetric::CP_LMS_TRAP: + found = root->find (L_CP_TOTAL_CPU); + break; + case BaseMetric::HWCNTR: + found = root->find ((item->get_flavors () & BaseMetric::DATASPACE) != 0 ? + L2_HWC_DSPACE : L2_HWC_GENERAL); + break; + case BaseMetric::SYNC_WAIT_TIME: + case BaseMetric::SYNC_WAIT_COUNT: + found = root->find (get_prof_data_type_name (DATA_SYNCH)); + break; + case BaseMetric::OMP_WORK: + case BaseMetric::OMP_WAIT: + case BaseMetric::OMP_OVHD: + found = root->find (get_prof_data_type_name (DATA_OMP)); + break; + case BaseMetric::IO_READ_TIME: + case BaseMetric::IO_READ_BYTES: + case BaseMetric::IO_READ_CNT: + case BaseMetric::IO_WRITE_TIME: + case BaseMetric::IO_WRITE_BYTES: + case BaseMetric::IO_WRITE_CNT: + case BaseMetric::IO_OTHER_TIME: + case BaseMetric::IO_OTHER_CNT: + case BaseMetric::IO_ERROR_TIME: + case BaseMetric::IO_ERROR_CNT: + found = root->find (get_prof_data_type_name (DATA_IOTRACE)); + break; + case BaseMetric::ONAME: + case BaseMetric::SIZES: + case BaseMetric::ADDRESS: + found = root->find (L1_STATIC); + break; + default: + found = root->find (L1_OTHER); + break; + } + assert (found != NULL); + switch (item->get_type ()) + { + case BaseMetric::CP_TOTAL: + case BaseMetric::CP_TOTAL_CPU: + found->isCompositeMetric = true; + break; + } + found = found->add_child (item); + } + register_node (found); + return found; +} + +void +BaseMetricTreeNode::register_node (BaseMetricTreeNode *node) +{ + if (!node->registered) + { + node->registered = true; + BaseMetricTreeNode *tmp = node->parent; + while (tmp) + { + tmp->num_registered_descendents++; + tmp = tmp->parent; + } + } +} + +BaseMetricTreeNode * +BaseMetricTreeNode::find (const char *_name) +{ + BaseMetricTreeNode *found = NULL; + if (dbe_strcmp (get_name (), _name) == 0) + return this; + if (bm && dbe_strcmp (bm->get_cmd (), _name) == 0) + return this; + BaseMetricTreeNode *child; + int index; + + Vec_loop (BaseMetricTreeNode*, children, index, child) + { + found = child->find (_name); + if (found) + return found; + } + return NULL; +} + +static void +int_get_registered_descendents (BaseMetricTreeNode* curr, + Vector<BaseMetricTreeNode*> *dest, bool nearest_only) +{ + if (!curr) + return; + if (curr->is_registered ()) + { + dest->append (curr); + if (nearest_only) + return; // soon as we hit a live node, stop following branch + } + int index; + BaseMetricTreeNode *child; + + Vec_loop (BaseMetricTreeNode*, curr->get_children (), index, child) + { + int_get_registered_descendents (child, dest, nearest_only); + } +} + +void +BaseMetricTreeNode::get_nearest_registered_descendents (Vector<BaseMetricTreeNode*> *dest) +{ + if (!dest || dest->size () != 0) + abort (); + bool nearest_only = true; + int_get_registered_descendents (this, dest, nearest_only); +} + +void +BaseMetricTreeNode::get_all_registered_descendents (Vector<BaseMetricTreeNode*> *dest) +{ + if (!dest || dest->size () != 0) + abort (); + bool nearest_only = false; + int_get_registered_descendents (this, dest, nearest_only); +} + +char * +BaseMetricTreeNode::get_description () +{ + if (bm) + { + Hwcentry* hw_ctr = bm->get_hw_ctr (); + if (hw_ctr) + return hw_ctr->short_desc; + } + return NULL; +} + +void +BaseMetricTreeNode::build_basic_tree () +{ +#define TREE_INSERT_DATA_TYPE(t) add_child(get_prof_data_type_name (t), get_prof_data_type_uname (t)) + BaseMetricTreeNode *level1, *level2; + // register L1_DURATION here because it has a value but is not a true metric + register_node (add_child (L1_DURATION, L1_DURATION_UNAME, UNIT_SECONDS, + UNIT_SECONDS_UNAME)); + register_node (add_child (L1_GCDURATION, L1_GCDURATION_UNAME, UNIT_SECONDS, + UNIT_SECONDS_UNAME)); + TREE_INSERT_DATA_TYPE (DATA_HEAP); + level1 = TREE_INSERT_DATA_TYPE (DATA_CLOCK); + level1 = level1->add_child (L_CP_TOTAL, GTXT ("XXX Total Thread Time")); + level1->isCompositeMetric = true; + level2 = level1->add_child (L_CP_TOTAL_CPU, GTXT ("XXX Total CPU Time")); + level2->isCompositeMetric = true; + + add_child (L1_OTHER, L1_OTHER_UNAME); + level1 = TREE_INSERT_DATA_TYPE (DATA_HWC); + level1->add_child (L2_HWC_DSPACE, L2_HWC_DSPACE_UNAME); + level1->add_child (L2_HWC_GENERAL, L2_HWC_GENERAL_UNAME); + TREE_INSERT_DATA_TYPE (DATA_SYNCH); + TREE_INSERT_DATA_TYPE (DATA_OMP); + TREE_INSERT_DATA_TYPE (DATA_IOTRACE); + add_child (L1_STATIC, L1_STATIC_UNAME); +} + +BaseMetricTreeNode * +BaseMetricTreeNode::add_child (BaseMetric *item) +{ + return add_child (new BaseMetricTreeNode (item)); +} + +BaseMetricTreeNode * +BaseMetricTreeNode::add_child (const char * _name, const char *_uname, + const char * _unit, const char * _unit_uname) +{ + return add_child (new BaseMetricTreeNode (_name, _uname, _unit, _unit_uname)); +} + +BaseMetricTreeNode * +BaseMetricTreeNode::add_child (BaseMetricTreeNode *new_node) +{ + new_node->parent = this; + new_node->root = root; + children->append (new_node); + return new_node; +} + +char * +BaseMetricTreeNode::dump () +{ + int len = 4; + char *s = bm ? bm->dump () : dbe_strdup ("<no base metric>"); + char *msg = dbe_sprintf ("%s\n%*c %*c unit='%s' unit_uname='%s' uname='%s' name='%s'\n", + STR (s), len, ' ', len, ' ', + STR (get_unit_uname ()), STR (get_unit ()), + STR (get_user_name ()), STR (get_name ())); + free (s); + return msg; +} diff --git a/gprofng/src/BaseMetricTreeNode.h b/gprofng/src/BaseMetricTreeNode.h new file mode 100644 index 0000000..0076dff --- /dev/null +++ b/gprofng/src/BaseMetricTreeNode.h @@ -0,0 +1,100 @@ +/* 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. */ + +#ifndef _BASEMETRICTREENODE_H +#define _BASEMETRICTREENODE_H + +#include "BaseMetric.h" + +// Unit values +#define UNIT_SECONDS "SECONDS" +#define UNIT_SECONDS_UNAME GTXT("secs.") +#define UNIT_BYTES "BYTES" +#define UNIT_BYTES_UNAME GTXT("bytes") + +// Name values for intermediate parent nodes that aren't defined elsewhere +#define L1_DURATION "PROFDATA_TYPE_DURATION" +#define L1_DURATION_UNAME GTXT("Experiment Duration") +#define L1_GCDURATION "PROFDATA_TYPE_GCDURATION" +#define L1_GCDURATION_UNAME GTXT("Java Garbage Collection Duration") +#define L2_HWC_DSPACE "PROFDATA_TYPE_HWC_DSPACE" +#define L2_HWC_DSPACE_UNAME GTXT("Memoryspace Hardware Counters") +#define L2_HWC_GENERAL "PROFDATA_TYPE_HWC_GENERAL" +#define L2_HWC_GENERAL_UNAME GTXT("General Hardware Counters") +#define L1_MPI_STATES "PROFDATA_TYPE_MPI_STATES" +#define L1_MPI_STATES_UNAME GTXT("MPI States") +#define L1_OTHER "PROFDATA_TYPE_OTHER" +#define L1_OTHER_UNAME GTXT("Derived and Other Metrics") +#define L1_STATIC "PROFDATA_TYPE_STATIC" +#define L1_STATIC_UNAME GTXT("Static") +#define L_CP_TOTAL "L_CP_TOTAL" +#define L_CP_TOTAL_CPU "L_CP_TOTAL_CPU" + +class BaseMetricTreeNode +{ +public: + BaseMetricTreeNode (); // builds basic metric tree (not including HWCs) + virtual ~BaseMetricTreeNode (); + BaseMetricTreeNode *register_metric (BaseMetric *item); + BaseMetricTreeNode *find (const char *name); + void get_nearest_registered_descendents (Vector<BaseMetricTreeNode*> *new_vec); + void get_all_registered_descendents (Vector<BaseMetricTreeNode*> *new_vec); + char *get_description(); + char *dump(); + + BaseMetricTreeNode *get_root () { return root; } + BaseMetricTreeNode *get_parent () { return parent; } + Vector<BaseMetricTreeNode*> *get_children () { return children; } + bool is_registered () { return registered; } + int get_num_registered_descendents () { return num_registered_descendents; } + bool is_composite_metric () { return isCompositeMetric; } + BaseMetric *get_BaseMetric () { return bm; } + char *get_name () { return name; } + char *get_user_name () { return uname; } + char *get_unit () { return unit; } + char *get_unit_uname () { return unit_uname; } + +private: + BaseMetricTreeNode (BaseMetric *item); + BaseMetricTreeNode (const char *name, const char *uname, + const char *_unit, const char *_unit_uname); + void init_vars (); + void build_basic_tree (); + BaseMetricTreeNode *add_child (BaseMetric *item); + BaseMetricTreeNode *add_child (const char *name, const char *uname, + const char *unit = NULL, const char *unit_uname = NULL); + BaseMetricTreeNode *add_child (BaseMetricTreeNode *new_node); + void register_node (BaseMetricTreeNode *); + + BaseMetricTreeNode *root; // root of tree + BaseMetricTreeNode *parent; // my parent + bool aggregation; // value is based on children's values + char *name; // bm->get_cmd() for metrics, unique string otherwise + char *uname; // user-visible text + char *unit; // see UNIT_* defines + char *unit_uname; // see UNIT_*_UNAME defines + Vector<BaseMetricTreeNode*> *children; // my children + bool isCompositeMetric; // value is sum of children + BaseMetric *bm; // metric for this node, or null + bool registered; // metric has been officially registered + int num_registered_descendents; // does not include self +}; + +#endif /* _BASEMETRICTREENODE_H */ diff --git a/gprofng/src/CacheMap.h b/gprofng/src/CacheMap.h new file mode 100644 index 0000000..a575749 --- /dev/null +++ b/gprofng/src/CacheMap.h @@ -0,0 +1,186 @@ +/* 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. */ + +/* + * Cache Map implementation. + * + * Cache Map makes the following assumptions: + * - Cache Map is used for very fast but not guaranteed mapping; + * - only REL_EQ Relation can be used; + * - all objects used as keys or values has to be managed + * outside CacheMap (f.e. deletion); + * - (Key_t)0 is invalid key; + * - (Value_t)0 is invalid value; + * - <TBC> + */ + +#ifndef _DBE_CACHEMAP_H +#define _DBE_CACHEMAP_H + +#include <assert.h> +#include <vec.h> +#include <Map.h> + +template <typename Key_t, typename Value_t> +class CacheMap : public Map<Key_t, Value_t> +{ +public: + + CacheMap (); + ~CacheMap (); + void put (Key_t key, Value_t val); + Value_t get (Key_t key); + Value_t get (Key_t key, typename Map<Key_t, Value_t>::Relation rel); + Value_t + remove (Key_t key); + +private: + + struct Entry + { + Key_t key; + Value_t val; + + Entry () + { + key = (Key_t) 0; + } + }; + + static const int INIT_SIZE; + static const int MAX_SIZE; + + static unsigned hash (Key_t key); + Entry *getEntry (Key_t key); + + int cursize; + int nputs; + int nchunks; + Entry **chunks; +}; + +template <typename Key_t, typename Value_t> +const int CacheMap<Key_t, Value_t>::INIT_SIZE = 1 << 14; +template <typename Key_t, typename Value_t> +const int CacheMap<Key_t, Value_t>::MAX_SIZE = 1 << 20; + +template <typename Key_t, typename Value_t>CacheMap<Key_t, Value_t> +::CacheMap () +{ + cursize = INIT_SIZE; + chunks = new Entry*[32]; + nchunks = 0; + chunks[nchunks++] = new Entry[cursize]; + nputs = 0; +} + +template <typename Key_t, typename Value_t> +CacheMap<Key_t, Value_t>::~CacheMap () +{ + for (int i = 0; i < nchunks; i++) + delete[] chunks[i]; + delete[] chunks; +} + +template <typename Key_t, typename Value_t> +unsigned +CacheMap<Key_t, Value_t>::hash (Key_t key) +{ + unsigned h = (unsigned) key ^ (unsigned) (key >> 32); + h ^= (h >> 20) ^ (h >> 12); + return h ^ (h >> 7) ^ (h >> 4); +} + +template <typename Key_t, typename Value_t> +void +CacheMap<Key_t, Value_t>::put (Key_t key, Value_t val) +{ + if (nputs >= cursize && cursize < MAX_SIZE) + { + // Allocate new chunk for entries. + chunks[nchunks++] = new Entry[cursize]; + cursize *= 2; + + // Copy all old entries to the newly allocated chunk + Entry *newchunk = chunks[nchunks - 1]; + int prevsz = 0; + int nextsz = INIT_SIZE; + for (int i = 0; i < nchunks - 1; i++) + { + Entry *oldchunk = chunks[i]; + for (int j = prevsz; j < nextsz; j++) + newchunk[j] = oldchunk[j - prevsz]; + prevsz = nextsz; + nextsz *= 2; + } + } + Entry *entry = getEntry (key); + entry->key = key; + entry->val = val; + nputs++; +} + +template <typename Key_t, typename Value_t> +typename CacheMap<Key_t, Value_t>::Entry * +CacheMap<Key_t, Value_t>::getEntry (Key_t key) +{ + unsigned idx = hash (key); + int i = nchunks - 1; + int j = cursize / 2; + for (; i > 0; i -= 1, j /= 2) + if (idx & j) + break; + if (i == 0) + j *= 2; + return &chunks[i][idx & (j - 1)]; +} + +template <typename Key_t, typename Value_t> +Value_t +CacheMap<Key_t, Value_t>::get (Key_t key) +{ + Entry *entry = getEntry (key); + return entry->key == key ? entry->val : (Value_t) 0; +} + +template <typename Key_t, typename Value_t> +Value_t +CacheMap<Key_t, Value_t>::get (Key_t key, typename Map<Key_t, Value_t>::Relation rel) +{ + if (rel != Map<Key_t, Value_t>::REL_EQ) + return (Value_t) 0; + return get (key); +} + +template <typename Key_t, typename Value_t> +Value_t +CacheMap<Key_t, Value_t>::remove (Key_t key) +{ + Entry *entry = getEntry (key); + Value_t res = (Value_t) 0; + if (entry->key == key) + { + res = entry->val; + entry->val = (Value_t) 0; + } + return res; +} + +#endif diff --git a/gprofng/src/CallStack.cc b/gprofng/src/CallStack.cc new file mode 100644 index 0000000..7671f9f --- /dev/null +++ b/gprofng/src/CallStack.cc @@ -0,0 +1,1250 @@ +/* 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 <new> + +#include "util.h" +#include "CacheMap.h" +#include "CallStack.h" +#include "DbeSession.h" +#include "DbeView.h" +#include "DbeLinkList.h" +#include "Experiment.h" +#include "Exp_Layout.h" +#include "Function.h" +#include "LoadObject.h" +#include "Module.h" + +Descendants::Descendants () +{ + count = 0; + limit = sizeof (first_data) / sizeof (CallStackNode *); + data = first_data; +} + +Descendants::~Descendants () +{ + if (data != first_data) + free (data); +} + +CallStackNode * +Descendants::find (Histable *hi, int *index) +{ + int cnt = count; + int left = 0; + for (int right = cnt - 1; left <= right;) + { + int ind = (left + right) / 2; + CallStackNode *node = data[ind]; + Histable *instr = node->get_instr (); + if (instr == hi) + { + if (index) + *index = ind; + return node; + } + if (instr->id < hi->id) + right = ind - 1; + else + left = ind + 1; + } + if (index) + *index = left; + return NULL; +} + +void +Descendants::append (CallStackNode* item) +{ + if (count < limit) + data[count++] = item; + else + insert (count, item); +} + +void +Descendants::insert (int ind, CallStackNode* item) +{ + CallStackNode **old_data = data; + int old_cnt = count; + if (old_cnt + 1 >= limit) + { + int new_limit = (limit == 0) ? DELTA : limit * 2; + CallStackNode **new_data = (CallStackNode **) malloc (new_limit * sizeof (CallStackNode *)); + for (int i = 0; i < ind; i++) + new_data[i] = old_data[i]; + new_data[ind] = item; + for (int i = ind; i < old_cnt; i++) + new_data[i + 1] = old_data[i]; + limit = new_limit; + data = new_data; + if (old_data != first_data) + free (old_data); + } + else + { + for (int i = ind; i < old_cnt; i++) + old_data[i + 1] = old_data[i]; + old_data[ind] = item; + } + count++; +} + +/* + * Private implementation of CallStack interface + */ + +// When performing pipeline optimization on resolve_frame_info + add_stack +// cstk_ctx structure contains the state (or context) for one iteration to pass on +// from Phase 2 to Phase 3 (More details in Experiment.cc) +class CallStackP : public CallStack +{ +public: + CallStackP (Experiment *exp); + + virtual ~CallStackP (); + + virtual void add_stack (DataDescriptor *dDscr, long idx, FramePacket *frp, cstk_ctx_chunk *cstCtxChunk); + virtual void *add_stack (Vector<Histable*> *objs); + virtual CallStackNode *get_node (int n); + virtual void print (FILE *); + +private: + + static const int CHUNKSZ = 16384; + + Experiment *experiment; + CallStackNode *root; + CallStackNode *jvm_node; + int nodes; + int nchunks; + CallStackNode **chunks; + Map<uint64_t, CallStackNode *> *cstackMap; + DbeLock *cstackLock; + + CallStackNode *add_stack (long start, long end, Vector<Histable*> *objs, CallStackNode *myRoot); + CallStackNode *new_Node (CallStackNode*, Histable*); + CallStackNode *find_preg_stack (uint64_t); + // objs are in the root..leaf order + void *add_stack_d (Vector<Histable*> *objs); + void add_stack_java (DataDescriptor *dDscr, long idx, FramePacket *frp, hrtime_t tstamp, uint32_t thrid, Vector<DbeInstr*>* natpcs, bool natpc_added, cstk_ctx_chunk *cstCtxChunk); + void add_stack_java_epilogue (DataDescriptor *dDscr, long idx, FramePacket *frp, hrtime_t tstamp, uint32_t thrid, Vector<DbeInstr*>* natpcs, Vector<Histable*>* jpcs, bool natpc_added); + + // Adjust HW counter event to find better trigger PC, etc. + DbeInstr *adjustEvent (DbeInstr *leafPC, DbeInstr * candPC, + Vaddr &eventEA, int abst_type); + Vector<DbeInstr*> *natpcsP; + Vector<Histable*> *jpcsP; +}; + +CallStackP::CallStackP (Experiment *exp) +{ + experiment = exp; + nchunks = 0; + chunks = NULL; + nodes = 0; + cstackMap = new CacheMap<uint64_t, CallStackNode *>; + cstackLock = new DbeLock (); + Function *total = dbeSession->get_Total_Function (); + root = new_Node (0, total->find_dbeinstr (0, 0)); + jvm_node = NULL; + natpcsP = NULL; + jpcsP = NULL; +} + +CallStackP::~CallStackP () +{ + delete cstackLock; + if (chunks) + { + for (int i = 0; i < nodes; i++) + { + CallStackNode *node = get_node (i); + node->~CallStackNode (); + } + for (int i = 0; i < nchunks; i++) + free (chunks[i]); + free (chunks); + } + delete natpcsP; + delete jpcsP; + destroy_map (CallStackNode *, cstackMap); +} + +CallStackNode * +CallStackP::new_Node (CallStackNode *anc, Histable *pcval) +{ + // cstackLock->aquireLock(); // Caller already locked it + if (nodes >= nchunks * CHUNKSZ) + { + CallStackNode **old_chunks = chunks; + nchunks++; + + // Reallocate Node chunk array + chunks = (CallStackNode **) malloc (nchunks * sizeof (CallStackNode *)); + for (int i = 0; i < nchunks - 1; i++) + chunks[i] = old_chunks[i]; + free (old_chunks); + // Allocate new chunk for nodes. + chunks[nchunks - 1] = (CallStackNode *) malloc (CHUNKSZ * sizeof (CallStackNode)); + } + nodes++; + CallStackNode *node = get_node (nodes - 1); + new (node) CallStackNode (anc, pcval); + // cstackLock->releaseLock(); + return node; +} + +CallStackNode * +CallStackP::find_preg_stack (uint64_t prid) +{ + DataView *dview = experiment->getOpenMPdata (); + dview->sort (PROP_CPRID); + Datum tval; + tval.setUINT64 (prid); + long idx = dview->getIdxByVals (&tval, DataView::REL_EQ); + if (idx < 0) + return root; + CallStackNode *node = (CallStackNode*) dview->getObjValue (PROP_USTACK, idx); + if (node != NULL) + return node; + uint64_t pprid = dview->getLongValue (PROP_PPRID, idx); + if (pprid == prid) + return root; + void *nat_stack = dview->getObjValue (PROP_MSTACK, idx); + Vector<Histable*> *pcs = getStackPCs (nat_stack); + + // Find the bottom frame + int btm; + bool inOMP = false; + DbeInstr *instr; + Histable *hist; + for (btm = 0; btm < pcs->size (); btm++) + { + hist = pcs->fetch (btm); + if (hist->get_type () == Histable::INSTR) + instr = (DbeInstr *) hist; + else // DBELINE + instr = (DbeInstr *) hist->convertto (Histable::INSTR); + LoadObject *lo = instr->func->module->loadobject; + if (!inOMP) + { + if (lo->flags & SEG_FLAG_OMP) + inOMP = true; + } + else if (!(lo->flags & SEG_FLAG_OMP)) + break; + } + + // Find the top frame + dview->sort (PROP_CPRID); + int top; + tval.setUINT64 (pprid); + long pidx = dview->getIdxByVals (&tval, DataView::REL_EQ); + if (pidx < 0) // No parent. Process the entire nat_stack + top = pcs->size () - 1; + else + { + uint32_t thrid = (uint32_t) dview->getIntValue (PROP_THRID, idx); + uint32_t pthrid = (uint32_t) dview->getIntValue (PROP_THRID, pidx); + if (thrid != pthrid) + { + // Parent is on a different stack. + // Process the entire nat_stack. Skip libthread. + for (top = pcs->size () - 1; top >= 0; top--) + { + hist = pcs->fetch (top); + if (hist->get_type () == Histable::INSTR) + instr = (DbeInstr *) hist; + else // DBELINE + instr = (DbeInstr *) hist->convertto (Histable::INSTR); + if (instr->func->module->loadobject->flags & SEG_FLAG_OMP) + break; + } + if (top < 0) // None found. May be incomplete call stack (x86) + top = pcs->size () - 1; + } + else + { + // Parent is on the same stack. Find match. + top = pcs->size () - 1; + void *pnat_stack = dview->getObjValue (PROP_MSTACK, pidx); + Vector<Histable*> *ppcs = getStackPCs (pnat_stack); + for (int ptop = ppcs->size () - 1; top >= 0 && ptop >= 0; + top--, ptop--) + { + if (pcs->fetch (top) != ppcs->fetch (ptop)) + break; + } + delete ppcs; + } + } + + // Process the found range + Vector<Histable*> *upcs = new Vector<Histable*>(128); + for (int i = btm; i <= top; ++i) + { + hist = (DbeInstr*) pcs->fetch (i); + if (hist->get_type () == Histable::INSTR) + instr = (DbeInstr *) hist; + else // DBELINE + instr = (DbeInstr *) hist->convertto (Histable::INSTR); + + if (instr->func->module->loadobject->flags & SEG_FLAG_OMP) + // Skip all frames from libmtsk + continue; + upcs->append (instr); + } + delete pcs; + node = find_preg_stack (pprid); + while (node != root) + { + upcs->append (node->instr); + node = node->ancestor; + } + node = (CallStackNode *) add_stack (upcs); + dview->setObjValue (PROP_USTACK, idx, node); + delete upcs; + return node; +} + +#define JNI_MARKER -3 + +// This is one iteration if the third stage of +// resolve_frame_info + add_stack pipeline. Works on building the java +// stacks +void +CallStackP::add_stack_java (DataDescriptor *dDscr, long idx, FramePacket *frp, + hrtime_t tstamp, uint32_t thrid, + Vector<DbeInstr*>* natpcs, bool natpc_added, + cstk_ctx_chunk *cstCtxChunk) +{ + Vector<Histable*> *jpcs = NULL; + cstk_ctx *cstctx = NULL; + if (cstCtxChunk != NULL) + { + cstctx = cstCtxChunk->cstCtxAr[idx % CSTCTX_CHUNK_SZ]; + jpcs = cstctx->jpcs; + jpcs->reset (); + } + if (jpcs == NULL) + { + // this is when we are not doing the pipeline optimization + // Temporary array for resolved addresses + // [leaf_pc .. root_pc] == [0..stack_size-1] + // Leave room for a possible "truncated" frame + if (jpcsP == NULL) + jpcsP = new Vector<Histable*>; + jpcs = jpcsP; + jpcs->reset (); + } + + // + // Construct the user stack + // + // Construct Java user stack + int jstack_size = frp->stackSize (true); + if (jstack_size) + { + // jpcs = new Vector<Histable*>( jstack_size ); + if (frp->isTruncatedStack (true)) + { + Function *truncf = dbeSession->getSpecialFunction (DbeSession::TruncatedStackFunc); + jpcs->append (truncf->find_dbeinstr (0, 0)); + } + + int nind = natpcs->size () - 1; // first native frame + for (int jind = jstack_size - 1; jind >= 0; jind--) + { + bool jleaf = (jind == 0); // is current java frame a leaf? + Vaddr mid = frp->getMthdFromStack (jind); + int bci = frp->getBciFromStack (jind); + DbeInstr *cur_instr = experiment->map_jmid_to_PC (mid, bci, tstamp); + jpcs->append (cur_instr); + if (bci == JNI_MARKER) + { + JMethod *j_method = (JMethod*) cur_instr->func; + // Find matching native function on the native stack + bool found = false; + for (; nind >= 0; nind--) + { + DbeInstr *nat_addr = natpcs->fetch (nind); + if (0 == nat_addr) + continue; + Function *nat_func = nat_addr->func; + if (!found && j_method->jni_match (nat_func)) + found = true; + if (found) + { + // XXX omazur: the following will skip JNI native method + // implemented in JVM itself. + // If we are back in JVM switch to processing Java + // frames if there are any. + if ((nat_func->module->loadobject->flags & SEG_FLAG_JVM) && !jleaf) + break; + jpcs->append (nat_addr); + } + } + } + } + } + add_stack_java_epilogue (dDscr, idx, frp, tstamp, thrid, natpcs, jpcs, natpc_added); +} + +// This is one iteration if the fourth stage of +// resolve_frame_info + add_stack pipeline. +// It adds the native and java stacks to the stackmap + +void +CallStackP::add_stack_java_epilogue (DataDescriptor *dDscr, long idx, FramePacket *frp, hrtime_t tstamp, uint32_t thrid, Vector<DbeInstr*>* natpcs, Vector<Histable*> *jpcs, bool natpc_added) +{ + CallStackNode *node = NULL; + if (!natpc_added) + { + node = (CallStackNode *) add_stack ((Vector<Histable*>*)natpcs); + dDscr->setObjValue (PROP_MSTACK, idx, node); + dDscr->setObjValue (PROP_XSTACK, idx, node); + dDscr->setObjValue (PROP_USTACK, idx, node); + } + + int jstack_size = frp->stackSize (true); + if (jstack_size) + { + if (jpcs != NULL) + node = (CallStackNode *) add_stack_d (jpcs); + if (node == NULL) + node = (CallStackNode*) dDscr->getObjValue (PROP_USTACK, idx); + dDscr->setObjValue (PROP_USTACK, idx, node); + Function *func = (Function*) node->instr->convertto (Histable::FUNCTION); + if (func != dbeSession->get_JUnknown_Function ()) + dDscr->setObjValue (PROP_XSTACK, idx, node); + } + + JThread *jthread = experiment->map_pckt_to_Jthread (thrid, tstamp); + if (jthread == JTHREAD_NONE && jstack_size != 0 && node != NULL) + { + Function *func = (Function*) node->instr->convertto (Histable::FUNCTION); + if (func != dbeSession->get_JUnknown_Function ()) + jthread = JTHREAD_DEFAULT; + } + dDscr->setObjValue (PROP_JTHREAD, idx, jthread); + if (jthread == JTHREAD_NONE || (jthread != JTHREAD_DEFAULT && jthread->is_system ())) + { + if (jvm_node == NULL) + { + Function *jvm = dbeSession->get_jvm_Function (); + if (jvm) + { + jvm_node = new_Node (root, jvm->find_dbeinstr (0, 0)); + CommonPacket::jvm_overhead = jvm_node; + } + } + dDscr->setObjValue (PROP_USTACK, idx, jvm_node); + } +} + +// This is one iteration of the 2nd stage of +// resolve_frame_info + add_stack() pipeline. Builds the stack for a given framepacket. +// When pipeline optimization is turnd off, cstctxchunk passed is NULL +void +CallStackP::add_stack (DataDescriptor *dDscr, long idx, FramePacket *frp, + cstk_ctx_chunk* cstCtxChunk) +{ + Vector<DbeInstr*> *natpcs = NULL; + cstk_ctx *cstctx = NULL; + int stack_size = frp->stackSize (); + if (cstCtxChunk != NULL) + { + cstctx = cstCtxChunk->cstCtxAr[idx % CSTCTX_CHUNK_SZ]; + natpcs = cstctx->natpcs; + natpcs->reset (); + } + if (natpcs == NULL) + { + // this is when we are not doing the pipeline optimization + // Temporary array for resolved addresses + // [leaf_pc .. root_pc] == [0..stack_size-1] + // Leave room for a possible "truncated" frame + if (natpcsP == NULL) + natpcsP = new Vector<DbeInstr*>; + natpcs = natpcsP; + natpcs->reset (); + } + + bool leaf = true; + hrtime_t tstamp = (hrtime_t) dDscr->getLongValue (PROP_TSTAMP, idx); + uint32_t thrid = (uint32_t) dDscr->getIntValue (PROP_THRID, idx); + + enum + { + NONE, + CHECK_O7, + USE_O7, + SKIP_O7 + } state = NONE; + + Vaddr o7_to_skip = 0; + for (int index = 0; index < stack_size; index++) + { + if (frp->isLeafMark (index)) + { + state = CHECK_O7; + continue; + } + + if (state == SKIP_O7) + { + // remember this bad o7 value since OMP might not recognize it + o7_to_skip = frp->getFromStack (index); + state = NONE; + continue; + } + + Vaddr va = frp->getFromStack (index); + DbeInstr *cur_instr = experiment->map_Vaddr_to_PC (va, tstamp); +#if ARCH(Intel)// TBR? FIXUP_XXX_SPARC_LINUX: switch should be on experiment ARCH, not dbe ARCH + // We need to adjust return addresses on intel + // in order to attribute inclusive metrics to + // proper call instructions. + if (experiment->exp_maj_version <= 9) + if (!leaf && cur_instr->addr != 0) + cur_instr = cur_instr->func->find_dbeinstr (0, cur_instr->addr - 1); +#endif + + // Skip PC's from PLT, update leaf and state accordingly + if ((cur_instr->func->flags & FUNC_FLAG_PLT) + && (leaf || state == CHECK_O7)) + { + if (state == CHECK_O7) + state = USE_O7; + leaf = false; + continue; + } + if (state == CHECK_O7) + { + state = USE_O7; + uint64_t saddr = cur_instr->func->save_addr; + if (cur_instr->func->isOutlineFunction) + // outline functions assume 'save' instruction + // Note: they accidentally have saddr == FUNC_ROOT + state = SKIP_O7; + else if (saddr == FUNC_ROOT) + { + // If a function is statically determined as a root + // but dynamically appears not, don't discard o7. + // One such case is __misalign_trap_handler on sparcv9. + if (stack_size == 3) + state = SKIP_O7; + } + else if (saddr != FUNC_NO_SAVE && cur_instr->addr > saddr) + state = SKIP_O7; + } + else if (state == USE_O7) + { + state = NONE; + if (cur_instr->flags & PCInvlFlag) + continue; + } + if (leaf) + { + Vaddr evpc = (Vaddr) dDscr->getLongValue (PROP_VIRTPC, idx); + if (evpc != 0 + && !(index > 0 && frp->isLeafMark (index - 1) + && evpc == (Vaddr) (-1))) + { + /* contains hwcprof info */ + cur_instr->func->module->read_hwcprof_info (); + + // complete ABS validation of candidate eventPC/eventEA + // and correction/adjustment of collected callstack leaf PC + DbeInstr *candPC = experiment->map_Vaddr_to_PC (evpc, tstamp); + Vaddr vaddr = (Vaddr) dDscr->getLongValue (PROP_VADDR, idx); + Vaddr tmp_vaddr = vaddr; + int abst_type; + uint32_t tag = dDscr->getIntValue (PROP_HWCTAG, idx); + if (tag < 0 || tag >= MAX_HWCOUNT) + abst_type = ABST_NOPC; + else + abst_type = experiment->coll_params.hw_tpc[tag]; + + // We need to adjust addresses for ABST_EXACT_PEBS_PLUS1 + // (Nehalem/SandyBridge PEBS identifies PC+1, not PC) + if (abst_type == ABST_EXACT_PEBS_PLUS1 && candPC->addr != 0) + candPC = candPC->func->find_dbeinstr (0, candPC->func->find_previous_addr (candPC->addr)); + + cur_instr = adjustEvent (cur_instr, candPC, tmp_vaddr, abst_type); + if (vaddr != tmp_vaddr) + { + if (tmp_vaddr < ABS_CODE_RANGE) + { + /* post processing backtrack failed */ + dDscr->setValue (PROP_VADDR, idx, tmp_vaddr); + dDscr->setValue (PROP_PADDR, idx, ABS_NULL); + /* hwcp->eventVPC = xxxxx leave eventPC alone, + * or can we set it to leafpc? */ + dDscr->setValue (PROP_PHYSPC, idx, ABS_NULL); + } + else + { + /* internal error: why would post-processing modify vaddr? */ + dDscr->setValue (PROP_PADDR, idx, (Vaddr) (-1)); + dDscr->setValue (PROP_PHYSPC, idx, (Vaddr) (-1)); + } + } + } + } + natpcs->append (cur_instr); + leaf = false; + + // A hack to deceive the user into believing that outlined code + // is called from the base function + DbeInstr *drvd = cur_instr->func->derivedNode; + if (drvd != NULL) + natpcs->append (drvd); + } + if (frp->isTruncatedStack ()) + { + Function *truncf = dbeSession->getSpecialFunction (DbeSession::TruncatedStackFunc); + natpcs->append (truncf->find_dbeinstr (0, 0)); + } + else if (frp->isFailedUnwindStack ()) + { + Function *funwf = dbeSession->getSpecialFunction (DbeSession::FailedUnwindFunc); + natpcs->append (funwf->find_dbeinstr (0, 0)); + } + + CallStackNode *node = (CallStackNode*) add_stack ((Vector<Histable*>*)natpcs); + dDscr->setObjValue (PROP_MSTACK, idx, node); + dDscr->setObjValue (PROP_XSTACK, idx, node); + dDscr->setObjValue (PROP_USTACK, idx, node); + + // OpenMP 3.0 stacks + stack_size = frp->ompstack->size (); + if (stack_size > 0 || frp->omp_state == OMP_IDLE_STATE) + { + Function *func; + Vector<Histable*> *omppcs = new Vector<Histable*>(stack_size); + Vector<Histable*> *ompxpcs = new Vector<Histable*>(stack_size); + switch (frp->omp_state) + { + case OMP_IDLE_STATE: + case OMP_RDUC_STATE: + case OMP_IBAR_STATE: + case OMP_EBAR_STATE: + case OMP_LKWT_STATE: + case OMP_CTWT_STATE: + case OMP_ODWT_STATE: + case OMP_ATWT_STATE: + { + func = dbeSession->get_OMP_Function (frp->omp_state); + DbeInstr *instr = func->find_dbeinstr (0, 0); + omppcs->append (instr); + ompxpcs->append (instr); + break; + } + } + Vector<Vaddr> *stck = frp->ompstack; + leaf = true; + for (int index = 0; index < stack_size; index++) + { + if (stck->fetch (index) == SP_LEAF_CHECK_MARKER) + { + state = CHECK_O7; + continue; + } + if (state == SKIP_O7) + { + state = NONE; + continue; + } + + // The OMP stack might not have enough information to know to discard a bad o7. + // So just remember what the native stack skipped. + if (o7_to_skip == stck->fetch (index)) + { + state = NONE; + continue; + } + Vaddr va = stck->fetch (index); + DbeInstr *cur_instr = experiment->map_Vaddr_to_PC (va, tstamp); + + // Skip PC's from PLT, update leaf and state accordingly + if ((cur_instr->func->flags & FUNC_FLAG_PLT) && + (leaf || state == CHECK_O7)) + { + if (state == CHECK_O7) + state = USE_O7; + leaf = false; + continue; + } + if (state == CHECK_O7) + { + state = USE_O7; + uint64_t saddr = cur_instr->func->save_addr; + if (cur_instr->func->isOutlineFunction) + // outline functions assume 'save' instruction + // Note: they accidentally have saddr == FUNC_ROOT + state = SKIP_O7; + else if (saddr == FUNC_ROOT) + { + // If a function is statically determined as a root + // but dynamically appears not, don't discard o7. + // One such case is __misalign_trap_handler on sparcv9. + if (stack_size == 3) + state = SKIP_O7; + } + else if (saddr != FUNC_NO_SAVE && cur_instr->addr > saddr) + state = SKIP_O7; + } + else if (state == USE_O7) + { + state = NONE; + if (cur_instr->flags & PCInvlFlag) + continue; + } + + DbeLine *dbeline = (DbeLine*) cur_instr->convertto (Histable::LINE); + if (cur_instr->func->usrfunc) + { + dbeline = dbeline->sourceFile->find_dbeline (cur_instr->func->usrfunc, dbeline->lineno); + omppcs->append (dbeline); + } + else if (dbeline->lineno > 0) + omppcs->append (dbeline); + else + omppcs->append (cur_instr); + if (dbeline->is_set (DbeLine::OMPPRAGMA) && + frp->omp_state == OMP_WORK_STATE) + dDscr->setValue (PROP_OMPSTATE, idx, OMP_OVHD_STATE); + ompxpcs->append (cur_instr); + leaf = false; + } + if (frp->omptruncated == SP_TRUNC_STACK_MARKER) + { + func = dbeSession->getSpecialFunction (DbeSession::TruncatedStackFunc); + DbeInstr *instr = func->find_dbeinstr (0, 0); + omppcs->append (instr); + ompxpcs->append (instr); + } + else if (frp->omptruncated == SP_FAILED_UNWIND_MARKER) + { + func = dbeSession->getSpecialFunction (DbeSession::FailedUnwindFunc); + DbeInstr *instr = func->find_dbeinstr (0, 0); + omppcs->append (instr); + ompxpcs->append (instr); + } + + // User model call stack + node = (CallStackNode*) add_stack (omppcs); + dDscr->setObjValue (PROP_USTACK, idx, node); + delete omppcs; + + // Expert call stack + node = (CallStackNode*) add_stack (ompxpcs); + dDscr->setObjValue (PROP_XSTACK, idx, node); + delete ompxpcs; + dDscr->setObjValue (PROP_JTHREAD, idx, JTHREAD_DEFAULT); + return; + } + + // OpenMP 2.5 stacks + if (frp->omp_cprid || frp->omp_state) + { + DataView *dview = experiment->getOpenMPdata (); + if (dview == NULL) + { + // It appears we may get OMP_SERL_STATE from a passive libmtsk + dDscr->setObjValue (PROP_JTHREAD, idx, JTHREAD_DEFAULT); + return; + } + if (dview->getDataDescriptor () == dDscr) + { + // Don't process the user stack for OpenMP fork events yet + dDscr->setObjValue (PROP_USTACK, idx, (void*) NULL); + dDscr->setObjValue (PROP_JTHREAD, idx, JTHREAD_DEFAULT); + return; + } + Vector<Histable*> *omppcs = new Vector<Histable*>(stack_size); + + // Construct OMP user stack + // Find the bottom frame + int btm = 0; + switch (frp->omp_state) + { + case OMP_IDLE_STATE: + { + Function *func = dbeSession->get_OMP_Function (frp->omp_state); + omppcs->append (func->find_dbeinstr (0, 0)); + // XXX: workaround for inconsistency between OMP_IDLE_STATE + // and omp_cprid != 0 + frp->omp_cprid = 0; + btm = natpcs->size (); + break; + } + case OMP_RDUC_STATE: + case OMP_IBAR_STATE: + case OMP_EBAR_STATE: + case OMP_LKWT_STATE: + case OMP_CTWT_STATE: + case OMP_ODWT_STATE: + case OMP_ATWT_STATE: + { + Function *func = dbeSession->get_OMP_Function (frp->omp_state); + omppcs->append (func->find_dbeinstr (0, 0)); + bool inOMP = false; + for (btm = 0; btm < natpcs->size (); btm++) + { + LoadObject *lo = natpcs->fetch (btm)->func->module->loadobject; + if (!inOMP) + { + if (lo->flags & SEG_FLAG_OMP) + inOMP = true; + } + else if (!(lo->flags & SEG_FLAG_OMP)) + break; + } + break; + } + case OMP_NO_STATE: + case OMP_WORK_STATE: + case OMP_SERL_STATE: + default: + break; + } + + // Find the top frame + int top = -1; + switch (frp->omp_state) + { + case OMP_IDLE_STATE: + break; + default: + { + dview->sort (PROP_CPRID); + Datum tval; + tval.setUINT64 (frp->omp_cprid); + long pidx = dview->getIdxByVals (&tval, DataView::REL_EQ); + if (pidx < 0) // No parent. Process the entire nat_stack + top = natpcs->size () - 1; + else + { + uint32_t pthrid = (uint32_t) dview->getIntValue (PROP_THRID, pidx); + if (thrid != pthrid) + { + // Parent is on a different stack. + // Process the entire nat_stack. Skip libthread. + for (top = natpcs->size () - 1; top >= 0; top--) + { + DbeInstr *instr = natpcs->fetch (top); + if (instr->func->module->loadobject->flags & SEG_FLAG_OMP) + break; + } + if (top < 0) // None found. May be incomplete call stack + top = natpcs->size () - 1; + } + else + { + // Parent is on the same stack. Find match. + top = natpcs->size () - 1; + void *pnat_stack = dview->getObjValue (PROP_MSTACK, pidx); + Vector<Histable*> *ppcs = getStackPCs (pnat_stack); + for (int ptop = ppcs->size () - 1; top >= 0 && ptop >= 0; + top--, ptop--) + { + if (natpcs->fetch (top) != ppcs->fetch (ptop)) + break; + } + delete ppcs; + } + } + // If no frames are found for Barrier/Reduction save at least one + if ((frp->omp_state == OMP_RDUC_STATE + || frp->omp_state == OMP_IBAR_STATE + || frp->omp_state == OMP_EBAR_STATE) + && top < btm && btm < natpcs->size ()) + top = btm; + } + } + for (int i = btm; i <= top; ++i) + { + DbeInstr *instr = natpcs->fetch (i); + if (instr->func->module->loadobject->flags & SEG_FLAG_OMP) + continue; // Skip all frames from libmtsk + omppcs->append (instr); + } + node = find_preg_stack (frp->omp_cprid); + while (node != root) + { + omppcs->append (node->instr); + node = node->ancestor; + } + node = (CallStackNode *) add_stack (omppcs); + dDscr->setObjValue (PROP_USTACK, idx, node); + delete omppcs; + dDscr->setObjValue (PROP_JTHREAD, idx, JTHREAD_DEFAULT); + return; + } + + // Construct Java user stack + add_stack_java (dDscr, idx, frp, tstamp, thrid, natpcs, true, NULL); +} + +// adjustment of leafPC/eventVA for XHWC packets with candidate eventPC +// Called from CallStack during initial processing of the events +DbeInstr * +CallStackP::adjustEvent (DbeInstr *leafPC, DbeInstr *candPC, Vaddr &eventVA, + int abst_type) +{ + // increment counter of dataspace events + experiment->dsevents++; + bool isPrecise; + if (abst_type == ABST_EXACT_PEBS_PLUS1) + isPrecise = true; + else if (abst_type == ABST_EXACT) + isPrecise = true; + else + isPrecise = false; + + if (isPrecise) + /* precise backtracking */ + /* assume within 1 instruction of leaf (this could be checked here) */ + // no change to eventVA or candPC + return candPC; + + Function *func = leafPC->func; + unsigned int bt_entries = func->module->bTargets.size (); + DbeInstr *bestPC = NULL; + + // bt == branch target (potential destination of a branch + if (bt_entries == 0) + { // no XHWCprof info for this module + // increment counter + experiment->dsnoxhwcevents++; + + // see if event is to be processed anyway + if (!dbeSession->check_ignore_no_xhwcprof ()) + { + // Don't ignore error + // XXX -- set error code in event VA -- replace with other mechanism + if (eventVA > ABS_CODE_RANGE) + eventVA = ABS_NULL; + eventVA |= ABS_NO_CTI_INFO; // => effective address can't be validated + bestPC = leafPC; // => no PC correction possible + } + else + bestPC = candPC; // assume the event valid + } + else + { + // we have the info to verify the backtracking + target_info_t *bt; + int bt_entry = bt_entries; + uint64_t leafPC_offset = func->img_offset + leafPC->addr; + uint64_t candPC_offset = candPC->func->img_offset + candPC->addr; + do + { + bt_entry--; + bt = func->module->bTargets.fetch (bt_entry); + /* bts seem to be sorted by offset, smallest to largest */ + } + while (bt_entry > 0 && bt->offset > leafPC_offset); + /* if bt_entry == 0, all items have been checked */ + + if (bt->offset > leafPC_offset) + { /* XXXX isn't is possible that all bt's are after leafPC_offset? */ + bestPC = leafPC; // actual event PC can't be determined + if (eventVA > ABS_CODE_RANGE) + eventVA = ABS_NULL; + eventVA |= ABS_INFO_FAILED; // effective address can't be validated + } + else if (bt->offset > candPC_offset) + { + // use synthetic PC corresponding to bTarget + bestPC = func->find_dbeinstr (PCTrgtFlag, bt->offset - func->img_offset); + if (eventVA > ABS_CODE_RANGE) + eventVA = ABS_NULL; + eventVA |= ABS_CTI_TARGET; // effective address can't be validated + } + else + bestPC = candPC; // accept provided virtual address as valid + } + return bestPC; +} + +void * +CallStackP::add_stack_d (Vector<Histable*> *objs) +{ + // objs: root..leaf + // Reverse objs + for (int i = 0, j = objs->size () - 1; i < j; ++i, --j) + objs->swap (i, j); + return add_stack (objs); +} + +CallStackNode::CallStackNode (CallStackNode *_ancestor, Histable *_instr) +{ + ancestor = _ancestor; + instr = _instr; + alt_node = NULL; +} + +CallStackNode::~CallStackNode () { } + +bool +CallStackNode::compare (long start, long end, Vector<Histable*> *objs, CallStackNode *mRoot) +{ + CallStackNode *p = this; + for (long i = start; i < end; i++, p = p->get_ancestor ()) + if (p == NULL || p->get_instr () != objs->get (i)) + return false; + return p == mRoot; +} + +void +CallStackNode::dump () +{ + const char *s = ""; + int sz = 0; + for (CallStackNode *p = this; p; p = p->get_ancestor ()) + { + fprintf (stderr, NTXT ("%.*s 0x%08llx id=0x%08llx %s\n"), sz, s, + (long long) p, (long long) p->get_instr ()->id, + STR (p->get_instr ()->get_name ())); + s = "-"; + sz += 1; + } +} + +long total_calls_add_stack, total_stacks, total_nodes, call_stack_size[201]; + +void * +CallStackP::add_stack (Vector<Histable*> *objs) +{ + // objs: leaf..root + uint64_t hash = objs->size (); + for (long i = objs->size () - 1; i >= 0; --i) + hash ^= (unsigned long long) objs->get (i); + + uint64_t key = hash ? hash : 1; + CallStackNode *node = cstackMap->get (key); +#ifdef DEBUG + if (DUMP_CALL_STACK) + { + total_calls_add_stack++; + call_stack_size[objs->size () > 200 ? 200 : objs->size ()]++; + Dprintf (DUMP_CALL_STACK, + "add_stack: %lld size=%lld key=0x%08llx cashNode=0x%08llx\n", + (long long) total_calls_add_stack, (long long) objs->size (), + (long long) key, (long long) node); + for (long i = 0, sz = VecSize (objs); i < sz; i++) + Dprintf (DUMP_CALL_STACK, " add_stack: %.*s 0x%08llx id=0x%08llx %s\n", + (int) i, NTXT (" "), (long long) objs->get (i), + (long long) objs->get (i)->id, STR (objs->get (i)->get_name ())); + } +#endif + if (node && node->compare (0, objs->size (), objs, root)) + { + Dprintf (DUMP_CALL_STACK, NTXT ("STACK FOUND: key=0x%08llx 0x%08llx id=0x%08llx %s\n"), + (long long) key, (long long) node, + (long long) node->get_instr ()->id, + STR (node->get_instr ()->get_name ())); + return node; + } + node = root; + for (long i = objs->size () - 1; i >= 0; i--) + { + Histable *instr = objs->get (i); + int old_count = node->count; + int left; + CallStackNode *nd = node->find (instr, &left); + if (nd) + { + node = nd; + continue; + } + cstackLock->aquireLock (); // Use one lock for all nodes + // node->aquireLock(); + if (old_count != node->count) + { + nd = node->find (instr, &left); + if (nd) + { // the other thread has created this node + cstackLock->releaseLock (); + // node->releaseLock(); + node = nd; + continue; + } + } + // New Call Stack + total_stacks++; + nd = node; + CallStackNode *first = NULL; + do + { + CallStackNode *anc = node; + total_nodes++; + node = new_Node (anc, objs->get (i)); + if (first) + anc->append (node); + else + first = node; + } + while (i-- > 0); + nd->insert (left, first); + cstackLock->releaseLock (); + // nd->releaseLock(); + break; + } + cstackMap->put (key, node); + if (DUMP_CALL_STACK) + node->dump (); + return node; +} + +CallStackNode * +CallStackP::get_node (int n) +{ + if (n < nodes) + return &chunks[n / CHUNKSZ][n % CHUNKSZ]; + return NULL; +} + +/* + * Debugging methods + */ +void +CallStackP::print (FILE *fd) +{ + FILE *f = (fd == NULL ? stderr : fd); + fprintf (f, GTXT ("CallStack: nodes = %d\n\n"), nodes); + int maxdepth = 0; + int maxwidth = 0; + const char *t; + char *n; + for (int i = 0; i < nodes; i++) + { + CallStackNode *node = &chunks[i / CHUNKSZ][i % CHUNKSZ]; + Histable *instr = node->instr; + if (instr->get_type () == Histable::LINE) + { + t = "L"; + n = ((DbeLine *) instr)->func->get_name (); + } + else if (instr->get_type () == Histable::INSTR) + { + t = "I"; + n = ((DbeInstr *) instr)->func->get_name (); + } + else + { + t = "O"; + n = instr->get_name (); + } + long long addr = (long long) instr->get_addr (); + fprintf (f, GTXT ("node: 0x%016llx anc: 0x%016llx -- 0x%016llX: %s %s\n"), + (unsigned long long) node, (unsigned long long) node->ancestor, + addr, t, n); + } + fprintf (f, GTXT ("md = %d, mw = %d\n"), maxdepth, maxwidth); +} + +/* + * Static CallStack methods + */ +CallStack * +CallStack::getInstance (Experiment *exp) +{ + return new CallStackP (exp); +} + +int +CallStack::stackSize (void *stack) +{ + CallStackNode *node = (CallStackNode *) stack; + int sz = 0; + for (; node; node = node->ancestor) + sz++; + return sz - 1; // don't count the root node +} + +Histable * +CallStack::getStackPC (void *stack, int n) +{ + CallStackNode *node = (CallStackNode *) stack; + while (n-- && node) + node = node->ancestor; + if (node == NULL) + return dbeSession->get_Unknown_Function ()->find_dbeinstr (PCInvlFlag, 0); + return node->instr; +} + +Vector<Histable*> * +CallStack::getStackPCs (void *stack, bool get_hide_stack) +{ + Vector<Histable*> *res = new Vector<Histable*>; + CallStackNode *node = (CallStackNode *) stack; + if (get_hide_stack && node->alt_node != NULL) + node = node->alt_node; + while (node && node->ancestor) + { // skip the root node + res->append (node->instr); + node = node->ancestor; + } + return res; +} + +int +CallStack::compare (void *stack1, void *stack2) +{ + // Quick comparision + if (stack1 == stack2) + return 0; + + CallStackNode *node1 = (CallStackNode *) stack1; + CallStackNode *node2 = (CallStackNode *) stack2; + while (node1 != NULL && node2 != NULL) + { + //to keep the result const on different platforms + //we use instr->id instead of instr + if (node1->instr->id < node2->instr->id) + return -1; + else if (node1->instr->id > node2->instr->id) + return 1; + node1 = node1->ancestor; + node2 = node2->ancestor; + } + if (node1 == NULL && node2 != NULL) + return -1; + else if (node1 != NULL && node2 == NULL) + return 1; + else + return 0; +} + +// LIBRARY VISIBILITY + +void +CallStack::setHideStack (void *stack, void *hideStack) +{ + CallStackNode *hNode = (CallStackNode *) stack; + hNode->alt_node = (CallStackNode *) hideStack; +} diff --git a/gprofng/src/CallStack.h b/gprofng/src/CallStack.h new file mode 100644 index 0000000..62e0686 --- /dev/null +++ b/gprofng/src/CallStack.h @@ -0,0 +1,114 @@ +/* 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. */ + +#ifndef _CALLSTACK_H +#define _CALLSTACK_H + +#include <stdio.h> +#include "dbe_structs.h" +#include "Experiment.h" +#include "DbeLock.h" + +class DataDescriptor; +class FramePacket; +class DbeInstr; +class Histable; +template <class ITEM> class Vector; +class CallStackNode; + +class Descendants /* : public DbeLock */ +{ +public: + Descendants (); + ~Descendants (); + CallStackNode *find (Histable *hi, int *index); + void append (CallStackNode *item); + void insert (int ind, CallStackNode *item); + int volatile count; + +private: + + enum + { + DELTA = 8 + }; + + int limit; + CallStackNode **data; + CallStackNode *first_data[4]; +}; + +class CallStackNode : public Descendants +{ +public: + CallStackNode (CallStackNode *_ancestor, Histable *_instr); + ~CallStackNode (); + bool compare (long start, long end, Vector<Histable*> *objs, CallStackNode *mRoot); + void dump (); + + CallStackNode * + get_ancestor () + { + return ancestor; + } + + Histable * + get_instr () + { + return instr; + } + + CallStackNode *alt_node; + Histable *instr; + CallStackNode *ancestor; +}; + +class CallStack +{ +public: + static CallStack *getInstance (Experiment *exp); + virtual ~CallStack () { }; + + virtual void add_stack (DataDescriptor *dDscr, long idx, FramePacket *frp, + cstk_ctx_chunk* cstCtxChunk) = 0; + + // Creates a call stack representation for objs and + // returns an opaque pointer to it + virtual void *add_stack (Vector<Histable*> *objs) = 0; + + // Debugging methods + virtual void print (FILE *) = 0; + + // Call stack inquiries + static int stackSize (void *stack); + static Histable *getStackPC (void *stack, int n); + static Vector<Histable*> *getStackPCs (void *stack, bool get_hide_stack = false); + static void setHideStack (void *stack, void *hideStack); + static int compare (void *stack1, void *stack2); + + virtual CallStackNode * + get_node (int) + { + return NULL; + }; + +}; + +#endif /* _CALLSTACK_H */ diff --git a/gprofng/src/CatchOutOfMemory.cc b/gprofng/src/CatchOutOfMemory.cc new file mode 100644 index 0000000..61b91cf --- /dev/null +++ b/gprofng/src/CatchOutOfMemory.cc @@ -0,0 +1,59 @@ +/* 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 <new> // std::bad_alloc +#include <stdio.h> // fprintf +#include <stdlib.h> // exit +#include "DbeApplication.h" + +static char *name = NULL; + +/** + * Out Of Memory exception handler + */ +void +out_of_mem () +{ + fprintf (stderr, "%s: %s: %s\n", "Error", name ? name : "", "Out of memory\n"); + exit (2); // Out of memory + // throw bad_alloc(); +} + +/** + * Calls real_main inside try{...}catch(std::bad_alloc *) + */ +int +catch_out_of_memory (int (*real_main)(int, char*[]), int argc, char *argv[]) +{ + int i = 0; + name = argv[0]; + std::set_new_handler (out_of_mem); + try + { + i = real_main (argc, argv); + } + catch (std::bad_alloc */*ba*/) + { + exit (2); // Out of memory + } + delete theDbeApplication; + return i; +} diff --git a/gprofng/src/ClassFile.cc b/gprofng/src/ClassFile.cc new file mode 100644 index 0000000..7dd64a7 --- /dev/null +++ b/gprofng/src/ClassFile.cc @@ -0,0 +1,1639 @@ +/* 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 <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "util.h" +#include "DbeSession.h" +#include "ClassFile.h" +#include "Function.h" +#include "StringBuilder.h" +#include "DbeFile.h" + +class ByteCodeInfo +{ +public: + + ByteCodeInfo (JMethod *_func, int _bci, int _lno) + { + func = _func; + bci = _bci; + lno = _lno; + }; + + JMethod *func; + int bci; + int lno; +}; + +typedef unsigned char u1; +typedef unsigned short u2; +typedef unsigned int u4; + +// Class File Constants +#define JAVA_MAGIC 0xcafebabe + +enum { + // First argument in access_flags_to_str() + ClassAccess = 1, + FieldAccess, + MethodAccess, + NestedClassAccess, + + // jdk/src/share/classes/sun/tools/java/RuntimeConstants.java + // Type codes + T_CLASS = 0x00000002, + T_BOOLEAN = 0x00000004, + T_CHAR = 0x00000005, + T_FLOAT = 0x00000006, + T_DOUBLE = 0x00000007, + T_BYTE = 0x00000008, + T_SHORT = 0x00000009, + T_INT = 0x0000000a, + T_LONG = 0x0000000b, + +// Access and modifier flags + ACC_PUBLIC = 0x00000001, + ACC_PRIVATE = 0x00000002, + ACC_PROTECTED = 0x00000004, + ACC_STATIC = 0x00000008, + ACC_FINAL = 0x00000010, + ACC_SYNCHRONIZED = 0x00000020, + ACC_VOLATILE = 0x00000040, + ACC_TRANSIENT = 0x00000080, + ACC_NATIVE = 0x00000100, + ACC_INTERFACE = 0x00000200, + ACC_ABSTRACT = 0x00000400, + ACC_STRICT = 0x00000800, + ACC_SYNTHETIC = 0x00001000, + ACC_ANNOTATION = 0x00002000, + ACC_ENUM = 0x00004000, + + ACC_SUPER = 0x00000020, + ACC_BRIDGE = 0x00000040, + ACC_VARARGS = 0x00000080, + +// Opcodes + opc_try = -3, + opc_dead = -2, + opc_label = -1, + opc_nop = 0, + opc_aconst_null = 1, + opc_iconst_m1 = 2, + opc_iconst_0 = 3, + opc_iconst_1 = 4, + opc_iconst_2 = 5, + opc_iconst_3 = 6, + opc_iconst_4 = 7, + opc_iconst_5 = 8, + opc_lconst_0 = 9, + opc_lconst_1 = 10, + opc_fconst_0 = 11, + opc_fconst_1 = 12, + opc_fconst_2 = 13, + opc_dconst_0 = 14, + opc_dconst_1 = 15, + opc_bipush = 16, + opc_sipush = 17, + opc_ldc = 18, + opc_ldc_w = 19, + opc_ldc2_w = 20, + opc_iload = 21, + opc_lload = 22, + opc_fload = 23, + opc_dload = 24, + opc_aload = 25, + opc_iload_0 = 26, + opc_iload_1 = 27, + opc_iload_2 = 28, + opc_iload_3 = 29, + opc_lload_0 = 30, + opc_lload_1 = 31, + opc_lload_2 = 32, + opc_lload_3 = 33, + opc_fload_0 = 34, + opc_fload_1 = 35, + opc_fload_2 = 36, + opc_fload_3 = 37, + opc_dload_0 = 38, + opc_dload_1 = 39, + opc_dload_2 = 40, + opc_dload_3 = 41, + opc_aload_0 = 42, + opc_aload_1 = 43, + opc_aload_2 = 44, + opc_aload_3 = 45, + opc_iaload = 46, + opc_laload = 47, + opc_faload = 48, + opc_daload = 49, + opc_aaload = 50, + opc_baload = 51, + opc_caload = 52, + opc_saload = 53, + opc_istore = 54, + opc_lstore = 55, + opc_fstore = 56, + opc_dstore = 57, + opc_astore = 58, + opc_istore_0 = 59, + opc_istore_1 = 60, + opc_istore_2 = 61, + opc_istore_3 = 62, + opc_lstore_0 = 63, + opc_lstore_1 = 64, + opc_lstore_2 = 65, + opc_lstore_3 = 66, + opc_fstore_0 = 67, + opc_fstore_1 = 68, + opc_fstore_2 = 69, + opc_fstore_3 = 70, + opc_dstore_0 = 71, + opc_dstore_1 = 72, + opc_dstore_2 = 73, + opc_dstore_3 = 74, + opc_astore_0 = 75, + opc_astore_1 = 76, + opc_astore_2 = 77, + opc_astore_3 = 78, + opc_iastore = 79, + opc_lastore = 80, + opc_fastore = 81, + opc_dastore = 82, + opc_aastore = 83, + opc_bastore = 84, + opc_castore = 85, + opc_sastore = 86, + opc_pop = 87, + opc_pop2 = 88, + opc_dup = 89, + opc_dup_x1 = 90, + opc_dup_x2 = 91, + opc_dup2 = 92, + opc_dup2_x1 = 93, + opc_dup2_x2 = 94, + opc_swap = 95, + opc_iadd = 96, + opc_ladd = 97, + opc_fadd = 98, + opc_dadd = 99, + opc_isub = 100, + opc_lsub = 101, + opc_fsub = 102, + opc_dsub = 103, + opc_imul = 104, + opc_lmul = 105, + opc_fmul = 106, + opc_dmul = 107, + opc_idiv = 108, + opc_ldiv = 109, + opc_fdiv = 110, + opc_ddiv = 111, + opc_irem = 112, + opc_lrem = 113, + opc_frem = 114, + opc_drem = 115, + opc_ineg = 116, + opc_lneg = 117, + opc_fneg = 118, + opc_dneg = 119, + opc_ishl = 120, + opc_lshl = 121, + opc_ishr = 122, + opc_lshr = 123, + opc_iushr = 124, + opc_lushr = 125, + opc_iand = 126, + opc_land = 127, + opc_ior = 128, + opc_lor = 129, + opc_ixor = 130, + opc_lxor = 131, + opc_iinc = 132, + opc_i2l = 133, + opc_i2f = 134, + opc_i2d = 135, + opc_l2i = 136, + opc_l2f = 137, + opc_l2d = 138, + opc_f2i = 139, + opc_f2l = 140, + opc_f2d = 141, + opc_d2i = 142, + opc_d2l = 143, + opc_d2f = 144, + opc_i2b = 145, + opc_i2c = 146, + opc_i2s = 147, + opc_lcmp = 148, + opc_fcmpl = 149, + opc_fcmpg = 150, + opc_dcmpl = 151, + opc_dcmpg = 152, + opc_ifeq = 153, + opc_ifne = 154, + opc_iflt = 155, + opc_ifge = 156, + opc_ifgt = 157, + opc_ifle = 158, + opc_if_icmpeq = 159, + opc_if_icmpne = 160, + opc_if_icmplt = 161, + opc_if_icmpge = 162, + opc_if_icmpgt = 163, + opc_if_icmple = 164, + opc_if_acmpeq = 165, + opc_if_acmpne = 166, + opc_goto = 167, + opc_jsr = 168, + opc_ret = 169, + opc_tableswitch = 170, + opc_lookupswitch = 171, + opc_ireturn = 172, + opc_lreturn = 173, + opc_freturn = 174, + opc_dreturn = 175, + opc_areturn = 176, + opc_return = 177, + opc_getstatic = 178, + opc_putstatic = 179, + opc_getfield = 180, + opc_putfield = 181, + opc_invokevirtual = 182, + opc_invokespecial = 183, + opc_invokestatic = 184, + opc_invokeinterface = 185, + opc_invokedynamic = 186, + opc_new = 187, + opc_newarray = 188, + opc_anewarray = 189, + opc_arraylength = 190, + opc_athrow = 191, + opc_checkcast = 192, + opc_instanceof = 193, + opc_monitorenter = 194, + opc_monitorexit = 195, + opc_wide = 196, + opc_multianewarray = 197, + opc_ifnull = 198, + opc_ifnonnull = 199, + opc_goto_w = 200, + opc_jsr_w = 201, + opc_breakpoint = 202, + +// Constant table + CONSTANT_UTF8 = 1, + CONSTANT_UNICODE = 2, + CONSTANT_INTEGER = 3, + CONSTANT_FLOAT = 4, + CONSTANT_LONG = 5, + CONSTANT_DOUBLE = 6, + CONSTANT_CLASS = 7, + CONSTANT_STRING = 8, + CONSTANT_FIELD = 9, + CONSTANT_METHOD = 10, + CONSTANT_INTERFACEMETHOD = 11, + CONSTANT_NAMEANDTYPE = 12, + CONSTANT_METHODHANDLE = 15, + CONSTANT_METHODTYPE = 16, + CONSTANT_INVOKEDYNAMIC = 18 +}; + +static char *opcNames[] = { + NTXT ("nop"), + NTXT ("aconst_null"), + NTXT ("iconst_m1"), + NTXT ("iconst_0"), + NTXT ("iconst_1"), + NTXT ("iconst_2"), + NTXT ("iconst_3"), + NTXT ("iconst_4"), + NTXT ("iconst_5"), + NTXT ("lconst_0"), + NTXT ("lconst_1"), + NTXT ("fconst_0"), + NTXT ("fconst_1"), + NTXT ("fconst_2"), + NTXT ("dconst_0"), + NTXT ("dconst_1"), + NTXT ("bipush"), + NTXT ("sipush"), + NTXT ("ldc"), + NTXT ("ldc_w"), + NTXT ("ldc2_w"), + NTXT ("iload"), + NTXT ("lload"), + NTXT ("fload"), + NTXT ("dload"), + NTXT ("aload"), + NTXT ("iload_0"), + NTXT ("iload_1"), + NTXT ("iload_2"), + NTXT ("iload_3"), + NTXT ("lload_0"), + NTXT ("lload_1"), + NTXT ("lload_2"), + NTXT ("lload_3"), + NTXT ("fload_0"), + NTXT ("fload_1"), + NTXT ("fload_2"), + NTXT ("fload_3"), + NTXT ("dload_0"), + NTXT ("dload_1"), + NTXT ("dload_2"), + NTXT ("dload_3"), + NTXT ("aload_0"), + NTXT ("aload_1"), + NTXT ("aload_2"), + NTXT ("aload_3"), + NTXT ("iaload"), + NTXT ("laload"), + NTXT ("faload"), + NTXT ("daload"), + NTXT ("aaload"), + NTXT ("baload"), + NTXT ("caload"), + NTXT ("saload"), + NTXT ("istore"), + NTXT ("lstore"), + NTXT ("fstore"), + NTXT ("dstore"), + NTXT ("astore"), + NTXT ("istore_0"), + NTXT ("istore_1"), + NTXT ("istore_2"), + NTXT ("istore_3"), + NTXT ("lstore_0"), + NTXT ("lstore_1"), + NTXT ("lstore_2"), + NTXT ("lstore_3"), + NTXT ("fstore_0"), + NTXT ("fstore_1"), + NTXT ("fstore_2"), + NTXT ("fstore_3"), + NTXT ("dstore_0"), + NTXT ("dstore_1"), + NTXT ("dstore_2"), + NTXT ("dstore_3"), + NTXT ("astore_0"), + NTXT ("astore_1"), + NTXT ("astore_2"), + NTXT ("astore_3"), + NTXT ("iastore"), + NTXT ("lastore"), + NTXT ("fastore"), + NTXT ("dastore"), + NTXT ("aastore"), + NTXT ("bastore"), + NTXT ("castore"), + NTXT ("sastore"), + NTXT ("pop"), + NTXT ("pop2"), + NTXT ("dup"), + NTXT ("dup_x1"), + NTXT ("dup_x2"), + NTXT ("dup2"), + NTXT ("dup2_x1"), + NTXT ("dup2_x2"), + NTXT ("swap"), + NTXT ("iadd"), + NTXT ("ladd"), + NTXT ("fadd"), + NTXT ("dadd"), + NTXT ("isub"), + NTXT ("lsub"), + NTXT ("fsub"), + NTXT ("dsub"), + NTXT ("imul"), + NTXT ("lmul"), + NTXT ("fmul"), + NTXT ("dmul"), + NTXT ("idiv"), + NTXT ("ldiv"), + NTXT ("fdiv"), + NTXT ("ddiv"), + NTXT ("irem"), + NTXT ("lrem"), + NTXT ("frem"), + NTXT ("drem"), + NTXT ("ineg"), + NTXT ("lneg"), + NTXT ("fneg"), + NTXT ("dneg"), + NTXT ("ishl"), + NTXT ("lshl"), + NTXT ("ishr"), + NTXT ("lshr"), + NTXT ("iushr"), + NTXT ("lushr"), + NTXT ("iand"), + NTXT ("land"), + NTXT ("ior"), + NTXT ("lor"), + NTXT ("ixor"), + NTXT ("lxor"), + NTXT ("iinc"), + NTXT ("i2l"), + NTXT ("i2f"), + NTXT ("i2d"), + NTXT ("l2i"), + NTXT ("l2f"), + NTXT ("l2d"), + NTXT ("f2i"), + NTXT ("f2l"), + NTXT ("f2d"), + NTXT ("d2i"), + NTXT ("d2l"), + NTXT ("d2f"), + NTXT ("i2b"), + NTXT ("i2c"), + NTXT ("i2s"), + NTXT ("lcmp"), + NTXT ("fcmpl"), + NTXT ("fcmpg"), + NTXT ("dcmpl"), + NTXT ("dcmpg"), + NTXT ("ifeq"), + NTXT ("ifne"), + NTXT ("iflt"), + NTXT ("ifge"), + NTXT ("ifgt"), + NTXT ("ifle"), + NTXT ("if_icmpeq"), + NTXT ("if_icmpne"), + NTXT ("if_icmplt"), + NTXT ("if_icmpge"), + NTXT ("if_icmpgt"), + NTXT ("if_icmple"), + NTXT ("if_acmpeq"), + NTXT ("if_acmpne"), + NTXT ("goto"), + NTXT ("jsr"), + NTXT ("ret"), + NTXT ("tableswitch"), + NTXT ("lookupswitch"), + NTXT ("ireturn"), + NTXT ("lreturn"), + NTXT ("freturn"), + NTXT ("dreturn"), + NTXT ("areturn"), + NTXT ("return"), + NTXT ("getstatic"), + NTXT ("putstatic"), + NTXT ("getfield"), + NTXT ("putfield"), + NTXT ("invokevirtual"), + NTXT ("invokespecial"), + NTXT ("invokestatic"), + NTXT ("invokeinterface"), + NTXT ("invokedynamic"), + NTXT ("new"), + NTXT ("newarray"), + NTXT ("anewarray"), + NTXT ("arraylength"), + NTXT ("athrow"), + NTXT ("checkcast"), + NTXT ("instanceof"), + NTXT ("monitorenter"), + NTXT ("monitorexit"), + NTXT ("wide"), + NTXT ("multianewarray"), + NTXT ("ifnull"), + NTXT ("ifnonnull"), + NTXT ("goto_w"), + NTXT ("jsr_w"), + NTXT ("breakpoint") +}; + + +#define APPEND_FLAG(len, buf, flag, x) \ + if (((x) & (flag)) != 0) \ + { \ + flag &= ~(x); \ + AppendString(len, buf, NTXT("%s%s"), delimiter, #x); \ + delimiter = NTXT("|"); \ + } + +static char * +access_flags_to_str (int kind, int flag) +{ + static char buf[256]; + size_t len = 0; + buf[0] = 0; + if (flag == 0) + { + AppendString (len, buf, NTXT ("0x%x"), (unsigned int) flag); + return buf; + } + const char *delimiter = ""; + if (kind == ClassAccess) + { + APPEND_FLAG (len, buf, flag, ACC_FINAL); + APPEND_FLAG (len, buf, flag, ACC_SUPER); + APPEND_FLAG (len, buf, flag, ACC_INTERFACE); + APPEND_FLAG (len, buf, flag, ACC_ABSTRACT); + APPEND_FLAG (len, buf, flag, ACC_SYNTHETIC); + APPEND_FLAG (len, buf, flag, ACC_ANNOTATION); + APPEND_FLAG (len, buf, flag, ACC_ENUM); + if (flag) + AppendString (len, buf, "%s0x%x", delimiter, (unsigned int) (flag)); + } + else if (kind == FieldAccess) + { + APPEND_FLAG (len, buf, flag, ACC_PUBLIC); + APPEND_FLAG (len, buf, flag, ACC_PRIVATE); + APPEND_FLAG (len, buf, flag, ACC_PROTECTED); + APPEND_FLAG (len, buf, flag, ACC_STATIC); + APPEND_FLAG (len, buf, flag, ACC_FINAL); + APPEND_FLAG (len, buf, flag, ACC_VOLATILE); + APPEND_FLAG (len, buf, flag, ACC_TRANSIENT); + APPEND_FLAG (len, buf, flag, ACC_SYNTHETIC); + APPEND_FLAG (len, buf, flag, ACC_ENUM); + if (flag) + AppendString (len, buf, "%s0x%x", delimiter, (unsigned int) (flag)); + } + else if (kind == MethodAccess) + { + APPEND_FLAG (len, buf, flag, ACC_PUBLIC); + APPEND_FLAG (len, buf, flag, ACC_PRIVATE); + APPEND_FLAG (len, buf, flag, ACC_PROTECTED); + APPEND_FLAG (len, buf, flag, ACC_STATIC); + APPEND_FLAG (len, buf, flag, ACC_FINAL); + APPEND_FLAG (len, buf, flag, ACC_SYNCHRONIZED); + APPEND_FLAG (len, buf, flag, ACC_BRIDGE); + APPEND_FLAG (len, buf, flag, ACC_VARARGS); + APPEND_FLAG (len, buf, flag, ACC_NATIVE); + APPEND_FLAG (len, buf, flag, ACC_ABSTRACT); + APPEND_FLAG (len, buf, flag, ACC_STRICT); + APPEND_FLAG (len, buf, flag, ACC_SYNTHETIC); + if (flag) + AppendString (len, buf, "%s0x%x", delimiter, (unsigned int) (flag)); + } + else if (kind == NestedClassAccess) + { + APPEND_FLAG (len, buf, flag, ACC_PUBLIC); + APPEND_FLAG (len, buf, flag, ACC_PRIVATE); + APPEND_FLAG (len, buf, flag, ACC_PROTECTED); + APPEND_FLAG (len, buf, flag, ACC_STATIC); + APPEND_FLAG (len, buf, flag, ACC_FINAL); + APPEND_FLAG (len, buf, flag, ACC_INTERFACE); + APPEND_FLAG (len, buf, flag, ACC_ABSTRACT); + APPEND_FLAG (len, buf, flag, ACC_SYNTHETIC); + APPEND_FLAG (len, buf, flag, ACC_ANNOTATION); + APPEND_FLAG (len, buf, flag, ACC_ENUM); + if (flag) + AppendString (len, buf, "%s0x%x", delimiter, (unsigned int) (flag)); + } + return buf; +} + +class DataReadException +{ +public: + + DataReadException (char *s) + { + str_err = s; + } + + ~DataReadException () + { + free (str_err); + } + + char * + toString () + { + return str_err; + } + +private: + char *str_err; +}; + +class DataInputStream +{ +public: + + DataInputStream (const unsigned char *bytes, int64_t sz) + { + bp = bp_orig = bytes; + bp_last = bp_orig + sz; + } + + DataInputStream (DataInputStream *in) + { + bp = bp_orig = in->bp_orig; + bp_last = in->bp_last; + } + + u1 + readByte () + { + check (1); + u1 val = *bp; + bp++; + return val; + } + + u2 + readUnsignedShort () + { + check (2); + u2 val = (bp[0] << 8) | bp[1]; + bp += 2; + return val; + } + + u4 + readUnsigned () + { + check (4); + u4 val = (bp[0] << 24) | (bp[1] << 16) | (bp[2] << 8) | bp[3]; + bp += 4; + return val; + } + + const u1 * + getptr () + { + return bp; + } + + const size_t + get_offset () + { + return bp - bp_orig; + } + + void + skip (int n) + { + check (n); + bp += n; + } + + void + reset () + { + bp = bp_orig; + } + + void + copy_bytes (char *buf, int64_t len) + { + check (len); + memcpy (buf, bp, len); + buf[len] = '\0'; + } + +private: + + void + check (int64_t sz) + { + if (sz < 0 || bp + sz > bp_last) + { + DataReadException *e1 = new DataReadException ( + dbe_sprintf (GTXT ("(Cannot read %lld byte(s) offset=0x%llx)\n"), + (long long) sz, (long long) get_offset ())); + throw (e1); + } + }; + + const unsigned char *bp_last; + const unsigned char *bp_orig; + const unsigned char *bp; +}; + +class BinaryConstantPool +{ +public: + BinaryConstantPool (DataInputStream &in); + ~BinaryConstantPool (); + + u1 + getType (int n) + { + return (n < nconst && n > 0) ? types[n] : 0; + }; + char *getString (int index); + +private: + static char *getTypeName (int ty); + static char *type_name_to_str (int ty); + static char *offset_to_str (long long offset); + int nconst; + u1 *types; + int64_t *offsets; + char **strings; + DataInputStream *input; +}; + +char * +BinaryConstantPool::type_name_to_str (int ty) +{ + static char buf[128]; + char *tyName = getTypeName (ty); + snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), tyName, ty); + return buf; +} + +char * +BinaryConstantPool::offset_to_str (long long offset) +{ + static char buf[128]; + snprintf (buf, sizeof (buf), NTXT ("offset=0x%06llx (%llu)"), offset, offset); + return buf; +} + +BinaryConstantPool::BinaryConstantPool (DataInputStream &in) +{ + nconst = 0; + types = NULL; + offsets = NULL; + strings = NULL; + input = new DataInputStream (in); + int cntConst = in.readUnsignedShort (); + if (cntConst > 0) + { + types = new u1[cntConst]; + types[0] = 0; + offsets = new int64_t [cntConst]; + strings = new char * [cntConst]; + strings[0] = NULL; + } + Dprintf (DUMP_JAVA_CLASS, NTXT ("# BinaryConstantPool: %d\n"), (int) nconst); + for (int i = 1; i < cntConst; i++) + { + nconst = i + 1; + strings[i] = NULL; + types[i] = in.readByte (); + offsets[i] = in.get_offset (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" %3d %-25s %-25s"), i, offset_to_str (offsets[i]), type_name_to_str (types[i])); + switch (types[i]) + { + case CONSTANT_UTF8: + { + u2 length = in.readUnsignedShort (); + in.skip (length); + Dprintf (DUMP_JAVA_CLASS, " length=%u\n", (unsigned int) length); + break; + } + case CONSTANT_INTEGER: + { + u4 bytes = in.readUnsigned (); + Dprintf (DUMP_JAVA_CLASS, " bytes=0x%08x\n", (unsigned int) bytes); + break; + } + case CONSTANT_FLOAT: + { + u4 bytes = in.readUnsigned (); + Dprintf (DUMP_JAVA_CLASS, " bytes=0x%08x\n", (unsigned int) bytes); + break; + } + case CONSTANT_LONG: + case CONSTANT_DOUBLE: + { + // JVM 4.4.5: all 8-byte constants take up + // two entries in the constant_pool table. + i++; + nconst++; + offsets[i] = 0; + strings[i] = NULL; + u4 high_bytes = in.readUnsigned (); + u4 low_bytes = in.readUnsigned (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" high_bytes=0x%08x low_bytes=0x%08x\n"), + (unsigned int) high_bytes, (unsigned int) low_bytes); + break; + } + case CONSTANT_CLASS: + { + u2 name_index = in.readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" name_index=%6u\n"), (unsigned int) name_index); + break; + } + case CONSTANT_STRING: + { + u2 string_index = in.readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" string_index=%4u\n"), (unsigned int) string_index); + break; + } + case CONSTANT_FIELD: + case CONSTANT_METHOD: + case CONSTANT_INTERFACEMETHOD: + { + u2 class_index = in.readUnsignedShort (); + u2 name_and_type_index = in.readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" class_index=%5u name_and_type_index=%u\n"), + (unsigned int) class_index, (unsigned int) name_and_type_index); + break; + } + case CONSTANT_NAMEANDTYPE: + { + u2 name_index = in.readUnsignedShort (); + u2 descriptor_index = in.readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, " name_index=%6u descriptor_index=%u\n", + (unsigned int) name_index, (unsigned int) descriptor_index); + break; + } + case CONSTANT_METHODHANDLE: + { + u1 reference_kind = in.readByte (); + u2 reference_index = in.readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, " reference_kind=%u reference_index=%u\n", + (unsigned int) reference_kind, (unsigned int) reference_index); + break; + } + case CONSTANT_METHODTYPE: + { + u2 descriptor_index = in.readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" descriptor_index=%u\n"), + (unsigned int) descriptor_index); + break; + } + case CONSTANT_INVOKEDYNAMIC: + { + u2 bootstrap_method_attr_index = in.readUnsignedShort (); + u2 name_and_type_index = in.readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" bootstrap_method_attr_index=%5u name_and_type_index=%u\n"), + (unsigned int) bootstrap_method_attr_index, + (unsigned int) name_and_type_index); + break; + } + default: + Dprintf (DUMP_JAVA_CLASS, NTXT ("\n")); + DataReadException *e1 = new DataReadException ( + dbe_sprintf (GTXT ("BinaryConstantPool[%d]: bad tag %d %s\n"), + i, types[i], offset_to_str (offsets[i]))); + throw (e1); + } + } +} + +BinaryConstantPool::~BinaryConstantPool () +{ + delete[] types; + delete[] offsets; + delete input; + if (strings) + { + for (int i = 0; i < nconst; i++) + free (strings[i]); + delete[] strings; + } +} + +#define CASE_S(x) case x: return (char *) #x + +char * +BinaryConstantPool::getTypeName (int ty) +{ + switch (ty) + { + CASE_S (CONSTANT_UTF8); + CASE_S (CONSTANT_INTEGER); + CASE_S (CONSTANT_FLOAT); + CASE_S (CONSTANT_LONG); + CASE_S (CONSTANT_DOUBLE); + CASE_S (CONSTANT_CLASS); + CASE_S (CONSTANT_STRING); + CASE_S (CONSTANT_FIELD); + CASE_S (CONSTANT_METHOD); + CASE_S (CONSTANT_INTERFACEMETHOD); + CASE_S (CONSTANT_NAMEANDTYPE); + CASE_S (CONSTANT_METHODHANDLE); + CASE_S (CONSTANT_METHODTYPE); + CASE_S (CONSTANT_INVOKEDYNAMIC); + default: return NTXT ("UNKNOWN_TYPE"); + } +} + +char * +BinaryConstantPool::getString (int index) +{ + if (index >= nconst || index <= 0) + return NULL; + if (strings[index]) + return strings[index]; + input->reset (); + input->skip (offsets[index]); + switch (types[index]) + { + case CONSTANT_CLASS: + case CONSTANT_STRING: + case CONSTANT_NAMEANDTYPE: + strings[index] = dbe_strdup (getString (input->readUnsignedShort ())); + return strings[index]; + case CONSTANT_METHOD: + input->readUnsignedShort (); // cl_inx + strings[index] = dbe_strdup (getString (input->readUnsignedShort ())); + return strings[index]; + case CONSTANT_UTF8: + break; + default: + return NULL; + } + u2 len = input->readUnsignedShort (); + strings[index] = (char *) malloc (len + 1); + input->copy_bytes (strings[index], len); + return strings[index]; +} + +ClassFile::ClassFile () : Module () +{ + input = NULL; + bcpool = NULL; + cf_buf = NULL; + cur_jmthd = NULL; + blanksCnt = 0; + cf_bufsz = 0; + lang_code = Sp_lang_java; + class_name = NULL; + class_filename = NULL; + source_name = NULL; + byteCodeInfo = NULL; +} + +char * +ClassFile::get_opc_name (int op) +{ + if (op >= 0 && ((size_t) op) < sizeof (opcNames) / sizeof (char*)) + return opcNames[op]; + switch (op) + { + case opc_try: + return NTXT ("try"); + case opc_dead: + return NTXT ("dead"); + case opc_label: + return NTXT ("label"); + default: + return NTXT ("Unknown op code"); + } +} + +void +ClassFile::openFile (const char *fname) +{ + if (fname == NULL) + return; + int fd = open64 (fname, O_RDONLY); + if (fd == -1) + { + append_msg (CMSG_ERROR, GTXT ("Cannot open file %s"), fname); + return; + } + struct stat64 stat_buf; + if ((fstat64 (fd, &stat_buf) == -1) || (stat_buf.st_size == 0)) + { + close (fd); + append_msg (CMSG_ERROR, GTXT ("Cannot read file %s"), fname); + return; + } + cf_bufsz = stat_buf.st_size; + cf_buf = (unsigned char *) malloc (cf_bufsz); + if (cf_bufsz != read_from_file (fd, cf_buf, cf_bufsz)) + { + free (cf_buf); + cf_buf = NULL; + close (fd); + append_msg (CMSG_ERROR, GTXT ("Cannot read file %s"), fname); + return; + } + close (fd); + + input = new DataInputStream (cf_buf, cf_bufsz); + u4 c_magic = input->readUnsigned (); + if (c_magic != JAVA_MAGIC) + { + append_msg (CMSG_ERROR, GTXT ("Not a class file: %s"), fname); + return; + } + /* u2 minor = */ input->readUnsignedShort (); + /* u2 major = */ input->readUnsignedShort (); + status = AE_OK; +} + +ClassFile::~ClassFile () +{ + free (cf_buf); + free (class_name); + free (class_filename); + free (source_name); + delete bcpool; + delete input; +} + +static void +convertName (char *s) +{ + while (*s) + { + if (*s == '/') + *s = '.'; + s++; + } +} + +void +ClassFile::printConstant (StringBuilder *sb, int index) +{ + u1 type = bcpool->getType (index); + switch (type) + { + case CONSTANT_METHOD: + { + char *str = bcpool->getString (index); + if (str) + { + convertName (str); + sb->append (str); + sb->append (NTXT ("()")); + } + break; + } + case CONSTANT_CLASS: + { + char *str = bcpool->getString (index); + if (str) + { + convertName (str); + sb->append (str); + } + break; + } + case CONSTANT_UTF8: + { + char *str = bcpool->getString (index); + if (str) + sb->append (str); + break; + } + case CONSTANT_STRING: + { + char *str = bcpool->getString (index); + if (str) + { + sb->append ('"'); + sb->append (str); + sb->append ('"'); + } + break; + } + default: + sb->append ('#'); + sb->append ((int) index); + break; + } +} + +long long +ClassFile::printCodeSequence (StringBuilder *sb, uint64_t addr, DataInputStream *in) +{ + int64_t offset = in->get_offset (); + sb->appendf (NTXT ("%08llx: "), (long long) addr); + int opcode = in->readByte (); + if (opcode == opc_wide) + { + opcode = in->readByte (); + sb->append (get_opc_name (opcode)); + sb->append (NTXT ("_w ")); + int arg = in->readUnsignedShort (); + switch (opcode) + { + case opc_aload: case opc_astore: + case opc_fload: case opc_fstore: + case opc_iload: case opc_istore: + case opc_lload: case opc_lstore: + case opc_dload: case opc_dstore: + case opc_ret: + sb->append (arg); + break; + case opc_iinc: + sb->append (arg); + sb->append (' '); + sb->append (in->readUnsignedShort ()); + break; + default: + sb->append (GTXT ("Invalid opcode")); + break; + } + } + else + { + sb->append (get_opc_name (opcode)); + sb->append (' '); + switch (opcode) + { + case opc_aload: case opc_astore: + case opc_fload: case opc_fstore: + case opc_iload: case opc_istore: + case opc_lload: case opc_lstore: + case opc_dload: case opc_dstore: + case opc_ret: + sb->append (in->readByte ()); + break; + case opc_iinc: + sb->append (in->readByte ()); + sb->append (' '); + sb->append (in->readByte ()); + break; + case opc_tableswitch: + { + int align = (addr + 1) % 4; // 1 byte is a length of opc_lookupswitch + if (align != 0) + { + in->skip (4 - align); // four byte boundry + } + long default_skip = in->readUnsigned (); + long low = in->readUnsigned (); + long high = in->readUnsigned (); + sb->appendf (GTXT ("%ld to %ld: default=0x%llx"), + (long) low, (long) high, (long long) (addr + default_skip)); + for (long i = low; i <= high; ++i) + /* u4 i1 = */ in->readUnsigned (); + break; + } + case opc_lookupswitch: + { + int align = (addr + 1) % 4; // 1 byte is a length of opc_lookupswitch + if (align != 0) + in->skip (4 - align); // four byte boundry + u4 default_skip = in->readUnsigned (); + u4 npairs = in->readUnsigned (); + sb->appendf (GTXT ("%d: default=0x%llx"), npairs, + (long long) (addr + default_skip)); + for (int i = 0, nints = npairs * 2; i < nints; i += 2) + { + /* u4 i1 = */ in->readUnsigned (); + /* u4 i2 = */ in->readUnsigned (); + } + break; + } + case opc_newarray: + switch (in->readByte ()) + { + case T_INT: + sb->append (GTXT ("int")); + break; + case T_LONG: + sb->append (GTXT ("long")); + break; + case T_FLOAT: + sb->append (GTXT ("float")); + break; + case T_DOUBLE: + sb->append (GTXT ("double")); + break; + case T_CHAR: + sb->append (GTXT ("char")); + break; + case T_SHORT: + sb->append (GTXT ("short")); + break; + case T_BYTE: + sb->append (GTXT ("byte")); + break; + case T_BOOLEAN: + sb->append (GTXT ("boolean")); + break; + default: + sb->append (GTXT ("BOGUS TYPE")); + break; + } + break; + case opc_anewarray: + sb->append (GTXT ("class ")); + printConstant (sb, in->readUnsignedShort ()); + break; + case opc_sipush: + sb->append (in->readUnsignedShort ()); + break; + case opc_bipush: + sb->append (in->readByte ()); + break; + case opc_ldc: + printConstant (sb, in->readByte ()); + break; + case opc_ldc_w: case opc_ldc2_w: + case opc_instanceof: case opc_checkcast: + case opc_new: + case opc_putstatic: case opc_getstatic: + case opc_putfield: case opc_getfield: + case opc_invokevirtual: + case opc_invokespecial: + case opc_invokestatic: + printConstant (sb, in->readUnsignedShort ()); + break; + case opc_invokeinterface: + { + u2 index = in->readUnsignedShort (); + u1 count = in->readByte (); + /* u1 zero = */ in->readByte (); + sb->appendf (" #%u, %u) ", (unsigned int) index, (unsigned int) count); + printConstant (sb, index); + break; + } + case opc_multianewarray: + { + u2 index = in->readUnsignedShort (); + printConstant (sb, index); + sb->appendf (GTXT (" dim #%d "), index); + break; + } + case opc_jsr: case opc_goto: + case opc_ifeq: case opc_ifge: case opc_ifgt: + case opc_ifle: case opc_iflt: case opc_ifne: + case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpge: + case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmplt: + case opc_if_acmpeq: case opc_if_acmpne: + case opc_ifnull: case opc_ifnonnull: + sb->appendf (NTXT ("0x%llx"), (long long) (addr + (short) in->readUnsignedShort ())); + break; + case opc_jsr_w: + case opc_goto_w: + sb->append (addr + (int) in->readUnsigned ()); + break; + default: + break; + } + } + return in->get_offset () - offset; +} + +void +ClassFile::readAttributes (int count) +{ + blanksCnt += 4; + for (int ax = 0; ax < count; ax++) + { + u2 attribute_name_index = input->readUnsignedShort (); + u4 attribute_length = input->readUnsigned (); + char *attribute_name = bcpool->getString (attribute_name_index); + if (!attribute_name) + { + Dprintf (DUMP_JAVA_CLASS, NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d\n"), + (int) blanksCnt, ' ', (int) (ax + 1), + (int) attribute_name_index, STR (attribute_name), (int) attribute_length); + input->skip (attribute_length); + continue; + } + + if (strcmp (attribute_name, NTXT ("SourceFile")) == 0) + { + u2 sourcefile_index = input->readUnsignedShort (); + source_name = dbe_strdup (bcpool->getString (sourcefile_index)); + Dprintf (DUMP_JAVA_CLASS, NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d file_name=%d %s\n"), + (int) blanksCnt, ' ', (int) (ax + 1), + (int) attribute_name_index, STR (attribute_name), (int) attribute_length, + (int) sourcefile_index, STR (source_name)); + } + else if (strcmp (attribute_name, NTXT ("InnerClasses")) == 0) + { + int niclasses = input->readUnsignedShort (); + for (int ix = 0; ix < niclasses; ix++) + { + u2 inner_class_info_index = input->readUnsignedShort (); + u2 outer_class_info_index = input->readUnsignedShort (); + u2 inner_name_index = input->readUnsignedShort (); + u2 inner_class_access_flags = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, + NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d name=%d '%s'\n" + "%*cinner_class_info_index=%d outer_class_info_index=%d flags=%s\n"), + (int) blanksCnt, ' ', (int) (ax + 1), + (int) attribute_name_index, STR (attribute_name), (int) attribute_length, + (int) inner_name_index, STR (bcpool->getString (inner_name_index)), + (int) (blanksCnt + 10), ' ', + (int) inner_class_info_index, (int) outer_class_info_index, + access_flags_to_str (NestedClassAccess, inner_class_access_flags)); + } + } + else if (strcmp (attribute_name, NTXT ("Code")) == 0) + { + u2 max_stack = input->readUnsignedShort (); + u2 max_locals = input->readUnsignedShort (); + u4 code_length = input->readUnsigned (); + if (cur_jmthd) + { + cur_jmthd->size = code_length; + cur_jmthd->img_fname = dbeFile->get_location (); + cur_jmthd->img_offset = input->get_offset (); + } + input->skip (code_length); + u2 exception_table_length = input->readUnsignedShort (); + input->skip (exception_table_length * (2 + 2 + 2 + 2)); + Dprintf (DUMP_JAVA_CLASS, + NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d max_stack=%d max_locals=%d code_length=%d exception_table_length=%d\n"), + (int) blanksCnt, ' ', (int) (ax + 1), + (int) attribute_name_index, STR (attribute_name), (int) attribute_length, + (int) max_stack, (int) max_locals, (int) code_length, (int) exception_table_length); + readAttributes (input->readUnsignedShort ()); + } + else if (strcmp (attribute_name, NTXT ("LineNumberTable")) == 0) + { + int nlines = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d nlines=%d\n"), + (int) blanksCnt, ' ', (int) (ax + 1), + (int) attribute_name_index, STR (attribute_name), (int) attribute_length, + (int) nlines); + for (int lx = 0; lx < nlines; lx++) + { + int bci = input->readUnsignedShort (); + int lno = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT ("%*c %3d: pc=%4d (0x%04x) line=%d\n"), + (int) (blanksCnt + 5), ' ', (int) (lx + 1), (int) bci, (int) bci, (int) lno); + if (cur_jmthd) + byteCodeInfo->append (new ByteCodeInfo (cur_jmthd, bci, lno)); + } + } + else + { + Dprintf (DUMP_JAVA_CLASS, NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d\n"), + (int) blanksCnt, ' ', (int) (ax + 1), + (int) attribute_name_index, STR (attribute_name), + (int) attribute_length); + input->skip (attribute_length); + } + } + blanksCnt -= 4; +} + +int +ClassFile::readFile () +{ + if (status != AE_NOTREAD) + return status; + status = AE_OTHER; + + // The ClassFile Structure http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html + try + { + blanksCnt = 4; + cur_jmthd = NULL; + char *fname = dbeFile->get_location (); + openFile (fname); + Dprintf (DUMP_JAVA_CLASS, NTXT ("\nClassFile::readFile status=%d %s location=%s\n"), + (unsigned int) status, STR (get_name ()), STR (fname)); + if (status != AE_OK) + return status; + byteCodeInfo = new Vector<ByteCodeInfo *>(512); + bcpool = new BinaryConstantPool (*input); + u2 access_flags = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT ("\naccess_flags=%s; %s\n"), + access_flags_to_str (ClassAccess, access_flags), + STR (dbeFile->get_name ())); + u2 classNameInd = input->readUnsignedShort (); + class_filename = dbe_strdup (bcpool->getString (classNameInd)); + if (class_filename) + { + class_name = strdup (class_filename); + convertName (class_name); + } + + // Get superclass name + u2 superClassInd = input->readUnsignedShort (); + //char *str = bcpool->getString(superClassInd); + //super_name = str ? convertName( str ) : NULL; + + // Read interfaces + int interfaces_count = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, + NTXT (" class_name=%3d %-20s superClass=%3d %s interfaces_count=%d\n"), + (int) classNameInd, STR (class_name), + (int) superClassInd, STR (bcpool->getString (superClassInd)), + (int) interfaces_count); + for (int i = 0; i < interfaces_count; i++) + { + u2 index = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" %6lld%s"), (long long) index, + (i % 8 == 7) || (i + 1 == interfaces_count) ? "\n" : ""); + } + + // Read fields + int fields_count = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" fields_count=%d\n"), fields_count); + for (int i = 0; i < fields_count; i++) + { + u2 fld_access_flags = input->readUnsignedShort (); + u2 name_index = input->readUnsignedShort (); + u2 descriptor_index = input->readUnsignedShort (); + u2 attributes_count = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, + NTXT (" %2d: name=%3d %-20s flags=%s; desc_ind=%d attr_count=%d\n"), + i, (int) name_index, STR (bcpool->getString (name_index)), + access_flags_to_str (FieldAccess, fld_access_flags), + (int) descriptor_index, (int) attributes_count); + readAttributes (attributes_count); + } + + // Read methods + int methods_count = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT ("\n methods_count=%d\n"), (int) methods_count); + int func_cnt = functions->size (); + for (int i = 0; i < methods_count; i++) + { + u2 mthd_access_flags = input->readUnsignedShort (); + u2 name_index = input->readUnsignedShort (); + u2 descriptor_index = input->readUnsignedShort (); + char *mname = bcpool->getString (name_index); + if (mname == NULL) + { + DataReadException *e1 = new DataReadException (dbe_sprintf (GTXT ("method name[%d] is NULL\n"), i)); + throw (e1); + } + char *msign = bcpool->getString (descriptor_index); + if (msign == NULL) + { + DataReadException *e1 = new DataReadException (dbe_sprintf (GTXT ("method signature[%d] is NULL\n"), i)); + throw (e1); + } + size_t len = strlen (class_name); + cur_jmthd = NULL; + for (int idx = 0; idx < func_cnt; idx++) + { + JMethod *jmthd = (JMethod*) functions->fetch (idx); + char *jmt_name = jmthd->get_name (Histable::SHORT); + if (strncmp (jmt_name, class_name, len) == 0) + { + if (strcmp (jmt_name + len + 1, mname) == 0 && + strcmp (jmthd->get_signature (), msign) == 0) + { + cur_jmthd = jmthd; + break; + } + } + } + if (cur_jmthd == NULL) + { + cur_jmthd = dbeSession->createJMethod (); + cur_jmthd->module = this; + cur_jmthd->set_signature (dbe_strdup (msign)); + char *nm = dbe_sprintf (NTXT ("%s.%s"), class_name, mname); + cur_jmthd->set_name (nm); + free (nm); + functions->append (cur_jmthd); + } + if ((mthd_access_flags & ACC_NATIVE) != 0) + { + cur_jmthd->flags |= FUNC_FLAG_NATIVE; + } + u2 attributes_count = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, + NTXT (" %2d: name=%d %-20s flags=%s desc_ind=%d attr_count=%d\n"), + (int) (i + 1), (int) name_index, STR (bcpool->getString (name_index)), + access_flags_to_str (MethodAccess, mthd_access_flags), + (int) descriptor_index, (int) attributes_count); + readAttributes (attributes_count); + cur_jmthd->popSrcFile (); + } + + // Read global attributes + u2 global_attributes_count = input->readUnsignedShort (); + Dprintf (DUMP_JAVA_CLASS, NTXT (" global_attributes_count=%d\n"), global_attributes_count); + readAttributes (global_attributes_count); + status = AE_OK; + } + catch (DataReadException *ex) + { + append_msg (CMSG_ERROR, GTXT ("Cannot read class file %s (%s)"), get_name (), ex->toString ()); + delete ex; + status = AE_OTHER; + } + + char *fnm = NULL; + if (class_filename) + { + if (strcmp (class_filename, get_name ()) != 0) + set_name (strdup (class_filename)); + if (source_name) + { + char *bname = strrchr (class_filename, '/'); + if (bname) + fnm = dbe_sprintf (NTXT ("%.*s/%s"), (int) (bname - class_filename), + class_filename, source_name); + else + fnm = strdup (source_name); + } + else + fnm = get_java_file_name (class_filename, false); + } + else if (source_name) + fnm = strdup (source_name); + if (fnm) + { + set_file_name (fnm); + main_source = findSource (file_name, true); + main_source->dbeFile->filetype |= DbeFile::F_JAVA_SOURCE; + } + + for (long i = 0, sz = VecSize (functions); i < sz; i++) + functions->get (i)->def_source = main_source; + JMethod *func = NULL; + for (long i = 0, sz = VecSize (byteCodeInfo); i < sz; i++) + { + ByteCodeInfo *p = byteCodeInfo->get (i); + if (func != p->func) + { + if (func) + func->popSrcFile (); + func = p->func; + func->line_first = p->lno; + func->pushSrcFile (main_source, 0); + } + func->line_last = p->lno; + func->add_PC_info (p->bci, p->lno, main_source); + } + if (func) + func->popSrcFile (); + Destroy (byteCodeInfo); + Dprintf (DUMP_JAVA_CLASS, NTXT ("\n status=%d class_filename=%s class_name=%s source_name=%s file_name=%s %s\n"), + (unsigned int) status, STR (class_filename), STR (class_name), + STR (source_name), STR (file_name), + STR (get_name ())); + return status; +} + +#define MAX_CLASS_SIZE 65536 + +char * +ClassFile::get_disasm (uint64_t inst_address, uint64_t end_address, + uint64_t start_address, uint64_t f_offset, int64_t &inst_size) +{ + int64_t offset = f_offset + (inst_address - start_address); + if ((cf_buf == NULL) || (inst_address >= end_address) || (offset >= cf_bufsz)) + { + inst_size = 0; + return NULL; + } + + // Check for an implausibly large size + if ((inst_address - start_address) > MAX_CLASS_SIZE) + { + append_msg (CMSG_ERROR, GTXT ("Cannot disassemble class file %s (%s), implausible size = %lld"), + get_name (), dbeFile->get_location (), + (end_address - start_address)); + inst_size = 0; + return NULL; + } + + StringBuilder sb; + DataInputStream *in = new DataInputStream (input); + try + { + in->skip (offset); + inst_size = printCodeSequence (&sb, inst_address - start_address, in); + } + catch (DataReadException *ex) + { + append_msg (CMSG_ERROR, GTXT ("Cannot disassemble class file %s (%s) %s"), + get_name (), dbeFile->get_location (), ex->toString ()); + delete ex; + inst_size = 0; + } + delete in; + if (inst_size == 0) + return NULL; + return sb.toString (); +} + +char * +ClassFile::get_java_file_name (char *clname, bool classSuffix) +{ + size_t len = strlen (clname); + if (len > 6 && streq (clname + len - 6, NTXT (".class"))) + len -= 6; + if (!classSuffix) + { // remove $SubClassName from "ClassName$SubClassName" + char *tmp = strchr (clname, '$'); + if (tmp) + len = tmp - clname; + } + char *clpath = (char *) malloc (len + 10); + for (size_t i = 0; i < len; i++) + clpath[i] = (clname[i] == '.') ? '/' : clname[i]; + snprintf (clpath + len, 10, classSuffix ? NTXT (".class") : NTXT (".java")); + return clpath; +} diff --git a/gprofng/src/ClassFile.h b/gprofng/src/ClassFile.h new file mode 100644 index 0000000..bd4c61b --- /dev/null +++ b/gprofng/src/ClassFile.h @@ -0,0 +1,63 @@ +/* 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. */ + +#ifndef _CLASSFILE_H +#define _CLASSFILE_H + +#include "Module.h" + +class DataInputStream; +class BinaryConstantPool; +class JMethod; +class StringBuilder; +class ByteCodeInfo; + +class ClassFile : public Module +{ +public: + ClassFile (); + virtual ~ClassFile (); + virtual int readFile (); + virtual char *get_disasm (uint64_t inst_address, uint64_t end_address, + uint64_t start_address, uint64_t f_offset, + int64_t &inst_size); + static char *get_java_file_name (char *clname, bool classSuffix); + +private: + + void openFile (const char *fname); + char *get_opc_name (int op); + void readAttributes (int count); + void printConstant (StringBuilder *sb, int index); + long long printCodeSequence (StringBuilder *sb, uint64_t addr, DataInputStream *in); + + unsigned char *cf_buf; + int64_t cf_bufsz; + int blanksCnt; + DataInputStream *input; + BinaryConstantPool *bcpool; + JMethod *cur_jmthd; + char *class_name; + char *class_filename; + char *source_name; + Vector<ByteCodeInfo *> *byteCodeInfo; +}; + +#endif diff --git a/gprofng/src/Command.cc b/gprofng/src/Command.cc new file mode 100644 index 0000000..d1620d7 --- /dev/null +++ b/gprofng/src/Command.cc @@ -0,0 +1,562 @@ +/* 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 <string.h> +#include <stdlib.h> +#include <sys/param.h> + +#include "gp-defs.h" +#include "Command.h" +#include "DbeSession.h" +#include "MemorySpace.h" +#include "i18n.h" +#include "StringBuilder.h" + +const char *Command::DEFAULT_CMD = "default"; // token for default +const char *Command::ALL_CMD = "all"; // token for all +const char *Command::ANY_CMD = "any"; // token for any +const char *Command::NONE_CMD = "none"; // token for none +const char *Command::HWC_CMD = "hwc"; // token for all HWC +const char *Command::BIT_CMD = "bit"; // token for any bit-generated metric +const char *Command::DEFAULT_METRICS = "ei.user:name"; // if no .rc files read +const char *Command::DEFAULT_SORT = "e.user:name"; // if no .rc files read + +static char *fhdr, *cchdr, *lahdr, *iohdr, *sdhdr, *lsthdr, *lohdr; +static char *methdr, *othdr, *mischdr, *deflthdr; +static char *selhdr, *filthdr, *outhdr, *exphdr, *obj_allhdr; +static char *unsuphdr, *indxobjhdr; +static char *helphdr, *rahdr, *ddhdr, *typehdr, *typehdr2; + +// This is the list of commands, which governs the parser scan, as +// well as the help command. +// A line with the tag NO_CMD is skipped in parsing, but is used +// to provide subheadings for the help +// The HELP line must be the last one in the list of commands +// to be shown by "-help"; The HHELP line must be the +// last one to be shown by "-xhelp" +// The LAST_CMD line must be the last one recognized by the parser +// +// The ordering of this list should match the ordering in the man +// page, and the subheader lines should match the subheadings in +// the man page. + +static char *desc[LAST_CMD]; + +static Cmdtable cmd_lst[] = { // list of commands + // User Commands + { NO_CMD, "", NULL, NULL, 0, &fhdr}, + { FUNCS, "functions", NULL, NULL, 0, &desc[FUNCS]}, + { METRICS, "metrics", NULL, "metric_spec", 1, &desc[METRICS]}, + { SORT, "sort", NULL, "metric_spec", 1, &desc[SORT]}, + { FDETAIL, "fsummary", NULL, NULL, 0, &desc[FDETAIL]}, + { FSINGLE, "fsingle", NULL, "function_name #", 2, &desc[FSINGLE]}, + + { NO_CMD, "", NULL, NULL, 0, &cchdr}, + { GPROF, "callers-callees", "gprof", NULL, 0, &desc[GPROF]}, + { CSINGLE, "csingle", NULL, "function_name #", 2, &desc[CSINGLE]}, + { CPREPEND, "cprepend", NULL, "function_name #", 2, &desc[CPREPEND]}, + { CAPPEND, "cappend", NULL, "function_name #", 2, &desc[CAPPEND]}, + { CRMFIRST, "crmfirst", NULL, NULL, 0, &desc[CRMFIRST]}, + { CRMLAST, "crmlast", NULL, NULL, 0, &desc[CRMLAST]}, + { CALLTREE, "calltree", "ctree", NULL, 0, &desc[CALLTREE]}, + + { NO_CMD, "", NULL, NULL, 0, &lahdr}, + { LEAKS, "leaks", NULL, NULL, 0, &desc[LEAKS]}, + { ALLOCS, "allocs", NULL, NULL, 0, &desc[ALLOCS]}, + { HEAP, "heap", NULL, NULL, 0, &desc[HEAP]}, + { HEAPSTAT, "heapstat", NULL, NULL, 0, &desc[HEAPSTAT]}, + + { NO_CMD, "", NULL, NULL, 0, &iohdr}, + { IOACTIVITY, "ioactivity", NULL, NULL, 0, &desc[IOACTIVITY]}, + { IOVFD, "iodetail", NULL, NULL, 0, &desc[IOVFD]}, + { IOCALLSTACK, "iocallstack", NULL, NULL, 0, &desc[IOCALLSTACK]}, + { IOSTAT, "iostat", NULL, NULL, 0, &desc[IOSTAT]}, + + // PC, line, source and dissassembly commands + { NO_CMD, "", NULL, NULL, 0, &sdhdr}, + { HOTPCS, "pcs", NULL, NULL, 0, &desc[HOTPCS]}, + { PDETAIL, "psummary", NULL, NULL, 0, &desc[PDETAIL]}, + { HOTLINES, "lines", NULL, NULL, 0, &desc[HOTLINES]}, + { LDETAIL, "lsummary", NULL, NULL, 0, &desc[LDETAIL]}, + { SOURCE, "source", NULL, "func/file #", 2, &desc[SOURCE]}, + { DISASM, "disasm", NULL, "func/file #", 2, &desc[DISASM]}, + { SCOMPCOM, "scc", NULL, "com_spec", 1, &desc[SCOMPCOM]}, + { STHRESH, "sthresh", NULL, "value", 1, &desc[STHRESH]}, + { DCOMPCOM, "dcc", NULL, "com_spec", 1, &desc[DCOMPCOM]}, + { COMPCOM, "cc", NULL, "com_spec", 1, &desc[COMPCOM]}, + { DTHRESH, "dthresh", NULL, "value", 1, &desc[DTHRESH]}, + { SETPATH, "setpath", NULL, "path_list", 1, &desc[SETPATH]}, + { ADDPATH, "addpath", NULL, "path_list", 1, &desc[ADDPATH]}, + { PATHMAP, "pathmap", NULL, "old_prefix new_prefix", 2, &desc[PATHMAP]}, + { LIBDIRS, "preload_libdirs", NULL, NULL, 1, &desc[PATHMAP]}, + + // Index Object commands + { NO_CMD, "", NULL, NULL, 0, &indxobjhdr}, + { INDXOBJ, "indxobj", NULL, "type", 1, &desc[INDXOBJ]}, + { INDXOBJLIST, "indxobj_list", NULL, NULL, 0, &desc[INDXOBJLIST]}, + { INDXOBJDEF, "indxobj_define", NULL, "type \"index-expr\"", 2, &desc[INDXOBJDEF]}, + + // Deadlock detection commands + { NO_CMD, "", NULL, NULL, 0, &ddhdr}, + { DEADLOCK_EVNTS, "deadlocks", NULL, NULL, 0, &desc[DEADLOCK_EVNTS]}, + { DEADLOCK_SUM, "dsummary", NULL, "{deadlock_id|all}", 1, &desc[DEADLOCK_SUM]}, + + { NO_CMD, "", NULL, NULL, 0, &lsthdr}, + { EXP_LIST, "experiment_list", "exp_list", NULL, 0, &desc[EXP_LIST]}, + { SAMPLE_LIST, "sample_list", NULL, NULL, 0, &desc[SAMPLE_LIST]}, + { LWP_LIST, "lwp_list", NULL, NULL, 0, &desc[LWP_LIST]}, + { THREAD_LIST, "thread_list", NULL, NULL, 0, &desc[THREAD_LIST]}, + { CPU_LIST, "cpu_list", NULL, NULL, 0, &desc[CPU_LIST]}, + + { NO_CMD, "", NULL, NULL, 0, &filthdr}, + { FILTERS, "filters", NULL, "filter-specification", 1, &desc[FILTERS]}, + { DESCRIBE, "describe", NULL, NULL, 0, &desc[DESCRIBE]}, + + { NO_CMD, "", NULL, NULL, 0, &selhdr}, + { SAMPLE_SELECT, "sample_select", NULL, "sample_spec", 1, &desc[SAMPLE_SELECT]}, + { LWP_SELECT, "lwp_select", NULL, "lwp_spec", 1, &desc[LWP_SELECT]}, + { THREAD_SELECT, "thread_select", NULL, "thread_spec", 1, &desc[THREAD_SELECT]}, + { CPU_SELECT, "cpu_select", NULL, "cpu_spec", 1, &desc[CPU_SELECT]}, + + { NO_CMD, "", NULL, NULL, 0, &lohdr}, + { OBJECT_LIST, "object_list", NULL, NULL, 0, &desc[OBJECT_LIST]}, + { OBJECT_SHOW, "object_show", NULL, "obj1,...", 1, &desc[OBJECT_SHOW]}, + { OBJECT_HIDE, "object_hide", NULL, "obj1,...", 1, &desc[OBJECT_HIDE]}, + { OBJECT_API, "object_api", NULL, "obj1,...", 1, &desc[OBJECT_API]}, + { DUMMY_CMD, " ", NULL, NULL, 0, &obj_allhdr}, + { OBJECTS_DEFAULT, "objects_default", NULL, NULL, 1, &desc[OBJECTS_DEFAULT]}, + + { OBJECT_SELECT, "object_select", NULL, "obj1,...", 1, &desc[OBJECT_SELECT]}, + + { NO_CMD, "", NULL, NULL, 0, &methdr}, + { METRIC_LIST, "metric_list", NULL, NULL, 0, &desc[METRIC_LIST]}, + { GMETRIC_LIST, "cmetric_list", "gmetric_list", NULL, 0, &desc[GMETRIC_LIST]}, + { INDX_METRIC_LIST, "indx_metric_list", NULL, NULL, 1, &desc[INDX_METRIC_LIST]}, + + { NO_CMD, "", NULL, NULL, 0, &outhdr}, + { OUTFILE, "outfile", NULL, "filename", 1, &desc[OUTFILE]}, + { APPENDFILE, "appendfile", NULL, "filename", 1, &desc[APPENDFILE]}, + { LIMIT, "limit", NULL, "n", 1, &desc[LIMIT]}, + { NAMEFMT, "name", NULL, "{long|short|mangled}[:{soname|nosoname}]", 1, &desc[NAMEFMT]}, + { VIEWMODE, "viewmode", NULL, "{user|expert|machine}", 1, &desc[VIEWMODE]}, + { COMPARE, "compare", NULL, "{on|off|delta|ratio}", 1, &desc[COMPARE]}, + { PRINTMODE, "printmode", NULL, "string", 1, &desc[PRINTMODE]}, + + { NO_CMD, "", NULL, NULL, 0, &othdr}, + { HEADER, "header", NULL, "exp_id", 1, &desc[HEADER]}, + { OBJECTS, "objects", NULL, NULL, 0, &desc[OBJECTS]}, + { OVERVIEW_NEW, "overview", NULL, NULL, 0, &desc[OVERVIEW_NEW]}, + { SAMPLE_DETAIL, "sample_detail", NULL, "exp_id", 1, &desc[SAMPLE_DETAIL]}, + { STATISTICS, "statistics", NULL, "exp_id", 1, &desc[STATISTICS]}, + + { NO_CMD, "", NULL, NULL, 0, &exphdr}, + { OPEN_EXP, "open_exp", NULL, "experiment", 1, &desc[OPEN_EXP]}, + { ADD_EXP, "add_exp", NULL, "experiment", 1, &desc[ADD_EXP]}, + { DROP_EXP, "drop_exp", NULL, "experiment", 1, &desc[DROP_EXP]}, + + { NO_CMD, "", NULL, NULL, 0, &deflthdr}, + { DMETRICS, "dmetrics", NULL, "metric_spec", 1, &desc[DMETRICS]}, + { DSORT, "dsort", NULL, "metric_spec", 1, &desc[DSORT]}, + { EN_DESC, "en_desc", NULL, "{on|off|=<regex>}", 1, &desc[EN_DESC]}, + + { NO_CMD, "", NULL, NULL, 0, &mischdr}, + { DUMMY_CMD, "<type>", NULL, NULL, 0, &typehdr}, + { DUMMY_CMD, " ", NULL, NULL, 0, &typehdr2}, + + { IFREQ, "ifreq", NULL, NULL, 0, &desc[IFREQ]}, + { PROCSTATS, "procstats", NULL, NULL, 0, &desc[PROCSTATS]}, + { SCRIPT, "script", NULL, "file", 1, &desc[SCRIPT]}, + { VERSION_cmd, "version", NULL, NULL, 0, &desc[VERSION_cmd]}, + { QUIT, "quit", "exit", NULL, 0, &desc[QUIT]}, + + { NO_CMD, "", NULL, NULL, 0, &helphdr}, + { HELP, "help", NULL, NULL, 0, &desc[HELP]}, + + { NO_CMD, "", NULL, NULL, 0, &unsuphdr}, + { HELP, "-help", NULL, NULL, 0, &desc[HELP]}, + { DUMPFUNC, "dfuncs", NULL, "string", 1, &desc[DUMPFUNC]}, + { DUMPDOBJS, "ddobjs", NULL, "string", 1, &desc[DUMPDOBJS]}, + { DUMPNODES, "dnodes", NULL, NULL, 0, &desc[DUMPNODES]}, + { DUMPSTACKS, "dstacks", NULL, NULL, 0, &desc[DUMPSTACKS]}, + { DUMPUNK, "dunkpc", NULL, NULL, 0, &desc[DUMPUNK]}, + { DUMPMAP, "dmap", NULL, NULL, 0, &desc[DUMPMAP]}, + { DUMPENTITIES, "dentities", NULL, NULL, 0, &desc[DUMPENTITIES]}, + { IGNORE_NO_XHWCPROF, "ignore_no_xhwcprof", NULL, NULL, 0, &desc[IGNORE_NO_XHWCPROF]}, + { IGNORE_FS_WARN, "ignore_fs_warn", NULL, NULL, 0, &desc[IGNORE_FS_WARN]}, + + { DUMP_PROFILE, "dprofile", NULL, NULL, 0, &desc[DUMP_PROFILE]}, + { DUMP_SYNC, "dsync", NULL, NULL, 0, &desc[DUMP_SYNC]}, + { DUMP_IOTRACE, "diotrace", NULL, NULL, 0, &desc[DUMP_IOTRACE]}, + { DUMP_HWC, "dhwc", NULL, NULL, 0, &desc[DUMP_HWC]}, + { DUMP_HEAP, "dheap", NULL, NULL, 0, &desc[DUMP_HEAP]}, + { RACE_ACCS, "r_accs", NULL, NULL, 0, &desc[RACE_ACCS]}, + + { DMPI_FUNCS, "dmpi_funcs", NULL, NULL, 0, &desc[DMPI_FUNCS]}, + { DMPI_MSGS, "dmpi_msgs", NULL, NULL, 0, &desc[DMPI_MSGS]}, + { DMPI_EVENTS, "dmpi_events", NULL, NULL, 0, &desc[DMPI_EVENTS]}, + + { DMEM, "dmem", NULL, NULL, 1, &desc[DMEM]}, + { DUMP_GC, "dumpgc", NULL, NULL, 0, &desc[DUMP_GC]}, + { DKILL, "dkill", NULL, NULL, 2, &desc[DKILL]}, + + { QQUIT, "xquit", NULL, NULL, 0, &desc[QQUIT]}, + // use xquit for memory leak detection in dbe; it's + // like quit, but deletes all data loaded + + { HHELP, "xhelp", NULL, NULL, 0, &desc[HHELP]}, + { WHOAMI, "-whoami", NULL, NULL, 0, &desc[WHOAMI]}, + + // these are not recognized at this point + { LOADOBJECT, "segments", "pmap", NULL, 0, &desc[LOADOBJECT]}, + { LOADOBJECT_LIST, "segment_list", NULL, NULL, 0, &desc[LOADOBJECT_LIST]}, + { LOADOBJECT_SELECT, "segment_select", NULL, "seg1,...", 1, &desc[LOADOBJECT_SELECT]}, + + { LAST_CMD, "xxxx", NULL, NULL, 0, NULL} +}; + +CmdType +Command::get_command (char *cmd, int &arg_count, int &cparam) +{ + int i; + int len = (int) strlen (cmd); + bool got = false; + CmdType token = UNKNOWN_CMD; + arg_count = 0; + cparam = -1; + if (*cmd == '\0') // - command + return STDIN; + if (*cmd == '#') // comment + return COMMENT; + if (strcmp (cmd, "V") == 0 || strcmp (cmd, "-version") == 0) + return VERSION_cmd; + if (strcmp (cmd, "-help") == 0) + return HELP; + if (strncmp (cmd, NTXT ("-whoami="), 8) == 0) + { + cparam = 8; + return WHOAMI; + } + + if (*cmd == '-') + cmd++; + for (i = 0;; i++) + { + if (cmd_lst[i].token == LAST_CMD) + break; + if (!strncasecmp (cmd, cmd_lst[i].str, len) || + (cmd_lst[i].alt && !strncasecmp (cmd, cmd_lst[i].alt, len))) + { + // Is it unambiguous? + if (!strcasecmp (cmd, cmd_lst[i].str) + || (cmd_lst[i].alt && !strcasecmp (cmd, cmd_lst[i].alt))) + { + // exact, full-length match + token = cmd_lst[i].token; + arg_count = cmd_lst[i].arg_count; + return token; + } + if (got) + return AMBIGUOUS_CMD; + got = true; + token = cmd_lst[i].token; + arg_count = cmd_lst[i].arg_count; + } + } + + // Did we find it? + if (token != UNKNOWN_CMD) + return token; + + // See if it's the name of a index object + if (dbeSession) + { + int indxtype = dbeSession->findIndexSpaceByName (cmd); + if (indxtype >= 0) + { + // found it + cparam = indxtype; + return INDXOBJ; + } + } + return token; +} + +const char * +Command::get_cmd_str (CmdType type) +{ + for (int i = 0;; i++) + { + if (cmd_lst[i].token == LAST_CMD) + break; + if (type == cmd_lst[i].token) + return cmd_lst[i].str; + } + return "xxxx"; +} + +char * +Command::get_err_string (Cmd_status err) +{ + switch (err) + { + case CMD_OK: + return NULL; + case CMD_BAD: + return GTXT ("command bad"); + case CMD_AMBIGUOUS: + return GTXT ("command ambiguous"); + case CMD_BAD_ARG: + return GTXT ("Invalid argument to command"); + case CMD_OUTRANGE: + return GTXT ("argument to command is out-of-range"); + case CMD_INVALID: + return GTXT ("invalid command"); + } + return NULL; +} + +void +Command::print_help (char *prog_name, bool cmd_line, bool usermode, FILE *outf) +{ + char *fmt, *msg; + int i; + StringBuilder sb; + enum CmdType nc; + init_desc (); + if (usermode) // show the hidden ones, too + nc = HELP; + else + nc = HHELP; + + if (cmd_line) + fprintf (outf, GTXT ("Usage: %s [ -script script | -command | - ] exper_1 ... exper_n\n"), + prog_name); + fprintf (outf, GTXT ("An alternate spelling for a command is shown in [], where applicable.\n\n" + "Those commands followed by a * may appear in .rc files.\n\n" + "Those commands followed by a $ can only appear in .rc files.\n\n")); + fmt = fmt_help (nc, ' '); + for (i = 0;; i++) + { + // check for end of list + if (cmd_lst[i].token == LAST_CMD) + break; + if (cmd_lst[i].token == NO_CMD) // this is a header line + fprintf (outf, NTXT (" %s\n"), *cmd_lst[i].desc); + else + { + if (strlen (cmd_lst[i].str) == 0) + continue; + // this is a real command line + sb.setLength (0); + sb.append (cmd_lst[i].str); + if (cmd_lst[i].alt) + { + sb.append ('['); + sb.append (cmd_lst[i].alt); + sb.append (']'); + } + if (cmd_lst[i].arg) + { + sb.append (' '); + sb.append (cmd_lst[i].arg); + } + msg = sb.toString (); + fprintf (outf, fmt, msg, *cmd_lst[i].desc); + free (msg); + } + // check for end of list + if (cmd_lst[i].token == nc) + break; + } +} + +// construct format for printing help +char * +Command::fmt_help (int nc, char head) +{ + int len, max_len, i; + static char fmt[BUFSIZ]; + + max_len = 0; + for (i = 0; i < nc; i++) + { + len = (int) strlen (cmd_lst[i].str); + if (cmd_lst[i].alt) + len += (int) strlen (cmd_lst[i].alt) + 2; + if (cmd_lst[i].arg) + len += (int) strlen (cmd_lst[i].arg) + 2; + if (max_len < len) + max_len = len; + } + snprintf (fmt, sizeof (fmt), NTXT (" %c%%-%ds %%s\n"), head, max_len + 1); + return fmt; +} + +void +Command::init_desc () +{ + if (desc[0] != NULL) + return; + desc[FUNCS] = GTXT ("display functions with current metrics"); + desc[HOTPCS] = GTXT ("display hot PC's with current metrics"); + desc[HOTLINES] = GTXT ("display hot lines with current metrics"); + desc[FDETAIL] = GTXT ("display summary metrics for each function"); + desc[OBJECTS] = GTXT ("display object list with errors or warnings"); + desc[COMPARE] = GTXT ("enable comparison mode for experiments *"); + desc[PRINTMODE] = GTXT ("set the mode for printing tables *"); + desc[LDETAIL] = GTXT ("display summary metrics for each hot line"); + desc[PDETAIL] = GTXT ("display summary metrics for each hot PC"); + desc[SOURCE] = GTXT ("display annotated source for function/file"); + desc[DISASM] = GTXT ("display annotated disassembly for function/file"); + desc[SCOMPCOM] = GTXT ("set compiler commentary classes for source *"); + desc[STHRESH] = GTXT ("set highlight threshold for source *"); + desc[DCOMPCOM] = GTXT ("set compiler commentary classes for disasm *"); + desc[COMPCOM] = GTXT ("set compiler commentary classes for both source and disasm *"); + desc[DTHRESH] = GTXT ("set highlight threshold for disasm *"); + desc[METRIC_LIST] = GTXT ("display the available metrics and dmetrics keywords"); + desc[METRICS] = GTXT ("set a new list of metrics"); + desc[SORT] = GTXT ("sort tables by the specified metric"); + desc[GPROF] = GTXT ("display the callers-callees for each function"); + desc[CALLTREE] = GTXT ("display the tree of function calls"); + desc[CALLFLAME] = GTXT ("request calltree flame chart -- not a command, but used in the tabs command"); + desc[GMETRIC_LIST] = GTXT ("display the available callers-callees metrics"); + desc[FSINGLE] = GTXT ("display the summary metrics for specified function"); + desc[CSINGLE] = GTXT ("display the callers-callees for the specified function"); + desc[CPREPEND] = GTXT ("add specified function to the head of the callstack fragment"); + desc[CAPPEND] = GTXT ("add specified function to the end of the callstack fragment"); + desc[CRMFIRST] = GTXT ("remove the first function from the callstack fragment"); + desc[CRMLAST] = GTXT ("remove the last function from the callstack fragment"); + desc[LEAKS] = GTXT ("display memory leaks, aggregated by callstack"); + desc[ALLOCS] = GTXT ("display allocations, aggregated by callstack"); + desc[HEAP] = GTXT ("display memory allocations and leaks, aggregated by callstack"); + desc[HEAPSTAT] = GTXT ("display heap statistics report"); + desc[IOACTIVITY] = GTXT ("display I/O activity report, aggregated by file name"); + desc[IOVFD] = GTXT ("display I/O activity report, aggregated by file descriptor"); + desc[IOCALLSTACK] = GTXT ("display I/O activity report, aggregated by callstack"); + desc[IOSTAT] = GTXT ("display I/O statistics report"); + desc[RACE_ACCS] = GTXT ("dump race access events"); + desc[DMPI_MSGS] = GTXT ("dump mpi messages"); + desc[DMPI_FUNCS] = GTXT ("dump mpi function calls"); + desc[DMPI_EVENTS] = GTXT ("dump mpi trace events"); + desc[DMEM] = GTXT ("debug command for internal use"); + desc[DUMP_GC] = GTXT ("dump Java garbage collector events"); + desc[DKILL] = GTXT ("send process p signal s"); + desc[DEADLOCK_EVNTS] = GTXT ("display deadlock events"); + desc[DEADLOCK_SUM] = GTXT ("display summary for the deadlock event"); + desc[HEADER] = GTXT ("display information about the experiment"); + desc[OVERVIEW_NEW] = GTXT ("display the overview of all loaded experiments"); + desc[SAMPLE_DETAIL] = GTXT ("display the current sample list with data"); + desc[STATISTICS] = GTXT ("display the execution statistics data"); + desc[EXP_LIST] = GTXT ("display the existing experiments"); + desc[DESCRIBE] = GTXT ("describe recorded data and tokens available for filtering data"); + desc[OBJECT_SHOW] = GTXT ("set load objects to show all functions *"); + desc[OBJECT_HIDE] = GTXT ("set load objects to hide functions *"); + desc[OBJECT_API] = GTXT ("set load objects to show API (entry point) only *"); + desc[OBJECTS_DEFAULT] = GTXT ("reset load objects show to defaults"); + desc[OBJECT_LIST] = GTXT ("display load objects, functions-shown flag"); + desc[OBJECT_SELECT] = GTXT ("set list of load objects whose functions are shown"); + desc[SAMPLE_LIST] = GTXT ("display the list of existing samples"); + desc[SAMPLE_SELECT] = GTXT ("set a new list of samples"); + desc[THREAD_LIST] = GTXT ("display the list of existing threads"); + desc[THREAD_SELECT] = GTXT ("set a new list of threads"); + desc[LWP_LIST] = GTXT ("display the list of existing LWPs"); + desc[LWP_SELECT] = GTXT ("set a new list of LWPs"); + desc[CPU_LIST] = GTXT ("display the list of CPUs"); + desc[CPU_SELECT] = GTXT ("set a new list of CPUs"); + desc[OUTFILE] = GTXT ("open filename for subsequent output"); + desc[APPENDFILE] = GTXT ("open filename for subsequent appended output"); + desc[LIMIT] = GTXT ("limit output to the first n entries (n=0 for no limit)"); + desc[NAMEFMT] = GTXT ("set long/short/mangled names for functions *"); + desc[VIEWMODE] = GTXT ("set viewmode user|expert|machine *"); + desc[EN_DESC] = GTXT ("enable descendant processes on|off|regex matches lineage or program name $"); + desc[SETPATH] = GTXT ("set search path for annotated src/dis"); + desc[ADDPATH] = GTXT ("add search path for annotated src/dis *"); + desc[PATHMAP] = GTXT ("remap path prefix for annotated src/dis *"); + desc[LIBDIRS] = GTXT ("set path where the gprofng libraries are installed"); + desc[SCRIPT] = GTXT ("read er_print commands from script file"); + desc[PROCSTATS] = GTXT ("display processing statistics"); + desc[ADD_EXP] = GTXT ("add experiment or group"); + desc[DROP_EXP] = GTXT ("drop experiment"); + desc[OPEN_EXP] = GTXT ("open experiment or group (drops all loaded experiments first)"); + desc[VERSION_cmd] = GTXT ("display the current release version"); + desc[HELP] = GTXT ("display the list of available commands"); + desc[QUIT] = GTXT ("terminate processing and exit"); + desc[DMETRICS] = GTXT ("set default function list metrics $"); + desc[DSORT] = GTXT ("set default function list sort metric $"); + desc[TLMODE] = GTXT ("set default timeline mode, align, depth $"); + desc[TLDATA] = GTXT ("set default timeline visible data $"); + desc[TABS] = GTXT ("set default visible tabs $"); + desc[RTABS] = GTXT ("set default visible tabs for Thread Analyzer Experiment $"); + desc[INDXOBJ] = GTXT ("display index objects of a specified type with current metrics"); + desc[INDXOBJLIST] = GTXT ("display list of index objects"); + desc[INDXOBJDEF] = GTXT ("define a new index object type *"); + desc[INDX_METRIC_LIST] = GTXT ("display the available index object metrics"); + desc[IFREQ] = GTXT ("display instruction-frequency report"); + desc[TIMELINE] = GTXT ("request timeline -- not a command, but used in the tabs command"); + desc[MPI_TIMELINE] = GTXT ("request mpi-timeline -- not a command, but used in the tabs command"); + desc[MPI_CHART] = GTXT ("request mpi chart -- not a command, but used in the tabs command"); + desc[DUALSOURCE] = GTXT ("request dualsource tab -- not a command, but used in the tabs command"); + desc[SOURCEDISAM] = GTXT ("request source/disassembly tab -- not a command, but used in the tabs command"); + desc[DUMPNODES] = GTXT ("dump pathtree node table"); + desc[DUMPSTACKS] = GTXT ("dump Experiment callstack tables"); + desc[DUMPUNK] = GTXT ("dump <Unknown> PCs"); + desc[DUMPFUNC] = GTXT ("dump functions whose name matches string"); + desc[DUMPDOBJS] = GTXT ("dump dataobjects whose name matches string"); + desc[DUMPMAP] = GTXT ("dump load-object map"); + desc[DUMPENTITIES] = GTXT ("dump threads, lwps, cpus"); + desc[DUMP_PROFILE] = GTXT ("dump clock profile events"); + desc[DUMP_SYNC] = GTXT ("dump synchronization trace events"); + desc[DUMP_IOTRACE] = GTXT ("dump IO trace events"); + desc[DUMP_HWC] = GTXT ("dump HWC profile events"); + desc[DUMP_HEAP] = GTXT ("dump heap trace events"); + desc[IGNORE_NO_XHWCPROF] = GTXT ("ignore absence of -xhwcprof info in dataspace profiling $"); + desc[IGNORE_FS_WARN] = GTXT ("ignore filesystem (nfs, ...) warning $"); + desc[HHELP] = GTXT ("display help including unsupported commands"); + desc[QQUIT] = GTXT ("terminate processing and exit"); + desc[LOADOBJECT] = GTXT ("display the address map with current metrics"); + desc[LOADOBJECT_LIST] = GTXT ("display segments, indicating which are selected"); + desc[LOADOBJECT_SELECT] = GTXT ("set a new list of segments"); + desc[FILTERS] = GTXT ("define a filter"); + + fhdr = GTXT ("\nCommands controlling the function list:"); + cchdr = GTXT ("\nCommands controlling the callers-callees and calltree lists:"); + lahdr = GTXT ("\nCommands controlling the leak and allocation lists:"); + iohdr = GTXT ("\nCommand controlling the I/O activity report:"); + rahdr = GTXT ("\nCommands controlling the race events lists:"); + ddhdr = GTXT ("\nCommands controlling the deadlock events lists:"); + typehdr = GTXT ("equivalent to \"memobj type\", or \"indxobj type\""); + typehdr2 = GTXT (" where type is a memory object or index object type"); + sdhdr = GTXT ("\nCommands controlling the source and disassembly listings:"); + lsthdr = GTXT ("\nCommands listing experiments, samples and threads:"); + lohdr = GTXT ("\nCommands controlling load object selection:"); + obj_allhdr = GTXT (" the special object name `all' refers to all load objects"); + methdr = GTXT ("\nCommands that list metrics:"); + othdr = GTXT ("\nCommands that print other displays:"); + outhdr = GTXT ("\nCommands that control output:"); + mischdr = GTXT ("\nMiscellaneous commands:"); + exphdr = GTXT ("\nCommands for experiments (scripts and interactive mode only):"); + deflthdr = GTXT ("\nDefault-setting commands:"); + selhdr = GTXT ("\nCommands controlling old-style filters/selection:"); + filthdr = GTXT ("\nCommands controlling filters:"); + indxobjhdr = GTXT ("\nCommands controlling the index objects:"); + unsuphdr = GTXT ("\nUnsupported commands:"); + helphdr = GTXT ("\nHelp command:"); +} diff --git a/gprofng/src/Command.h b/gprofng/src/Command.h new file mode 100644 index 0000000..4dd28ad --- /dev/null +++ b/gprofng/src/Command.h @@ -0,0 +1,286 @@ +/* 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. */ + +#ifndef _COMMAND_H +#define _COMMAND_H + + +#include <stdio.h> +#include <sys/types.h> + +#include "Metric.h" +#include "Hist_data.h" +#include "dbe_types.h" +#include "vec.h" +#include "enums.h" + +// This enum lists all the commands parsed by er_print +// The ordering here is not important, but LAST_CMD must +// be defined as the last command for which a help line will exist +// Command.cc has a matching list, and the ordering in +// that list determines what shows up under the help and xhelp commands. +// In particular, the entry for HELP is the last one printed +// for the help command, and the entry for HHELP is the last +// one printed for xhelp. + +enum CmdType +{ + // Pathtree-related commands + FUNCS = 0, + HOTPCS, + HOTLINES, + FDETAIL, + OBJECTS, + LDETAIL, + PDETAIL, + SOURCE, + DISASM, + METRIC_LIST, + METRICS, + SORT, + GPROF, + GMETRIC_LIST, + FSINGLE, + CSINGLE, + CPREPEND, + CAPPEND, + CRMFIRST, + CRMLAST, + CALLTREE, + CALLFLAME, + + // Source/disassembly control commands + SCOMPCOM, + STHRESH, + DCOMPCOM, + COMPCOM, + DTHRESH, + + // Heap trace-related commands + LEAKS, + ALLOCS, + HEAP, + HEAPSTAT, + + // I/O trace-related commands + IOACTIVITY, + IOVFD, + IOCALLSTACK, + IOSTAT, + + // Race detection related commands + RACE_EVNTS, + RACE_SUM, + + // Deadlock detection commands + DEADLOCK_EVNTS, + DEADLOCK_SUM, + + // DataSpace commands + DOBJECTS, + DO_SINGLE, + DO_LAYOUT, + DO_METRIC_LIST, + + // MemorySpace commands + MEMOBJ, + MEMOBJLIST, + MEMOBJDEF, + MEMOBJDROP, + MACHINEMODEL, + + // Custom tab commands + INDXOBJDEF, + INDXOBJLIST, + INDXOBJ, + INDX_METRIC_LIST, + + // Old-style filtering commands + OBJECT_LIST, + OBJECT_SELECT, + SAMPLE_LIST, + SAMPLE_SELECT, + THREAD_LIST, + THREAD_SELECT, + LWP_LIST, + LWP_SELECT, + CPU_LIST, + CPU_SELECT, + + // Shared Object display commands + OBJECT_SHOW, + OBJECT_HIDE, + OBJECT_API, + OBJECTS_DEFAULT, + + // the new filtering commands + FILTERS, + + // Miscellaneous commands + COMPARE, + PRINTMODE, + HEADER, + OVERVIEW_NEW, + SAMPLE_DETAIL, + STATISTICS, + EXP_LIST, + DESCRIBE, + OUTFILE, + APPENDFILE, + LIMIT, + NAMEFMT, + VIEWMODE, + EN_DESC, + SETPATH, + ADDPATH, + PATHMAP, + LIBDIRS, + SCRIPT, + VERSION_cmd, + QUIT, + PROCSTATS, + + // Experiments handling commands + ADD_EXP, + DROP_EXP, + OPEN_EXP, + + // .rc-only Commands + DMETRICS, + DSORT, + TLMODE, + TLDATA, + TABS, + TIMELINE, + MPI_TIMELINE, + MPI_CHART, + TIMELINE_CLASSIC_TBR, + SOURCE_V2, + DISASM_V2, + RTABS, + DUALSOURCE, + SOURCEDISAM, + + HELP, // this is the last of the commands listed with "help" + IFREQ, + DUMPNODES, + DUMPSTACKS, + DUMPUNK, + DUMPFUNC, + DUMPDOBJS, + DUMPMAP, + DUMPENTITIES, + DUMP_PROFILE, + DUMP_SYNC, + DUMP_HWC, + DUMP_HEAP, + DUMP_IOTRACE, + RACE_ACCS, + DMPI_FUNCS, + DMPI_MSGS, + DMPI_EVENTS, + DMEM, + DUMP_GC, + DKILL, + IGNORE_NO_XHWCPROF, + IGNORE_FS_WARN, + QQUIT, + HHELP, // this is the last command listed with "xhelp" + NO_CMD, // Dummy command, used for headers in help + DUMMY_CMD, // Dummy command, used for help + + // unused commands + LOADOBJECT, + LOADOBJECT_LIST, + LOADOBJECT_SELECT, + + // Internal-only Commands + LAST_CMD, // No more commands for which a help line is possible + STDIN, + COMMENT, + WHOAMI, + + // Error return "commands" + AMBIGUOUS_CMD, + UNKNOWN_CMD +}; + +typedef struct +{ + const CmdType token; // command key + const char *str; // command string + const char *alt; // alternate command string + const char *arg; // argument string for help + const int arg_count; // no. of arguments + char **desc; // description for help +} Cmdtable; + +// Command class: never instantiated, completely static +class Command +{ +public: + + // look up a string in the command table, return type, set number of args + static CmdType get_command (char *cmd, int &arg_count, int ¶m); + static const char *get_cmd_str (CmdType type); + static void print_help (char *prog_name, bool cmd_line, bool usermode, FILE *outf); + static char *get_err_string (Cmd_status err); + + static const char *DEFAULT_METRICS; // default if no .rc files read + static const char *DEFAULT_SORT; // default if no .rc files read + static const char *DEFAULT_CMD; // token for default + static const char *ALL_CMD; // token for all + static const char *ANY_CMD; // token for any + static const char *NONE_CMD; // token for none + static const char *HWC_CMD; // token for all HWC + static const char *BIT_CMD; // token for any bit-derived metric + +private: + static const int user_no; // the last user command + static const int hidden_no; // the last hidden command + static const int command_no; // the last parsable command + + static void init_desc (); + static char *fmt_help (int nc, char head); +}; + +// Analyzer display tabs +struct DispTab +{ + DispTab (int ntype, int num, bool vis, CmdType token) + { + type = ntype; + order = num; + visible = vis; + available = true; + cmdtoken = token; + } + + void setAvailability (bool val) { available = val; } + + int type; // Display type + int order; // Order in which tabs should appear in GUI + bool visible; // Is Tab visible + bool available; // Is tab available for this experiment + CmdType cmdtoken; // command token + int param; // command parameter (used for memory space) +}; + +#endif /* ! _COMMAND_H */ diff --git a/gprofng/src/CompCom.cc b/gprofng/src/CompCom.cc new file mode 100644 index 0000000..3a035a9 --- /dev/null +++ b/gprofng/src/CompCom.cc @@ -0,0 +1,313 @@ +/* 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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <locale.h> +#include <sys/param.h> + +#include "demangle.h" +#include "gp-defs.h" +#include "StringBuilder.h" +#include "CompCom.h" +#include "Elf.h" +#include "util.h" +#include "i18n.h" +#include "comp_com.c" + +CompComment::CompComment (Elf *_elf, int _compcom) +{ + elf = _elf; + compcom = _compcom; + elf_cls = elf->elf_getclass (); +} + +CompComment::~CompComment () { } + +int +CompComment::get_align (int64_t offset, int align) +{ + int val = (int) (offset % align); + if (val) + val = align - val; + return val; +} + +/* + * Preprocesses the header structure, builds a table of messages with the line + * numbers, PCoffsets, original index, and compmsg pointer for each message. + * If the show_bits field is not in the message, this routine would fill it in + * from the mapping from COMPMSG_ID + */ +int +CompComment::compcom_open (CheckSrcName check_src) +{ + if (check_src == NULL) + return 0; + Elf_Data *data = elf->elf_getdata (compcom); + uint64_t b_offset = data->d_off; + if (get_align (b_offset, 4)) // not align 4 + return 0; + char *CommData = (char *) data->d_buf; + uint64_t offset = b_offset; + for (uint64_t e_offset = b_offset + data->d_size; offset < e_offset;) + { + offset += get_align (offset, (int) data->d_align); + if (offset >= e_offset) + return 0; + compcomhdr *hdr = (compcomhdr *) (CommData + (offset - b_offset)); + int hdr_msgcount = elf->decode (hdr->msgcount); + int hdr_srcname = elf->decode (hdr->srcname); + int hdr_stringlen = elf->decode (hdr->stringlen); + int hdr_paramcount = elf->decode (hdr->paramcount); + size_t length = sizeof (compcomhdr) + hdr_msgcount * sizeof (compmsg) + + hdr_paramcount * sizeof (int32_t); + if (offset + length + hdr_stringlen > e_offset || hdr_srcname < 0 + || hdr_srcname >= hdr_stringlen) + return 0; + + // check source file + char *src_name = (char *) (((char*) hdr) + length + hdr_srcname); + if (check_src (src_name)) + { + msgs = (compmsg *) (((char *) hdr) + sizeof (compcomhdr)); + params = (int32_t *) ((char *) msgs + hdr_msgcount * sizeof (compmsg)); + strs = (char *) ((char *) params + hdr_paramcount * sizeof (int32_t)); + + // initialize the I18N/L10N strings & set the visible table + ccm_vis_init (); + return hdr_msgcount; + } + offset += (length + hdr_stringlen); + } + return 0; +} + +char * +CompComment::get_demangle_name (char *fname) +{ + if (*fname == '_') + return cplus_demangle (fname, DMGL_PARAMS); + return NULL; +} + +/* + * takes the message, and returns the I18N string for the message. + */ +char * +CompComment::compcom_format (int index, compmsg *msg, int &visible) +{ + compmsg *p = msgs + index; + msg->instaddr = elf->decode (p->instaddr); + msg->lineno = elf->decode (p->lineno); + msg->msg_type = elf->decode (p->msg_type); + msg->nparam = elf->decode (p->nparam); + msg->param_index = elf->decode (p->param_index); + + int vindex = ccm_vis_index (msg->msg_type); + char *mbuf; + Ccm_Primtype_t prim_ty; + visible = ccm_attrs[vindex].vis; + if (ccm_attrs[vindex].msg == NULL) + { + /* Print CCM_UNKNOWN message */ + int uindex = ccm_vis_index (CCM_UNKNOWN); + visible = ccm_attrs[uindex].vis; + return dbe_sprintf (ccm_attrs[uindex].msg, vindex); + } + + /* + * Construct the output buffer based on the primitive types of the + * message parameters. + * + * Parameter lists have to be handled carefully -- the 1 parameter + * is built up of all the elements separated by ", ". + * + * Old way: Case by message format string. + */ + int *ind = params + msg->param_index; + int plist_idx = ccm_paramlist_index (msg->msg_type); + if (plist_idx <= 0) + { + /* No parameter list to handle; 0 parameters case is handled */ + + enum + { + MAX_COMPCOM_ARGS = 13 + }; + char *parms[MAX_COMPCOM_ARGS]; + if (msg->nparam >= MAX_COMPCOM_ARGS) + { + fprintf (stderr, + GTXT ("Warning: improperly formatted compiler commentary message (%d parameters >= %d);\n please report this bug against the compiler\n"), + msg->nparam, MAX_COMPCOM_ARGS); + return NULL; + } + for (int i = 0; i < MAX_COMPCOM_ARGS; i++) + parms[i] = NULL; // initialize array + int prm_cnt = ccm_num_params (msg->msg_type); + if (prm_cnt != msg->nparam) + { + fprintf (stderr, + GTXT ("Warning, improperly formatted compiler commentary message (parameter count mismatch = %d, param# = %d, msg_type = %x, `%s');\n please report this bug against the compiler\n"), + prm_cnt, msg->nparam, msg->msg_type, ccm_attrs[vindex].msg); + return NULL; + } + for (int i = 0; i < msg->nparam; i++) + { + /* Parameters in message-type numbered from '1' */ + prim_ty = ccm_param_primtype (msg->msg_type, i + 1); + if (prim_ty == CCM_PRIMTYPE_INTEGER) + { + unsigned long v = elf->decode (ind[i]); + parms[i] = (char*) v; + } + else if (prim_ty == CCM_PRIMTYPE_STRING) + { + char *fname = strs + elf->decode (ind[i]); + char *demName = get_demangle_name (fname); + parms[i] = demName ? demName : dbe_strdup (fname); + } + else if (prim_ty == CCM_PRIMTYPE_HEXSTRING) + parms[i] = dbe_sprintf (elf_cls == ELFCLASS32 ? NTXT ("0x%08llx") : NTXT ("0x%016llx"), + (unsigned long long) msg->instaddr); + else + { + fprintf (stderr, + GTXT ("Warning, improperly formatted compiler commentary message (unexpected primitive type %d);\n please report this bug against the compiler\n"), + prim_ty); + // Dummy code to avoid compiler's warning: static function ccm_param_hightype is not used + Ccm_Hitype_t hightype = CCM_HITYPE_NONE; + if (hightype != CCM_HITYPE_NONE) + hightype = ccm_param_hightype (msg->msg_type, i + 1); + return NULL; + } + } + + /* + * Must make sure to pass _ALL_ params; may pass more because + * the format won't access the 'extra' parameters if all the + * rules for messages have been followed. + */ + mbuf = dbe_sprintf (ccm_attrs[vindex].msg, parms[0], parms[1], parms[2], + parms[3], parms[4], parms[5], parms[6], parms[7], + parms[8], parms[9], parms[10], parms[11]); + // Cleanup allocated memory. + for (int i = 0; i < msg->nparam; i++) + { + prim_ty = ccm_param_primtype (msg->msg_type, i + 1); + if (prim_ty == CCM_PRIMTYPE_STRING || prim_ty == CCM_PRIMTYPE_HEXSTRING) + free (parms[i]); + } + } + else + { + /* + * Parameter list messages never have 0 parameters; the + * primitive type for the parameter list elements is always + * the same. And as of 22-Sep-2006, it was always + * CCM_PRIMTYPE_STRING. + * + * Account for different bases of parameter indices and + * 'nparam' count (1 and 0, respectively). + */ + char *parms[3]; + if (plist_idx > (int) ((sizeof (parms) / sizeof (char*)))) + { + fprintf (stderr, + GTXT ("Warning: improperly formatted compiler commentary message (msg->nparam=%d plist_idx=%d);\n please report this bug against the compiler\n"), + msg->nparam, plist_idx); + return NULL; + } + for (size_t i = 0; i < (sizeof (parms) / sizeof (char*)); i++) + parms[i] = NULL; // initialize array + + StringBuilder sb; + prim_ty = ccm_param_primtype (msg->msg_type, plist_idx); + for (int i = plist_idx - 1; i < msg->nparam; i++) + { + if (i != plist_idx - 1) + sb.append (GTXT (", ")); + if (prim_ty == CCM_PRIMTYPE_INTEGER) + sb.append (elf->decode (ind[i])); + else if (prim_ty == CCM_PRIMTYPE_STRING) + { + char *fname = strs + elf->decode (ind[i]); + char *demName = get_demangle_name (fname); + if (demName) + { + sb.append (demName); + delete demName; + } + else + sb.append (fname); + } + else if (prim_ty == CCM_PRIMTYPE_HEXSTRING) + sb.appendf (elf_cls == ELFCLASS32 ? NTXT ("0x%08llx") : NTXT ("0x%016llx"), + (unsigned long long) msg->instaddr); + } + parms[plist_idx - 1] = sb.toString (); + + for (int i = 0; i < plist_idx - 1; i++) + { + prim_ty = ccm_param_primtype (msg->msg_type, i + 1); + if (prim_ty == CCM_PRIMTYPE_INTEGER) + { + unsigned long v = elf->decode (ind[i]); + parms[i] = (char*) v; + } + else if (prim_ty == CCM_PRIMTYPE_STRING) + { + char *fname = strs + elf->decode (ind[i]); + char *demName = get_demangle_name (fname); + parms[i] = demName ? demName : dbe_strdup (fname); + } + else if (prim_ty == CCM_PRIMTYPE_HEXSTRING) + parms[i] = dbe_sprintf (elf_cls == ELFCLASS32 ? NTXT ("0x%08llx") : NTXT ("0x%016llx"), + (unsigned long long) msg->instaddr); + else + { + fprintf (stderr, + GTXT ("Warning, improperly formatted compiler commentary message (unexpected primitive type %d);\n please report this bug against the compiler\n"), + prim_ty); + return NULL; + } + } + + /* + * We have reduced the parameter list to a single string (as + * the printf format specifier requires), so only have + * 'plist_idx' parameters. + */ + mbuf = dbe_sprintf (ccm_attrs[vindex].msg, parms[0], parms[1], parms[2]); + + // Cleanup allocated memory. + free (parms[plist_idx - 1]); + for (int i = 0; i < plist_idx - 1; i++) + { + prim_ty = ccm_param_primtype (msg->msg_type, i + 1); + if (prim_ty == CCM_PRIMTYPE_STRING || prim_ty == CCM_PRIMTYPE_STRING) + free (parms[i]); + } + } + return mbuf; +} diff --git a/gprofng/src/CompCom.h b/gprofng/src/CompCom.h new file mode 100644 index 0000000..e653939 --- /dev/null +++ b/gprofng/src/CompCom.h @@ -0,0 +1,63 @@ +/* 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. */ + +#ifndef _COMPCOM_H +#define _COMPCOM_H + +#include <sys/types.h> +#include "comp_com.h" + +class Elf; +typedef int (*CheckSrcName) (char *); + +class CompComment +{ +public: + CompComment (Elf *_elf, int _compcom); + ~CompComment (); + int compcom_open (CheckSrcName check_src); + char *compcom_format (int index, compmsg *msg, int &visible); + +private: + int get_align (int64_t, int align); + char *get_demangle_name (char *fname); + + Elf *elf; + int compcom, elf_cls; + compmsg *msgs; /* the array of messages */ + int32_t *params; /* the parameters used in the messages parameters are + * either integers or string-indices */ + char *strs; /* the strings used in the messages */ +}; + +class ComC +{ +public: + ComC () { com_str = NULL; }; + ~ComC () { free (com_str); }; + + int sec; + int type; + int visible; + int line; + char *com_str; +}; + +#endif /* _COMPCOM_H */ diff --git a/gprofng/src/DataObject.cc b/gprofng/src/DataObject.cc new file mode 100644 index 0000000..870a531 --- /dev/null +++ b/gprofng/src/DataObject.cc @@ -0,0 +1,193 @@ +/* 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 <assert.h> +#include <string.h> +#include <ctype.h> + +#include "util.h" +#include "DbeSession.h" +#include "Application.h" +#include "DataObject.h" +#include "Module.h" +#include "debug.h" + +DataObject::DataObject () +{ + name = NULL; + parent = NULL; + master = NULL; + _unannotated_name = NULL; + _typename = NULL; + _instname = NULL; + scope = NULL; + EAs = new Vector<DbeEA*>; + size = 0; + offset = (uint64_t) (-1); +} + +DataObject::~DataObject () +{ + free (_unannotated_name); + free (_typename); + free (_instname); + EAs->destroy (); + delete EAs; +} + +// get_addr() doesn't return an actual address for a DataObject +// but rather synthesises an address-like identifier tuple. +// XXXX since an aggregate and its first element have identical tuples +// may need to arrange for special-purpose sorting "by address" +uint64_t +DataObject::get_addr () +{ + uint64_t addr; + if (parent && parent->get_typename ()) + addr = MAKE_ADDRESS (parent->id, offset); // element + else if (parent) + addr = MAKE_ADDRESS (parent->id, id) | 0x8000000000000000ULL; // Scalar, Unknown + else if (id == dbeSession->get_Scalars_DataObject ()->id) + addr = MAKE_ADDRESS (id, 0) | 0x8000000000000000ULL; // Scalar aggregate + else if (id == dbeSession->get_Unknown_DataObject ()->id) + addr = MAKE_ADDRESS (id, 0) | 0x8000000000000000ULL; // Unknown aggregate + else + addr = MAKE_ADDRESS (id, 0); // aggregate + return addr; +} + +Histable * +DataObject::convertto (Histable_type type, Histable *) +{ + return type == DOBJECT ? this : NULL; +} + +char +DataObject::get_offset_mark () +{ + enum + { + blocksize = 32 + }; + + if (size == 0 || offset == -1) + return '?'; // undefined + if (size > blocksize) + return '#'; // requires multiple blocks + if (size == blocksize && (offset % blocksize == 0)) + return '<'; // fits block entirely + if (offset % blocksize == 0) + return '/'; // starts block + if ((offset + size) % blocksize == 0) + return '\\'; // closes block + if (offset / blocksize == ((offset + size) / blocksize)) + return '|'; // inside block + return 'X'; // crosses blocks unnecessarily +} + +char * +DataObject::get_offset_name () +{ + char *offset_name; + if (parent && parent->get_typename ()) // element + offset_name = dbe_sprintf (GTXT ("%c%+6lld .{%s %s}"), + get_offset_mark (), (long long) offset, + _typename ? _typename : GTXT ("NO_TYPE"), + _instname ? _instname : GTXT ("-")); // "NO_NAME" + else if ((offset != -1) && (offset > 0)) // filler + offset_name = dbe_sprintf (GTXT ("%c%+6lld %s"), get_offset_mark (), + (long long) offset, get_name ()); + else if (parent) // Scalar/Unknown element + offset_name = dbe_sprintf (GTXT (" .%s"), get_unannotated_name ()); + else // aggregate + offset_name = dbe_strdup (get_name ()); + return offset_name; +} + +void +DataObject::set_dobjname (char *type_name, char *inst_name) +{ + _unannotated_name = _typename = _instname = NULL; + if (inst_name) + _instname = dbe_strdup (inst_name); + + char *buf; + if (parent == dbeSession->get_Scalars_DataObject ()) + { + if (type_name) + _typename = dbe_strdup (type_name); + _unannotated_name = dbe_sprintf (NTXT ("{%s %s}"), type_name, + inst_name ? inst_name : NTXT ("-")); + buf = dbe_sprintf (NTXT ("%s.%s"), parent->get_name (), _unannotated_name); + } + else if (parent == dbeSession->get_Unknown_DataObject ()) + { + _unannotated_name = dbe_strdup (type_name); + buf = dbe_sprintf (NTXT ("%s.%s"), parent->get_name (), _unannotated_name); + } + else + { + if (type_name) + _typename = dbe_strdup (type_name); + if (parent && parent->get_typename ()) + buf = dbe_sprintf (NTXT ("%s.{%s %s}"), + parent->get_name () ? parent->get_name () : NTXT ("ORPHAN"), + type_name ? type_name : NTXT ("NO_TYPE"), + inst_name ? inst_name : NTXT ("-")); // "NO_NAME" + else + buf = dbe_sprintf (NTXT ("{%s %s}"), + type_name ? type_name : NTXT ("NO_TYPE"), + inst_name ? inst_name : NTXT ("-")); // "NO_NAME" + } + name = buf; + dbeSession->dobj_updateHT (this); +} + +void +DataObject::set_name (char *string) +{ + name = dbe_strdup (string); + dbeSession->dobj_updateHT (this); +} + +DbeEA * +DataObject::find_dbeEA (Vaddr EA) +{ + DbeEA *dbeEA; + int left = 0; + int right = EAs->size () - 1; + while (left <= right) + { + int index = (left + right) / 2; + dbeEA = EAs->fetch (index); + if (EA < dbeEA->eaddr) + right = index - 1; + else if (EA > dbeEA->eaddr) + left = index + 1; + else + return dbeEA; + } + + // None found, create a new one + dbeEA = new DbeEA (this, EA); + EAs->insert (left, dbeEA); + return dbeEA; +} diff --git a/gprofng/src/DataObject.h b/gprofng/src/DataObject.h new file mode 100644 index 0000000..f70bbad --- /dev/null +++ b/gprofng/src/DataObject.h @@ -0,0 +1,82 @@ +/* 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. */ + +#ifndef _DATAOBJECT_H +#define _DATAOBJECT_H + +// A DataObject object represents a distinct dataobject. + +#include "dbe_structs.h" +#include "Histable.h" + +extern char *DOBJ_UNSPECIFIED; +extern char *DOBJ_UNIDENTIFIED; +extern char *DOBJ_UNDETERMINED; +extern char *DOBJ_ANON; +extern char *DOBJ_UNASCERTAINABLE; +extern char *DOBJ_UNVERIFIABLE; +extern char *DOBJ_UNRESOLVABLE; + +class DataObject : public Histable +{ +public: + DataObject (); + ~DataObject (); + + static const unsigned UNSPECIFIED_ID = 0xFFFFFFFF; + + int64_t size; // size of the dataobject in bytes + int64_t offset; // offset of dataobject from parent + DataObject *parent; // this dataobject's parent (if any) + Histable *scope; // scope of this dataobject + DataObject *master; // this dataobject's master (if any) + + Histable_type get_type () { return DOBJECT; } + int64_t get_size () { return size; } + int64_t get_offset () { return offset; } + DataObject *get_parent () { return parent; } + DataObject *get_master () { return master; } + char *get_typename () { return _typename; } + char *get_instname () { return _instname; } + Histable *get_scope () { return scope; } + + char *get_unannotated_name () + { // name without a <Scalar> or <Unknown> prefix + if (_unannotated_name) + return _unannotated_name; + return get_name (); + } + + uint64_t get_addr (); + char get_offset_mark (); + char *get_offset_name (); + void set_dobjname (char *type_name, char *inst_name); // dobj->parent must already be set + void set_name (char *); + Histable *convertto (Histable_type type, Histable *obj = NULL); + DbeEA *find_dbeEA (Vaddr EA); + +private: + char *_unannotated_name; // name without a <Scalar> or <Unknown> prefix + char *_typename; // name of this dataobject's type + char *_instname; // name of this dataobject instance + Vector<DbeEA*> *EAs; +}; + +#endif /* _DATAOBJECT_H */ diff --git a/gprofng/src/DataSpace.cc b/gprofng/src/DataSpace.cc new file mode 100644 index 0000000..e5b48dd --- /dev/null +++ b/gprofng/src/DataSpace.cc @@ -0,0 +1,558 @@ +/* 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 <stdlib.h> +#include <stdarg.h> + +#include "util.h" +#include "Application.h" +#include "CallStack.h" +#include "Experiment.h" +#include "Exp_Layout.h" +#include "DataObject.h" +#include "DbeSession.h" +#include "MetricList.h" +#include "Function.h" +#include "Module.h" +#include "MemObject.h" +#include "DbeView.h" +#include "Metric.h" +#include "DataSpace.h" +#include "LoadObject.h" + +#include "debug.h" +#include "ABS.h" + +//char *DOBJ_UNSPECIFIED = STXT("(Not identified by the compiler as a memory-referencing instruction)"); +char *DOBJ_UNSPECIFIED = STXT("(No type information)"); +char *DOBJ_UNIDENTIFIED = STXT("(No identifying descriptor provided by the compiler)"); +char *DOBJ_UNDETERMINED = STXT("(Not determined from the symbolic information provided by the compiler)"); +char *DOBJ_ANON = STXT("(Padding in structure)"); + +// run-time codes +// ABS_UNSUPPORTED = 0x01, /* inappropriate HWC event type */ +// ABS_BLOCKED = 0x02, /* runtime backtrack blocker reached */ +// ABS_INCOMPLETE = 0x03, /* runtime backtrack limit reached */ +// ABS_REG_LOSS = 0x04, /* address register contaminated */ +// ABS_INVALID_EA = 0x05, /* invalid effective address value */ + +const char *ABS_RT_CODES[NUM_ABS_RT_CODES] = { + "(OK)", + "(Dataspace data not requested during data collection)", + "(Backtracking was prevented by a jump or call instruction)", + "(Backtracking did not find trigger PC)", + "(Could not determine VA because registers changed after trigger instruction)", + "(Memory-referencing instruction did not specify a valid VA)", + "(UNKNOWN)" +}; + +// post-processing codes +// ABS_NO_CTI_INFO = 0x10, /* no AnalyzerInfo for validation */ +// ABS_INFO_FAILED = 0x20, /* info failed to validate backtrack */ +// ABS_CTI_TARGET = 0x30, /* CTI target invalidated backtrack */ +char *DOBJ_UNASCERTAINABLE = STXT("(Module with trigger PC not compiled with -xhwcprof)"); +char *DOBJ_UNVERIFIABLE = STXT("(Backtracking failed to find a valid branch target)"); +char *DOBJ_UNRESOLVABLE = STXT("(Backtracking traversed a branch target)"); + +char *ABS_PP_CODES[NUM_ABS_PP_CODES] = { + STXT ("(OK)"), + DOBJ_UNASCERTAINABLE, + DOBJ_UNVERIFIABLE, + DOBJ_UNRESOLVABLE, + STXT ("(<INTERNAL ERROR DURING POST-PROCESSING>)") +}; + +DataSpace::DataSpace (DbeView *_dbev, int /* _picked */) +{ + dbev = _dbev; +} + +DataSpace::~DataSpace () { } + +void +DataSpace::reset () { } + +char * +DataSpace::status_str () +{ + return NULL; +} + +Histable * +DataSpace::get_hist_obj (Histable::Type type, DataView *dview, long i) +{ + DataObject *dobj = NULL; + char *errcode = NTXT ("<internal error>"); + switch (type) + { + case Histable::DOBJECT: + dobj = (DataObject*) dview->getObjValue (PROP_HWCDOBJ, i); + if (dobj == NULL) + { + Vaddr leafVA = (Vaddr) dview->getLongValue (PROP_VADDR, i); + unsigned rt_code = (unsigned) ABS_GET_RT_CODE (leafVA); + unsigned pp_code = (unsigned) ABS_GET_PP_CODE (leafVA); + if (leafVA < ABS_CODE_RANGE + && (pp_code || (rt_code && rt_code != ABS_REG_LOSS))) + { + if (rt_code >= NUM_ABS_RT_CODES) + rt_code = NUM_ABS_RT_CODES - 1; + if (pp_code >= NUM_ABS_PP_CODES) + pp_code = NUM_ABS_PP_CODES - 1; + if (rt_code) + errcode = PTXT (ABS_RT_CODES[rt_code]); + else + errcode = PTXT (ABS_PP_CODES[pp_code]); + } + else + { + // associate dataobject with event + int index; + + // search for memop in Module infoList + void *cstack = dview->getObjValue (PROP_MSTACK, i); + Histable *leafPCObj = CallStack::getStackPC (cstack, 0); + DbeInstr *leafPC = NULL; + if (leafPCObj->get_type () == Histable::INSTR) + leafPC = (DbeInstr*) leafPCObj; + else // DBELINE + leafPC = (DbeInstr*) leafPCObj->convertto (Histable::INSTR); + Function *func = leafPC->func; + uint64_t leafPC_offset = func->img_offset + leafPC->addr; + Module *mod = func->module; + uint32_t dtype_id = 0; + inst_info_t *info = NULL; + Vec_loop (inst_info_t*, mod->infoList, index, info) + { + if (info->offset == leafPC_offset) + { + dtype_id = info->memop->datatype_id; + break; + } + } + dobj = mod->get_dobj (dtype_id); + if (dobj == NULL) + { + // ensure dobj is determined + if (dtype_id == DataObject::UNSPECIFIED_ID) + errcode = PTXT (DOBJ_UNSPECIFIED); + else + errcode = PTXT (DOBJ_UNIDENTIFIED); + } + else + { + // determine associated master dataobject + if (!dobj->master && dobj->scope) + dobj->master = dbeSession->createMasterDataObject (dobj); + if (dobj->scope) + dobj = dobj->master; // use associated master + } + } + if (!dobj) + { + // if dobj is not set yet, supply a dobj for errcode + // search for a dobj with the same name + dobj = dbeSession->find_dobj_by_name (errcode); + if (dobj == NULL) + { + // create new DataObject for unknown code + dobj = (DataObject*) dbeSession->createHistObject (Histable::DOBJECT); + dobj->size = 0; + dobj->offset = -1; + dobj->parent = dbeSession->get_Unknown_DataObject (); + dobj->set_dobjname (errcode, NULL); // dobj->parent must already be set + } + } + dview->setObjValue (PROP_HWCDOBJ, i, dobj); + } + break; + default: + break; + } + return dobj; +} + +Hist_data * +DataSpace::compute_metrics (MetricList *mlist, Histable::Type type, + Hist_data::Mode mode, Histable *sel_obj) +{ + int nmetrics = mlist->get_items ()->size (); + int sort_ind = -1; + Hist_data::HistItem *hi; + int index; + + // reset event_data count for all datatypes + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + for (int i = 0, sz = lobjs ? lobjs->size () : -1; i < sz; i++) + { + LoadObject *lo = lobjs->fetch (i); + Vector<Module*> *modules = lo->seg_modules; + for (int j = 0, msize = modules ? modules->size () : -1; j < msize; j++) + { + Module *mod = modules->fetch (j); + mod->reset_datatypes (); + } + } + Hist_data *hist_data = new Hist_data (mlist, type, mode); + + // add each experiment, skipping disabled and broken experiments + for (index = 0; index < dbeSession->nexps (); index++) + { + Experiment *exp = dbeSession->get_exp (index); + if (exp->broken) + continue; + + Collection_params *params = exp->get_params (); + if (!params->xhw_mode) + continue; + + char *expt_name = exp->get_expt_name (); + char *base_name = strrchr (expt_name, '/'); + base_name = base_name ? base_name + 1 : expt_name; + + // Determine mapping of experiment HWC metrics to hist_data metric list + int *xlate = new int[MAX_HWCOUNT]; + for (unsigned i = 0; i < MAX_HWCOUNT; i++) + { + xlate[i] = -1; + if (params->hw_interval[i] > 0) + { + const char *ctr_name = params->hw_aux_name[i]; + int mindex; + Metric *met; + Vec_loop (Metric*, mlist->get_items (), mindex, met) + { + if (dbe_strcmp (met->get_cmd (), ctr_name) == 0) + xlate[i] = mindex; + } + } + } + + // + // Process hardware profiling data + // + DataView *dview = dbev->get_filtered_events (index, DATA_HWC); + if (dview) + { + DataDescriptor *ddscr = dview ->getDataDescriptor (); + if (ddscr->getProp (PROP_HWCDOBJ) == NULL) + { + PropDescr *prop = new PropDescr (PROP_HWCDOBJ, NTXT ("HWCDOBJ")); + prop->uname = NULL; + prop->vtype = TYPE_OBJ; + ddscr->addProperty (prop); + } + } + if (dview && dview->getSize () != 0) + { + char *msg = NULL; + for (long i = 0; i < dview->getSize (); i++) + { + if (i % 5000 == 0) + { + int percent = (int) (100.0 * i / dview->getSize ()); + if (percent == 0 && msg == NULL) + msg = dbe_sprintf (GTXT ("Filtering HW Profile Address Data: %s"), base_name); + theApplication->set_progress (percent, (percent != 0) ? NULL : msg); + } + + uint32_t tag = dview->getIntValue (PROP_HWCTAG, i); + if (tag < 0 || tag >= MAX_HWCOUNT) + continue; // invalid HWC tag in the record; ignore it + int mHwcntr_idx = xlate[tag]; + if (mHwcntr_idx < 0) + continue; + + Vaddr leafVA = (Vaddr) dview->getLongValue (PROP_VADDR, i); + if (leafVA == ABS_UNSUPPORTED) + continue; // skip this record + Histable *obj = get_hist_obj (type, dview, i); + if (obj == NULL) + continue; + uint64_t interval = dview->getLongValue (PROP_HWCINT, i); + if (HWCVAL_HAS_ERR (interval)) + continue; + if (mode == Hist_data::ALL) + { // data_objects + hi = hist_data->append_hist_item (obj); + hi->value[mHwcntr_idx].ll += interval; + for (DataObject *dobj = ((DataObject *) obj)->parent; dobj; dobj = dobj->parent) + { + hi = hist_data->append_hist_item (dobj); + hi->value[mHwcntr_idx].ll += interval; + } + } + else if (mode == Hist_data::LAYOUT || mode == Hist_data::DETAIL) + { // data_single + { + // for data layout, insert elements that have no metrics yet + DataObject *tmpParent = ((DataObject *) obj)->parent; + if (tmpParent && tmpParent->get_typename ()) + { + // dobj is an aggregate element + if (!hist_data->find_hist_item (tmpParent)) + { + // parent not yet a member of hist_data + // supply parent's children with 0 values for layout + Vector<DataObject*> *elements = dbeSession->get_dobj_elements (tmpParent); + for (long eli = 0, sz = elements->size (); eli < sz; eli++) + { + DataObject* element = elements->fetch (eli); + assert (!hist_data->find_hist_item (element)); + hi = hist_data->append_hist_item (element); + } + } + } + } + + // Same as for mode == Hist_data::ALL: + hi = hist_data->append_hist_item (obj); + hi->value[mHwcntr_idx].ll += interval; + for (DataObject *dobj = ((DataObject *) obj)->parent; dobj; dobj = dobj->parent) + { + hi = hist_data->append_hist_item (dobj); + hi->value[mHwcntr_idx].ll += interval; + } + } + else if (mode == Hist_data::SELF) + { // used by dbeGetSummary() + if (obj == sel_obj) + { + hi = hist_data->append_hist_item (obj); + hi->value[mHwcntr_idx].ll += interval; + } + else + { + for (DataObject *dobj = ((DataObject *) obj)->parent; dobj; dobj = dobj->parent) + { + if ((Histable*) dobj == sel_obj) + { + hi = hist_data->append_hist_item (dobj); + hi->value[mHwcntr_idx].ll += interval; + break; + } + } + } + } + // Update total + hist_data->total->value[mHwcntr_idx].ll += interval; + } + free (msg); + theApplication->set_progress (0, NTXT ("")); + } + delete[] xlate; + } + + // include a regular HistItem for <Total> -- for all DataObjects, and MemObjects + DataObject *dtot = dbeSession->get_Total_DataObject (); + if (mode == Hist_data::ALL || mode == Hist_data::DETAIL + || mode == Hist_data::LAYOUT || + sel_obj == dtot) + { + hi = hist_data->append_hist_item (dtot); + for (int mind = 0; mind < nmetrics; mind++) + hi->value[mind] = hist_data->total->value[mind]; + } + if (hist_data->get_status () != Hist_data::SUCCESS) + return hist_data; + theApplication->set_progress (0, GTXT ("Constructing Metrics")); + + // Determine by which metric to sort if any + bool rev_sort = mlist->get_sort_rev (); + + // Compute static metrics: SIZES, ADDRESS. + for (int mind = 0; mind < nmetrics; mind++) + { + Metric *mtr = mlist->get_items ()->fetch (mind); + if (mlist->get_sort_ref_index () == mind) + sort_ind = mind; + else if (!mtr->is_visible () && !mtr->is_tvisible () + && !mtr->is_pvisible ()) + continue; + Metric::Type mtype = mtr->get_type (); + if (mtype == Metric::SIZES) + { + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + Histable *h = mtr->get_comparable_obj (hi->obj); + hi->value[mind].tag = VT_LLONG; + hi->value[mind].ll = h ? h->get_size () : 0; + } + } + else if (mtype == Metric::ONAME + && (mode == Hist_data::SELF + || ((DataObject*) sel_obj == dbeSession->get_Total_DataObject ()))) + { + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + hi->value[mind].tag = VT_OFFSET; // offset labels + } + } + else if (mtype == Metric::ADDRESS) + { // pseudo-address + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + hi->value[mind].tag = VT_ADDRESS; + Histable *h = mtr->get_comparable_obj (hi->obj); + hi->value[mind].ll = h ? h->get_addr () : 0; + } + // force sort by offset // XXXX should visibility also be set? + if (mode == Hist_data::SELF) + { // used by dbeGetSummary() + sort_ind = mind; + //hist_data->metrics->fetch(mind)->set_visible(T); + } + } + else + { + ValueTag vtype = mtr->get_vtype (); + switch (vtype) + { + case VT_ULLONG: // most Data-derived HWC metrics are VT_ULLONG + hist_data->total->value[mind].tag = vtype; + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + hi->value[mind].tag = vtype; + } + break; + case VT_DOUBLE: + { + double prec = mtr->get_precision (); + hist_data->total->value[mind].tag = vtype; + hist_data->total->value[mind].d = hist_data->total->value[mind].ll / prec; + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + hi->value[mind].tag = vtype; + hi->value[mind].d = hi->value[mind].ll / prec; + } + break; + } + default: + if (mtr->get_subtype () != Metric::STATIC) + abort (); + break; + } + } + } + hist_data->sort (sort_ind, rev_sort); + hist_data->compute_minmax (); + theApplication->set_progress (0, NTXT ("")); + return hist_data; +} + + +// generate annotated structure info for data_layout +// note: similar data traversal found in er_print_histogram::dump_detail() +Hist_data * +DataSpace::get_layout_data (Hist_data *sorted_data, + Vector<int> *marks, int /* _threshold */) +{ + Hist_data *data_items = NULL; + Hist_data::HistItem *new_item; + MetricList *mlist = new MetricList (sorted_data->get_metric_list ()); + int no_metrics = mlist->get_items ()->size (); + int index, addr_index = -1, name_index = -1; + Dprintf (DEBUG_DATAOBJ, NTXT ("DataSpace::get_layout_data(ALL)\n")); + + // Allocate a new Hist_data for the list, to be copied from the DataObect list + data_items = new Hist_data (mlist, Histable::DOBJECT, Hist_data::MODL); + data_items->set_status (sorted_data->get_status ()); + + // suppress threshold setting + // XXX this threshold should probably not be used + sorted_data->set_threshold ((double) 75. / 100.0); + TValue* all_empty = new TValue[no_metrics]; + memset (all_empty, 0, sizeof (TValue) * no_metrics); + + Metric *mitem; + Vec_loop (Metric*, mlist->get_items (), index, mitem) + { + // new data items have same total as original items + data_items->total->value[index] = sorted_data->total->value[index]; + // empty metric items need matching types + all_empty[index].tag = mitem->get_vtype (); + if (mitem->get_type () == Metric::ONAME) name_index = index; + if (mitem->get_type () == Metric::ADDRESS) addr_index = index; + } + + int64_t next_elem_offset = 0; + for (long i = 0; i < sorted_data->size (); i++) + { + Hist_data::HistItem* ditem = sorted_data->fetch (i); + DataObject* dobj = (DataObject*) (ditem->obj); + if (!dobj->get_parent ()) + { // doesn't have a parent; top level item + next_elem_offset = 0; + if (i > 0) + { // add a blank line as separator + // fixme xxxxx, is it really ok to create a DataObject just for this? + DataObject* empty = new DataObject (); + empty->size = 0; + empty->offset = 0; + empty->set_name (NTXT ("")); + new_item = sorted_data->new_hist_item (empty, Module::AT_EMPTY, all_empty); + new_item->value[name_index].tag = VT_LABEL; + new_item->value[name_index].l = NULL; + data_items->append_hist_item (new_item); + } + // then add the aggregate + new_item = sorted_data->new_hist_item (dobj, Module::AT_SRC, ditem->value); + new_item->value[name_index].tag = VT_OFFSET; + new_item->value[name_index].l = dbe_strdup (dobj->get_name ()); + data_items->append_hist_item (new_item); + } + else + { // is a child + if (dobj->get_parent ()->get_typename ()) + { // typed sub-element that has offset + if (dobj->offset > next_elem_offset) + { // filler entry + // hole in offsets + // fixme xxxxx, is it really ok to create a DataObject just for this? + DataObject* filler = new DataObject (); + filler->set_name (PTXT (DOBJ_ANON)); + filler->size = (dobj->offset - next_elem_offset); + filler->offset = next_elem_offset; + new_item = sorted_data->new_hist_item (filler, Module::AT_EMPTY, all_empty); + new_item->value[name_index].tag = VT_OFFSET; + new_item->value[name_index].l = dbe_strdup (filler->get_offset_name ()); + if (addr_index >= 0) + { + new_item->value[addr_index].tag = VT_ADDRESS; + new_item->value[addr_index].ll = (dobj->get_addr () - filler->size); + } + data_items->append_hist_item (new_item); + } + next_elem_offset = dobj->offset + dobj->size; + } + // then add the aggregate's subelement + if (marks) + if (sorted_data->above_threshold (ditem)) + marks->append (data_items->size ()); + new_item = sorted_data->new_hist_item (dobj, Module::AT_DIS, ditem->value); + new_item->value[name_index].tag = VT_OFFSET; + new_item->value[name_index].l = dbe_strdup (dobj->get_offset_name ()); + data_items->append_hist_item (new_item); + } + } + delete[] all_empty; + return data_items; +} diff --git a/gprofng/src/DataSpace.h b/gprofng/src/DataSpace.h new file mode 100644 index 0000000..c4d80fb --- /dev/null +++ b/gprofng/src/DataSpace.h @@ -0,0 +1,55 @@ +/* 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. */ + +#ifndef _DATASPACE_H +#define _DATASPACE_H + +#include <stdio.h> + +#include "dbe_structs.h" +#include "vec.h" +#include "Exp_Layout.h" +#include "Hist_data.h" +#include "Histable.h" +#include "Metric.h" + +class DbeView; +class DataView; + +class DataSpace +{ +public: + DataSpace (DbeView *_dbev, int picked = 0); + ~DataSpace (); + void reset (); + Hist_data *compute_metrics (MetricList *, Histable::Type, + Hist_data::Mode, Histable*); + Hist_data *get_layout_data (Hist_data *sorted_data, Vector<int> *marks, + int threshold); + + static char *status_str (); + +private: + Histable *get_hist_obj (Histable::Type, DataView*, long); + + DbeView *dbev; +}; + +#endif /* _DATASPACE_H */ diff --git a/gprofng/src/DataStream.cc b/gprofng/src/DataStream.cc new file mode 100644 index 0000000..8e244e2 --- /dev/null +++ b/gprofng/src/DataStream.cc @@ -0,0 +1,55 @@ +/* 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 "util.h" +#include "DataStream.h" +#include "debug.h" + +DataStream::DataStream (char *filename) : Data_window (filename) +{ + set_span (0, -1); +} + +DataStream::~DataStream () { } + +void +DataStream::set_span (int64_t f_offset, int64_t sz) +{ + span_offset = 0; + span_fileoffset = f_offset; + int64_t fsz = get_fsize (); + span_size = sz == -1 ? fsz : sz; + if (span_fileoffset >= fsz) + span_fileoffset = fsz; + if (span_size > fsz - span_fileoffset) + span_size = fsz - span_fileoffset; +} + +int64_t +DataStream::read (void *buf, int64_t len) +{ + if (len > span_size - span_offset) + len = span_size - span_offset; + int64_t off = span_offset + span_fileoffset; + span_offset += len; + get_data (off, len, buf); + return len; +} diff --git a/gprofng/src/DataStream.h b/gprofng/src/DataStream.h new file mode 100644 index 0000000..59ee293 --- /dev/null +++ b/gprofng/src/DataStream.h @@ -0,0 +1,51 @@ +/* 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. */ + +#ifndef _DATASTREAM_H +#define _DATASTREAM_H + +#include "Data_window.h" + +// sequential access to the file +class DataStream : public Data_window +{ +public: + // Create an empty data window. + DataStream (char *file_name); + ~DataStream (); + void set_span (int64_t f_offset, int64_t sz); + int64_t read (void *buf, int64_t len); + + template <typename Key_t> inline int64_t + read (Key_t &val) + { + int64_t sz = read (&val, sizeof (val)); + if (need_swap_endian && sz == sizeof (val)) + swapByteOrder (&val, sizeof (val)); + return sz; + } + +private: + int64_t span_offset; + int64_t span_size; // the window size + int64_t span_fileoffset; // the window begin on the file +}; + +#endif /* _DATASTREAM_H */ diff --git a/gprofng/src/Data_window.cc b/gprofng/src/Data_window.cc new file mode 100644 index 0000000..d9b0067 --- /dev/null +++ b/gprofng/src/Data_window.cc @@ -0,0 +1,241 @@ +/* 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 <sys/types.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <unistd.h> // for close(); + +#include "util.h" +#include "Data_window.h" +#include "debug.h" + +enum +{ + MINBUFSIZE = 1 << 16, + WIN_ALIGN = 8 +}; + +Data_window::Data_window (char *file_name) +{ + Dprintf (DEBUG_DATA_WINDOW, NTXT ("Data_window:%d %s\n"), (int) __LINE__, STR (file_name)); + page_size = sysconf (_SC_PAGESIZE); + need_swap_endian = false; + opened = false; + fsize = 0; + base = NULL; + woffset = 0; + wsize = 0; + basesize = 0; + fname = dbe_strdup (file_name); + mmap_on_file = false; + use_mmap = false; +#if DEBUG + if (DBE_USE_MMAP) + use_mmap = true; +#endif /* DEBUG */ + fd = open64 (fname, O_RDONLY); + if (fd == -1) + return; + fsize = lseek (fd, 0, SEEK_END); + if (fsize == 0) + { + close (fd); + fd = -1; + return; + } + opened = true; + if (use_mmap) + { + if (fsize != -1) + { + base = (void*) mmap (NULL, (size_t) fsize, PROT_READ, MAP_PRIVATE, fd, 0); + close (fd); + fd = -1; + if (base == MAP_FAILED) + { + base = NULL; + use_mmap = false; + return; + } + mmap_on_file = true; + wsize = fsize; + } + } +} + +void * +Data_window::bind (int64_t file_offset, int64_t minSize) +{ + Span span; + span.length = fsize - file_offset; + span.offset = file_offset; + return bind (&span, minSize); +} + +void * +Data_window::bind (Span *span, int64_t minSize) +{ + // Do any necessary mapping to access the desired span of data + // and return a pointer to the first byte. + Dprintf (DEBUG_DATA_WINDOW, NTXT ("Data_window:bind:%d offset=%llx:%lld minSize=%lld \n"), + (int) __LINE__, (long long) span->offset, (long long) span->length, (long long) minSize); + if (minSize == 0 || span->length < minSize) + return NULL; + + if (span->offset < woffset || span->offset + minSize > woffset + wsize) + { + // Remap the window + if (span->offset + minSize > fsize) + return NULL; + int myfd = fd; + if (myfd == -1) + { + if (fname) + myfd = open64 (fname, O_RDONLY, 0); + if (myfd == -1) + return NULL; + } + bool remap_failed = true; + if (use_mmap) + { + if (base) + { + munmap ((caddr_t) base, (size_t) wsize); + base = NULL; + } + woffset = span->offset & ~(page_size - 1); + wsize = page_size * ((MINBUFSIZE + page_size - 1) / page_size); + if (span->offset + minSize > woffset + wsize) + // Extend a window + wsize += page_size * ((span->offset + minSize - + woffset - wsize + page_size - 1) / page_size); + base = (void *) mmap (0, (size_t) wsize, PROT_READ, MAP_SHARED, fd, woffset); + if (base == MAP_FAILED) + { + base = NULL; + use_mmap = false; + } + remap_failed = (base == NULL); + } + if (remap_failed) + { + remap_failed = false; + woffset = span->offset & ~(WIN_ALIGN - 1); + wsize = minSize + (span->offset % WIN_ALIGN); + if (wsize < MINBUFSIZE) + wsize = MINBUFSIZE; + if (wsize > fsize) + wsize = fsize; + if (basesize < wsize) + { // Need to realloc 'base' + free (base); + basesize = wsize; + base = (void *) malloc (basesize); + Dprintf (DEBUG_DATA_WINDOW, + NTXT ("Data_window:bind:%d realloc basesize=%llx woffset=%lld \n"), + (int) __LINE__, (long long) basesize, (long long) woffset); + if (base == NULL) + { + basesize = 0; + remap_failed = true; + } + } + if (wsize > fsize - woffset) + wsize = fsize - woffset; + off_t woff = (off_t) woffset; + if (base == NULL || woff != lseek (myfd, woff, SEEK_SET) + || wsize != read_from_file (myfd, base, wsize)) + remap_failed = true; + } + if (fd == -1) + close (myfd); + if (remap_failed) + { + woffset = 0; + wsize = 0; + return NULL; + } + } + return (void *) ((char*) base + span->offset - woffset); +} + +void * +Data_window::get_data (int64_t offset, int64_t size, void *datap) +{ + if (size <= 0) + return NULL; + void *buf = bind (offset, size); + if (buf == NULL) + return NULL; + if (datap == NULL && !mmap_on_file) + // Can be remmaped or reallocated. Need to make a copy + datap = (void *) malloc (size); + if (datap) + { + memcpy (datap, buf, (size_t) size); + return datap; + } + return buf; +} + +Data_window::~Data_window () +{ + free (fname); + if (fd != -1) + close (fd); + if (base) + { + if (use_mmap) + munmap ((caddr_t) base, (size_t) wsize); + else + free (base); + } +} + +int64_t +Data_window::get_buf_size () +{ + int64_t sz = MINBUFSIZE; + if (sz < basesize) + sz = basesize; + if (sz > fsize) + sz = fsize; + return sz; +} + +int64_t +Data_window::copy_to_file (int f, int64_t offset, int64_t size) +{ + long long bsz = get_buf_size (); + for (long long n = 0; n < size;) + { + long long sz = (bsz <= (size - n)) ? bsz : (size - n); + void *b = bind (offset + n, sz); + if (b == NULL) + return n; + long long len = write (f, b, sz); + if (len <= 0) + return n; + n += len; + } + return size; +} diff --git a/gprofng/src/Data_window.h b/gprofng/src/Data_window.h new file mode 100644 index 0000000..a3e98c0 --- /dev/null +++ b/gprofng/src/Data_window.h @@ -0,0 +1,99 @@ +/* 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. */ + +#ifndef _DATA_WINDOW_H +#define _DATA_WINDOW_H + +// The Data_window class hiearchy is used to access raw data in the +// experiment record. +// +// The Data_window base class implements a set of windows into a raw data file. +// It is responsible for mapping and unmapping regions of the file as +// requested by other levels inside of the DBE. + +#include "util.h" + +class Data_window +{ +public: + + // Span in data file + typedef struct + { + int64_t offset; // file offset + int64_t length; // span length + } Span; + + Data_window (char *filename); + ~Data_window (); + + // Return address of "offset" byte of window for "length" bytes. + // Return 0 on error or locked. + void *bind (Span *span, int64_t minSize); + void *bind (int64_t file_offset, int64_t minSize); + void *get_data (int64_t offset, int64_t size, void *datap); + int64_t get_buf_size (); + int64_t copy_to_file (int f, int64_t offset, int64_t size); + + bool not_opened () { return !opened; } + off64_t get_fsize () { return fsize; } + + template <typename Key_t> inline Key_t + get_align_val (Key_t *vp) + { + if (sizeof (Key_t) <= sizeof (int)) + return *vp; + // 64-bit value can have a wrong alignment + Key_t val = (Key_t) 0; + uint32_t *p1 = (uint32_t *) vp; + uint32_t *p2 = (uint32_t*) (&val); + p2[0] = p1[0]; + p2[1] = p1[1]; + return val; + } + + template <typename Key_t> inline Key_t + decode (Key_t &v) + { + Key_t val = get_align_val (&v); + if (need_swap_endian) + swapByteOrder (&val, sizeof (val)); + return val; + } + + bool need_swap_endian; + char *fname; // file name + +protected: + int fd; // file descriptor + bool mmap_on_file; + +private: + long page_size; // used in mmap() + bool use_mmap; + bool opened; + int64_t fsize; // file size + void *base; // current window + int64_t woffset; // offset of current window + int64_t wsize; // size of current window + int64_t basesize; // size of allocated window +}; + +#endif /* _DATA_WINDOW_H */ diff --git a/gprofng/src/Dbe.cc b/gprofng/src/Dbe.cc new file mode 100644 index 0000000..1a6e521 --- /dev/null +++ b/gprofng/src/Dbe.cc @@ -0,0 +1,10371 @@ +/* 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 <errno.h> +#include <sys/types.h> // open, chmod +#include <signal.h> +#include <fcntl.h> // open +#include <strings.h> +#include <unistd.h> + +#include "util.h" +#include "Histable.h" +#include "DbeSession.h" +#include "DbeView.h" +#include "BaseMetric.h" +#include "CallStack.h" +#include "collctrl.h" +#include "Command.h" +#include "Dbe.h" +#include "DbeApplication.h" +#include "DefaultMap.h" +#include "LoadObject.h" +#include "Experiment.h" +#include "IndexObject.h" +#include "IOActivity.h" +#include "PreviewExp.h" +#include "Function.h" +#include "Hist_data.h" +#include "MetricList.h" +#include "Module.h" +#include "DataSpace.h" +#include "MemorySpace.h" +#include "DataObject.h" +#include "MemObject.h" +#include "Filter.h" +#include "FilterSet.h" +#include "FilterExp.h" +#include "Sample.h" +#include "Print.h" +#include "StringBuilder.h" +#include "dbe_types.h" +#include "ExpGroup.h" +#include "vec.h" +#include "UserLabel.h" +#include "DbeFile.h" +#include "PathTree.h" + +// Data structures for managing the collector control info for Collection GUI +static Coll_Ctrl *col_ctr = NULL; + +template<> VecType Vector<int>::type () +{ + return VEC_INTEGER; +} + +template<> VecType Vector<unsigned>::type () +{ + return VEC_INTEGER; +} + +template<> VecType Vector<char>::type () +{ + return VEC_CHAR; +} + +template<> VecType Vector<bool>::type () +{ + return VEC_BOOL; +} + +template<> VecType Vector<double>::type () +{ + return VEC_DOUBLE; +} + +template<> VecType Vector<long long>::type () +{ + return VEC_LLONG; +} + +template<> VecType Vector<uint64_t>::type () +{ + return VEC_LLONG; +} + +template<> VecType Vector<void*>::type () +{ + return VEC_VOIDARR; +} + +template<> VecType Vector<char*>::type () +{ + return VEC_STRING; +} + +template<> VecType Vector<Vector<int>*>::type () +{ + return VEC_INTARR; +} + +template<> VecType Vector<Vector<char*>*>::type () +{ + return VEC_STRINGARR; +} + +template<> VecType Vector<Vector<long long>*>::type () +{ + return VEC_LLONGARR; +} + +// gcc won't instantiate Vector<unsigned>::type() without it +Vector<unsigned> __dummy_unsigned_vector; + +#define CASE_S(x) case x: return #x +static const char * +dsp_type_to_string (int t) +{ + switch (t) + { + CASE_S (DSP_FUNCTION); + CASE_S (DSP_LINE); + CASE_S (DSP_PC); + CASE_S (DSP_SOURCE); + CASE_S (DSP_DISASM); + CASE_S (DSP_SELF); + CASE_S (DSP_CALLER); + CASE_S (DSP_CALLEE); + CASE_S (DSP_CALLTREE); + CASE_S (DSP_TIMELINE); + CASE_S (DSP_STATIS); + CASE_S (DSP_EXP); + CASE_S (DSP_LEAKLIST); + CASE_S (DSP_MEMOBJ); + CASE_S (DSP_DATAOBJ); + CASE_S (DSP_DLAYOUT); + CASE_S (DSP_SRC_FILE); + CASE_S (DSP_IFREQ); + CASE_S (DSP_RACES); + CASE_S (DSP_INDXOBJ); + CASE_S (DSP_DUALSOURCE); + CASE_S (DSP_SOURCE_DISASM); + CASE_S (DSP_DEADLOCKS); + CASE_S (DSP_SOURCE_V2); + CASE_S (DSP_DISASM_V2); + CASE_S (DSP_IOACTIVITY); + CASE_S (DSP_OVERVIEW); + CASE_S (DSP_IOCALLSTACK); + CASE_S (DSP_HEAPCALLSTACK); + CASE_S (DSP_SAMPLE); + default: + break; + } + return NTXT ("ERROR"); +} + +enum +{ + COMPARE_BIT = 1 << 8, + MTYPE_MASK = (1 << 8) - 1, + GROUP_ID_SHIFT = 16 +}; + +static DbeView * +getDbeView (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + return dbev; +} + + +Vector<char*> * +dbeGetInitMessages () +{ + // If any comments from the .rc files, send them to the GUI + Emsg *msg = theDbeApplication->fetch_comments (); + int size = 0; + while (msg != NULL) + { + size++; + msg = msg->next; + } + + // Initialize Java String array + Vector<char*> *list = new Vector<char*>(size); + msg = theDbeApplication->fetch_comments (); + size = 0; + int i = 0; + while (msg != NULL) + { + char *str = msg->get_msg (); + list->store (i, dbe_strdup (str)); + i++; + msg = msg->next; + } + + // now delete the comments + theDbeApplication->delete_comments (); + return list; +} + +Vector<char*> * +dbeGetExpPreview (int /*dbevindex*/, char *exp_name) +{ + PreviewExp *preview = new PreviewExp (); + preview->experiment_open (exp_name); + preview->open_epilogue (); + + // Initialize Java String array + Vector<char*> *info = preview->preview_info (); + int size = info->size (); + Vector<char*> *list = new Vector<char*>(size); + + // Get experiment names + for (int i = 0; i < size; i++) + { + char *str = info->fetch (i); + if (str == NULL) + str = GTXT ("N/A"); + list->store (i, dbe_strdup (str)); + } + delete info; + delete preview; + return list; +} + +char * +dbeGetExpParams (int /*dbevindex*/, char *exp_name) +{ + PreviewExp *preview = new PreviewExp (); + preview->experiment_open (exp_name); + + // Initialize Java String array + char *arg_list = dbe_strdup (preview->getArgList ()); + delete preview; + return arg_list; +} + +/** + * Gets File Attributes according to the specified format + * Supported formats: + * "/bin/ls -dl " - see 'man ls' for details + * @param filename + * @param format + * @return char * attributes + */ +char * +dbeGetFileAttributes (const char *filename, const char *format) +{ + if (format != NULL) + { + if (!strcmp (format, NTXT ("/bin/ls -dl "))) + { + // A kind of "/bin/ls -dl " simulation + struct stat64 sbuf; + sbuf.st_mode = 0; + dbe_stat (filename, &sbuf); + if (S_IREAD & sbuf.st_mode) + { // Readable + if (S_ISDIR (sbuf.st_mode) != 0) + return dbe_sprintf (NTXT ("%s %s\n"), NTXT ("drwxrwxr-x"), filename); + else if (S_ISREG (sbuf.st_mode) != 0) + return dbe_sprintf (NTXT ("%s %s\n"), NTXT ("-rwxrwxr-x"), filename); + } + } + } + return dbe_strdup (NTXT ("")); +} + +/** + * Gets list of files for specified directory according to the specified format + * Supported formats: + * "/bin/ls -a" - see 'man ls' for details + * "/bin/ls -aF" - see 'man ls' for details + * @param dirname + * @param format + * @return char * files + */ +char * +dbeGetFiles (const char *dirname, const char *format) +{ + if (format != NULL) + return dbe_read_dir (dirname, format); + return dbe_strdup (NTXT ("")); +} + +/** + * Creates the directory named by this full path name, including any + * necessary but nonexistent parent directories. + * @param dirname + * @return result + */ +char * +dbeCreateDirectories (const char *dirname) +{ + if (dirname != NULL) + { + char *res = dbe_create_directories (dirname); + if (res != NULL) + return res; + } + return dbe_strdup (NTXT ("")); +} + +/** + * Deletes the file or the directory named by the specified path name. + * If this pathname denotes a directory, then the directory must be empty in order to be deleted. + * @param const char *pathname + * @return int result + */ +char * +dbeDeleteFile (const char *pathname) +{ + // return unlink(pathname); + if (pathname != NULL) + { + char *res = dbe_delete_file (pathname); + if (res != NULL) + return res; + } + return dbe_strdup (NTXT ("")); +} + +/** + * Reads the file named by the specified path name. + * Temporary limitation: file should be "text only" and its size should be less than the 1 MB limit. + * If the operation was successful, the contents is in the first element, and second element is NULL. + * If the operation failed, then first element is NULL, and second element contains the error message. + * @param const char *pathname + * @return Vector<char*> *result + */ +Vector<char*> * +dbeReadFile (const char *pathname) +{ + Vector<char*> *result = new Vector<char*>(2); + int limit = 1024 * 1024; // Temporary limit: 1 MB + char * contents = (char *) malloc (limit); + StringBuilder sb; + if (NULL == contents) + { + sb.sprintf (NTXT ("\nError: Cannot allocate %d bytes\n"), limit); + result->store (0, NULL); + result->store (1, sb.toString ()); // failure + return result; + } + int fd = open (pathname, O_RDONLY); + if (fd >= 0) + { + int64_t bytes = read_from_file (fd, contents, limit); + close (fd); + if (bytes >= limit) + { + sb.sprintf (NTXT ("\nError: file size is greater than the limit (%d bytes)\n"), limit); + result->store (0, NULL); + result->store (1, sb.toString ()); // failure + } + else + { + contents[bytes] = '\0'; // add string terminator + result->store (0, contents); + result->store (1, NULL); // success + } + } + else + { + sb.sprintf (NTXT ("\nError: Cannot open file %s\n"), pathname); + result->store (0, NULL); + result->store (1, sb.toString ()); // failure + free (contents); + } + return result; +} + +/** + * Writes the file named by the specified path name. + * Temporary limitation: file should be "text only" and its size should be less than the 1 MB limit. + * If the operation failed, then -1 is returned. + * @param const char *pathname + * @return int result (written bytes) + */ +int +dbeWriteFile (const char *pathname, const char *contents) +{ + int result = -1; // error + size_t len = 0; + if (NULL != contents) + len = strlen (contents); + size_t limit = 1024 * 1024; // Temporary limit: 1 MB + if (len > limit) return result; + unlink (pathname); + mode_t mode = S_IRUSR | S_IWUSR; + int fd = open (pathname, O_WRONLY | O_CREAT | O_TRUNC, mode); + if (fd >= 0) + { // replace file contents + chmod (pathname, /*S_IRUSR || S_IWUSR*/ 0600); // rw for owner only + ssize_t bytes = 0; + if (len > 0) + bytes = write (fd, contents, len); + close (fd); + result = (int) bytes; + } + return result; +} + +/** + * Gets list of running processes according to the specified format + * Supported formats: + * "/bin/ps -ef" - see 'man ps' for details + * @param format + * @return char * processes + */ +char * +dbeGetRunningProcesses (const char *format) +{ + if (format != NULL) + return dbe_get_processes (format); + return dbe_strdup (NTXT ("")); +} + +// +// Open experiment +// +char * +dbeOpenExperimentList (int /* dbevindex */, Vector<Vector<char*>*> *groups, + bool sessionRestart) +{ + if (sessionRestart) + dbeSession->reset (); + char *errstr; + // Open experiments + try + { + errstr = dbeSession->setExperimentsGroups (groups); + } + catch (ExperimentLoadCancelException *) + { + errstr = dbe_strdup (NTXT ("Experiment Load Cancelled")); + } + return errstr; +} + +// +// Drop experiments +// +char * +dbeDropExperiment (int /* dbevindex */, Vector<int> *drop_index) +{ + for (int i = drop_index->size () - 1; i >= 0; i--) + { + char *ret = dbeSession->drop_experiment (drop_index->fetch (i)); + if (ret != NULL) + return ret; + } + return NULL; +} + +/** + * Read .er.rc file from the specified location + * @param path + * @return + */ +char * +dbeReadRCFile (int dbevindex, char* path) +{ + DbeView *dbev = getDbeView (dbevindex); + char *err_msg = dbev->get_settings ()->read_rc (path); + return err_msg; +} + +char * +dbeSetExperimentsGroups (Vector<Vector<char*>*> *groups) +{ + int cmp_mode = dbeSession->get_settings ()->get_compare_mode (); + if (groups->size () < 2) + cmp_mode = CMP_DISABLE; + else if (cmp_mode == CMP_DISABLE) + cmp_mode = CMP_ENABLE; + for (int i = 0;; i++) + { + DbeView *dbev = dbeSession->getView (i); + if (dbev == NULL) + break; + dbev->get_settings ()->set_compare_mode (cmp_mode); + } + char *err_msg = dbeSession->setExperimentsGroups (groups); + + // automatically load machine model if applicable + dbeDetectLoadMachineModel (0); + return err_msg; +} + +Vector<Vector<char*>*> * +dbeGetExperimensGroups () +{ + Vector<Vector<char*>*> *grops = dbeSession->getExperimensGroups (); + return grops; +} + +Vector<int> * +dbeGetFounderExpId (Vector<int> *expIds) +{ + Vector<int> *ret = new Vector<int>(expIds->size ()); + for (int i = 0; i < expIds->size (); i++) + { + int expId = expIds->fetch (i); + Experiment *exp = dbeSession->get_exp (expId); + if (exp != NULL) + { + int founderExpId = exp->getBaseFounder ()->getExpIdx (); + ret->store (i, founderExpId); + } + else + ret->store (i, -1); + } + return ret; +} + +Vector<int> * +dbeGetUserExpId (Vector<int> *expIds) +{ + // returns "User Visible" ids used for EXPID filters and timeline processes + Vector<int> *ret = new Vector<int>(expIds->size ()); + for (int i = 0; i < expIds->size (); i++) + { + int expId = expIds->fetch (i); + Experiment *exp = dbeSession->get_exp (expId); + if (exp != NULL) + { + int userExpId = exp->getUserExpId (); + ret->store (i, userExpId); + } + else + ret->store (i, -1); + } + return ret; +} + +// +// Get experiment groupid +// +Vector<int> * +dbeGetExpGroupId (Vector<int> *expIds) +{ + Vector<int> *ret = new Vector<int>(expIds->size ()); + for (int i = 0; i < expIds->size (); i++) + { + int expId = expIds->fetch (i); + Experiment *exp = dbeSession->get_exp (expId); + if (exp != NULL) + { + int gId = exp->groupId; + ret->store (i, gId); + } + else + ret->store (i, -1); + } + return ret; +} + +Vector<char*> * +dbeGetExpsProperty (const char *prop_name) +{ + long nexps = dbeSession->nexps (); + if (prop_name == NULL || nexps == 0) + return NULL; + Vector<char*> *list = new Vector<char*>(nexps); + StringBuilder sb; + int empty = 1; + int prop = 99; + if (strcasecmp (prop_name, NTXT ("ERRORS")) == 0) + prop = 1; + else if (strcasecmp (prop_name, NTXT ("WARNINGS")) == 0) + prop = 2; + if (prop < 3) + { + for (long i = 0; i < nexps; i++) + { + Experiment *exp = dbeSession->get_exp (i); + char *nm = exp->get_expt_name (); + sb.setLength (0); + for (Emsg *emsg = (prop == 1) ? exp->fetch_errors () : exp->fetch_warnings (); + emsg; emsg = emsg->next) + sb.appendf (NTXT ("%s: %s\n"), STR (nm), STR (emsg->get_msg ())); + char *s = NULL; + if (sb.length () > 0) + { + s = sb.toString (); + empty = 0; + } + list->append (s); + } + } + if (empty) + { + delete list; + list = NULL; + } + return list; +} + +// +// Get experiment names +// +Vector<char*> * +dbeGetExpName (int /*dbevindex*/) +{ + int size = dbeSession->nexps (); + if (size == 0) + return NULL; + // Initialize Java String array + Vector<char*> *list = new Vector<char*>(size); + + // Get experiment names + for (int i = 0; i < size; i++) + { + Experiment *texp = dbeSession->get_exp (i); + char *buf = dbe_sprintf (NTXT ("%s [%s]"), texp->get_expt_name (), + texp->utargname != NULL ? texp->utargname : GTXT ("(unknown)")); + list->store (i, buf); + } + return list; +} + +// +// Get experiment state +// +Vector<int> * +dbeGetExpState (int /* dbevindex */) +{ + int size = dbeSession->nexps (); + if (size == 0) + return NULL; + // Initialize Java array + Vector<int> *state = new Vector<int>(size); + + // Get experiment state + for (int i = 0; i < size; i++) + { + Experiment *exp = dbeSession->get_exp (i); + int set = EXP_SUCCESS; + if (exp->get_status () == Experiment::FAILURE) + set |= EXP_FAILURE; + if (exp->get_status () == Experiment::INCOMPLETE) + set |= EXP_INCOMPLETE; + if (exp->broken) + set |= EXP_BROKEN; + if (exp->obsolete) + set |= EXP_OBSOLETE; + state->store (i, set); + } + return state; +} + +// +// Get enabled experiment indices +// +Vector<bool> * +dbeGetExpEnable (int dbevindex) +{ + DbeView *dbev = getDbeView (dbevindex); + int size = dbeSession->nexps (); + if (dbev == NULL || size == 0) + return NULL; + + // Get enabled experiment + Vector<bool> *enable = new Vector<bool>(size); + for (int i = 0; i < size; i++) + { + bool val = dbev->get_exp_enable (i) && !dbeSession->get_exp (i)->broken; + enable->store (i, val); + } + return enable; +} + +// +// Get enabled experiment indices +// +bool +dbeSetExpEnable (int dbevindex, Vector<bool> *enable) +{ + DbeView *dbev = getDbeView (dbevindex); + bool ret = false; + int size = dbeSession->nexps (); + if (dbev == NULL || size == 0) + return false; + + // set enable, as per input vector + for (int i = 0; i < size; i++) + if (!dbeSession->get_exp (i)->broken + && dbev->get_exp_enable (i) != enable->fetch (i)) + { + dbev->set_exp_enable (i, enable->fetch (i)); + ret = true; + } + return ret; +} + +// +// Get experiment info +// +Vector<char*> * +dbeGetExpInfo (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + int size = dbeSession->nexps (); + if (size == 0) + return NULL; + + // Initialize Java String array + Vector<char*> *list = new Vector<char*>(size * 2 + 1); + + // Get experiment names + Vector<LoadObject*> *text_segments = dbeSession->get_text_segments (); + char *msg = pr_load_objects (text_segments, NTXT ("")); + delete text_segments; + list->store (0, msg); + int k = 1; + for (int i = 0; i < size; i++) + { + Experiment *exp = dbeSession->get_exp (i); + char *msg0 = pr_mesgs (exp->fetch_notes (), NTXT (""), NTXT ("")); + char *msg1 = pr_mesgs (exp->fetch_errors (), GTXT ("No errors\n"), NTXT ("")); + char *msg2 = pr_mesgs (exp->fetch_warnings (), GTXT ("No warnings\n"), NTXT ("")); + char *msg3 = pr_mesgs (exp->fetch_comments (), NTXT (""), NTXT ("")); + char *msg4 = pr_mesgs (exp->fetch_pprocq (), NTXT (""), NTXT ("")); + msg = dbe_sprintf (NTXT ("%s%s%s%s"), msg1, msg2, msg3, msg4); + list->store (k++, msg0); + list->store (k++, msg); + free (msg1); + free (msg2); + free (msg3); + free (msg4); + } + return list; +} + +bool +dbeGetViewModeEnable () +{ + return dbeSession->has_ompavail () || dbeSession->has_java (); +} + +bool +dbeGetJavaEnable () +{ + return dbeSession->has_java (); +} + +int +dbeUpdateNotes (int dbevindex, int exp_id, int type, char* text, bool handle_file) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + int size = dbeSession->nexps (); + if (size == 0) + return -1; + Experiment *exp = dbeSession->get_exp (exp_id); + return (type == 0) ? exp->save_notes (text, handle_file) : exp->delete_notes (handle_file); +} + +// +// Get load object names +// +Vector<char*> * +dbeGetLoadObjectName (int /* dbevindex */) +{ + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + int size = lobjs->size (); + + // Initialize Java String array + Vector<char*> *list = new Vector<char*>(size); + + // Get load object names + LoadObject *lo; + int index; + Vec_loop (LoadObject*, lobjs, index, lo) + { + list->store (index, dbe_strdup (lo->get_name ())); + } + delete lobjs; + return list; +} + +// XXX Will use later when order has to be passed too, +// Get complete List of tabs +// +Vector<void*> * +dbeGetTabList (int /* dbevindex */) +{ + //DbeView *dbev = getDbeView (dbevindex); + //Vector<void*> *tabs = dbeSession->get_TabList(); + //return tabs; + return NULL; +} + +// +// Returns list of available tabs +// +Vector<void*> * +dbeGetTabListInfo (int dbevindex) +{ + int index; + DispTab *dsptab; + DbeView *dbev = getDbeView (dbevindex); + + // make sure the tabs are initialized properly + dbev->get_settings ()->proc_tabs (theDbeApplication->rdtMode); + Vector<DispTab*> *tabs = dbev->get_TabList (); + + // Get number of available tabs + int size = 0; + Vec_loop (DispTab*, tabs, index, dsptab) + { + if (!dsptab->available) + continue; + size++; + } + Vector<void*> *data = new Vector<void*>(2); + Vector<int> *typelist = new Vector<int>(size); + Vector<char*> *cmdlist = new Vector<char*>(size); + Vector<int> *ordlist = new Vector<int>(size); + + // Build list of avaliable tabs + int i = 0; + + Vec_loop (DispTab*, tabs, index, dsptab) + { + if (!dsptab->available) + continue; + typelist->store (i, dsptab->type); + cmdlist->store (i, dbe_strdup (Command::get_cmd_str (dsptab->cmdtoken))); + ordlist->store (i, dsptab->order); + i++; + } + data->store (0, typelist); + data->store (1, cmdlist); + data->store (2, ordlist); + return data; +} + +// Return visibility state for all available tabs +// +Vector<bool> * +dbeGetTabSelectionState (int dbevindex) +{ + int index; + DispTab *dsptab; + DbeView *dbev = getDbeView (dbevindex); + Vector<DispTab*> *tabs = dbev->get_TabList (); + + // Get number of available tabs + int size = 0; + Vec_loop (DispTab*, tabs, index, dsptab) + { + if (!dsptab->available) + continue; + size++; + } + Vector<bool> *states = new Vector<bool>(size); + + // Get visibility bit for all available tabs + int i = 0; + Vec_loop (DispTab*, tabs, index, dsptab) + { + if (!dsptab->available) + continue; + states->store (i++, dsptab->visible); + } + return states; +} + +// Set visibility bit for a tab +void +dbeSetTabSelectionState (int dbevindex, Vector<bool> *selected) +{ + int index; + DispTab *dsptab; + DbeView *dbev = getDbeView (dbevindex); + Vector<DispTab*> *tabs = dbev->get_TabList (); + int i = 0; + Vec_loop (DispTab*, tabs, index, dsptab) + { + if (!dsptab->available) + continue; + dsptab->visible = selected->fetch (i++); + } +} + +// Return visibility state for all available MemObj tabs +Vector<bool> * +dbeGetMemTabSelectionState (int dbevindex) +{ + int index; + bool dsptab; + DbeView *dbev = getDbeView (dbevindex); + Vector<bool> *memtabs = dbev->get_MemTabState (); + + // set the output vector + int size = memtabs->size (); + Vector<bool> *states = new Vector<bool>(size); + + // Get visibility bit for all available tabs + int i = 0; + Vec_loop (bool, memtabs, index, dsptab) + { + states->store (i++, dsptab); + } + return states; +} + +// Set visibility bit for a memory tab +// +void +dbeSetMemTabSelectionState (int dbevindex, Vector<bool> *selected) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->set_MemTabState (selected); +} + +// Return visibility state for all available index tabs +Vector<bool> * +dbeGetIndxTabSelectionState (int dbevindex) +{ + int index; + bool dsptab; + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<bool> *indxtabs = dbev->get_IndxTabState (); + + // set the output vector + int size = indxtabs->size (); + Vector<bool> *states = new Vector<bool>(size); + + // Get visibility bit for all available tabs + int i = 0; + Vec_loop (bool, indxtabs, index, dsptab) + { + states->store (i++, dsptab); + } + return states; +} + +// Set visibility bit for a index tab +void +dbeSetIndxTabSelectionState (int dbevindex, Vector<bool> *selected) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->set_IndxTabState (selected); +} + +// +// Get search path +// +Vector<char*> * +dbeGetSearchPath (int /*dbevindex*/) +{ + Vector<char*> *path = dbeSession->get_search_path (); + int size = path->size (); + Vector<char*> *list = new Vector<char*>(size); + int index; + char *name; + Vec_loop (char*, path, index, name) + { + list->store (index, dbe_strdup (name)); + } + return list; +} + +// +// Set search path +// +void +dbeSetSearchPath (int /*dbevindex*/, Vector<char*> *path) +{ + dbeSession->set_search_path (path, true); + return; +} + +// +// Get pathmaps +// +Vector<void*> * +dbeGetPathmaps (int /*dbevindex*/) +{ + int index; + pathmap_t *pthmap; + Vector<pathmap_t*> *path = dbeSession->get_pathmaps (); + int size = path->size (); + Vector<void*> *data = new Vector<void*>(2); + Vector<char*> *oldlist = new Vector<char*>(size); + Vector<char*> *newlist = new Vector<char*>(size); + + int i = 0; + Vec_loop (pathmap_t*, path, index, pthmap) + { + oldlist->store (i, dbe_strdup (pthmap->old_prefix)); + newlist->store (i, dbe_strdup (pthmap->new_prefix)); + i++; + } + data->store (0, oldlist); + data->store (1, newlist); + return data; +} // dbeGetPathmaps + +char * +dbeSetPathmaps (Vector<char*> *from, Vector<char*> *to) +{ + if (from == NULL || to == NULL || from->size () != to->size ()) + return dbe_strdup ("dbeSetPathmaps: size of 'from' does not match for size of 'to'\n"); + Vector<pathmap_t*> *newPath = new Vector<pathmap_t*>(from->size ()); + for (int i = 0, sz = from->size (); i < sz; i++) + { + char *err = Settings::add_pathmap (newPath, from->get (i), to->get (i)); + if (err) + { + newPath->destroy (); + delete newPath; + return err; + } + } + dbeSession->set_pathmaps (newPath); + return NULL; +} + +// +// Add pathmap +char * +dbeAddPathmap (int /* dbevindex */, char *from, char *to) +{ + Vector<pathmap_t*> *pmp = dbeSession->get_pathmaps (); + char *err = Settings::add_pathmap (pmp, from, to); + return err; +} + +// +// Get error/warning string of data +char * +dbeGetMsg (int dbevindex, int type) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + char *msgstr = NULL; + if (type == ERROR_MSG) + msgstr = dbev->get_error_msg (); + else if (type == WARNING_MSG) + msgstr = dbev->get_warning_msg (); + else if (type == PSTAT_MSG) + msgstr = dbev->get_processor_msg (PSTAT_MSG); + else if (type == PWARN_MSG) + msgstr = dbev->get_processor_msg (PWARN_MSG); + return msgstr ? dbe_strdup (msgstr) : NULL; +} + +// Create a DbeView, given new index, and index of view to clone +int +dbeInitView (int id, int cloneid) +{ + return dbeSession->createView (id, cloneid); +} + + +// Delete a DbeView +void +dbeDeleteView (int dbevindex) +{ + dbeSession->dropView (dbevindex); + return; +} // dbeDeleteView + +MetricList * +dbeGetMetricListV2 (int dbevindex, MetricType mtype, + Vector<int> *type, Vector<int> *subtype, Vector<bool> *sort, + Vector<int> *vis, Vector<char*> *cmd, + Vector<char*> *expr_spec, Vector<char*> *legends) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + MetricList *mlist = new MetricList (mtype); + for (int i = 0, msize = type->size (); i < msize; i++) + { + BaseMetric *bm = dbev->register_metric_expr ((BaseMetric::Type) type->fetch (i), + cmd->fetch (i), + expr_spec->fetch (i)); + Metric *m = new Metric (bm, (Metric::SubType) subtype->fetch (i)); + m->set_raw_visbits (vis->fetch (i)); + if (m->legend == NULL) + m->legend = dbe_strdup (legends->fetch (i)); + mlist->append (m); + if (sort->fetch (i)) + { + mlist->set_sort_ref_index (i); + } + } + return mlist; +} + +static Vector<void*> * +dbeGetMetricList (MetricList *mlist) +{ + int clock_val = dbeSession->get_clock (-1); + Vector<Metric*> *items = mlist->get_items (); + int size = items->size (); + + Vector<int> *type = new Vector<int>(size); + Vector<int> *subtype = new Vector<int>(size); + Vector<int> *clock = new Vector<int>(size); + Vector<int> *flavors = new Vector<int>(size); + Vector<int> *vis = new Vector<int>(size); + Vector<bool> *sorted = new Vector<bool>(size); + Vector<int> *value_styles = new Vector<int>(size); + Vector<char*> *aux = new Vector<char*>(size); + Vector<char*> *name = new Vector<char*>(size); + Vector<char*> *abbr = new Vector<char*>(size); + Vector<char*> *comd = new Vector<char*>(size); + Vector<char*> *unit = new Vector<char*>(size); + Vector<char*> *user_name = new Vector<char*>(size); + Vector<char*> *expr_spec = new Vector<char*>(size); + Vector<char*> *legend = new Vector<char*>(size); + Vector<int> *valtype = new Vector<int>(size); + Vector<char*> *data_type_name = new Vector<char*>(size); + Vector<char*> *data_type_uname = new Vector<char*>(size); + Vector<char*> *short_desc = new Vector<char*>(size); + + int sort_index = mlist->get_sort_ref_index (); + // Fill metric elements + for (int i = 0; i < size; i++) + { + Metric *m = items->fetch (i); + type->append (m->get_type ()); + subtype->append (m->get_subtype ()); + flavors->append (m->get_flavors ()); + abbr->append (dbe_strdup (m->get_abbr ())); + char *s = m->get_abbr_unit (); + if ((m->get_visbits () & VAL_RATIO) != 0) + s = NULL; + unit->append (dbe_strdup (s ? s : NTXT (""))); + value_styles->append (m->get_value_styles ()); + vis->append (m->get_visbits ()); + sorted->append (i == sort_index); + clock->append (m->get_type () == Metric::HWCNTR ? clock_val + : m->get_clock_unit ()); + aux->append (dbe_strdup (m->get_aux ())); + name->append (dbe_strdup (m->get_name ())); + comd->append (dbe_strdup (m->get_cmd ())); + user_name->append (dbe_strdup (m->get_username ())); + expr_spec->append (dbe_strdup (m->get_expr_spec ())); + legend->append (dbe_strdup (m->legend)); + valtype->append (m->get_vtype2 ()); + + char* _data_type_name = NULL; + char* _data_type_uname = NULL; + int data_type = m->get_packet_type (); + if (data_type >= 0 && data_type < DATA_LAST) + { + _data_type_name = dbe_strdup (get_prof_data_type_name (data_type)); + _data_type_uname = dbe_strdup (get_prof_data_type_uname (data_type)); + } + data_type_name->append (_data_type_name); + data_type_uname->append (_data_type_uname); + + char* _short_desc = NULL; + if (m->get_type () == Metric::HWCNTR) + { + Hwcentry * hwctr = m->get_hw_ctr (); + if (hwctr) + _short_desc = dbe_strdup (hwctr->short_desc); + } + short_desc->append (_short_desc); + } + + // Set Java array + Vector<void*> *data = new Vector<void*>(16); + data->append (type); + data->append (subtype); + data->append (clock); + data->append (flavors); + data->append (value_styles); + data->append (user_name); + data->append (expr_spec); + data->append (aux); + data->append (name); + data->append (abbr); + data->append (comd); + data->append (unit); + data->append (vis); + data->append (sorted); + data->append (legend); + data->append (valtype); + data->append (data_type_name); + data->append (data_type_uname); + data->append (short_desc); + return data; +} + +Vector<void*> * +dbeGetRefMetricsV2 () +{ + MetricList *mlist = new MetricList (MET_NORMAL); + Vector<BaseMetric*> *base_metrics = dbeSession->get_base_reg_metrics (); + for (long i = 0, sz = base_metrics->size (); i < sz; i++) + { + BaseMetric *bm = base_metrics->fetch (i); + Metric *m; + if (bm->get_flavors () & Metric::EXCLUSIVE) + { + m = new Metric (bm, Metric::EXCLUSIVE); + m->enable_all_visbits (); + mlist->append (m); + } + else if (bm->get_flavors () & BaseMetric::STATIC) + { + m = new Metric (bm, BaseMetric::STATIC); + m->enable_all_visbits (); + mlist->append (m); + } + } + Vector<void*> *data = dbeGetMetricList (mlist); + delete mlist; + return data; +} + +Vector<void*> * +dbeGetCurMetricsV2 (int dbevindex, MetricType mtype) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + MetricList *mlist = dbev->get_metric_list (mtype); + Vector<void*> *data = dbeGetMetricList (mlist); + return data; +} + +// YXXX we should refactor Metrics/BaseMetrics so that it no longer uses VAL_VALUE to enable time. +static int +convert_visbits_to_gui_checkbox_bits (BaseMetric *bm, const int visbits) +{ + // The purpose of this function is to handle the following case: + // When bm->get_value_styles() supports VAL_TIMEVAL but not VAL_VALUE + // Metric and BaseMetric use (visbits&VAL_VALUE) to enable time. + // However, the Overview expects the VAL_TIMEVAL bit to enable time. + // Inputs: visbits as returned by BaseMetric->get_default_visbits(); + // Returns: valuebits, as used for checks in GUI checkboxes + int valuebits = visbits; + const int value_styles = bm->get_value_styles (); + if ((value_styles & VAL_TIMEVAL) && // supports time + !(value_styles & VAL_VALUE)) + { // but not value + unsigned mask = ~(VAL_VALUE | VAL_TIMEVAL); + valuebits = (unsigned) valuebits & mask; // clear bits + if (visbits & VAL_VALUE) + valuebits |= VAL_TIMEVAL; // set VAL_TIMEVAL + if (visbits & VAL_TIMEVAL) + valuebits |= VAL_TIMEVAL; // weird, this should never happen. + } + return valuebits; +} + +static Vector<void*> * +dbeGetMetricTreeNode (BaseMetricTreeNode* curr, MetricList *mlist, + bool include_unregistered, bool has_clock_profiling_data) +{ + Vector<void*> *data = new Vector<void*>(2); + + // ----- fields + Vector<void*> *fields = new Vector<void*>(); + Vector<char*> *name = new Vector<char*>(1); + Vector<char*> *username = new Vector<char*>(1); + Vector<char*> *description = new Vector<char*>(1); + Vector<int> * flavors = new Vector<int>(1); + Vector<int> * vtype = new Vector<int>(1); + Vector<int> * vstyles_capable = new Vector<int>(1); + + // Specifies which default styles should be enabled when a metric is enabled. + // Also, specifies if metric should start enabled + Vector<int> *vstyles_e_defaults = new Vector<int>(1); + Vector<int> *vstyles_i_defaults = new Vector<int>(1); + Vector<bool> *registered = new Vector<bool>(1); + Vector<bool> *aggregation = new Vector<bool>(1); + Vector<bool> *has_value = new Vector<bool>(1); + Vector<char*> *unit = new Vector<char*>(1); + Vector<char*> *unit_uname = new Vector<char*>(1); + + char *_name = NULL; + char *_username = NULL; + char *_description = dbe_strdup (curr->get_description ()); + + // BaseMetric fields + int _flavors = 0; // SubType bitmask: (e.g. EXCLUSIVE) + int _vtype = 0; // ValueTag: e.g. VT_INT, VT_FLOAT, ... + int _vstyles_capable = 0; // ValueType bitmask, e.g. VAL_TIMEVAL + int _vstyles_e_default_values = 0; // default visibility settings, exclusive/static + int _vstyles_i_derault_values = 0; // default visibility settings, inclusive + bool _registered = curr->is_registered () + || curr->get_num_registered_descendents () > 0; + bool _aggregation = curr->is_composite_metric () + && curr->get_num_registered_descendents () > 0; + bool _has_value = false; //not used yet; for nodes that don't have metrics + char *_unit = NULL; + char *_unit_uname = NULL; + + BaseMetric *bm = curr->get_BaseMetric (); + if (bm) + { + _name = dbe_strdup (bm->get_cmd ()); + _username = dbe_strdup (bm->get_username ()); + if (!include_unregistered && !curr->is_registered ()) + abort (); + _flavors = bm->get_flavors (); + _vtype = bm->get_vtype (); + _vstyles_capable = bm->get_value_styles (); + int e_visbits = bm->get_default_visbits (BaseMetric::EXCLUSIVE); + int i_visbits = bm->get_default_visbits (BaseMetric::INCLUSIVE); + _vstyles_e_default_values = convert_visbits_to_gui_checkbox_bits (bm, e_visbits); + _vstyles_i_derault_values = convert_visbits_to_gui_checkbox_bits (bm, i_visbits); + // not all metrics shown in er_print cmd line should be selected in the GUI at startup: + if (has_clock_profiling_data && bm->get_hw_ctr ()) + { + bool hide = true; // by default, hide HWCs + if (dbe_strcmp (bm->get_hw_ctr ()->name, NTXT ("c_stalls")) == 0 || + dbe_strcmp (bm->get_hw_ctr ()->name, NTXT ("K_c_stalls")) == 0) + { + bool is_time = (bm->get_value_styles () & VAL_TIMEVAL) != 0; + if (is_time) + // By default, show time variant of c_stalls + hide = false; + } + if (hide) + { + _vstyles_e_default_values |= VAL_HIDE_ALL; + _vstyles_i_derault_values |= VAL_HIDE_ALL; + } + } + } + else + { + // not a base metric + _name = dbe_strdup (curr->get_name ()); + _username = dbe_strdup (curr->get_user_name ()); + if (curr->get_unit ()) + { // represents a value + _has_value = true; + _unit = dbe_strdup (curr->get_unit ()); + _unit_uname = dbe_strdup (curr->get_unit_uname ()); + } + } + name->append (_name); // unique id string (dmetrics cmd) + username->append (_username); // user-visible name + description->append (_description); + flavors->append (_flavors); // SubType bitmask: (e.g. EXCLUSIVE) + vtype->append (_vtype); // ValueTag: e.g. VT_INT, VT_FLOAT, ... + vstyles_capable->append (_vstyles_capable); // ValueType bitmask, e.g. VAL_TIMEVAL + vstyles_e_defaults->append (_vstyles_e_default_values); + vstyles_i_defaults->append (_vstyles_i_derault_values); + registered->append (_registered); // is a "live" metric + aggregation->append (_aggregation); // value derived from children nodes + has_value->append (_has_value); // value generated from other source + unit->append (_unit); // See BaseMetric.h, e.g. UNIT_SECONDS + unit_uname->append (_unit_uname); //See BaseMetric.h, + + fields->append (name); + fields->append (username); + fields->append (description); + fields->append (flavors); + fields->append (vtype); + fields->append (vstyles_capable); + fields->append (vstyles_e_defaults); + fields->append (vstyles_i_defaults); + fields->append (registered); + fields->append (aggregation); + fields->append (has_value); + fields->append (unit); + fields->append (unit_uname); + data->append (fields); + + // ----- children + Vector<BaseMetricTreeNode*> *children = curr->get_children (); + int num_children = children->size (); + Vector<void*> *children_list = new Vector<void*>(num_children); + BaseMetricTreeNode *child_node; + int index; + + Vec_loop (BaseMetricTreeNode*, children, index, child_node) + { + if (include_unregistered /* fetch everything */ + || child_node->is_registered () + || child_node->get_num_registered_descendents () > 0) + { + //Special case for metrics that aren't registered + // but have registered children + // Linux example: Total Time is unregistered, CPU Time is registered + if (!include_unregistered && /* not fetching everything */ + !child_node->is_registered () && + (child_node->get_BaseMetric () != NULL || + child_node->is_composite_metric ())) + { + Vector<BaseMetricTreeNode*> *registered_descendents = + new Vector<BaseMetricTreeNode*>(); + child_node->get_nearest_registered_descendents (registered_descendents); + int idx2; + BaseMetricTreeNode*desc_node; + Vec_loop (BaseMetricTreeNode*, registered_descendents, idx2, desc_node) + { + Vector<void*> *desc_data; + desc_data = dbeGetMetricTreeNode (desc_node, mlist, + include_unregistered, has_clock_profiling_data); + children_list->append (desc_data); + } + delete registered_descendents; + continue; + } + Vector<void*> *child_data; + child_data = dbeGetMetricTreeNode (child_node, mlist, + include_unregistered, has_clock_profiling_data); + children_list->append (child_data); + } + } + data->append (children_list); + return data; +} + +Vector<void*> * +dbeGetRefMetricTree (int dbevindex, bool include_unregistered) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + MetricList *mlist = dbev->get_metric_list (MET_NORMAL); + bool has_clock_profiling_data = false; + for (long i = 0, sz = mlist->get_items ()->size (); i < sz; i++) + { + Metric *m = mlist->get_items ()->fetch (i); + if (m->get_packet_type () == DATA_CLOCK) + { + has_clock_profiling_data = true; + break; + } + } + BaseMetricTreeNode *curr = dbeSession->get_reg_metrics_tree (); + return dbeGetMetricTreeNode (curr, mlist, include_unregistered, has_clock_profiling_data); +} + +static Vector<void*> * +dbeGetTableDataV2Data (DbeView *dbev, Hist_data *data); + +static Vector<void*> *dbeGetTableDataOneColumn (Hist_data *data, int met_ind); +static Vector<void*> * +dbeGetTableDataOneColumn (DbeView *dbev, Vector<Hist_data::HistItem*> *data, + ValueTag vtype, int metricColumnNumber); + +static hrtime_t +dbeCalcGroupDuration (int grInd) +{ + int thisGroupSize = 1; + hrtime_t max_time = 0; + Experiment *exp; + if (dbeSession->expGroups->size () > 0) + { + ExpGroup *grp = dbeSession->expGroups->fetch (grInd); + thisGroupSize = grp->exps->size (); + for (int ii = 0; ii < thisGroupSize; ii++) + { + exp = grp->exps->fetch (ii); + Vector<DataDescriptor*> *ddscr = exp->getDataDescriptors (); + delete ddscr;// getDataDescriptors() forces reading of experiment data + if (exp != NULL) + { + hrtime_t tot_time = exp->getLastEvent () - exp->getStartTime () + + exp->getRelativeStartTime (); + if (max_time < tot_time) + max_time = tot_time; + } + } + } + else + { + exp = dbeSession->get_exp (0); + if (exp != NULL) + max_time = exp->getLastEvent () - exp->getStartTime (); + } + return max_time; //nanoseconds +} + +static hrtime_t +dbeCalcGroupGCDuration (int grInd) +{ + int thisGroupSize = 1; + hrtime_t tot_time = 0; + Experiment *exp; + if (dbeSession->expGroups->size () > 0) + { + ExpGroup *grp = dbeSession->expGroups->fetch (grInd); + thisGroupSize = grp->exps->size (); + for (int ii = 0; ii < thisGroupSize; ii++) + { + exp = grp->exps->fetch (ii); + Vector<DataDescriptor*> *ddscr = exp->getDataDescriptors (); + delete ddscr; // getDataDescriptors() forces reading of experiment data + if (exp != NULL) + tot_time += exp->getGCDuration (); + } + } + else + { + exp = dbeSession->get_exp (0); + if (exp != NULL) + tot_time = exp->getGCDuration (); + } + return tot_time; //nanoseconds +} + +Vector<void*> * +dbeGetRefMetricTreeValues (int dbevindex, Vector<char *> *metric_cmds, + Vector<char *> *non_metric_cmds) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + // valueTable will have N "columns" of values, where N is the number of + // requested metrics and non-metrics. + // Each column will be a vector with M "rows", where M is the number of + // compare groups. + // highlightTable mirrors the structure of valueTable. Each cell indicates + // if the corresponding valueTable cell is "hot" (interesting) + int numMetrics = metric_cmds->size (); + int numNonMetrics = non_metric_cmds->size (); + int totalColumns = numMetrics + numNonMetrics; // Columns + Vector<void*> *valueTable = new Vector<void*>(totalColumns); + Vector<void*> *highlightTable = new Vector<void*>(totalColumns); + + // the return value consists of the two tables discussed above. + Vector<void*> *rc = new Vector<void*>(2); + rc->append (valueTable); + rc->append (highlightTable); + if (dbeSession->nexps () == 0) + { // no experiments are loaded + for (int jj = 0; jj < totalColumns; jj++) + { + Vector<void *> *columnData = new Vector<void *>(); + valueTable->append (columnData); + highlightTable->append (columnData); + } + return rc; + } + + int ngroups = dbeSession->expGroups->size (); // Rows (one per compare group) + if (ngroups == 0 || !dbev->comparingExperiments ()) + ngroups = 1; + + Vector<double> *groupTotalTime = new Vector<double>(ngroups); + Vector<double> *groupCpuTime = new Vector<double>(ngroups); + // initialize highlight table + for (int ii = 0; ii < totalColumns; ii++) + { // metrics + Vector<bool> *columnData = new Vector<bool>(ngroups); + highlightTable->append (columnData); + for (int grInd = 0; grInd < ngroups; grInd++) + columnData->store (grInd, false); // non-highlight + } + + if (numMetrics > 0) + { + MetricList *bmlist; + // set bmlist to list of requested base metrics + BaseMetricTreeNode *root = dbeSession->get_reg_metrics_tree (); + int index; + char *mcmd; + Vector<BaseMetric*> *base_metrics = new Vector<BaseMetric*>(); + Vec_loop (char *, metric_cmds, index, mcmd) + { + BaseMetricTreeNode *bmt_node = root->find (mcmd); + if (!bmt_node) + abort (); //YXXX weird + BaseMetric * baseNetric = bmt_node->get_BaseMetric (); + if (!baseNetric) + abort (); + base_metrics->append (baseNetric); + } + + // MET_INDX will create MetricList of Exclusive metrics + bmlist = new MetricList (base_metrics, MET_SRCDIS); + + // Use the Function List to fetch <Total> values + // A temporary table, v_totals, stores <total> by group + Vector<Hist_data::HistItem *> *v_totals = new Vector<Hist_data::HistItem *>(ngroups); + for (int grInd = 0; grInd < ngroups; grInd++) + { + MetricList *mlist; + if (ngroups > 1) + mlist = dbev->get_compare_mlist (bmlist, grInd); + else + mlist = bmlist; + if (mlist->size () != numMetrics) + abort (); + + Hist_data *data; + data = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::ALL); + Hist_data::HistItem * totals = data->get_totals (); + v_totals->append (totals); + } + + // store the Hist_data totals in valueTable + { + Metric *mitem; + int index; + Vec_loop (Metric*, bmlist->get_items (), index, mitem) + { + Vector<void*> * columnData = dbeGetTableDataOneColumn (dbev, + v_totals, mitem->get_vtype (), index); + valueTable->append (columnData); + } + } + + // 7207285: hack for hwc profiling cycles conversion: + { + Metric *mitem; + int index; + Vec_loop (Metric*, bmlist->get_items (), index, mitem) + { + if (mitem->is_time_val () + && mitem->get_vtype () == VT_ULLONG) + { + Vector<long long> *cycleValues = (Vector<long long> *)valueTable->fetch (index); + Vector<double> *timeValues = new Vector<double>(ngroups); + assert (cycleValues->size () == ngroups); + for (int grInd = 0; grInd < ngroups; grInd++) + { + long long cycles = cycleValues->fetch (grInd); + int expId; + if (dbeSession->expGroups->size () > 0) + { + ExpGroup *gr = dbeSession->expGroups->fetch (grInd); + Experiment *exp = gr->exps->fetch (0); + expId = exp->getExpIdx (); + } + else + expId = -1; + int clock = dbeSession->get_clock (expId); + double time; + if (clock) + time = cycles / (1.e+6 * clock); + else + time = cycles; //weird + timeValues->store (grInd, time); + } + delete cycleValues; + valueTable->store (index, timeValues); + } + } + } + + // Scan metrics for best measure of CPU time + int bestCpuTimeIndx = -1; + { + Metric *mitem; + int index; + Vec_loop (Metric*, bmlist->get_items (), index, mitem) + { + BaseMetric::Type type = mitem->get_type (); + if (type == BaseMetric::CP_KERNEL_CPU) + { + bestCpuTimeIndx = index; + break; // CP_KERNEL_CPU trumps other measures + } + if (type == BaseMetric::CP_TOTAL_CPU) + { + // clock profiling CPU time + bestCpuTimeIndx = index; + // keep looking in case CP_KERNEL_CPU also exists + continue; + } + + bool isTime = ((mitem->get_value_styles () & VAL_TIMEVAL) != 0); + bool isHwcCycles = (type == BaseMetric::HWCNTR + && (dbe_strcmp (mitem->get_aux (), "cycles") == 0) + && isTime); + if (isHwcCycles) + if (bestCpuTimeIndx < 0) + bestCpuTimeIndx = index; + } + if (bestCpuTimeIndx >= 0) + { + Vector<double> *timeValues = (Vector<double> *)valueTable->fetch (bestCpuTimeIndx); + if (timeValues->type () == VEC_DOUBLE) + for (int grInd = 0; grInd < ngroups; grInd++) + { + double time = timeValues->fetch (grInd); + groupCpuTime->append (time); + } + } + } + + // Scan metrics for Total Thread time + { + Metric *mitem; + int index; + Vec_loop (Metric*, bmlist->get_items (), index, mitem) + { + BaseMetric::Type type = mitem->get_type (); + if (type == BaseMetric::CP_TOTAL) + { + Vector<double> *timeValues = (Vector<double> *)valueTable->fetch (index); + if (timeValues->type () != VEC_DOUBLE) + continue; // weird + for (int grInd = 0; grInd < ngroups; grInd++) + { + double time = timeValues->fetch (grInd); + groupTotalTime->append (time); + } + break; + } + } + } + + // highlight metrics based on cpu time +#define CPUSEC_PERCENT_THRESHOLD 10.0 +#define HWC_OVERFLOWS_PER_CPUSEC_THRESHOLD 15 + { + Metric *mitem; + int index; + Vec_loop (Metric*, bmlist->get_items (), index, mitem) + { + BaseMetric::Type type = mitem->get_type (); + Vector<bool> * columnHilites = (Vector<bool> *)highlightTable->fetch (index); + + // always highlight the following + if (index == bestCpuTimeIndx) + { + for (int grInd = 0; grInd < ngroups; grInd++) + columnHilites->store (grInd, true); + continue; + } + + // skip certain types + bool typeIsCycles = (type == BaseMetric::HWCNTR + && dbe_strcmp (mitem->get_aux (), NTXT ("cycles")) == 0); + bool typeIsInsts = (type == BaseMetric::HWCNTR + && dbe_strcmp (mitem->get_aux (), NTXT ("insts")) == 0); + if (type == BaseMetric::CP_TOTAL + || type == BaseMetric::CP_TOTAL_CPU + || type == BaseMetric::CP_LMS_USER + || type == BaseMetric::CP_LMS_SYSTEM + || type == BaseMetric::CP_LMS_TRAP + || type == BaseMetric::CP_LMS_USER_LOCK + || type == BaseMetric::CP_LMS_SLEEP + || type == BaseMetric::CP_KERNEL_CPU + || type == BaseMetric::OMP_WORK + || typeIsCycles + || typeIsInsts + // || type == BaseMetric::CP_TOTAL_WAIT + ) + continue; // types we never highlight + + // for time values, compare against CPUSEC_PERCENT_THRESHOLD + bool isTime = ((mitem->get_value_styles () & VAL_TIMEVAL) != 0); + if (isTime) + { + if (groupCpuTime->size () == 0) + continue; // no time to use as reference + Vector<double> *timeValues = (Vector<double> *)valueTable->fetch (index); + if (timeValues->type () != VEC_DOUBLE) + continue; // weird + for (int grInd = 0; grInd < ngroups; grInd++) + { + double thistime = timeValues->fetch (grInd); + double usertime = groupCpuTime->fetch (grInd); + if (thistime / (CPUSEC_PERCENT_THRESHOLD / 100) > usertime) + columnHilites->store (grInd, true); + } + continue; + } + + // for HWC event counts, look at rate of events + if (type == BaseMetric::HWCNTR) + { + Hwcentry *hwctr = mitem->get_hw_ctr (); + if (!hwctr) + continue; // weird + if (!hwctr->metric) + continue; // raw counter + if (groupCpuTime->size () == 0) + continue; // no time to use as reference + if (mitem->get_base_metric ()->get_dependent_bm ()) + continue; // has a derived time metric, only flag time version + Vector<long long> *llValues = (Vector<long long> *)valueTable->fetch (index); + if (llValues->type () != VEC_LLONG) + continue; // weird + int overflowVal = hwctr->val; //overflow count + if (!overflowVal) + continue; // weird + if (overflowVal > (4000000)) + // cut off events that are very frequent like loads/stores + // 4Ghz * (0.01 seconds/event) / (4000000 events/overflow) = 10 cycles + continue; + // for HWCs we could base it on the overflow rate + for (int grInd = 0; grInd < ngroups; grInd++) + { + double thisVal = llValues->fetch (grInd); + thisVal /= overflowVal; + double usertime = groupCpuTime->fetch (grInd); + if (thisVal > usertime * HWC_OVERFLOWS_PER_CPUSEC_THRESHOLD) + columnHilites->store (grInd, true); + } + continue; + } + + // check for non-zero counts of the following + if (type == BaseMetric::DEADLOCKS || + type == BaseMetric::RACCESS || + type == BaseMetric::HEAP_ALLOC_BYTES || + type == BaseMetric::HEAP_LEAK_BYTES) + { + Vector<long long> *llValues = (Vector<long long> *)valueTable->fetch (index); + if (llValues->type () != VEC_LLONG) + continue; // weird + for (int grInd = 0; grInd < ngroups; grInd++) + { + long long thisVal = llValues->fetch (grInd); + if (thisVal) + columnHilites->store (grInd, true); + } + continue; + } + // continue adding cases as needed + } + } + } + + if (numNonMetrics > 0) + { + int index; + char *mcmd; + Vec_loop (char *, non_metric_cmds, index, mcmd) + { + if (dbe_strcmp (mcmd, NTXT ("YXXX_TOTAL_TIME_PLUS_THREADS")) == 0 + && groupCpuTime->size () == ngroups) + { + Vector<char *> *columnData = new Vector<char *>(ngroups); + for (int grInd = 0; grInd < ngroups; grInd++) + { + double totaltime = groupTotalTime->fetch (grInd); + columnData->append (dbe_sprintf (NTXT ("%0.3f %s"), totaltime, GTXT ("Seconds"))); + } + valueTable->append (columnData); + } + else if (dbe_strcmp (mcmd, L1_DURATION) == 0) + { + Vector<double> *columnData = new Vector<double>(ngroups); + for (int grInd = 0; grInd < ngroups; grInd++) + { + hrtime_t duration = dbeCalcGroupDuration (grInd); + double seconds = duration * 1.e-9; + columnData->append (seconds); + } + valueTable->append (columnData); + } + else if (dbe_strcmp (mcmd, L1_GCDURATION) == 0) + { + Vector<double> *columnData = new Vector<double>(ngroups); + for (int grInd = 0; grInd < ngroups; grInd++) + { + hrtime_t duration = dbeCalcGroupGCDuration (grInd); + double seconds = duration * 1.e-9; + columnData->append (seconds); + } + valueTable->append (columnData); + } + else + { + Vector<char *> *columnData = new Vector<char *>(ngroups); + char * valueString = NTXT ("<unknown>"); + for (int grInd = 0; grInd < ngroups; grInd++) + columnData->append (dbe_strdup (valueString)); + valueTable->append (columnData); + } + } + } + return rc; +} + +Vector<char*> * +dbeGetOverviewText (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + Vector<char*> *info = new Vector<char*>; + char *field; + int ngroups = dbeSession->expGroups->size (); // Rows (one per compare group) + if (ngroups == 0 || !dbev->comparingExperiments ()) + ngroups = 1; + for (int grInd = 0; grInd < ngroups; grInd++) + { + int thisGroupSize = 1; + Experiment *exp; + if (dbeSession->expGroups->size () > 0) + { + ExpGroup *gr = dbeSession->expGroups->fetch (grInd); + exp = gr->exps->fetch (0); + thisGroupSize = gr->exps->size (); + } + else + { + if (dbeSession->nexps () == 0) + return info; + exp = dbeSession->get_exp (0); + } + char * expHeader; + if (ngroups == 1) + expHeader = dbe_strdup (GTXT ("Experiment :")); + else if (grInd == 0) + expHeader = dbe_strdup (GTXT ("Base Group : ")); + else if (ngroups == 2) + expHeader = dbe_strdup (GTXT ("Compare Group : ")); + else + expHeader = dbe_sprintf (GTXT ("Compare Group %d : "), grInd); + if (thisGroupSize == 1) + info->append (dbe_sprintf ("%s%s", expHeader, exp->get_expt_name ())); + else + info->append (dbe_sprintf ("%s%s (plus %d more)", + expHeader, exp->get_expt_name (), thisGroupSize - 1)); + free (expHeader); + field = exp->uarglist; + if (field && field[0]) + info->append (dbe_sprintf (GTXT (" Target : '%s'"), field)); + field = exp->hostname; + if (field && field[0]) + info->append (dbe_sprintf (NTXT (" %s %s (%s, %s)"), + GTXT ("Host :"), + field, + exp->architecture ? exp->architecture + : GTXT ("<CPU architecture not recorded>"), + exp->os_version ? exp->os_version + : GTXT ("<OS version not recorded>"))); + long start_sec = exp->start_sec; + char *p = ctime (&start_sec); // does this need to be freed? YXXX + hrtime_t tot_time = dbeCalcGroupDuration (grInd); + double seconds = tot_time * 1.e-9; + info->append (dbe_sprintf (NTXT (" %s %s %s %0.3f %s"), + GTXT ("Start Time :"), p, + GTXT ("Duration :"), seconds, + GTXT ("Seconds"))); + // Number of descendants/processes would be nice + info->append (dbe_strdup (NTXT (""))); + } + return info; +} + +//-------------------------------------------------------------------------- +// Set Sort by index +// +void +dbeSetSort (int dbevindex, int sort_index, MetricType mtype, bool reverse) +{ + DbeView *dbev; + + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->setSort (sort_index, mtype, reverse); + return; +} + +// +// Get annotation setting +// +Vector<int> * +dbeGetAnoValue (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<int> *set = new Vector<int>(9); + set->store (0, dbev->get_src_compcom ()); + set->store (1, dbev->get_dis_compcom ()); + set->store (2, dbev->get_thresh_src ()); + set->store (3, dbev->get_thresh_src ()); + set->store (4, dbev->get_src_visible ()); + set->store (5, (int) dbev->get_srcmetric_visible ()); + set->store (6, (int) dbev->get_hex_visible ()); + set->store (7, (int) dbev->get_cmpline_visible ()); + set->store (8, (int) dbev->get_func_scope ()); + return set; +} + +// +// Set annotation setting +// +void +dbeSetAnoValue (int dbevindex, Vector<int> *set) +{ + DbeView *dbev; + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + if (set->size () != 10) + return; + dbev->set_src_compcom (set->fetch (0)); + dbev->set_dis_compcom (set->fetch (1)); + dbev->set_thresh_src (set->fetch (2)); + dbev->set_thresh_dis (set->fetch (3)); + dbev->set_src_visible (set->fetch (4)); + dbev->set_srcmetric_visible ((bool)set->fetch (5)); + dbev->set_hex_visible ((bool)set->fetch (6)); + dbev->set_cmpline_visible ((bool)set->fetch (7)); + dbev->set_func_scope (set->fetch (8)); + dbev->set_funcline_visible ((bool)set->fetch (9)); + return; +} + +// +// Get name formats +// +int +dbeGetNameFormat (int dbevindex) +{ + DbeView *dbev; + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Histable::NameFormat fmt = dbev->get_name_format (); + return Histable::fname_fmt (fmt); +} + +bool +dbeGetSoName (int dbevindex) +{ + DbeView *dbev; + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Histable::NameFormat fmt = dbev->get_name_format (); + return Histable::soname_fmt (fmt); +} + +// +// Set name formats +// +void +dbeSetNameFormat (int dbevindex, int nformat, bool soname) +{ + DbeView *dbev; + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->set_name_format (nformat, soname); +} + +// +// Get View mode +// +int +dbeGetViewMode (int dbevindex) +{ + DbeView *dbev; + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + return (int) dbev->get_view_mode (); +} + +// Set View mode +void +dbeSetViewMode (int dbevindex, int nmode) +{ + DbeView *dbev; + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->set_view_mode ((VMode) nmode); + return; +} + +// Get timeline setting +// +Vector<void*> * +dbeGetTLValue (int dbevindex) +{ + DbeView *dbev; + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<char *> *strings = new Vector<char *>(); + char *tldata_cmd = dbev->get_tldata (); + strings->store (0, tldata_cmd); + + Vector<int> *ints = new Vector<int>(3); + int val; + val = dbev->get_tlmode (); + ints->store (0, val); + val = dbev->get_stack_align (); + ints->store (1, val); + val = dbev->get_stack_depth (); + ints->store (2, val); + + Vector<void*> *objs = new Vector<void*>(2); + objs->store (0, strings); + objs->store (1, ints); + return objs; +} + +// +// Set timeline setting +// +void +dbeSetTLValue (int dbevindex, const char *tldata_cmd, + int entitiy_prop_id, int stackalign, int stackdepth) +{ + DbeView *dbev; + dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->set_tldata (tldata_cmd); + dbev->set_tlmode (entitiy_prop_id); + dbev->set_stack_align (stackalign); + dbev->set_stack_depth (stackdepth); + return; +} + +// +// Get founder experiments and their descendants +// +Vector<void*> * +dbeGetExpFounderDescendants () +{ + int size = dbeSession->nexps (); + if (size == 0) + return NULL; + Vector<void*> *table = new Vector<void*>(2); + Vector<int> *founderExpIds = new Vector<int>(); + Vector<Vector<int> *> *subExpIds = new Vector<Vector<int>*>(); + for (int index = 0; index < size; index++) + { + Experiment *exp = dbeSession->get_exp (index); + if (exp->founder_exp == NULL) + { + founderExpIds->append (exp->getExpIdx ()); + Vector<int> *subExps = new Vector<int>(); + for (int i = 0; i < exp->children_exps->size (); i++) + { + Experiment * subExp = exp->children_exps->fetch (i); + subExps->append (subExp->getExpIdx ()); + } + subExpIds->append (subExps); + } + } + table->store (0, founderExpIds); + table->store (1, subExpIds); + return table; +} + +// +// Get experiment selection +// +Vector<void*> * +dbeGetExpSelection (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + int size = dbeSession->nexps (); + if (size == 0) + return NULL; + Vector<void*> *table = new Vector<void*>(3); + Vector<char*> *names = new Vector<char*>(size); + Vector<bool> *enable = new Vector<bool>(size); + Vector<int> *userExpIds = new Vector<int>(size); + + // Get experiment names + for (int index = 0; index < size; index++) + { + Experiment *exp = dbeSession->get_exp (index); + char *buf = dbeGetName (dbevindex, index); + names->store (index, buf); + bool val; + val = dbev->get_exp_enable (index); + enable->store (index, val); + userExpIds->store (index, exp->getUserExpId ()); + } + table->store (0, names); + table->store (1, enable); + table->store (2, userExpIds); + return table; +} + +int +dbeValidateFilterExpression (char *str_expr) +{ + if (str_expr == NULL) + return 0; + Expression *expr = dbeSession->ql_parse (str_expr); + if (expr == NULL) + return 0; + delete expr; + return 1; +} + +Vector<void*> * +dbeGetFilterKeywords (int /* dbevindex */) +{ + Vector <char*> *kwCategory = new Vector<char *>(); + Vector <char*> *kwCategoryI18N = new Vector<char *>(); + Vector <char*> *kwDataType = new Vector<char *>(); + Vector <char*> *kwKeyword = new Vector<char *>(); + Vector <char*> *kwFormula = new Vector<char *>(); + Vector <char*> *kwDescription = new Vector<char *>(); + Vector <void*> *kwEnumDescs = new Vector<void *>(); + + Vector<void*> *res = new Vector<void*>(7); + res->append (kwCategory); + res->append (kwCategoryI18N); + res->append (kwDataType); + res->append (kwKeyword); + res->append (kwFormula); + res->append (kwDescription); + res->append (kwEnumDescs); + + char *vtypeNames[] = VTYPE_TYPE_NAMES; + // section header for global definitions + kwCategory->append (dbe_strdup (NTXT ("FK_SECTION"))); + kwCategoryI18N->append (dbe_strdup (GTXT ("Global Definitions"))); + kwDataType->append (NULL); + kwKeyword->append (NULL); + kwFormula->append (NULL); + kwDescription->append (NULL); + kwEnumDescs->append (NULL); + dbeSession->get_filter_keywords (res); + MemorySpace::get_filter_keywords (res); + + // loop thru all founder experiments + int nexp = dbeSession->nexps (); + for (int ii = 0; ii < nexp; ++ii) + { + Experiment* fexp = dbeSession->get_exp (ii); + if (fexp->founder_exp != NULL) + continue; // is a child; should be covered when we get to founder + + // section header for each founder + // section header for founder experiment + kwCategory->append (dbe_strdup (NTXT ("FK_SECTION"))); + kwCategoryI18N->append (dbe_sprintf (NTXT ("%s [EXPGRID==%d]"), + fexp->get_expt_name (), + fexp->groupId)); + kwDataType->append (NULL); + kwKeyword->append (NULL); + kwFormula->append (NULL); + kwDescription->append (NULL); + kwEnumDescs->append (NULL); + + int nchildren = fexp->children_exps->size (); + Experiment *exp; + // category header: Experiments + { + char *propUName = dbeSession->getPropUName (PROP_EXPID); + + // store list of subexperiments in kwEnumDescs + Vector <char*> *enumDescs = new Vector<char *>(); + int jj = 0; + exp = fexp; + while (1) + { + char * expBasename = get_basename (exp->get_expt_name ()); + char * targetName = exp->utargname ? exp->utargname + : (char *) GTXT ("(unknown)"); + enumDescs->append (dbe_sprintf (NTXT ("(%d) -> %s [%s, PID %d]"), + exp->getUserExpId (), expBasename, + targetName, exp->getPID ())); + if (jj >= nchildren) + break; + exp = fexp->children_exps->fetch (jj); + jj++; + } + kwCategory->append (dbe_strdup (NTXT ("FK_EXPLIST"))); + kwCategoryI18N->append (dbe_strdup (GTXT ("Experiments"))); + kwDataType->append (dbe_strdup (vtypeNames[TYPE_INT32])); + kwKeyword->append (dbe_strdup (NTXT ("EXPID"))); + kwFormula->append (NULL); + kwDescription->append (propUName); + kwEnumDescs->append (enumDescs); + } + + // select representative experiment + if (nchildren == 0) + exp = fexp; // founder + else + exp = fexp->children_exps->fetch (0); // first child + int expIdx = exp->getExpIdx (); + Vector<void*> *data = dbeGetDataDescriptorsV2 (expIdx); + if (data == NULL) + continue; + Vector<int> *dataId = (Vector<int>*)data->fetch (0); + Vector<char*> *dataName = (Vector<char*>*)data->fetch (1); + Vector<char*> *dataUName = (Vector<char*>*)data->fetch (2); + if (dataId == NULL || dataName == NULL) + { + destroy (data); + continue; + } + // loop thru data descriptors + int ndata = dataId->size (); + for (int j = 0; j < ndata; ++j) + { + // category: data name (e.g. Clock Profiling) + char * catName = dataName->fetch (j); + char * dUname = dataUName ? dataUName->fetch (j) : catName; + char * catUname = dUname ? dUname : catName; + + Vector<void*> *props = dbeGetDataPropertiesV2 (expIdx, dataId->fetch (j)); + if (props == NULL) + continue; + Vector<char*> *propUName = (Vector<char*>*)props->fetch (1); + Vector<int> *propTypeId = (Vector<int> *)props->fetch (2); + Vector<char*> *propType = (Vector<char*>*)props->fetch (3); + Vector<char*> *propName = (Vector<char*>*)props->fetch (5); + Vector<Vector<char*>*> *propStateNames = + (Vector<Vector<char*>*> *)props->fetch (6); + Vector<Vector<char*>*> *propStateUNames = + (Vector<Vector<char*>*> *)props->fetch (7); + if (propName == NULL || propUName == NULL || propType == NULL + || propName->size () <= 0) + { + destroy (props); + continue; + } + int nprop = propName->size (); + for (int k = 0; k < nprop; ++k) + { + if (propTypeId->fetch (k) == TYPE_OBJ) + continue; + if (dbe_strcmp (propName->fetch (k), NTXT ("FRINFO")) == 0) + continue; + + // store list of states in kwEnumDescs + Vector<char*> *enumDescs = new Vector<char *>(); + Vector<char*>* stateNames = propStateNames->fetch (k); + Vector<char*>* stateUNames = propStateUNames->fetch (k); + int nStates = stateNames ? stateNames->size () : 0; + for (int kk = 0; kk < nStates; ++kk) + { + const char *stateName = stateNames->fetch (kk); + if (stateName == NULL || strlen (stateName) == 0) + continue; + const char *stateUName = stateUNames->fetch (kk); + if (stateUName == NULL || strlen (stateUName) == 0) + stateUName = stateName; + enumDescs->append (dbe_sprintf (NTXT ("(%d) -> %s"), kk, stateUName)); + } + kwCategory->append (dbe_strdup (catName)); + kwCategoryI18N->append (dbe_strdup (catUname)); + kwDataType->append (dbe_strdup (propType->fetch (k))); + kwKeyword->append (dbe_strdup (propName->fetch (k))); + kwFormula->append (NULL); + kwDescription->append (dbe_strdup (propUName->fetch (k))); + kwEnumDescs->append (enumDescs); + } + destroy (props); + } + destroy (data); + } + return (res); +} + +// GetFilters -- returns the list of filters for the indexed experiment +// returns false if there's a problem; true otherwise +// +Vector<void*> * +dbeGetFilters (int dbevindex, int nexp) +{ + FilterNumeric *filt; + int index; + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<FilterNumeric *>*filters = dbev->get_all_filters (nexp); + if (filters == NULL) + return NULL; + + // return an array of filter data for that experiment + Vector <int> *findex = new Vector<int>(); // index of the filters + Vector <char*> *shortname = new Vector<char *>(); + // short name of filter + Vector <char*> *i18n_name = new Vector<char *>(); + // External I18N'd name of filter + Vector <char*> *pattern = new Vector<char *>(); + // current setting string + Vector <char*> *status = new Vector<char *>(); + // current status of filter (%, range, etc.) + + Vec_loop (FilterNumeric *, filters, index, filt) + { + findex->append (index); + shortname->append (dbe_strdup (filt->get_cmd ())); + i18n_name->append (dbe_strdup (filt->get_name ())); + pattern->append (dbe_strdup (filt->get_pattern ())); + status->append (dbe_strdup (filt->get_status ())); + } + Vector<void*> *res = new Vector<void*>(5); + res->store (0, findex); + res->store (1, shortname); + res->store (2, i18n_name); + res->store (3, pattern); + res->store (4, status); + return (res); +} + +// Set a filter string for a view +// Returns NULL if OK, error message if not + +char * +dbeSetFilterStr (int dbevindex, char *filter_str) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->clear_error_msg (); + dbev->clear_warning_msg (); + char *ret = dbev->set_filter (filter_str); + return ret; +} + +// Get the current filter setting for the view +char * +dbeGetFilterStr (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + char *ret = dbev->get_filter (); + return ret; +} + +// Update a filters for a single experiment +// Returns true if any filter->set_pattern() returns true, +// implying rereading the data is needed (i.e., a filter changed) +// +bool +dbeUpdateFilters (int dbevindex, Vector<bool> *selected, Vector<char *> *pattern_str) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->clear_error_msg (); + dbev->clear_warning_msg (); + + // Get index of first selected experiment + int size = selected->size (); + int nselexp = -1; + for (int index = 0; index < size; index++) + { + if (selected->fetch (index) == true) + { + nselexp = index; + break; + } + } + if (nselexp == -1) // No experiment selected + return false; + + bool ret = false; + for (int j = 0; j < size; j++) + { + if (selected->fetch (j) == false) + continue; + bool error; + if (dbev->set_pattern (j, pattern_str, &error)) + ret = true; + } + dbev->update_advanced_filter (); + return ret; +} + +char * +dbeComposeFilterClause (int dbevindex, int type, int subtype, Vector<int> *selections) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + // ask the cached data to generate the string + Hist_data *data; + switch (type) + { + case DSP_FUNCTION: + data = dbev->func_data; + break; + case DSP_DLAYOUT: + data = dbev->dlay_data; + break; + case DSP_DATAOBJ: + data = dbev->dobj_data; + break; + case DSP_MEMOBJ: + case DSP_INDXOBJ: + data = dbev->get_indxobj_data (subtype); + break; + case DSP_LINE: + data = dbev->line_data; + break; + case DSP_PC: + data = dbev->pc_data; + break; + case DSP_SOURCE: + data = dbev->src_data; + break; + case DSP_DISASM: + data = dbev->dis_data; + break; + case DSP_IOACTIVITY: + data = dbev->iofile_data; + break; + case DSP_IOVFD: + data = dbev->iovfd_data; + break; + case DSP_IOCALLSTACK: + data = dbev->iocs_data; + break; + case DSP_HEAPCALLSTACK: + data = dbev->heapcs_data; + break; + default: + return NULL; + } + if (data == NULL) + return NULL; + + // Get array of object indices, and compose filter string + Vector<uint64_t> *obj_ids = data->get_object_indices (selections); + if (obj_ids == NULL || obj_ids->size () == 0) + return NULL; + + uint64_t sel; + int index; + int found = 0; + char buf[128]; + StringBuilder sb; + sb.append ('('); + switch (type) + { + case DSP_LINE: + case DSP_PC: + case DSP_SOURCE: + case DSP_DISASM: + case DSP_FUNCTION: + sb.append (NTXT ("LEAF IN ")); + break; + case DSP_MEMOBJ: + case DSP_INDXOBJ: + sb.append (dbeSession->getIndexSpaceName (subtype)); + sb.append (NTXT (" IN ")); + break; + } + Vec_loop (uint64_t, obj_ids, index, sel) + { + if (found == 0) + { + found = 1; + sb.append ('('); + } + else + sb.append (NTXT (", ")); + snprintf (buf, sizeof (buf), NTXT ("%llu"), (long long) sel); + sb.append (buf); + } + if (found == 1) + sb.append (')'); + + switch (type) + { + case DSP_DLAYOUT: + case DSP_DATAOBJ: + sb.append (NTXT (" SOME IN DOBJ")); + break; + } + sb.append (')'); + return sb.toString (); +} + +// +// Get load object states +// +Vector<void *> * +dbeGetLoadObjectList (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + int size = lobjs->size (); + + // Initialize Java boolean array + Vector<char *> *names = new Vector<char *>(size); + Vector<int> *states = new Vector<int>(size); + Vector<int> *indices = new Vector<int>(size); + Vector<char *> *paths = new Vector<char *>(size); + Vector<int> *isJava = new Vector<int>(size); + + // Get load object states + int index; + LoadObject *lo; + char *lo_name; + + // lobjectsNoJava is a trimmed list of indices provided to front-end skipping the Java + // classes. lobjectsNoJava preserves the mapping of the index into the complete lobjs + // vector. What front-end sees as lobj[i] is really lobj[lobjectsNoJava[i]]; + + // This list is constructed every time GetLoadObjectList() or GetLoadObjectState() is + // called. Possibility of further optimization by making it more persistent. + // Only consumer of this list is dbeSetLoadObjectState + int new_index = 0; + if (dbev->lobjectsNoJava == NULL) + dbev->lobjectsNoJava = new Vector<int>(1); + else + dbev->lobjectsNoJava->reset (); + + Vec_loop (LoadObject*, lobjs, index, lo) + { + // Set 0, 1, or 2 for show/hide/api + enum LibExpand expand = dbev->get_lo_expand (lo->seg_idx); + + lo_name = lo->get_name (); + if (lo_name != NULL) + { + size_t len = strlen (lo_name); + if (len > 7 && streq (lo_name + len - 7, NTXT (".class>"))) + isJava->store (new_index, 1); + else + isJava->store (new_index, 0); + } + else + isJava->store (new_index, 0); + dbev->lobjectsNoJava->append (index); + + names->store (new_index, dbe_sprintf (NTXT ("%s"), lo_name)); + states->store (new_index, (int) expand); + indices->store (new_index, (int) lo->seg_idx); + paths->store (new_index, dbe_sprintf (NTXT ("%s"), lo->get_pathname ())); + new_index++; + } + Vector<void*> *res = new Vector<void*>(5); + res->store (0, names); + res->store (1, states); + res->store (2, indices); + res->store (3, paths); + res->store (4, isJava); + delete lobjs; + return res; +} + +Vector<int> * +dbeGetLoadObjectState (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + int size = lobjs->size (); + + // Initialize Java boolean array + Vector<int> *states = new Vector<int>(size); + char *lo_name; + + // lobjectsNoJava is a trimmed list of indices provided to front-end skipping the Java + // classes. lobjectsNoJava preserves the mapping of the index into the complete lobjs + // vector. What front-end sees as lobj[i] is really lobj[lobjectsNoJava[i]]; + + // This list is constructed every time GetLoadObjectList() or GetLoadObjectState() is + // called. Possibility of further optimization by making it more persistent. + // Only consumer of this list is dbeSetLoadObjectState + int new_index = 0; + if (dbev->lobjectsNoJava == NULL) + dbev->lobjectsNoJava = new Vector<int>(1); + else + dbev->lobjectsNoJava->reset (); + + // Get load object states + int index; + LoadObject *lo; + + Vec_loop (LoadObject*, lobjs, index, lo) + { + // Set 0, 1, or 2 for show/hide/api + lo_name = lo->get_name (); + if (lo_name != NULL) + { + size_t len = strlen (lo_name); + if (len > 7 && streq (lo_name + len - 7, NTXT (".class>"))) + continue; + } + else + dbev->lobjectsNoJava->append (index); + + enum LibExpand expand = dbev->get_lo_expand (lo->seg_idx); + states->store (new_index, (int) expand); + new_index++; + } + delete lobjs; + return states; +} + +// Set load object states +void +dbeSetLoadObjectState (int dbevindex, Vector<int> *selected) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + + int index; + bool changed = false; + + LoadObject *lo; + int new_index = 0; + dbev->setShowAll (); + Vec_loop (LoadObject*, lobjs, index, lo) + { + if (dbev->lobjectsNoJava != NULL) + { + // This loadobject is a java class and was skipped + if (dbev->lobjectsNoJava->fetch (new_index) != index) + continue; + } + // Get array of settings + enum LibExpand expand = (enum LibExpand) selected->fetch (new_index); + if (expand == LIBEX_HIDE) + { + dbev->resetShowAll (); + dbeSession->set_lib_visibility_used (); + } + changed = changed | dbev->set_libexpand (lo->get_pathname (), expand); + new_index++; + } + delete lobjs; + if (changed == true) + { + dbev->setShowHideChanged (); + dbev->update_lo_expands (); + } + + return; +} + +// Reset load object states +void +dbeSetLoadObjectDefaults (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->set_libdefaults (); +} + +// Get Machine model +Vector<char*>* +dbeGetCPUVerMachineModel (int dbevindex) +{ + Vector<char*>* table = new Vector<char*>(); + DbeView *dbev = dbeSession->getView (dbevindex); + char * mach_model = dbev->get_settings ()->get_machinemodel (); + if (mach_model != NULL) + { + table->append (mach_model); + return table; + } + int grsize = dbeSession->expGroups->size (); + for (int j = 0; j < grsize; j++) + { + ExpGroup *gr = dbeSession->expGroups->fetch (j); + Vector<Experiment*> *exps = gr->exps; + for (int i = 0, sz = exps->size (); i < sz; i++) + { + Experiment *exp = exps->fetch (i); + char *model = exp->machinemodel; + if (model != NULL) + table->append (dbe_strdup (model)); + } + } + return table; +} + +// automatically load machine model if applicable +void +dbeDetectLoadMachineModel (int dbevindex) +{ + if (dbeSession->is_datamode_available ()) + { + char *model = dbeGetMachineModel (); + if (model == NULL) + { + Vector<char*>* models = dbeGetCPUVerMachineModel (dbevindex); + char * machineModel = NTXT ("generic"); + if (models->size () > 0) + { + machineModel = models->get (0); + for (int i = 1; i < models->size (); i++) + { + if (strncmp (models->get (i), machineModel, strlen (machineModel)) == 0) + { + machineModel = NTXT ("generic"); + break; + } + } + dbeLoadMachineModel (machineModel); + } + delete models; + } + } +} + +// Managing Memory Objects +char * +dbeDefineMemObj (char *name, char *index_expr, char *machinemodel, + char *sdesc, char *ldesc) +{ + return MemorySpace::mobj_define (name, index_expr, machinemodel, sdesc, ldesc); +} + +char * +dbeDeleteMemObj (char *name) +{ + return MemorySpace::mobj_delete (name); +} + +Vector<void*> * +dbeGetMemObjects (int /*dbevindex*/) +{ + Vector<void*> *res = MemorySpace::getMemObjects (); + return res; +} + +// Managing machine model +char * +dbeLoadMachineModel (char *name) +{ + return dbeSession->load_mach_model (name); +} + +char * +dbeGetMachineModel () +{ + return dbeSession->get_mach_model (); +} + +Vector <char *> * +dbeListMachineModels () +{ + return dbeSession->list_mach_models (); +} + +// Managing Index Objects +char * +dbeDefineIndxObj (char *name, char *index_expr, char *sdesc, char *ldesc) +{ + return dbeSession->indxobj_define (name, NULL, index_expr, sdesc, ldesc); +} + +Vector<void*> * +dbeGetIndxObjDescriptions (int /*dbevindex*/) +{ + Vector<void*> *res = dbeSession->getIndxObjDescriptions (); + return res; +} + +Vector<void*> * +dbeGetCustomIndxObjects (int /*dbevindex*/) +{ + Vector<void*> *res = dbeSession->getCustomIndxObjects (); + return res; +} + +void +dbeSetSelObj (int dbevindex, Obj sel_obj_or_ind, int type, int subtype) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Histable *sel_obj; + Hist_data *data; + int sel_ind = (int) sel_obj_or_ind; + + switch (type) + { + case DSP_FUNCTION: + data = dbev->func_data; + break; + case DSP_LINE: + data = dbev->line_data; + break; + case DSP_PC: + data = dbev->pc_data; + break; + case DSP_CALLER: + data = dbev->callers; + break; + case DSP_CALLEE: + data = dbev->callees; + break; + case DSP_SOURCE: + data = dbev->src_data; + break; + case DSP_DISASM: + data = dbev->dis_data; + break; + case DSP_DLAYOUT: + data = dbev->dlay_data; + if (data == NULL) + { + dbev->sel_binctx = NULL; + return; + } + if (sel_ind >= 0 && sel_ind < dbev->dlay_data->size ()) + dbev->sel_dobj = dbev->dlay_data->fetch (sel_ind)->obj; + return; + case DSP_DATAOBJ: + data = dbev->dobj_data; + if (data == NULL) + { + dbev->sel_binctx = NULL; + return; + } + if (sel_ind >= 0 && sel_ind < dbev->dobj_data->size ()) + dbev->sel_dobj = dbev->dobj_data->fetch (sel_ind)->obj; + return; + case DSP_MEMOBJ: + case DSP_INDXOBJ: + dbev->set_indxobj_sel (subtype, sel_ind); + sel_obj = dbev->get_indxobj_sel (subtype); + if (sel_obj && sel_obj->get_type () == Histable::INDEXOBJ) + dbev->set_sel_obj (((IndexObject*) sel_obj)->get_obj ()); + return; + case DSP_SOURCE_V2: + case DSP_DISASM_V2: + case DSP_TIMELINE: + case DSP_LEAKLIST: + case DSP_RACES: + case DSP_DEADLOCKS: + case DSP_DUALSOURCE: + case DSP_SOURCE_DISASM: + case DSP_IOACTIVITY: + case DSP_IOVFD: + case DSP_IOCALLSTACK: + case DSP_HEAPCALLSTACK: + case DSP_MINICALLER: + dbev->set_sel_obj ((Histable *) sel_obj_or_ind); + return; + default: + // abort(); + return; + } + if (type != DSP_SOURCE && type != DSP_DISASM && type != DSP_SOURCE_V2 + && type != DSP_DISASM_V2) + dbev->sel_binctx = NULL; + + if (data == NULL || data->get_status () != Hist_data::SUCCESS + || sel_ind >= data->size ()) + return; + + if (sel_ind >= 0 && sel_ind < data->size ()) + dbev->set_sel_obj (data->fetch (sel_ind)->obj); +} + +void +dbeSetSelObjV2 (int dbevindex, uint64_t id) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->set_sel_obj (dbeSession->findObjectById (id)); +} + +Obj +dbeGetSelObj (int dbevindex, int type, int subtype) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + Histable *sel_obj = NULL; + switch (type) + { + case DSP_FUNCTION: + sel_obj = dbev->get_sel_obj (Histable::FUNCTION); + break; + case DSP_LINE: + case DSP_SOURCE: + case DSP_SOURCE_V2: + sel_obj = dbev->get_sel_obj (Histable::LINE); + break; + case DSP_PC: + case DSP_DISASM: + case DSP_DISASM_V2: + sel_obj = dbev->get_sel_obj (Histable::INSTR); + break; + case DSP_SRC_FILE: + sel_obj = dbev->get_sel_obj (Histable::SOURCEFILE); + break; + case DSP_DATAOBJ: + case DSP_DLAYOUT: + if (dbev->sel_dobj) + sel_obj = dbev->sel_dobj->convertto (Histable::DOBJECT); + break; + case DSP_MEMOBJ: + case DSP_INDXOBJ: + sel_obj = dbev->get_indxobj_sel (subtype); + break; + default: + abort (); + } + Dprintf (DEBUG_DBE, NTXT ("### dbeGetSelObj: Dbe.cc:%d %s (%d) returns %s\n"), + __LINE__, dsp_type_to_string (type), type, sel_obj ? sel_obj->dump () : "NULL"); + return (Obj) sel_obj; +} + +Obj +dbeConvertSelObj (Obj obj, int type) +{ + Histable *sel_obj = (Histable *) obj; + Dprintf (DEBUG_DBE, NTXT ("### dbeConvertSelObj: Dbe.cc:%d %s (%d) sel_obj=%s\n"), + __LINE__, dsp_type_to_string (type), type, sel_obj ? sel_obj->dump () + : "NULL"); + if (sel_obj == NULL) + return (Obj) NULL; + switch (type) + { + case DSP_FUNCTION: + return (Obj) sel_obj->convertto (Histable::FUNCTION); + case DSP_LINE: + return (Obj) sel_obj->convertto (Histable::LINE); + case DSP_SOURCE: + case DSP_SOURCE_V2: + { + SourceFile* srcCtx = NULL; + if (sel_obj->get_type () == Histable::INSTR) + { + DbeInstr* dbei = (DbeInstr *) sel_obj; + srcCtx = (SourceFile*) dbei->convertto (Histable::SOURCEFILE); + } + else if (sel_obj->get_type () == Histable::LINE) + { + DbeLine * dbel = (DbeLine *) sel_obj; + srcCtx = dbel->sourceFile; + } + sel_obj = sel_obj->convertto (Histable::LINE, srcCtx); + Dprintf (DEBUG_DBE, NTXT ("### dbeConvertSelObj: Dbe.cc:%d %s (%d) returns %s\n"), + __LINE__, dsp_type_to_string (type), type, sel_obj ? sel_obj->dump () : "NULL"); + if (sel_obj && sel_obj->get_type () == Histable::LINE) + { + DbeLine * dbel = (DbeLine *) sel_obj; + return (Obj) dbel->dbeline_base; + } + return (Obj) sel_obj->convertto (Histable::LINE, srcCtx); + } + case DSP_PC: + case DSP_DISASM: + case DSP_DISASM_V2: + return (Obj) sel_obj->convertto (Histable::INSTR); + case DSP_SRC_FILE: + return (Obj) sel_obj->convertto (Histable::SOURCEFILE); + default: + abort (); + } + return (Obj) NULL; +} + +uint64_t +dbeGetSelObjV2 (int dbevindex, char *typeStr) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Histable *obj = NULL; + if (typeStr != NULL) + { + if (streq (typeStr, NTXT ("FUNCTION"))) + obj = dbev->get_sel_obj (Histable::FUNCTION); + else if (streq (typeStr, NTXT ("INSTRUCTION"))) + obj = dbev->get_sel_obj (Histable::INSTR); + else if (streq (typeStr, NTXT ("SOURCELINE"))) + obj = dbev->get_sel_obj (Histable::LINE); + else if (streq (typeStr, NTXT ("SOURCEFILE"))) + obj = dbev->get_sel_obj (Histable::SOURCEFILE); + } + Dprintf (DEBUG_DBE, NTXT ("### dbeGetSelObjV2: Dbe.cc:%d %s returns %s\n"), + __LINE__, STR (typeStr), obj ? obj->dump () : "NULL"); + return obj != NULL ? obj->id : (uint64_t) - 1; +} + +Vector<uint64_t> * +dbeGetSelObjsIO (int dbevindex, Vector<uint64_t> *ids, int type) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<uint64_t> *res = NULL; + Vector<uint64_t> *result = new Vector<uint64_t>(); + for (int i = 0; i < ids->size (); i++) + { + res = dbeGetSelObjIO (dbevindex, ids->fetch (i), type); + if (res != NULL) + { + result->addAll (res); + delete res; + } + } + return result; +} + +Vector<uint64_t> * +dbeGetSelObjIO (int dbevindex, uint64_t id, int type) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Histable *obj = NULL; + Vector<uint64_t> *res = NULL; + int size = 0; + switch (type) + { + case DSP_IOACTIVITY: + obj = dbev->get_sel_obj_io (id, Histable::IOACTFILE); + size = obj != NULL ? ((FileData*) obj)->getVirtualFds ()->size () : 0; + if (size) + { + res = new Vector<uint64_t>(); + Vector<int64_t> *vfds = ((FileData*) obj)->getVirtualFds (); + for (int i = 0; i < size; i++) + res->append (vfds->fetch (i)); + } + break; + case DSP_IOVFD: + obj = dbev->get_sel_obj_io (id, Histable::IOACTVFD); + if (obj) + { + res = new Vector<uint64_t>(); + res->append (obj->id); + } + break; + case DSP_IOCALLSTACK: + obj = dbev->get_sel_obj_io (id, Histable::IOCALLSTACK); + if (obj) + { + Vector<Obj> *instrs = dbeGetStackPCs (dbevindex, obj->id); + if (instrs == NULL) + return NULL; + int stsize = instrs->size (); + res = new Vector<uint64_t>(stsize); + for (int i = 0; i < stsize; i++) + { + Histable *objFunc = (DbeInstr*) (instrs->fetch (i)); + if (objFunc->get_type () != Histable::LINE) + { + objFunc = objFunc->convertto (Histable::FUNCTION); + res->insert (0, objFunc->id); + } + } + delete instrs; + } + break; + default: + break; + } + return res; +} + +uint64_t +dbeGetSelObjHeapTimestamp (int dbevindex, uint64_t id) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Histable *obj = NULL; + uint64_t res = 0; + Vector<uint64_t> *peakStackIds; + Vector<hrtime_t> *peakTimestamps; + + // Find and return the timestamp for the peak + bool foundPeakId = false; + if (id > 0) + { + obj = dbev->get_sel_obj_heap (0); + if (obj != NULL) + { + peakStackIds = ((HeapData*) obj)->getPeakStackIds (); + peakTimestamps = ((HeapData*) obj)->getPeakTimestamps (); + for (int i = 0; i < peakStackIds->size (); i++) + { + if (id == peakStackIds->fetch (i)) + { + res = peakTimestamps->fetch (i); + foundPeakId = true; + break; + } + } + } + } + + // Return the first timestamp for the peak + // if the callstack id is zero or it + // doesn't match with the peak stack id + if (id == 0 || !foundPeakId) + { + obj = dbev->get_sel_obj_heap (0); + res = obj != NULL ? ((HeapData*) obj)->getPeakTimestamps ()->fetch (0) : 0; + } + return res; +} + +int +dbeGetSelObjHeapUserExpId (int dbevindex, uint64_t id) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Histable *obj = NULL; + int res = 0; + obj = dbev->get_sel_obj_heap (id); + res = obj != NULL ? ((HeapData*) obj)->getUserExpId () : 0; + return res; +} + +// +// Get index of selected function/object +// +int +dbeGetSelIndex (int dbevindex, Obj sel_obj, int type, int subtype) +{ + Hist_data *data; + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + switch (type) + { + case DSP_FUNCTION: + data = dbev->func_data; + break; + case DSP_LINE: + data = dbev->line_data; + break; + case DSP_PC: + data = dbev->pc_data; + break; + case DSP_SOURCE: + case DSP_SOURCE_V2: + data = dbev->src_data; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + data = dbev->dis_data; + break; + case DSP_DLAYOUT: + data = dbev->dlay_data; + break; + case DSP_DATAOBJ: + data = dbev->dobj_data; + break; + case DSP_MEMOBJ: + case DSP_INDXOBJ: + data = dbev->get_indxobj_data (subtype); + break; + default: + data = NULL; + break; + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return -1; + + Histable *chk_obj = (Histable *) sel_obj; + Vector<Hist_data::HistItem*> *histItems = data->get_hist_items (); + if (histItems == NULL || chk_obj == NULL) + return -1; + for (int i = 0, sz = histItems->size (); i < sz; i++) + { + if (histItems->get (i)->obj == chk_obj) + return i; + if (histItems->get (i)->obj == NULL) + continue; + if (histItems->get (i)->obj->get_type () == Histable::LINE + && chk_obj->get_type () == Histable::LINE) + { + if (((DbeLine*) histItems->get (i)->obj)->convertto (Histable::FUNCTION) + == ((DbeLine*) chk_obj)->convertto (Histable::FUNCTION) + && ((DbeLine*) histItems->get (i)->obj)->lineno + == ((DbeLine*) chk_obj)->lineno) + return i; + } + else if (histItems->get (i)->obj->get_type () == Histable::INSTR + && chk_obj->get_type () == Histable::INSTR) + if (((DbeInstr*) histItems->get (i)->obj)->convertto (Histable::FUNCTION) + == ((DbeInstr*) chk_obj)->convertto (Histable::FUNCTION) + && ((DbeInstr*) histItems->get (i)->obj)->addr + == ((DbeInstr*) chk_obj)->addr) + return i; + } + + Histable *chk_obj1 = NULL; + switch (type) + { + case DSP_FUNCTION: + chk_obj1 = chk_obj->convertto (Histable::FUNCTION); + break; + case DSP_LINE: + case DSP_SOURCE: + case DSP_SOURCE_V2: + chk_obj1 = chk_obj->convertto (Histable::LINE); + break; + case DSP_PC: + case DSP_DISASM: + case DSP_DISASM_V2: + chk_obj1 = chk_obj->convertto (Histable::INSTR); + break; + } + if (chk_obj1 && chk_obj != chk_obj1) + for (int i = 0, sz = histItems->size (); i < sz; i++) + if (histItems->get (i)->obj == chk_obj1) + return i; + + if (type == DSP_LINE) + { + for (int i = 0, sz = histItems->size (); i < sz; i++) + if (histItems->get (i)->obj != NULL + && chk_obj->convertto (Histable::FUNCTION) + == histItems->get (i)->obj->convertto (Histable::FUNCTION)) + return i; + } + else if (type == DSP_PC) + { + for (int i = 0, sz = histItems->size (); i < sz; i++) + if (histItems->get (i)->obj != NULL + && (histItems->get (i)->obj)->convertto (Histable::FUNCTION) + == (chk_obj)->convertto (Histable::FUNCTION) + && ((DbeLine*) histItems->get (i)->obj->convertto (Histable::LINE))->lineno + == ((DbeLine*) chk_obj->convertto (Histable::LINE))->lineno) + return i; + for (int i = 0, sz = histItems->size (); i < sz; i++) + if (histItems->get (i)->obj != NULL + && (histItems->get (i)->obj)->convertto (Histable::FUNCTION) + == (chk_obj)->convertto (Histable::FUNCTION)) + return i; + } + + // If we clicked on an mfunction line in the called-by call mini in user mode for omp + // we might not find that function in func data + if (dbev->isOmpDisMode () && type == DSP_FUNCTION) + { + int p = dbeGetSelIndex (dbevindex, sel_obj, DSP_DISASM, subtype); + if (p != -1) + return p; + } + return -1; +} + +// Print data +// +char * +dbePrintData (int dbevindex, int type, int subtype, char *printer, + char *fname, FILE *outfile) +{ + Histable *current_obj; + Function *func; + Module *module; + MetricList *mlist_orig; + bool header; + Print_params params; + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + + // Set print parameters + if (printer != NULL) + { + params.dest = DEST_PRINTER; + params.name = printer; + } + else if (outfile != NULL) + { + params.dest = DEST_OPEN_FILE; + params.openfile = outfile; + params.name = NULL; + } + else + { + params.dest = DEST_FILE; + params.name = fname; + if (*(params.name) == '\0') + { + free (params.name); + return dbe_strdup (GTXT ("Please enter the name of the file to which to print")); + } + } + params.ncopies = 1; + if (outfile != NULL) + header = false; + else + header = !(type == DSP_SOURCE || type == DSP_SOURCE_V2 || type == DSP_DISASM_V2); + + params.header = header; + + // figure out what kind of metrics to use + if (type == DSP_SELF || type == DSP_CALLER || type == DSP_CALLEE + || type == DSP_CALLTREE) + mlist_orig = dbev->get_metric_list (MET_CALL); + else if (type == DSP_DATAOBJ || type == DSP_DLAYOUT || type == DSP_MEMOBJ) + mlist_orig = dbev->get_metric_list (MET_DATA); + else if (type == DSP_INDXOBJ) + mlist_orig = dbev->get_metric_list (MET_INDX); + else if (type == DSP_IOACTIVITY || type == DSP_IOVFD + || type == DSP_IOCALLSTACK) + mlist_orig = dbev->get_metric_list (MET_IO); + else if (type == DSP_HEAPCALLSTACK) + mlist_orig = dbev->get_metric_list (MET_HEAP); + else + mlist_orig = dbev->get_metric_list (MET_NORMAL); + + // make a compacted version of the input list + // the list will either be moved to the generated data, + // or freed below if it wasn't needed + MetricList *mlist = new MetricList (mlist_orig); + Hist_data *data = NULL; + er_print_common_display *cd = NULL; + int ix; + // Set data + switch (type) + { + case DSP_FUNCTION: + case DSP_LINE: + case DSP_PC: + case DSP_MEMOBJ: + case DSP_INDXOBJ: + case DSP_DATAOBJ: + data = dbev->get_hist_data (mlist, + ((type == DSP_FUNCTION) ? Histable::FUNCTION : + (type == DSP_LINE) ? Histable::LINE : + (type == DSP_PC) ? Histable::INSTR : + (type == DSP_INDXOBJ) ? Histable::INDEXOBJ : + (type == DSP_MEMOBJ) ? Histable::MEMOBJ + : Histable::DOBJECT), + subtype, Hist_data::ALL); + if (data->get_status () != Hist_data::SUCCESS) + return DbeView::status_str (DbeView::DBEVIEW_NO_DATA); // no strdup() + + cd = new er_print_histogram (dbev, data, mlist, MODE_LIST, + dbev->get_limit (), + mlist->get_sort_name (), NULL, true, true); + break; + case DSP_DLAYOUT: + { + data = dbev->get_hist_data (mlist, Histable::DOBJECT, 0, Hist_data::LAYOUT); + if (data->get_status () != Hist_data::SUCCESS) + return DbeView::status_str (DbeView::DBEVIEW_NO_DATA); // no strdup() + cd = new er_print_histogram (dbev, data, mlist, MODE_ANNOTATED, + dbev->get_thresh_dis (), + mlist->get_sort_name (), NULL, true, true); + break; + } + + // source and disassembly + case DSP_SOURCE: + case DSP_DISASM: + case DSP_SOURCE_V2: + case DSP_DISASM_V2: + if (dbev->sel_obj == NULL) + return NULL; + current_obj = dbev->sel_obj->convertto (Histable::FUNCTION); + if (current_obj->get_type () != Histable::FUNCTION) + return dbe_strdup (GTXT ("Not a real function; no source or disassembly available.")); + func = (Function*) current_obj->convertto (Histable::FUNCTION); + if (func->flags & FUNC_FLAG_SIMULATED) + return dbe_strdup (GTXT ("Not a real function; no source or disassembly available.")); + if (func->get_name () == NULL) + return dbe_strdup (GTXT ("Source location not recorded in experiment")); + module = func->module; + if (module == NULL || module->get_name () == NULL) + return dbe_strdup (GTXT ("Object name not recorded in experiment")); + ix = module->loadobject->seg_idx; + if (dbev->get_lo_expand (ix) == LIBEX_HIDE) + return dbe_strdup (GTXT ("No source or disassembly available for hidden object")); + cd = new er_print_histogram (dbev, dbev->func_data, mlist, MODE_ANNOTATED, + type == DSP_DISASM || type == DSP_DISASM_V2, + mlist->get_sort_name (), + func, false, false); + break; + + // callers-callees + case DSP_SELF: + case DSP_CALLER: + case DSP_CALLEE: + if (dbev->sel_obj == NULL) + return NULL; + current_obj = dbev->sel_obj->convertto (Histable::FUNCTION); + cd = new er_print_histogram (dbev, dbev->func_data, mlist, MODE_GPROF, 1, + mlist->get_sort_name (), current_obj, + false, false); + break; + + // statistics; this won't use the metric list copied above, so delete it + case DSP_STATIS: + cd = new er_print_experiment (dbev, 0, dbeSession->nexps () - 1, + true, true, true, true, false); + delete mlist; + break; + case DSP_EXP: + cd = new er_print_experiment (dbev, 0, dbeSession->nexps () - 1, + true, true, false, false, false); + delete mlist; + break; + case DSP_LEAKLIST: + cd = new er_print_leaklist (dbev, true, true, dbev->get_limit ()); + delete mlist; + break; + case DSP_HEAPCALLSTACK: + cd = new er_print_heapactivity (dbev, Histable::HEAPCALLSTACK, false, + dbev->get_limit ()); + delete mlist; + break; + case DSP_IOACTIVITY: + cd = new er_print_ioactivity (dbev, Histable::IOACTFILE, false, + dbev->get_limit ()); + delete mlist; + break; + case DSP_IOVFD: + cd = new er_print_ioactivity (dbev, Histable::IOACTVFD, false, + dbev->get_limit ()); + delete mlist; + break; + + // the io call stack + case DSP_IOCALLSTACK: + cd = new er_print_ioactivity (dbev, Histable::IOCALLSTACK, false, + dbev->get_limit ()); + delete mlist; + break; + + // some unknown panel -- return an error string + default: + delete mlist; + return dbe_strdup (GTXT ("Print not available")); + } + + // Start printing + char *buf = NULL; + + // first open the file/device/whatever + if (cd->open (¶ms) == 0) + { + // now call the actual print routine + cd->data_dump (); + if (params.dest == DEST_PRINTER) + { + if (streq ((char *) params.name, NTXT ("-"))) + { + // Special case - return report to the GUI + int maxbytes = 2 * 1024 * 1024; // IPC large buffer limit + char *report = cd->get_output (maxbytes); + delete data; + delete cd; + return report; // TEMPORARY + } + } + if (cd->print_output () == false) + buf = dbe_sprintf (NTXT ("%s: %s"), + GTXT ("Unable to submit print request to"), + params.name); + } + else + // if unable to set up the print, return an error + buf = dbe_sprintf (NTXT ("%s: %s"), + GTXT ("Unable to open file"), + params.name); + + // dbe_free((void *) params.name); XXX when should this happen? + if (data) + if (data->isViewOwned () == false) + delete data; + delete cd; + return buf; +} + +// Set limit for print data +// +char * +dbeSetPrintLimit (int dbevindex, int limit) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + return (dbev->set_limit (limit)); +} + +// get limit for print data +int +dbeGetPrintLimit (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + int limit = dbev->get_limit (); + return limit; +} + +// set printmode for data +char * +dbeSetPrintMode (int dbevindex, char * pmode) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + char *r = dbev->set_printmode (pmode); + return r; +} + +// get printmode for data +int +dbeGetPrintMode (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + return (dbev->get_printmode ()); +} + +// get printmode for data +char * +dbeGetPrintModeString (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + return ( dbev->get_printmode_str ()); +} + +// get print delimiter for csv data +char +dbeGetPrintDelim (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + return (dbev->get_printdelimiter ()); +} + +// Set Source/Object/Load-Object file names +static void +set_file_names (Function *func, char *names[3]) +{ + Module *module = func->module; + LoadObject *loadobject = module->loadobject; + if (loadobject == NULL) + loadobject = dbeSession->get_Unknown_LoadObject (); + free (names[0]); + free (names[1]); + free (names[2]); + SourceFile *sf = func->getDefSrc (); + char *src_name = sf->dbeFile->get_location_info (); + DbeFile *df = module->dbeFile; + if (df == NULL || (df->filetype & DbeFile::F_JAVACLASS) == 0) + df = module->loadobject->dbeFile; + char *lo_name = df->get_location_info (); + char *dot_o_name = lo_name; + if (module->dot_o_file) + dot_o_name = module->dot_o_file->dbeFile->get_location_info (); + names[0] = dbe_sprintf (NTXT ("%s: %s"), GTXT ("Source File"), src_name); + names[1] = dbe_sprintf (NTXT ("%s: %s"), GTXT ("Object File"), dot_o_name); + names[2] = dbe_sprintf (NTXT ("%s: %s"), GTXT ("Load Object"), lo_name); +} + +// dbeSetFuncData +// Master function to generate all Tab data for the analyzer +// Returns the index of the selected item in the specified list +// +// After calling it to set up, the Analyzer calls dbeGetFuncList +// to format the generated data and return the table +// Most of the data is destined for a JTable +// +int +dbeSetFuncData (int dbevindex, Obj sel_obj, int type, int subtype) +{ + MetricList *_mlist; + Histable *org_obj; + Hist_data *data = NULL; + int index, sel_index; + Function *func; + char *name; + int ix; + + DbeView *dbev = dbeSession->getView (dbevindex); + sel_index = -1; + dbev->resetOmpDisMode (); + dbev->error_msg = dbev->warning_msg = NULL; + + // get metric list, make a compact duplicate + _mlist = dbev->get_metric_list (MET_NORMAL); + MetricList *mlist = new MetricList (_mlist); + + // Remove old function/obj list data & Get new function/obj list data + org_obj = (Histable *) sel_obj; + + // Figure out which "function" data is being asked for, i.e., + // which of the analyzer displays is asking for data + switch (type) + { + // the various tables: functions, lines, PCs, DataObjects, IndexObjects + case DSP_FUNCTION: + case DSP_LINE: + case DSP_PC: + case DSP_DATAOBJ: + case DSP_MEMOBJ: + case DSP_INDXOBJ: + switch (type) + { + case DSP_FUNCTION: + if (dbev->func_data) + delete dbev->func_data; + dbev->func_data = data = dbev->get_hist_data (mlist, + Histable::FUNCTION, subtype, Hist_data::ALL); + break; + case DSP_LINE: + if (dbev->line_data) + delete dbev->line_data; + dbev->line_data = data = dbev->get_hist_data (mlist, + Histable::LINE, subtype, Hist_data::ALL); + break; + case DSP_PC: + if (dbev->pc_data) + delete dbev->pc_data; + dbev->pc_data = data = dbev->get_hist_data (mlist, + Histable::INSTR, subtype, Hist_data::ALL); + break; + case DSP_DATAOBJ: + if (dbev->dobj_data) + delete dbev->dobj_data; + mlist = dbev->get_metric_list (MET_DATA); + dbev->dobj_data = data = dbev->get_hist_data (mlist, + Histable::DOBJECT, subtype, Hist_data::ALL); + break; + case DSP_MEMOBJ: + mlist = dbev->get_metric_list (MET_DATA); + data = dbev->get_hist_data (mlist, Histable::MEMOBJ, subtype, + Hist_data::ALL); + dbev->indx_data->store (subtype, data); + break; + case DSP_INDXOBJ: + mlist = dbev->get_metric_list (MET_INDX); + data = dbev->get_hist_data (mlist, Histable::INDEXOBJ, subtype, + Hist_data::ALL); + dbev->indx_data->store (subtype, data); + break; + default: + break; + } + + // Set the selection of row item + if (data->get_status () == Hist_data::SUCCESS) + { + // otherwise, look for it + sel_index = -1; + if (org_obj) + { + Hist_data::HistItem *hi; + Vec_loop (Hist_data::HistItem*, data->get_hist_items (), index, hi) + { + if (hi->obj == org_obj) + { + sel_index = index; + break; + } + } + if (sel_index == -1 && (type == DSP_LINE || type == DSP_PC)) + { + Vec_loop (Hist_data::HistItem*, data->get_hist_items (), index, hi) + { + name = hi->obj->get_name (); + if (strcmp (name, NTXT ("<Total>")) && + strcmp (name, GTXT ("<Unknown>"))) + { + int done = 0; + switch (type) + { + case DSP_LINE: + if (org_obj->convertto (Histable::FUNCTION) + == hi->obj->convertto (Histable::FUNCTION)) + { + sel_index = index; + done = 1; + } + break; + case DSP_PC: + if (hi->obj->convertto (Histable::FUNCTION) + == org_obj->convertto (Histable::FUNCTION) + && ((DbeLine*) hi->obj->convertto (Histable::LINE))->lineno + == ((DbeLine*) org_obj->convertto (Histable::LINE))->lineno) + { + sel_index = index; + done = 1; + } + break; + } + if (done) + break; + } + } + } + if (sel_index == -1 && type == DSP_PC) + { + Vec_loop (Hist_data::HistItem*, data->get_hist_items (), index, hi) + { + name = hi->obj->get_name (); + if (strcmp (name, NTXT ("<Total>")) && + strcmp (name, GTXT ("<Unknown>"))) + { + int done = 0; + if (hi->obj->convertto (Histable::FUNCTION) == + org_obj->convertto (Histable::FUNCTION)) + { + sel_index = index; + done = 1; + } + if (done) + break; + } + } + } + } + if (sel_index == -1) + { + Hist_data::HistItem *hi; + Vec_loop (Hist_data::HistItem*, data->get_hist_items (), index, hi) + { + name = hi->obj->get_name (); + if (strcmp (name, NTXT ("<Total>")) && + strcmp (name, GTXT ("<Unknown>"))) + { + sel_index = index; + break; + } + } + } + } + else + dbev->error_msg = DbeView::status_str (DbeView::DBEVIEW_NO_DATA); + return sel_index; + // the end of the code for the regular tables + + // Data Layout + case DSP_DLAYOUT: + if (dbev->dlay_data) + delete dbev->dlay_data; + dbev->marks->reset (); + mlist = dbev->get_metric_list (MET_DATA); + + // initial dobj list ... + data = dbev->get_hist_data (mlist, Histable::DOBJECT, subtype, + Hist_data::LAYOUT); + // .. provides metric data for layout + dbev->dlay_data = data = dbev->get_data_space ()->get_layout_data (data, + dbev->marks, dbev->get_thresh_dis ()); + if (data->get_status () != Hist_data::SUCCESS) + dbev->error_msg = DbeView::status_str (DbeView::DBEVIEW_NO_DATA); + return sel_index; + + // Source or disassembly + case DSP_SOURCE_V2: + case DSP_DISASM_V2: + case DSP_SOURCE: + case DSP_DISASM: + { + if (org_obj == NULL) + { + dbev->error_msg = DbeView::status_str (DbeView::DBEVIEW_NO_SEL_OBJ); + return sel_index; + } + if (org_obj->get_type () != Histable::FUNCTION) + { + dbev->error_msg = dbe_strdup ( + GTXT ("Not a real function; no source or disassembly available.")); + return sel_index; + } + func = (Function *) org_obj; + if (func->flags & FUNC_FLAG_SIMULATED) + { + dbev->error_msg = dbe_strdup ( + GTXT ("Not a real function; no source or disassembly available.")); + return sel_index; + } + if (func->get_name () == NULL) + { + dbev->error_msg = dbe_strdup ( + GTXT ("Source location not recorded in experiment")); + return sel_index; + } + Module *module = func->module; + if ((module == NULL) || (module->get_name () == NULL)) + { + dbev->error_msg = dbe_strdup ( + GTXT ("Object name not recorded in experiment")); + return sel_index; + } + ix = module->loadobject->seg_idx; + if (dbev->get_lo_expand (ix) == LIBEX_HIDE) + { + dbev->error_msg = dbe_strdup ( + GTXT ("No source or disassembly available for hidden object")); + return sel_index; + } + + if ((type == DSP_DISASM || type == DSP_DISASM_V2) + && dbev->get_view_mode () == VMODE_USER + && dbeSession->is_omp_available ()) + dbev->setOmpDisMode (); + + dbev->marks->reset (); + SourceFile *srcContext = NULL; + switch (dbev->sel_obj->get_type ()) + { + case Histable::FUNCTION: + { + Function *f = (Function *) dbev->sel_obj; + srcContext = f->getDefSrc (); + dbev->sel_binctx = f->module; + break; + } + case Histable::LINE: + { + DbeLine *dl = (DbeLine *) dbev->sel_obj; + srcContext = dl->sourceFile; + Function *f = dl->func; + if (f) + dbev->sel_binctx = f; + break; + } + case Histable::INSTR: + { + Function *f = (Function *) dbev->sel_obj->convertto (Histable::FUNCTION); + if (f) + { + dbev->sel_binctx = f; + srcContext = f->getDefSrc (); + } + break; + } + default: + break; + } + mlist = dbev->get_metric_list (MET_SRCDIS); + + // for source and disassembly the name needs to be invisible, + // but that's handled in the module code + if (type == DSP_SOURCE) + { + if (dbev->src_data) + delete dbev->src_data; + + // func_data computation needed for get_totals + if (dbev->func_data == NULL) + dbev->func_data = data = dbev->get_hist_data (mlist, + Histable::FUNCTION, subtype, Hist_data::ALL); + dbev->marks2dsrc->reset (); + dbev->marks2dsrc_inc->reset (); + data = dbev->src_data = module->get_data (dbev, mlist, + Histable::LINE, dbev->func_data->get_totals ()->value, + srcContext, func, dbev->marks, + dbev->get_thresh_src (), dbev->get_src_compcom (), + dbev->get_src_visible (), dbev->get_hex_visible (), + false, false, dbev->marks2dsrc, dbev->marks2dsrc_inc); + set_file_names (func, dbev->names_src); + if (srcContext) + { + free (dbev->names_src[0]); + dbev->names_src[0] = dbe_sprintf (GTXT ("Source File: %s"), + srcContext->dbeFile->get_location_info ()); + } + Obj obj = (Obj) func->convertto (Histable::LINE, srcContext); + sel_index = dbeGetSelIndex (dbevindex, obj, type, subtype); + } + else + { /* type == DSP_DISASM */ + if (dbev->dis_data) + delete dbev->dis_data; + + // func_data computation needed for get_totals + if (dbev->func_data == NULL) + dbev->func_data = data = dbev->get_hist_data (mlist, + Histable::FUNCTION, subtype, Hist_data::ALL); + dbev->marks2ddis->reset (); + dbev->marks2ddis_inc->reset (); + data = dbev->dis_data = module->get_data (dbev, mlist, + Histable::INSTR, dbev->func_data->get_totals ()->value, + srcContext, func, dbev->marks, dbev->get_thresh_dis (), + dbev->get_dis_compcom (), dbev->get_src_visible (), + dbev->get_hex_visible (), dbev->get_func_scope (), + false, dbev->marks2ddis, dbev->marks2ddis_inc); + set_file_names (func, dbev->names_dis); + if (srcContext) + { + free (dbev->names_dis[0]); + dbev->names_dis[0] = dbe_sprintf (GTXT ("Source File: %s"), + srcContext->dbeFile->get_location_info ()); + } + Obj obj = (Obj) func->convertto (Histable::INSTR); + sel_index = dbeGetSelIndex (dbevindex, obj, type, subtype); + } + return sel_index; + } + + // the three cases for caller-callee + case DSP_SELF: + case DSP_CALLER: + case DSP_CALLEE: + if (org_obj == NULL) + { + dbev->error_msg = DbeView::status_str (DbeView::DBEVIEW_NO_SEL_OBJ); + return sel_index; + } + + // Caller data + if (dbev->callers) + delete dbev->callers; + mlist = dbev->get_metric_list (MET_CALL); + dbev->callers = dbev->get_hist_data (mlist, Histable::FUNCTION, subtype, + Hist_data::CALLERS, org_obj); + if (dbev->callers->get_status () != Hist_data::SUCCESS) + { + dbev->error_msg = DbeView::status_str (DbeView::DBEVIEW_NO_DATA); + return sel_index; + } + + // Callee data + if (dbev->callees) + delete dbev->callees; + dbev->callees = dbev->get_hist_data (mlist, Histable::FUNCTION, subtype, + Hist_data::CALLEES, org_obj); + if (dbev->callees->get_status () != Hist_data::SUCCESS) + { + dbev->error_msg = DbeView::status_str (DbeView::DBEVIEW_NO_DATA); + return sel_index; + } + + // Center Function item + if (dbev->fitem_data) + delete dbev->fitem_data; + dbev->fitem_data = dbev->get_hist_data (mlist, Histable::FUNCTION, subtype, + Hist_data::SELF, org_obj); + if (dbev->fitem_data->get_status () != Hist_data::SUCCESS) + { + dbev->error_msg = DbeView::status_str (DbeView::DBEVIEW_NO_DATA); + return sel_index; + } + return sel_index; + default: + abort (); + } + return sel_index; +} + +Vector<void*>* +dbeGetTotals (int dbevindex, int dsptype, int subtype) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + MetricList *mlist = dbev->get_metric_list (dsptype, subtype); + Hist_data *data = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::ALL); + Hist_data::HistItem *totals = data->get_totals (); + Vector<void*> *tbl = new Vector<void*>(mlist->size ()); + for (long i = 0, sz = mlist->size (); i < sz; i++) + { + Metric *m = mlist->get (i); + switch (m->get_vtype ()) + { + case VT_DOUBLE: + { + Vector<double> *lst = new Vector<double>(1); + lst->append (totals->value[i].d); + tbl->append (lst); + break; + } + case VT_INT: + { + Vector<int> *lst = new Vector<int>(1); + lst->append (totals->value[i].i); + tbl->append (lst); + break; + } + case VT_LLONG: + case VT_ULLONG: + case VT_ADDRESS: + { + Vector<long long> *lst = new Vector<long long>(1); + lst->append (totals->value[i].ll); + tbl->append (lst); + break; + } + case VT_LABEL: + { + Vector<char *> *lst = new Vector<char *>(1); + Histable::NameFormat nfmt = dbev->get_name_format (); + lst->append (dbe_strdup (totals->obj->get_name (nfmt))); + tbl->append (lst); + break; + } + default: + abort (); + } + } + Vector<void*> *res = new Vector<void*>(2); + res->append (dbeGetMetricList (mlist)); + res->append (tbl); + return res; +} + +Vector<void*>* +dbeGetHotMarks (int dbevindex, int type) +{ + Vector<void*>* table = new Vector<void*>(2); + Vector<int>* table0 = new Vector<int> (); + Vector<int>* table1 = new Vector<int> (); + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + return NULL; + + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + for (int i = 0; i < dbev->marks2dsrc->size (); i++) + { + table0->append (dbev->marks2dsrc->fetch (i).index1); + table1->append (dbev->marks2dsrc->fetch (i).index2); + } + break; + case DSP_DISASM: + case DSP_DISASM_V2: + for (int i = 0; i < dbev->marks2ddis->size (); i++) + { + table0->append (dbev->marks2ddis->fetch (i).index1); + table1->append (dbev->marks2ddis->fetch (i).index2); + } + break; + default: + break; + } + table->store (0, table0); + table->store (1, table1); + return table; +} + +Vector<void*>* +dbeGetHotMarksInc (int dbevindex, int type) +{ + Vector<void*>* table = new Vector<void*>(2); + Vector<int>* table0 = new Vector<int> (); + Vector<int>* table1 = new Vector<int> (); + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + return NULL; + + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + for (int i = 0; i < dbev->marks2dsrc_inc->size (); i++) + { + table0->append (dbev->marks2dsrc_inc->fetch (i).index1); + table1->append (dbev->marks2dsrc_inc->fetch (i).index2); + } + break; + case DSP_DISASM: + case DSP_DISASM_V2: + for (int i = 0; i < dbev->marks2ddis_inc->size (); i++) + { + table0->append (dbev->marks2ddis_inc->fetch (i).index1); + table1->append (dbev->marks2ddis_inc->fetch (i).index2); + } + break; + default: + break; + } + table->store (0, table0); + table->store (1, table1); + return table; +} + +Vector<void*>* +dbeGetSummaryHotMarks (int dbevindex, Vector<Obj> *sel_objs, int type) +{ + Vector<void*>* table = new Vector<void*>(2); + Vector<int>* table0 = new Vector<int> (); + Vector<int>* table1 = new Vector<int> (); + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + return NULL; + if (sel_objs == NULL || sel_objs->size () == 0) + return NULL; + + Hist_data *data; + Vector<int_pair_t> *marks2d; + Vector<int_pair_t>* marks2d_inc; + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + data = dbev->src_data; + marks2d = dbev->marks2dsrc; + marks2d_inc = dbev->marks2dsrc_inc; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + data = dbev->dis_data; + marks2d = dbev->marks2ddis; + marks2d_inc = dbev->marks2ddis_inc; + break; + default: + data = NULL; + marks2d = NULL; + marks2d_inc = NULL; + break; + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS + || marks2d_inc == NULL || marks2d == NULL) + return NULL; + + MetricList *orig_mlist = data->get_metric_list (); + MetricList *prop_mlist = new MetricList (dbev->get_metric_ref (MET_NORMAL)); + if (prop_mlist && dbev->comparingExperiments ()) + prop_mlist = dbev->get_compare_mlist (prop_mlist, 0); + Metric *mitem; + int index, index2; + index2 = 0; + Vec_loop (Metric*, prop_mlist->get_items (), index, mitem) + { + if (mitem->get_subtype () == Metric::STATIC) + continue; + + for (int i = 0; i < marks2d_inc->size (); i++) + { + int found = 0; + for (int j = 0; j < sel_objs->size (); j++) + { + int sel_index = (int) sel_objs->fetch (j); + int marked_index = marks2d_inc->fetch (i).index1; + if (sel_index == marked_index) + { + found = 1; + break; + } + } + if (!found) + continue; + int mindex = marks2d_inc->fetch (i).index2; + Metric *orig_metric = orig_mlist->get_items ()->fetch (mindex); + if (orig_metric->get_id () == mitem->get_id () + && mitem->get_subtype () == Metric::INCLUSIVE) + { + table0->append (index2); + table1->append (1); + } + } + + for (int i = 0; i < marks2d->size (); i++) + { + int found = 0; + for (int j = 0; j < sel_objs->size (); j++) + { + int sel_index = (int) sel_objs->fetch (j); + int marked_index = marks2d->fetch (i).index1; + if (sel_index == marked_index) + { + found = 1; + break; + } + } + if (!found) + continue; + int mindex = marks2d->fetch (i).index2; + Metric *orig_metric = orig_mlist->get_items ()->fetch (mindex); + if (orig_metric->get_id () == mitem->get_id () + && mitem->get_subtype () == Metric::EXCLUSIVE) + { + table0->append (index2); + table1->append (0); + } + } + if (!(mitem->get_subtype () == Metric::EXCLUSIVE + || mitem->get_subtype () == Metric::DATASPACE)) + index2++; + } + table->store (0, table0); + table->store (1, table1); + return table; +} + +// Get a vector of function ids of data(begin, begin + length - 1) +// Currently only support source/disassembly view +Vector<uint64_t>* +dbeGetFuncId (int dbevindex, int type, int begin, int length) +{ + Vector<uint64_t>* table = new Vector<uint64_t > (); + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + + Hist_data *data; + Function* given_func = NULL; + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + data = dbev->src_data; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + data = dbev->dis_data; + break; + default: + data = NULL; + abort (); + } + + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + + if (begin < 0 || begin + length > data->size ()) + return NULL; + + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + case DSP_DISASM: + case DSP_DISASM_V2: + { + for (int i = begin; i < begin + length; i++) + { + given_func = NULL; + Histable * sel_obj = data->fetch (i)->obj; + if (sel_obj != NULL) + given_func = (Function*) (sel_obj)->convertto (Histable::FUNCTION, (Histable*) dbev); + if (given_func == NULL) + table->append (0); + else + table->append (given_func->id); + } + } + break; + default: + abort (); + } + return table; +} + +Vector<void*>* +dbeGetFuncCallerInfo (int dbevindex, int type, Vector<int>* idxs, int groupId) +{ + Vector<void*>* data = new Vector<void*>(); + if (type == DSP_SOURCE_V2 || type == DSP_DISASM_V2) + { + Obj sel_func = dbeGetSelObj (dbevindex, DSP_FUNCTION, 0); + if (sel_func == 0) + return data; + Vector<Obj> * cmpObjs = dbeGetComparableObjsV2 (dbevindex, sel_func, type); + if (cmpObjs == NULL) + return data; + DbeView *dbev = dbeSession->getView (dbevindex); + int mtype = MET_COMMON | COMPARE_BIT | ((groupId + 1) << GROUP_ID_SHIFT); + MetricList *mlist = dbev->get_metric_list ((MetricType) (mtype & MTYPE_MASK), + (mtype & COMPARE_BIT) != 0, + mtype >> GROUP_ID_SHIFT); + Histable *selObj = (Histable *) cmpObjs->fetch (groupId); + int subtype = 0; + Hist_data *hist_data = dbev->get_data (mlist, selObj, type, subtype); + if (hist_data == NULL) + return data; + } + for (int i = 0; i < idxs->size (); i++) + data->append (dbeGetFuncCallerInfoById (dbevindex, type, idxs->fetch (i))); + return data; +} + +// +// Get Table of Caller info: +// param: idx -- selected AT_FUNC row +// return: callsite_id, callsite_name (function: file: line) +Vector<void*>* +dbeGetFuncCallerInfoById (int dbevindex, int type, int idx) +{ + Vector<void*>* table = new Vector<void*>(3); + Vector<uint64_t>* table0 = new Vector<uint64_t> (); + Vector<int>* table1 = new Vector<int> (); + Vector<char*>* table2 = new Vector<char*>(); + + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Hist_data *data; + Function* given_func = NULL; + Vector<Histable*> *instr_info = NULL; + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + data = dbev->src_data; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + data = dbev->dis_data; + break; + default: + data = NULL; + abort (); + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + + if (idx < 0 || idx >= data->size ()) + return NULL; + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + case DSP_DISASM: + case DSP_DISASM_V2: + { + Histable * sel_obj = data->fetch (idx)->obj; + if (sel_obj == NULL) + return NULL; + given_func = (Function*) (sel_obj)->convertto (Histable::FUNCTION, (Histable*) dbev); + if (given_func == NULL) + return NULL; + PathTree * ptree = dbev->get_path_tree (); + if (ptree == NULL) + return NULL; + instr_info = ptree->get_clr_instr (given_func); + DefaultMap<uint64_t, int> * line_seen = new DefaultMap<uint64_t, int>(); + for (int j = 0; j < ((Vector<Histable*>*)instr_info)->size (); j++) + { + Histable *instr = ((Vector<Histable*>*)instr_info)->fetch (j); + Function *cur_func = NULL; + if (instr->get_type () == Histable::INSTR) + cur_func = ((DbeInstr*) instr)->func; + else if (instr->get_type () == Histable::LINE) + cur_func = ((DbeLine*) instr)->func; + if (cur_func == NULL || (cur_func->flags & FUNC_FLAG_SIMULATED)) + continue; // skip functions like <Total> + Histable* line; + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + if (cur_func != NULL) + { + SourceFile *sourceFile = cur_func->getDefSrc (); + if (sourceFile == NULL || + (sourceFile->flags & SOURCE_FLAG_UNKNOWN) != 0) + continue; // skip functions like <Function: %s, instructions without line numbers> + } + line = instr->convertto (Histable::LINE, NULL); + break; + case DSP_DISASM: + case DSP_DISASM_V2: + line = instr->convertto (Histable::INSTR, NULL); + break; + default: + abort (); + } + uint64_t func_id = cur_func->id; + uint64_t line_id = instr->id; + int is_null = 0; + int line_no = -1; + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + is_null = (((DbeLine*) line)->func == NULL) ? 1 : 0; + if (is_null) + ((DbeLine*) line)->func = cur_func; + line_no = ((DbeLine*) line)->lineno; + if (line_seen->get (line_id) == 0) + { + line_seen->put (line_id, 1); + table0->append (func_id); + table1->append (line_no); + Histable::NameFormat nfmt = dbev->get_name_format (); + table2->append (dbe_strdup (line->get_name (nfmt))); + } + if (is_null) + ((DbeLine*) line)->func = NULL; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + is_null = (((DbeInstr*) line)->func == NULL) ? 1 : 0; + if (is_null) + ((DbeInstr*) line)->func = cur_func; + line_no = ((DbeInstr*) line)->addr; + if (line_seen->get (line_id) == 0) + { + line_seen->put (line_id, 1); + table0->append (func_id); + table1->append (line_no); + Histable::NameFormat nfmt = dbev->get_name_format (); + table2->append (dbe_strdup (line->get_name (nfmt))); + } + if (is_null) + ((DbeInstr*) line)->func = NULL; + break; + default: + abort (); + } + } + delete line_seen; + delete instr_info; + } + break; + default: + abort (); + } + table->store (0, table0); + table->store (1, table1); + table->store (2, table2); + return table; +} + +Vector<void*>* +dbeGetFuncCalleeInfo (int dbevindex, int type, Vector<int>* idxs, int groupId) +{ + Vector<void*>* data = new Vector<void*>(); + if (type == DSP_SOURCE_V2 || type == DSP_DISASM_V2) + { + Obj sel_func = dbeGetSelObj (dbevindex, DSP_FUNCTION, 0); + if (sel_func == 0) + return data; + Vector<Obj> * cmpObjs = dbeGetComparableObjsV2 (dbevindex, sel_func, type); + if (cmpObjs == NULL) + return data; + DbeView *dbev = dbeSession->getView (dbevindex); + int mtype = MET_COMMON | COMPARE_BIT | ((groupId + 1) << GROUP_ID_SHIFT); + MetricList *mlist = dbev->get_metric_list ((MetricType) (mtype & MTYPE_MASK), + (mtype & COMPARE_BIT) != 0, + mtype >> GROUP_ID_SHIFT); + Histable *selObj = (Histable *) cmpObjs->fetch (groupId); + int subtype = 0; + Hist_data *hist_data = dbev->get_data (mlist, selObj, type, subtype); + if (hist_data == NULL) + return data; + } + + for (int i = 0; i < idxs->size (); i++) + data->append (dbeGetFuncCalleeInfoById (dbevindex, type, idxs->fetch (i))); + return data; +} + +// +// Get Table of Callee info: +// param: idx -- selected AT_FUNC row +// return: callsite_row, callee_id, callee_name +// +Vector<void*>* +dbeGetFuncCalleeInfoById (int dbevindex, int type, int idx) +{ + Vector<void*>* table = new Vector<void*>(3); + Vector<int>* table0 = new Vector<int>(); + Vector<uint64_t>* table1 = new Vector<uint64_t > (); + Vector<char*>* table2 = new Vector<char*>(); + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Hist_data *data; + Function* given_func = NULL; + Vector<Histable*> *instr_info = NULL; + Vector<void*> *func_info = NULL; + + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + data = dbev->src_data; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + data = dbev->dis_data; + break; + default: + data = NULL; + abort (); + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + if (idx < 0 || idx >= data->size ()) + return NULL; + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + case DSP_DISASM: + case DSP_DISASM_V2: + { + Histable * sel_obj = data->fetch (idx)->obj; + if (sel_obj == NULL) + return NULL; + given_func = (Function*) (sel_obj)->convertto (Histable::FUNCTION, (Histable*) dbev); + if (given_func == NULL) + return NULL; + PathTree * ptree = dbev->get_path_tree (); + if (ptree == NULL) + return NULL; + Vector<Histable*> *instrs = NULL; + Vector<void*> *callee_instrs = ptree->get_cle_instr (given_func, instrs); + func_info = new Vector<void*>(); + instr_info = new Vector<Histable*>(); + for (long a = 0, sz_a = callee_instrs ? callee_instrs->size () : 0; a < sz_a; a++) + { + Vector<Histable*> *temp = ((Vector<Vector<Histable*>*>*)callee_instrs)->get (a); + DefaultMap<Function*, int> * func_seen = new DefaultMap<Function*, int>(); + Histable* instr0 = (Histable*) instrs->fetch (a); + for (long b = 0, sz_b = temp ? temp->size () : 0; b < sz_b; b++) + { + Histable *instr = temp->get (b); + if (instr->get_type () == Histable::INSTR) + { + Function* func1 = ((DbeInstr *) instr)->func; + func_seen->put (func1, 1); + } + else if (instr->get_type () == Histable::LINE) + { + Function* func1 = ((DbeLine *) instr)->func; + func_seen->put (func1, 1); + } + } + Vector<Function*> *funcs = func_seen->keySet (); + delete func_seen; + if (funcs->size () > 0) + { + instr_info->append (instr0); + func_info->append (funcs); + } + } + delete instrs; + destroy (callee_instrs); + + DefaultMap<uint64_t, Vector<int>* > * instr_idxs = new DefaultMap<uint64_t, Vector<int>* >(); + DefaultMap<uint64_t, int> * func_idxs = new DefaultMap<uint64_t, int>(); + for (long j = 0, sz_j = instr_info ? instr_info->size () : 0; j < sz_j; j++) + { + Histable *instr = instr_info->get (j); + Function *cur_func = NULL; + if (instr->get_type () == Histable::INSTR) + cur_func = ((DbeInstr*) instr)->func; + else if (instr->get_type () == Histable::LINE) + cur_func = ((DbeLine*) instr)->func; + if (cur_func != NULL && (cur_func->flags & FUNC_FLAG_SIMULATED)) + continue; // skip functions like <Total> + Histable* line; + switch (type) + { + case DSP_SOURCE: + case DSP_SOURCE_V2: + if (cur_func != NULL) + { + SourceFile *sourceFile = cur_func->getDefSrc (); + if (sourceFile == NULL || + (sourceFile->flags & SOURCE_FLAG_UNKNOWN) != 0) + // skip functions like <Function: %s, instructions without line numbers> + continue; + } + line = instr->convertto (Histable::LINE, NULL); + if (type == DSP_SOURCE_V2) + line = dbev->get_compare_obj (line); + break; + case DSP_DISASM: + case DSP_DISASM_V2: + line = instr; + if (type == DSP_DISASM_V2) + line = dbev->get_compare_obj (line); + break; + default: + abort (); + } + if (func_idxs->get (line->id) == 0) + { + func_idxs->put (line->id, 1); + Vector<int> *temp_idx = new Vector<int>(); + temp_idx->append (j); + instr_idxs->put (line->id, temp_idx); + } + else + { + Vector<int> *temp_idx = instr_idxs->get (line->id); + temp_idx->append (j); + } + } + for (long i = 0; i < data->size (); i++) + { + Histable* line = data->fetch (i)->obj; + if (line == NULL) + continue; + Vector<int> * instr_idx = instr_idxs->get (line->id); + if (instr_idx == NULL) + continue; + for (long j = 0; j < instr_idx->size (); j++) + { + Vector<void*>* callee_funcs_vec = (Vector<void*>*)func_info; + if (callee_funcs_vec->size () == 0) + continue; + Vector<Function*>* callee_funcs_value = (Vector<Function*>*)callee_funcs_vec->fetch (instr_idx->fetch (j)); + for (int k = 0; callee_funcs_value != NULL && k < callee_funcs_value->size (); k++) + { + uint64_t funcobj_id = ((Function*) callee_funcs_value->fetch (k))->id; + int old_size = table0->size (); + if (old_size > 0 && i == table0->fetch (old_size - 1) + && funcobj_id == table1->fetch (old_size - 1)) + continue; + table0->append (i); + table1->append (funcobj_id); + table2->append (dbe_strdup (((Function*) callee_funcs_value->fetch (k))->get_name ())); + } + } + } + delete instr_idxs; + delete func_idxs; + destroy (func_info); + delete instr_info; + } + break; + default: + abort (); + } + table->store (0, table0); + table->store (1, table1); + table->store (2, table2); + return table; +} + +// +// Get Table of Function List data with only total values +// +Vector<void*> * +dbeGetFuncListMini (int dbevindex, int type, int /*subtype*/) +{ + Hist_data *data; + DbeView *dbev = dbeSession->getView (dbevindex); + switch (type) + { + case DSP_FUNCTION: + data = dbev->func_data; + break; + default: + data = NULL; + break; + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + + MetricList *mlist = data->get_metric_list (); + + // Get table size: count visible metrics + int nvisible = 0; + for (long i = 0, sz = mlist->size (); i < sz; i++) + { + Metric *m = mlist->get (i); + if (m->is_visible () || m->is_tvisible () || m->is_pvisible ()) + nvisible++; + } + Vector<void*> *table = new Vector<void*>(nvisible + 1); + + // Fill function list elements + Hist_data::HistItem *totals = data->get_totals (); + for (long i = 0, sz = mlist->size (); i < sz; i++) + { + Metric *m = mlist->get (i); + if (!m->is_visible () && !m->is_tvisible () && !m->is_pvisible ()) + continue; + TValue res; + TValue *v = data->get_value (&res, i, totals); + if ((m->get_visbits () & VAL_RATIO) != 0) + { + Vector<double> *col = new Vector<double>(1); + double d = (v->tag != VT_LABEL) ? v->to_double () : 100.; // NaN + col->append (d); + table->append (col); + continue; + } + switch (m->get_vtype ()) + { + case VT_INT: + { + Vector<int> *col = new Vector<int>(1); + col->append (v->i); + table->append (col); + break; + } + case VT_ADDRESS: + case VT_ULLONG: + case VT_LLONG: + { + Vector<long long> *col = new Vector<long long>(1); + col->append (v->ll); + table->append (col); + break; + } + case VT_LABEL: + { + Vector<char *> *col = new Vector<char *>(1); + col->append (dbe_strdup (v->l)); + table->append (col); + break; + } + case VT_DOUBLE: + default: + { + Vector<double> *col = new Vector<double>(1); + col->append (v->d); + table->append (col); + break; + } + } + } + table->append (NULL); + return table; +} + +// Get Table of Function List data +Vector<void*> * +dbeGetFuncList (int dbevindex, int type, int subtype) +{ + MetricList *mlist; + Metric *mitem; + int nitems, nvisible; + int index, index2, nv; + char *cell; + Vector<int> *ji_list; + Hist_data *data; + Hist_data::HistItem *item; + + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + + // fprintf(stderr, NTXT("XXX dbeGetFuncList, FuncListDisp_type = %d\n"), type); + switch (type) + { + case DSP_FUNCTION: + data = dbev->func_data; + break; + case DSP_LINE: + data = dbev->line_data; + break; + case DSP_PC: + data = dbev->pc_data; + break; + case DSP_SOURCE: + case DSP_SOURCE_V2: + data = dbev->src_data; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + data = dbev->dis_data; + break; + case DSP_SELF: + data = dbev->fitem_data; + break; + case DSP_CALLER: + data = dbev->callers; + break; + case DSP_CALLEE: + data = dbev->callees; + break; + case DSP_DLAYOUT: + data = dbev->dlay_data; + break; + case DSP_DATAOBJ: + data = dbev->dobj_data; + break; + case DSP_MEMOBJ: + case DSP_INDXOBJ: + data = dbev->get_indxobj_data (subtype); + break; + default: + data = NULL; + break; + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + mlist = data->get_metric_list (); + + // Get table size: count visible metrics + nitems = data->size (); + nvisible = 0; + Vec_loop (Metric*, mlist->get_items (), index, mitem) + { + if (mitem->is_visible () || mitem->is_tvisible () || mitem->is_pvisible ()) + nvisible++; + } + + // Initialize Java String array + Vector<void*> *table = new Vector<void*>(nvisible + 1); + + // Mark Hi-value metric items for annotated src/dis/layout + if (type == DSP_SOURCE || type == DSP_DISASM || type == DSP_DLAYOUT + || type == DSP_SOURCE_V2 || type == DSP_DISASM_V2) + { + ji_list = new Vector<int>(nitems); + + if (dbev->marks->size () > 0) + index = dbev->marks->fetch (0); + else + index = -1; + int mindex = 0; + for (index2 = 0; index2 < nitems; index2++) + { + item = data->fetch (index2); + if (index2 == index) + { + ji_list->store (index2, -item->type); + if (++mindex < dbev->marks->size ()) + index = dbev->marks->fetch (mindex); + else + index = -1; + } + else + ji_list->store (index2, item->type); + } + table->store (nvisible, ji_list); + } + else + table->store (nvisible, NULL); + + // Fill function list elements + nv = 0; + + Vec_loop (Metric*, mlist->get_items (), index, mitem) + { + if (!mitem->is_visible () && !mitem->is_tvisible () && + !mitem->is_pvisible ()) + continue; + + // Fill values + switch (mitem->get_vtype ()) + { + case VT_LABEL: + { + Vector<char*> *jobjects = new Vector<char*>(nitems); + char *buf = NULL; + size_t bufsz = 0; + int lspace = 0; + if (type == DSP_SOURCE || type == DSP_DISASM || type == DSP_SOURCE_V2 + || type == DSP_DISASM_V2) + { + // if this is source or disassembly, where we'll insert + // a preface into the output line, figure out how wide + // it needs to be + // first, scan all the lines, to get the maximum line number + bufsz = 1024; + buf = (char *) malloc (bufsz); + int max_lineno = 0; + int hidx; + Hist_data::HistItem *hitem; + Vec_loop (Hist_data::HistItem*, data, hidx, hitem) + { + if (!hitem->obj) + continue; + if (hitem->obj->get_type () == Histable::LINE && + ((DbeLine*) hitem->obj)->lineno > max_lineno) + max_lineno = ((DbeLine*) hitem->obj)->lineno; + else if (hitem->obj->get_type () == Histable::INSTR + && ((DbeInstr*) hitem->obj)->lineno > max_lineno) + max_lineno = ((DbeInstr*) hitem->obj)->lineno; + } + + // we have the maximum integer over all linenumbers in the file + // figure out how many digits are needed + lspace = snprintf (buf, bufsz, NTXT ("%d"), max_lineno); + } + for (index2 = 0; index2 < nitems; index2++) + { + item = data->fetch (index2); + if (type == DSP_DLAYOUT) + cell = dbe_strdup (((DataObject*) (item->obj))->get_offset_name ()); + else if (type == DSP_SOURCE || type == DSP_DISASM || type == DSP_SOURCE_V2 || type == DSP_DISASM_V2) + { + // This code is duplicated in output.cc, yet it's + // intended for presentation purpose and thus is + // potentially different for er_print and analyzer. + switch (item->type) + { + case Module::AT_SRC_ONLY: + case Module::AT_SRC: + if (item->obj == NULL) + snprintf (buf, bufsz, NTXT (" %*c. "), lspace, ' '); + else + snprintf (buf, bufsz, NTXT (" %*d. "), lspace, ((DbeLine*) item->obj)->lineno); + break; + case Module::AT_FUNC: + case Module::AT_QUOTE: + snprintf (buf, bufsz, NTXT ("%*c"), lspace + 3, ' '); + break; + case Module::AT_DIS: + case Module::AT_DIS_ONLY: + if (item->obj == NULL || ((DbeInstr*) item->obj)->lineno == -1) + snprintf (buf, bufsz, NTXT ("%*c[%*s] "), lspace + 3, ' ', lspace, NTXT ("?")); + else + snprintf (buf, bufsz, NTXT ("%*c[%*d] "), lspace + 3, ' ', lspace, + ((DbeInstr*) item->obj)->lineno); + break; + case Module::AT_COM: + case Module::AT_EMPTY: + *buf = (char) 0; + break; + } + // get the line's text + char *s = item->value[index].l; + if (s != NULL) + { + // copy the string expanding all tabulations + // (JTable doesn't render them) + char *d = buf + strlen (buf); + char c; + size_t column = 0; + do + { + c = *s++; + if (c == '\t') + { + do + { + *d++ = ' '; + column++; + } + while (column & 07); + } + else + { + *d++ = c; + column++; + } + if (column + 32 > bufsz) + { + // Reallocate the buffer + size_t curlen = d - buf; + bufsz += 1024; + char *buf_new = (char *) malloc (bufsz); + strncpy (buf_new, buf, curlen); + buf_new[curlen] = '\0'; + free (buf); + buf = buf_new; + d = buf + curlen; + } + } + while (c != (char) 0); + } + cell = dbe_strdup (buf); + free (item->value[index].l); + item->value[index].l = NULL; //YXXX missing from dbeGetFuncListV2 + } + else + { + // omazur: why don't we have it as metric value + Histable::NameFormat nfmt = dbev->get_name_format (); + cell = dbe_strdup (item->obj->get_name (nfmt)); + } + jobjects->store (index2, cell); + } + if (type == DSP_SOURCE || type == DSP_DISASM || type == DSP_SOURCE_V2 + || type == DSP_DISASM_V2) + free (buf); + table->store (nv++, jobjects); + break; + } + default: + table->store (nv++, dbeGetTableDataOneColumn (data, index)); + break; + } + } + return table; +} + +Vector<Obj> * +dbeGetComparableObjsV2 (int /* dbevindex */, Obj sel_obj, int type) +{ + long grsize = dbeSession->expGroups->size (); + Vector<Obj> *res = new Vector<Obj> (grsize + 1); + for (long j = 0; j < grsize; j++) + res->append ((Obj) NULL); + res->append (sel_obj); + Histable *obj = (Histable *) sel_obj; + if (obj == NULL) + return res; + Function *func = (Function *) obj->convertto (Histable::FUNCTION); + if (func == NULL) + return res; + Vector<Histable *> *cmpObjs = func->get_comparable_objs (); + if (cmpObjs == NULL || cmpObjs->size () != grsize) + return res; + + Histable::Type conv_type = (type == DSP_SOURCE || type == DSP_SOURCE_V2) ? + Histable::LINE : Histable::INSTR; + switch (obj->get_type ()) + { + case Histable::FUNCTION: + for (long j = 0; j < grsize; j++) + res->store (j, (Obj) cmpObjs->get (j)); + return res; + case Histable::INSTR: + case Histable::LINE: + { + SourceFile *srcContext = (SourceFile *) obj->convertto (Histable::SOURCEFILE); + char *bname = get_basename (srcContext->get_name ()); + for (long j = 0; j < grsize; j++) + { + Function *func1 = (Function *) cmpObjs->get (j); + if (func == func1) + { + if (conv_type == Histable::LINE) + res->store (j, (Obj) obj); + else + res->store (j, (Obj) obj->convertto (conv_type, srcContext)); + continue; + } + if (func1 == NULL) + continue; + Vector<SourceFile*> *sources = func1->get_sources (); + SourceFile *sf = NULL; + for (long j1 = 0, sz1 = sources ? sources->size () : 0; j1 < sz1; j1++) + { + SourceFile *sf1 = sources->get (j1); + if (sf1 == srcContext) + { // the same file + sf = srcContext; + break; + } + else if (sf == NULL) + { + char *bname1 = get_basename (sf1->get_name ()); + if (dbe_strcmp (bname, bname1) == 0) + sf = sf1; + } + } + res->store (j, (Obj) func1->convertto (conv_type, srcContext)); + } + break; + } + default: + break; + } + return res; +} + +// Get Table of Function List data +Vector<void *> * +dbeGetFuncListV2 (int dbevindex, int mtype, Obj sel_obj, int type, int subtype) +{ + Metric *mitem; + int nitems, nvisible; + int index, index2, nv; + char *cell; + Hist_data::HistItem *item; + DbeView *dbev = dbeSession->getView (dbevindex); + dbev->error_msg = dbev->warning_msg = NULL; + MetricList *mlist = dbev->get_metric_list ((MetricType) (mtype & MTYPE_MASK), + (mtype & COMPARE_BIT) != 0, + mtype >> GROUP_ID_SHIFT); + Histable *selObj = (Histable *) sel_obj; + int old_compare_mode = dbev->get_compare_mode (); + if ((mtype & COMPARE_BIT) != 0) + dbev->reset_compare_mode (CMP_DISABLE); + Hist_data *data = dbev->get_data (mlist, selObj, type, subtype); + dbev->reset_compare_mode (old_compare_mode); + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + nitems = data->size (); + nvisible = mlist->get_items ()->size (); + + // Initialize Java String array + Vector<void*> *table = new Vector<void*>(nvisible + 3); + // Mark Hi-value metric items for annotated src/dis/layout + if (type == DSP_SOURCE || type == DSP_DISASM || type == DSP_DLAYOUT + || type == DSP_SOURCE_V2 || type == DSP_DISASM_V2) + { + Vector<int> *types = new Vector<int>(nitems); + Vector<Obj> *ids = new Vector<Obj > (nitems); + if (dbev->marks->size () > 0) + index = dbev->marks->fetch (0); + else + index = -1; + int mindex = 0; + for (int i = 0; i < nitems; i++) + { + item = data->fetch (i); + ids->store (i, (Obj) item->obj); + if (i == index) + { + types->store (i, -item->type); + if (++mindex < dbev->marks->size ()) + index = dbev->marks->fetch (mindex); + else + index = -1; + } + else + types->store (i, item->type); + } + table->store (nvisible, types); + table->store (nvisible + 1, ids); + } + else + { + table->store (nvisible, NULL); + table->store (nvisible + 1, NULL); + } + + // Fill function list elements + nv = 0; + Vec_loop (Metric*, mlist->get_items (), index, mitem) + { + if (!mitem->is_visible () && !mitem->is_tvisible () && + !mitem->is_pvisible ()) + continue; + + // Fill values + switch (mitem->get_vtype ()) + { + default: + table->store (nv++, dbeGetTableDataOneColumn (data, index)); + break; + case VT_LABEL: + Vector<char*> *jobjects = new Vector<char*>(nitems); + char *buf = NULL; + size_t bufsz = 0; + int lspace = 0; + if (type == DSP_SOURCE || type == DSP_DISASM || type == DSP_SOURCE_V2 + || type == DSP_DISASM_V2) + { + // if this is source or disassembly, where we'll insert + // a preface into the output line, figure out how wide + // it needs to be + // first, scan all the lines, to get the maximum line number + bufsz = 1024; + buf = (char *) malloc (bufsz); + int max_lineno = 0; + int hidx; + Hist_data::HistItem *hitem; + Vec_loop (Hist_data::HistItem*, data, hidx, hitem) + { + if (!hitem->obj) + continue; + if (hitem->obj->get_type () == Histable::LINE && + ((DbeLine*) hitem->obj)->lineno > max_lineno) + max_lineno = ((DbeLine*) hitem->obj)->lineno; + else if (hitem->obj->get_type () == Histable::INSTR + && ((DbeInstr*) hitem->obj)->lineno > max_lineno) + max_lineno = ((DbeInstr*) hitem->obj)->lineno; + } + + // we have the maximum integer over all linenumbers in the file + // figure out how many digits are needed + lspace = snprintf (buf, bufsz, NTXT ("%d"), max_lineno); + } + + for (index2 = 0; index2 < nitems; index2++) + { + item = data->fetch (index2); + if (type == DSP_DLAYOUT) + cell = dbe_strdup (((DataObject*) (item->obj))->get_offset_name ()); + else if (type == DSP_SOURCE || type == DSP_DISASM || type == DSP_SOURCE_V2 || type == DSP_DISASM_V2) + { + // This code is duplicated in output.cc, yet it's + // intended for presentation purpose and thus is + // potentially different for er_print and analyzer. + switch (item->type) + { + case Module::AT_SRC_ONLY: + case Module::AT_SRC: + if (item->obj == NULL) + snprintf (buf, bufsz, NTXT (" %*c. "), lspace, ' '); + else + snprintf (buf, bufsz, NTXT (" %*d. "), lspace, + ((DbeLine*) item->obj)->lineno); + break; + case Module::AT_FUNC: + case Module::AT_QUOTE: + snprintf (buf, bufsz, NTXT ("%*c"), lspace + 3, ' '); + break; + case Module::AT_DIS: + case Module::AT_DIS_ONLY: + if (item->obj == NULL || ((DbeInstr*) item->obj)->lineno == -1) + snprintf (buf, bufsz, NTXT ("%*c[%*s] "), lspace + 3, ' ', + lspace, NTXT ("?")); + else + snprintf (buf, bufsz, NTXT ("%*c[%*d] "), lspace + 3, ' ', + lspace, + ((DbeInstr*) item->obj)->lineno); + break; + case Module::AT_COM: + case Module::AT_EMPTY: + *buf = (char) 0; + break; + } + // get the line's text + char *s = item->value[index].l; + if (s != NULL) + { + // copy the string expanding all tabulations + // (JTable doesn't render them) + char *d = buf + strlen (buf); + char c; + size_t column = 0; + do + { + c = *s++; + if (c == '\t') + { + do + { + *d++ = ' '; + column++; + } + while (column & 07); + } + else + { + *d++ = c; + column++; + } + if (column + 32 > bufsz) + { + // Reallocate the buffer + size_t curlen = d - buf; + bufsz += 1024; + char *buf_new = (char *) malloc (bufsz); + strncpy (buf_new, buf, curlen); + buf_new[curlen] = '\0'; + free (buf); + buf = buf_new; + d = buf + curlen; + } + } + while (c != (char) 0); + } + cell = dbe_strdup (buf); + } + else + { + Histable::NameFormat nfmt = dbev->get_name_format (); + cell = dbe_strdup (item->obj->get_name (nfmt)); + } + jobjects->store (index2, cell); + } + + if (type == DSP_SOURCE || type == DSP_DISASM || type == DSP_SOURCE_V2 + || type == DSP_DISASM_V2) + free (buf); + table->store (nv++, jobjects); + break; + } + } + table->append (dbeGetMetricList (mlist)); + return table; +} // dbeGetFuncListV2 + +// +// Get Table DataV2 +// +Vector<void*> * +dbeGetTableDataV2 (int dbevindex, char *mlistStr, char *modeStr, char *typeStr, + char *subtypeStr, Vector<uint64_t> *ids) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + + // Process metric list specification + if (mlistStr == NULL) + return NULL; + bool met_call = false; + MetricList *mlist = NULL; + if (streq (mlistStr, NTXT ("MET_NORMAL"))) + mlist = dbev->get_metric_list (MET_NORMAL); + else if (streq (mlistStr, NTXT ("MET_CALL"))) + { + met_call = true; + mlist = dbev->get_metric_list (MET_CALL); + } + else if (streq (mlistStr, NTXT ("MET_CALL_AGR"))) + mlist = dbev->get_metric_list (MET_CALL_AGR); + else if (streq (mlistStr, NTXT ("MET_DATA"))) + mlist = dbev->get_metric_list (MET_DATA); + else if (streq (mlistStr, NTXT ("MET_INDX"))) + mlist = dbev->get_metric_list (MET_INDX); + else if (streq (mlistStr, NTXT ("MET_IO"))) + mlist = dbev->get_metric_list (MET_IO); + else if (streq (mlistStr, NTXT ("MET_HEAP"))) + mlist = dbev->get_metric_list (MET_HEAP); + else + return NULL; + + // Process mode specification + if (modeStr == NULL) + return NULL; + Hist_data::Mode mode = (Hist_data::Mode)0; + if (streq (modeStr, NTXT ("CALLERS"))) + mode = Hist_data::CALLERS; + else if (streq (modeStr, NTXT ("CALLEES"))) + mode = Hist_data::CALLEES; + else if (streq (modeStr, NTXT ("SELF"))) + mode = Hist_data::SELF; + else if (streq (modeStr, NTXT ("ALL"))) + mode = Hist_data::ALL; + else + return NULL; + + // Process type specification + if (typeStr == NULL) + return NULL; + Histable::Type type = Histable::OTHER; + if (streq (typeStr, NTXT ("FUNCTION"))) + type = Histable::FUNCTION; + else if (streq (typeStr, NTXT ("INDEXOBJ"))) + type = Histable::INDEXOBJ; + else if (streq (typeStr, NTXT ("IOACTFILE"))) + type = Histable::IOACTFILE; + else if (streq (typeStr, NTXT ("IOACTVFD"))) + type = Histable::IOACTVFD; + else if (streq (typeStr, NTXT ("IOCALLSTACK"))) + type = Histable::IOCALLSTACK; + else if (streq (typeStr, NTXT ("HEAPCALLSTACK"))) + type = Histable::HEAPCALLSTACK; + else if (streq (typeStr, NTXT ("LINE"))) + type = Histable::LINE; + else if (streq (typeStr, NTXT ("INSTR"))) + type = Histable::INSTR; + else + // XXX Accepting objects other than above may require a different + // implementation of the id -> Histable mapping below + return NULL; + + // Process subtype specification + int subtype = 0; + if (subtypeStr != NULL) + subtype = atoi (subtypeStr); + Vector<Histable*> *hobjs = NULL; + if (ids != NULL) + { + hobjs = new Vector<Histable*>(); + for (int i = 0; i < ids->size (); ++i) + { + Histable::Type obj_type = type; + if ((obj_type == Histable::LINE || obj_type == Histable::INSTR) + && subtype == 0) + obj_type = Histable::FUNCTION; + Histable *hobj = dbeSession->findObjectById (obj_type, subtype, ids->fetch (i)); + if ((obj_type == Histable::LINE || obj_type == Histable::INSTR) + && subtype == 0 && hobj == NULL) + return NULL; + hobjs->append (hobj); + } + } + + PathTree::PtreeComputeOption flag = PathTree::COMPUTEOPT_NONE; + if (dbev->isOmpDisMode () && type == Histable::FUNCTION + && mode == Hist_data::CALLEES && met_call) + flag = PathTree::COMPUTEOPT_OMP_CALLEE; + + Hist_data *data = dbev->get_hist_data (mlist, type, subtype, mode, hobjs, NULL, NULL, flag); + return dbeGetTableDataV2Data (dbev, data); +} + +static Vector<void*> * +dbeGetTableDataV2Data (DbeView * /*dbev*/, Hist_data *data) +{ + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + MetricList *mlist; + mlist = data->get_metric_list (); + int nitems = data->size (); + + // Initialize Java String array + Vector<void*> *table = new Vector<void*>(mlist->size () + 1); + + // Fill function list elements + for (long i = 0, sz = mlist->size (); i < sz; i++) + { + Metric *mitem = mlist->get (i); + if (!mitem->is_visible () && !mitem->is_tvisible () && + !mitem->is_pvisible ()) + continue; + table->append (dbeGetTableDataOneColumn (data, i)); + } + + // Add an array of Histable IDs + Vector<uint64_t> *idList = new Vector<uint64_t>(nitems); + for (int i = 0; i < nitems; ++i) + { + Hist_data::HistItem *item = data->fetch (i); + if (item->obj->get_type () == Histable::LINE + || item->obj->get_type () == Histable::INSTR) + idList->store (i, (uint64_t) (item->obj)); + else + idList->store (i, item->obj->id); + } + table->append (idList); + return table; +} // dbeGetTableData + +//YXXX try to use the following to consolidate similar cut/paste code + +static Vector<void*> * +dbeGetTableDataOneColumn (Hist_data *data, int met_ind) +{ + // Allocates a vector and fills it with metric values for one column + TValue res; + Metric *m = data->get_metric_list ()->get (met_ind); + if ((m->get_visbits () & VAL_RATIO) != 0) + { + Vector<double> *col = new Vector<double>(data->size ()); + for (long row = 0, sz_row = data->size (); row < sz_row; row++) + { + TValue *v = data->get_value (&res, met_ind, row); + double d = (v->tag != VT_LABEL) ? v->to_double () : 100.; // NaN + col->append (d); + } + return (Vector<void*> *) col; + } + + switch (m->get_vtype ()) + { + case VT_DOUBLE: + { + Vector<double> *col = new Vector<double>(data->size ()); + for (long row = 0, sz_row = data->size (); row < sz_row; row++) + { + TValue *v = data->get_value (&res, met_ind, row); + col->append (v->d); + } + return (Vector<void*> *) col; + } + case VT_INT: + { + Vector<int> *col = new Vector<int>(data->size ()); + for (long row = 0, sz_row = data->size (); row < sz_row; row++) + { + TValue *v = data->get_value (&res, met_ind, row); + col->append (v->i); + } + return (Vector<void*> *) col; + } + case VT_ULLONG: + case VT_LLONG: + { + Vector<long long> *col = new Vector<long long>(data->size ()); + for (long row = 0, sz_row = data->size (); row < sz_row; row++) + { + TValue *v = data->get_value (&res, met_ind, row); + col->append (v->ll); + } + return (Vector<void*> *) col; + } + case VT_ADDRESS: + { + Vector<long long> *col = new Vector<long long>(data->size ()); + for (long row = 0, sz_row = data->size (); row < sz_row; row++) + { + TValue *v = data->get_value (&res, met_ind, row); + // set the highest bit to mark this jlong as + // a VT_ADDRESS (rather than a regular VT_LLONG) + col->append (v->ll | 0x8000000000000000ULL); + } + return (Vector<void*> *) col; + } + case VT_LABEL: + { + Vector<char *> *col = new Vector<char *>(data->size ()); + for (long row = 0, sz_row = data->size (); row < sz_row; row++) + { + TValue *v = data->get_value (&res, met_ind, row); + col->append (dbe_strdup (v->l)); + } + return (Vector<void*> *) col; + } + default: + return NULL; + } +} + +static Vector<void*> * +dbeGetTableDataOneColumn (DbeView *dbev, Vector<Hist_data::HistItem*> *data, + ValueTag vtype, int metricColumnNumber) +// Allocates a vector and fills it with metric values for one column +{ + Vector<void*> *column_data = NULL; + int nitems = data->size (); // number of rows + int index = metricColumnNumber; + switch (vtype) + { + case VT_DOUBLE: + { + Vector<double> *jd_list = new Vector<double>(nitems); + for (int index2 = 0; index2 < nitems; index2++) + { + Hist_data::HistItem *item = data->fetch (index2); + jd_list->store (index2, item->value[index].d); + } + column_data = (Vector<void*> *)jd_list; + break; + } + case VT_INT: + { + Vector<int> *ji_list = new Vector<int>(nitems); + for (int index2 = 0; index2 < nitems; index2++) + { + Hist_data::HistItem *item = data->fetch (index2); + ji_list->store (index2, item->value[index].i); + } + column_data = (Vector<void*> *)ji_list; + break; + } + case VT_ULLONG: + case VT_LLONG: + { + Vector<long long> *jl_list = new Vector<long long>(nitems); + for (int index2 = 0; index2 < nitems; index2++) + { + Hist_data::HistItem *item = data->fetch (index2); + jl_list->store (index2, item->value[index].ll); + } + column_data = (Vector<void*> *)jl_list; + break; + } + case VT_ADDRESS: + { + Vector<long long> *jl_list = new Vector<long long>(nitems); + for (int index2 = 0; index2 < nitems; index2++) + { + Hist_data::HistItem *item = data->fetch (index2); + + // set the highest bit to mark this jlong as + // a VT_ADDRESS (rather than a regular VT_LLONG) + uint64_t addr = item->value[index].ll; + addr |= 0x8000000000000000ULL; + jl_list->store (index2, addr); + } + column_data = (Vector<void*> *)jl_list; + break; + } + case VT_LABEL: + { + Vector<char*> *jobjects = new Vector<char*>(nitems); + for (int index2 = 0; index2 < nitems; index2++) + { + Hist_data::HistItem *item = data->fetch (index2); + + // omazur: why don't we have it as metric value + Histable::NameFormat nfmt = dbev->get_name_format (); + char *str = dbe_strdup (item->obj->get_name (nfmt)); + jobjects->store (index2, str); + } + column_data = (Vector<void*> *)jobjects; + break; + } + default: + abort (); + } + return column_data; +} + +int +dbeGetCallTreeNumLevels (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + PathTree * ptree = dbev->get_path_tree (); + if (ptree == NULL) + return 0; + return ptree->get_ftree_depth (); +} + +Vector<void*>* +dbeGetCallTreeLevel (int dbevindex, char *mcmd, int level) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + PathTree * ptree = dbev->get_path_tree (); + if (ptree == NULL) + return NULL; + if (mcmd == NULL) + return NULL; + BaseMetric *bm = dbeSession->find_base_reg_metric (mcmd); + if (bm == NULL) + return NULL; + return ptree->get_ftree_level (bm, level); +} + +Vector<void*>* +dbeGetCallTreeLevels (int dbevindex, char *mcmd) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + PathTree * ptree = dbev->get_path_tree (); + if (ptree == NULL) + return NULL; + if (mcmd == NULL) + return NULL; + BaseMetric *bm = dbeSession->find_base_reg_metric (mcmd); + if (bm == NULL) + return NULL; + + int depth = ptree->get_ftree_depth (); + Vector<void*> *results = new Vector<void*>(depth); + for (int ii = 0; ii < depth; ii++) + results->append (ptree->get_ftree_level (bm, ii)); + return results; +} + +Vector<void*>* +dbeGetCallTreeLevelFuncs (int dbevindex, int start_level, int end_level) +{ // (0,-1) -> all levels + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + PathTree * ptree = dbev->get_path_tree (); + if (ptree == NULL) + return NULL; + + int depth = ptree->get_ftree_depth (); + if (start_level < 0) + start_level = 0; + if (end_level < 0 || end_level >= depth) + end_level = depth - 1; + + Histable::NameFormat nfmt = dbev->get_name_format (); //YXXX or fixed format? + Vector<char*> *funcNames = new Vector<char*>(); + Vector<long long> *funcIds = new Vector<long long>(); + Vector<Obj> *funcObjs = new Vector<Obj>(); + + if (start_level == 0 && end_level == depth - 1) + return dbeGetCallTreeFuncs (dbevindex); + else + { + for (int ii = start_level; ii <= end_level; ii++) + { + Vector<void*> *info = ptree->get_ftree_level (NULL, ii); /*no metric*/ + if (!info) + continue; + Vector<long long> *fids = (Vector<long long> *)info->get (2); + if (!fids) + continue; + int index; + long long fid; + Vec_loop (long long, fids, index, fid) + { + funcIds->append (fid); + Histable *obj = dbeSession->findObjectById (fid); + char * fname = obj ? dbe_strdup (obj->get_name (nfmt)) : NULL; + funcNames->append (fname); + funcObjs->append ((unsigned long) obj); // avoiding sign extension + } + destroy (info); + } + } + Vector<void*> *results = new Vector<void*>(3); + results->append (funcIds); + results->append (funcNames); + results->append (funcObjs); + return results; +} + +Vector<void*> * +dbeGetCallTreeFuncs (int dbevindex) +{ // does not require ptree->get_ftree_level() to be computed + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + PathTree * ptree = dbev->get_path_tree (); + if (ptree == NULL) + return 0; + Vector<Function*>* funcs = ptree->get_funcs (); // Unique functions in tree + if (funcs == NULL) + return NULL; + + long sz = funcs->size (); + Vector<void*> *results = new Vector<void*>(3); + Vector<long long> *funcIds = new Vector<long long>(sz); + Vector<char*> *funcNames = new Vector<char*>(sz); + Vector<Obj> *funcObjs = new Vector<Obj>(sz); + + int index; + Function * func; + Histable::NameFormat nfmt = dbev->get_name_format (); //YXXX or fixed format? + Vec_loop (Function *, funcs, index, func) + { + funcIds->append (func->id); // do we need IDs? + char *fname = dbe_strdup (func->get_name (nfmt)); + funcNames->append (fname); + funcObjs->append ((unsigned long) func); // avoiding sign extension + } + results->put (0, funcIds); + results->put (1, funcNames); + results->put (2, funcObjs); + destroy (funcs); + return results; +} + +Vector<void*>* +dbeGetCallTreeChildren (int dbevindex, char *mcmd, Vector<int /*NodeIdx*/>*node_idxs) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + if (node_idxs == NULL || node_idxs->size () == 0) + return NULL; + long sz = node_idxs->size (); + PathTree * ptree = dbev->get_path_tree (); + if (ptree == NULL) + return NULL; + if (mcmd == NULL) + return NULL; + BaseMetric *bm = dbeSession->find_base_reg_metric (mcmd); + if (bm == NULL) + return NULL; + + Vector<void*> *results = new Vector<void*>(sz); + for (long ii = 0; ii < sz; ii++) + { + PathTree::NodeIdx nodeIdx = node_idxs->get (ii); // upcasted from int + results->append (ptree->get_ftree_node_children (bm, nodeIdx)); + } + return results; +} + +Vector<int> * +dbeGetGroupIds (int /*dbevindex*/) +{ + Vector<ExpGroup*> *groups = dbeSession->expGroups; + int sz = groups->size (); + Vector<int> *grIds = new Vector<int>(sz); + for (int i = 0; i < sz; i++) + grIds->store (i, groups->fetch (i)->groupId); + return grIds; +} + +// +// Get label for name column +// +Vector<char*> * +dbeGetNames (int dbevindex, int type, Obj sel_obj) +{ + char *s0, *s1, *s2; + bool need_strdup = true; + switch (type) + { + case DSP_SOURCE_V2: + case DSP_DISASM_V2: + case DSP_SOURCE: + case DSP_DISASM: + { + if (sel_obj) + { + Histable *selObj = (Histable*) sel_obj; + Function *func = (Function *) selObj->convertto (Histable::FUNCTION); + if (func) + { + char *names[3] = {NULL, NULL, NULL}; + set_file_names (func, names); + s0 = names[0]; + s1 = names[1]; + s2 = names[2]; + need_strdup = false; + break; + } + } + DbeView *dbev = dbeSession->getView (dbevindex); + char **names = type == DSP_SOURCE || type == DSP_SOURCE_V2 ? dbev->names_src : dbev->names_dis; + s0 = names[0]; + s1 = names[1]; + s2 = names[2]; + break; + } + case DSP_LINE: + s0 = GTXT ("Lines"); + s1 = GTXT ("Function, line # in \"sourcefile\""); + s2 = NTXT (""); + break; + case DSP_PC: + s0 = GTXT ("PCs"); + s1 = GTXT ("Function + offset"); + s2 = NTXT (""); + break; + case DSP_DLAYOUT: + s0 = GTXT ("Name"); + s1 = GTXT ("* +offset .element"); + s2 = NTXT (""); + break; + default: + s0 = GTXT ("Name"); + s1 = s2 = NTXT (""); + break; + } + if (need_strdup) + { + s0 = dbe_strdup (s0); + s1 = dbe_strdup (s1); + s2 = dbe_strdup (s2); + } + Vector<char*> *table = new Vector<char*>(3); + table->store (0, s0); + table->store (1, s1); + table->store (2, s2); + return table; +} + +// +// Get Total/Maximum element of Function List +// +Vector<void*> * +dbeGetTotalMax (int dbevindex, int type, int subtype) +{ + Hist_data *data; + int index; + Hist_data::HistItem *total_item, *maximum_item; + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + + switch (type) + { + case DSP_LINE: + data = dbev->line_data; + break; + case DSP_PC: + data = dbev->pc_data; + break; + case DSP_CALLER: + data = dbev->callers; + break; + case DSP_SELF: + case DSP_CALLEE: + data = dbev->callees; + break; + case DSP_DLAYOUT: + data = dbev->dlay_data; + break; + case DSP_DATAOBJ: + data = dbev->dobj_data; + break; + case DSP_MEMOBJ: + data = dbev->get_indxobj_data (subtype); + break; + case DSP_INDXOBJ: + data = dbev->get_indxobj_data (subtype); + break; + case DSP_FUNCTION: // annotated src/dis use func total/max + case DSP_SOURCE: + case DSP_DISASM: + case DSP_SOURCE_V2: + case DSP_DISASM_V2: + data = dbev->func_data; + break; + default: + abort (); + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + + // Get list size + // XXX -- the original list has all items, visible or not; + // XXX -- the one from Hist_data has only visible items, + // XXX -- and should be the only ones computed + // XXX -- Analyzer got confused (yesterday), when we used the shorter list + // XXX -- Why can we fetch total/max for metrics never + // XXX -- computed without core dumping? + MetricList *mlist2 = data->get_metric_list (); + int size = mlist2->get_items ()->size (); + + // Initialize Java array + Vector<void*> *total_max = new Vector<void*>(2); + Vector<double> *total = new Vector<double>(size); + Vector<double> *maximum = new Vector<double>(size); + + // Fill total/maximum element + total_item = data->get_totals (); + maximum_item = data->get_maximums (); + + for (index = 0; index < size; index++) + { + total->store (index, total_item->value[index].to_double ()); + maximum->store (index, maximum_item->value[index].to_double ()); + } + total_max->store (0, total); + total_max->store (1, maximum); + return total_max; +} + +// +// Get Table of Overview List +Vector<void*> * +dbeGetStatisOverviewList (int dbevindex) +{ + int size; + Ovw_data **data; + Ovw_data::Ovw_item labels, *totals; + int nitems; + int index, index2; + + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->error_msg = dbev->warning_msg = NULL; + + size = dbeSession->nexps (); + totals = new Ovw_data::Ovw_item[size + 1]; + data = new Ovw_data*[size + 1]; + data[0] = new Ovw_data (); + + for (index = 1; index <= size; index++) + { + data[index] = dbev->get_ovw_data (index - 1); + if (data[index] == NULL) + { + Ovw_data::reset_item (&totals[index]); // set contents to zeros + continue; + } + data[0]->sum (data[index]); + totals[index] = data[index]->get_totals (); //shallow copy! + } + totals[0] = data[0]->get_totals (); + + // Get table size + labels = data[0]->get_labels (); + nitems = labels.size + 4; + + // Initialize Java String array + Vector<void*> *table = new Vector<void*>(size + 4); + Vector<char*> *jobjects = new Vector<char*>(nitems); + + // Set the label + jobjects->store (0, dbe_strdup (GTXT ("Start Time (sec.)"))); + jobjects->store (1, dbe_strdup (GTXT ("End Time (sec.)"))); + jobjects->store (2, dbe_strdup (GTXT ("Duration (sec.)"))); + jobjects->store (3, dbe_strdup (GTXT ("Total Thread Time (sec.)"))); + jobjects->store (4, dbe_strdup (GTXT ("Average number of Threads"))); + + for (index2 = 5; index2 < nitems; index2++) + jobjects->store (index2, dbe_strdup (labels.values[index2 - 4].l)); + table->store (0, jobjects); + + // Set the data + for (index = 0; index <= size; index++) + { + Vector<double> *jd_list = new Vector<double>(nitems); + jd_list->store (0, tstodouble (totals[index].start)); + jd_list->store (1, tstodouble (totals[index].end)); + jd_list->store (2, tstodouble (totals[index].duration)); + jd_list->store (3, tstodouble (totals[index].tlwp)); + jd_list->store (4, totals[index].nlwp); + for (index2 = 5; index2 < nitems; index2++) + jd_list->store (index2, tstodouble (totals[index].values[index2 - 4].t)); + table->store (index + 1, jd_list); + } + for (index = 0; index <= size; index++) + delete data[index]; + delete[] data; + delete[] totals; + return table; +} + +// Get Table of Statistics List +Vector<void*> * +dbeGetStatisList (int dbevindex) +{ + int size; + Stats_data **data; + int nitems; + int index, index2; + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + dbev->error_msg = dbev->warning_msg = NULL; + if ((size = dbeSession->nexps ()) == 0) + return NULL; + + // Get statistics data + data = (Stats_data **) malloc ((size + 1) * sizeof (Stats_data *)); + data[0] = new Stats_data (); + for (index = 1; index <= size; index++) + { + data[index] = dbev->get_stats_data (index - 1); + if (data[index] == NULL) + continue; + data[0]->sum (data[index]); + } + + // Get table size + nitems = data[0]->size (); + + // Initialize Java String array + Vector<void*> *table = new Vector<void*>(size + 2); + Vector<char*> *jobjects = new Vector<char*>(nitems); + + // Set the label + for (index2 = 0; index2 < nitems; index2++) + jobjects->store (index2, dbe_strdup (data[0]->fetch (index2).label)); + table->store (0, jobjects); + + // Set the data + for (index = 0; index <= size; index++) + { + Vector<double> *jd_list = new Vector<double>(nitems); + for (index2 = 0; index2 < nitems; index2++) + { + double val = 0; + if (data[index]) + val = data[index]->fetch (index2).value.to_double (); + jd_list->store (index2, val); + } + table->store (index + 1, jd_list); + } + if (data) + { + for (index = 0; index <= size; index++) + delete data[index]; + free (data); + } + return table; +} + + +// +// Set summary list +// +static void +setSummary (Vector<Histable*> *objs, Vector<int> *saligns, + Vector<char> *mnemonic, Vector<char*> *jlabels, Vector<char*> *jvalues) +{ + char *sname = NULL, *oname = NULL, *lname = NULL, *alias = NULL, + *mangle = NULL, *address = NULL, *size = NULL, + *name_0 = NULL, *sname_0 = NULL, *oname_0 = NULL, *lname_0 = NULL, + *alias_0 = NULL, *mangle_0 = NULL; + Function *func, *last_func = NULL; + int one_func = 1; + + // Get the source/object/load-object files & aliases + long long ll_size = 0; + for (long i = 0; i < objs->size (); i++) + { + Histable *current_obj = objs->fetch (i); + Histable::Type htype = current_obj->get_type (); + if (htype == Histable::LOADOBJECT) + lname = ((LoadObject *) current_obj)->dbeFile->get_location_info (); + else if ((func = (Function*) current_obj->convertto (Histable::FUNCTION)) != NULL) + { + if (one_func && last_func != NULL && last_func != func) + one_func = 0; + last_func = func; + sname = NULL; + DbeLine *dbeline = (DbeLine*) current_obj->convertto (Histable::LINE); + if (dbeline) + { + SourceFile *sf; + if (dbeline->lineno == 0 && dbeline->include != NULL) + sf = dbeline->include; + else if (dbeline->sourceFile != NULL) + sf = dbeline->sourceFile; + else + sf = func->getDefSrc (); + if (sf) + sname = sf->dbeFile->get_location_info (); + } + char *func_name = func->get_name (); + mangle = func->get_mangled_name (); + if (mangle && streq (func_name, mangle)) + mangle = NULL; + Module *module = func->module; + if (module != NULL) + { + module->read_stabs (); + if (sname == NULL || strlen (sname) == 0) + { + SourceFile *sf = module->getMainSrc (); + sname = sf->dbeFile->get_location_info (); + } + DbeFile *df = module->dbeFile; + if (df == NULL || (df->filetype & DbeFile::F_JAVACLASS) == 0) + df = module->loadobject->dbeFile; + lname = df->get_location_info (); + oname = lname; + if (module->dot_o_file) + oname = module->dot_o_file->dbeFile->get_location_info (); + } + + if (htype == Histable::INSTR && dbeSession->is_datamode_available ()) + alias = ((DbeInstr*) current_obj)->get_descriptor (); + } + + char *name = current_obj->get_name (); + if (i == 0) + { + name_0 = name; + lname_0 = lname; + sname_0 = sname; + oname_0 = oname; + mangle_0 = mangle; + alias_0 = alias; + if (objs->size () == 1) + { + uint64_t addr = current_obj->get_addr (); + address = dbe_sprintf (NTXT ("%lld:0x%08llX"), + (long long) ADDRESS_SEG (addr), + (long long) ADDRESS_OFF (addr)); + } + } + else + { + if (name_0 != name) + name_0 = NULL; + if (lname_0 != lname) + lname_0 = NULL; + if (sname_0 != sname) + sname_0 = NULL; + if (oname_0 != oname) + oname_0 = NULL; + if (mangle_0 != mangle) + mangle_0 = NULL; + if (alias_0 != alias) + alias_0 = NULL; + } + if (current_obj->get_size () == -1) + { + if (size == NULL) + size = dbe_strdup (GTXT ("(Unknown)")); + } + else + ll_size += current_obj->get_size (); + } + if (size == NULL) + size = dbe_sprintf (NTXT ("%lld"), ll_size); + if (name_0 == NULL) + { + if (objs->size () > 1) + { + char *func_name = last_func == NULL ? NULL : + (one_func == 0 ? NULL : last_func->get_name ()); + name_0 = dbe_sprintf (NTXT ("%s%s%s (%lld %s)"), + func_name == NULL ? "" : func_name, + func_name == NULL ? "" : ": ", + GTXT ("Multiple Selection"), + (long long) objs->size (), + GTXT ("objects")); + } + } + else + name_0 = dbe_strdup (name_0); + + // Set the name area + saligns->store (0, TEXT_LEFT); + mnemonic->store (0, 'N'); + jlabels->store (0, dbe_strdup (GTXT ("Name"))); + jvalues->store (0, name_0); + + saligns->store (1, TEXT_LEFT); + mnemonic->store (1, 'P'); + jlabels->store (1, dbe_strdup (GTXT ("PC Address"))); + jvalues->store (1, address); + + saligns->store (2, TEXT_LEFT); + mnemonic->store (2, 'z'); + jlabels->store (2, dbe_strdup (GTXT ("Size"))); + jvalues->store (2, size); + + saligns->store (3, TEXT_RIGHT); + mnemonic->store (3, 'r'); + jlabels->store (3, dbe_strdup (GTXT ("Source File"))); + jvalues->store (3, dbe_strdup (sname_0)); + + saligns->store (4, TEXT_RIGHT); + mnemonic->store (4, 'b'); + jlabels->store (4, dbe_strdup (GTXT ("Object File"))); + jvalues->store (4, dbe_strdup (oname_0)); + + saligns->store (5, TEXT_LEFT); + mnemonic->store (5, 'j'); + jlabels->store (5, dbe_strdup (GTXT ("Load Object"))); + jvalues->store (5, dbe_strdup (lname_0)); + + saligns->store (6, TEXT_LEFT); + mnemonic->store (6, 'm'); + jlabels->store (6, dbe_strdup (GTXT ("Mangled Name"))); + jvalues->store (6, dbe_strdup (mangle_0)); + + saligns->store (7, TEXT_LEFT); + mnemonic->store (7, 'A'); + jlabels->store (7, dbe_strdup (GTXT ("Aliases"))); + jvalues->store (7, dbe_strdup (alias_0)); +} + +// Set memory-object summary list +// +static void +setMemSummary (Vector<Histable*> *objs, Vector<int> *saligns, + Vector<char> *mnemonic, Vector<char*> *jlabels, + Vector<char*> *jvalues) +{ + saligns->store (0, TEXT_LEFT); + mnemonic->store (0, 'M'); + jlabels->store (0, dbe_strdup (GTXT ("Memory Object"))); + if (objs->size () == 1) + { + Histable *current_obj = objs->fetch (0); + jvalues->store (0, dbe_strdup (current_obj->get_name ())); + } + else + { + char *name = dbe_sprintf (NTXT ("%s (%lld %s)"), + GTXT ("Multiple Selection"), + (long long) objs->size (), GTXT ("objects")); + jvalues->store (0, name); + } +} + +// Set index-object summary list +// +static void +setIndxSummary (Vector<Histable*> *objs, Vector<int> *saligns, + Vector<char> *mnemonic, Vector<char*> *jlabels, + Vector<char*> *jvalues) +{ + saligns->store (0, TEXT_LEFT); + mnemonic->store (0, 'I'); + jlabels->store (0, dbe_strdup (GTXT ("Index Object"))); + + if (objs->size () == 1) + { + Histable *current_obj = objs->fetch (0); + jvalues->store (0, dbe_strdup (current_obj->get_name ())); + } + else + { + char *name = dbe_sprintf (NTXT ("%s (%lld %s)"), GTXT ("Multiple Selection"), + (long long) objs->size (), GTXT ("objects")); + jvalues->store (0, name); + } +} + +// Set I/O activity summary list +// +static void +setIOActivitySummary (Vector<Histable*> *objs, Vector<int> *saligns, + Vector<char> *mnemonic, Vector<char*> *jlabels, + Vector<char*> *jvalues) +{ + saligns->store (0, TEXT_LEFT); + mnemonic->store (0, 'O'); + jlabels->store (0, dbe_strdup (GTXT ("I/O Activity"))); + if (objs->size () == 1) + { + Histable *current_obj = objs->fetch (0); + jvalues->store (0, dbe_strdup (current_obj->get_name ())); + } + else + { + char *name = dbe_sprintf (NTXT ("%s (%lld %s)"), GTXT ("Multiple Selection"), + (long long) objs->size (), GTXT ("objects")); + jvalues->store (0, name); + } +} + +// Set heap activity summary list +// +static void +setHeapActivitySummary (Vector<Histable*> *objs, Vector<int> *saligns, + Vector<char> *mnemonic, Vector<char*> *jlabels, + Vector<char*> *jvalues) +{ + saligns->store (0, TEXT_LEFT); + mnemonic->store (0, 'O'); + jlabels->store (0, dbe_strdup (GTXT ("Heap Activity"))); + + if (objs->size () == 1) + { + Histable *current_obj = objs->fetch (0); + jvalues->store (0, dbe_strdup (current_obj->get_name ())); + } + else + { + char *name = dbe_sprintf (NTXT ("%s (%lld %s)"), GTXT ("Multiple Selection"), + (long long) objs->size (), GTXT ("objects")); + jvalues->store (0, name); + } +} + +// +// Set data-object summary list +// +static void +setDataSummary (Vector<Histable*> *objs, Vector<int> *saligns, + Vector<char> *mnemonic, Vector<char*> *jlabels, + Vector<char*> *jvalues) +{ + char *name, *type, *member, *elist; + DataObject *dobj; + Vector<DataObject *> *delem; + Histable *scope; + int index; + char *size, *offset, *elements, *scopename; + + // Get the data object elements + member = elist = type = size = offset = elements = scopename = NULL; + + if (objs->size () == 1) + { + Histable *current_obj = objs->fetch (0); + name = dbe_strdup (current_obj->get_name ()); + dobj = (DataObject *) current_obj; + type = dobj->get_typename (); + scope = dobj->get_scope (); + delem = dbeSession->get_dobj_elements (dobj); + if (type == NULL) + type = GTXT ("(Synthetic)"); + if (!scope) + scopename = dbe_strdup (GTXT ("(Global)")); + else + { + switch (scope->get_type ()) + { + case Histable::FUNCTION: + scopename = dbe_sprintf (NTXT ("%s(%s)"), + ((Function*) scope)->module->get_name (), + scope->get_name ()); + break; + case Histable::LOADOBJECT: + case Histable::MODULE: + default: + scopename = dbe_strdup (scope->get_name ()); + break; + } + } + + if (dobj->get_offset () != -1) + { + if (dobj->get_parent ()) + member = dbe_strdup (dobj->get_parent ()->get_name ()); + offset = dbe_sprintf (NTXT ("%lld"), (long long) dobj->get_offset ()); + } + size = dbe_sprintf ("%lld", (long long) dobj->get_size ()); + + if (delem->size () > 0) + { + elements = dbe_sprintf (NTXT ("%lld"), (long long) delem->size ()); + StringBuilder sb_tmp, sb; + sb.append (GTXT ("Offset Size Name\n")); + for (index = 0; index < delem->size (); index++) + { + DataObject *ditem = delem->fetch (index); + sb_tmp.sprintf (NTXT ("%6lld %5lld %s\n"), + (long long) ditem->get_offset (), + (long long) ditem->get_size (), ditem->get_name ()); + sb.append (&sb_tmp); + } + if (sb.charAt (sb.length () - 1) == '\n') + sb.setLength (sb.length () - 1); + elist = sb.toString (); + } + } + else + name = dbe_sprintf (NTXT ("%s (%lld %s)"), GTXT ("Multiple Selection"), + (long long) objs->size (), GTXT ("objects")); + + saligns->store (0, TEXT_LEFT); + mnemonic->store (0, 'D'); + jlabels->store (0, dbe_strdup (GTXT ("Data Object"))); + jvalues->store (0, name); + + saligns->store (1, TEXT_LEFT); + mnemonic->store (1, 'S'); + jlabels->store (1, dbe_strdup (GTXT ("Scope"))); + jvalues->store (1, scopename); + + saligns->store (2, TEXT_LEFT); + mnemonic->store (2, 'T'); + jlabels->store (2, dbe_strdup (GTXT ("Type"))); + jvalues->store (2, dbe_strdup (type)); + + saligns->store (3, TEXT_LEFT); + mnemonic->store (3, 'M'); + jlabels->store (3, dbe_strdup (GTXT ("Member of"))); + jvalues->store (3, member); + + saligns->store (4, TEXT_LEFT); + mnemonic->store (4, 'O'); + jlabels->store (4, dbe_strdup (GTXT ("Offset"))); + jvalues->store (4, offset); + + saligns->store (5, TEXT_LEFT); + mnemonic->store (5, 'z'); + jlabels->store (5, dbe_strdup (GTXT ("Size"))); + jvalues->store (5, size); + + saligns->store (6, TEXT_LEFT); + mnemonic->store (6, 'E'); + jlabels->store (6, dbe_strdup (GTXT ("Elements"))); + jvalues->store (6, elements); + + saligns->store (7, TEXT_LEFT); + mnemonic->store (7, 'L'); + jlabels->store (7, dbe_strdup (GTXT ("List"))); + jvalues->store (7, elist); +} + +#define SUMMARY_NAME 8 +#define DSUMMARY_NAME 8 +#define LSUMMARY_NAME 7 +#define IMSUMMARY_NAME 1 + +Vector<void*> * +dbeGetSummaryV2 (int dbevindex, Vector<Obj> *sel_objs, int type, int subtype) +{ + if (sel_objs == NULL || sel_objs->size () == 0) + return NULL; + DbeView *dbev = dbeSession->getView (dbevindex); + Vector<Histable*>*objs = new Vector<Histable*>(sel_objs->size ()); + for (int i = 0; i < sel_objs->size (); i++) + { + Histable *obj = (Histable *) sel_objs->fetch (i); + if (obj == NULL) + continue; + char *nm = obj->get_name (); + if (streq (nm, NTXT ("<Total>"))) + { + // Special case for 'Total'. + // Multi selection which includes 'Total' is only 'Total' + objs->reset (); + objs->append (obj); + break; + } + objs->append (obj); + } + if (objs->size () == 0) + return NULL; + + // Set name area + int nname = SUMMARY_NAME; + Vector<int> *saligns = new Vector<int>(nname); + Vector<char>*mnemonic = new Vector<char>(nname); + Vector<char*> *jlabels = new Vector<char*>(nname); + Vector<char*> *jvalues = new Vector<char*>(nname); + Vector<void*> *name_objs = new Vector<void*>(4); + name_objs->store (0, saligns); + name_objs->store (1, mnemonic); + name_objs->store (2, jlabels); + name_objs->store (3, jvalues); + setSummary (objs, saligns, mnemonic, jlabels, jvalues); + + MetricList *prop_mlist = new MetricList (dbev->get_metric_ref (MET_NORMAL)); + if (prop_mlist && dbev->comparingExperiments ()) + prop_mlist = dbev->get_compare_mlist (prop_mlist, 0); + + int nitems = prop_mlist->get_items ()->size (); + + // Set the metrics area + jlabels = new Vector<char*>(nitems); + Vector<double> *clock_list = new Vector<double>(nitems); + Vector<double> *excl_list = new Vector<double>(nitems); + Vector<double> *ep_list = new Vector<double>(nitems); + Vector<double> *incl_list = new Vector<double>(nitems); + Vector<double> *ip_list = new Vector<double>(nitems); + Vector<int> *vtype = new Vector<int>(nitems); + + // Initialize Java String array + Vector<void*> *metric_objs = new Vector<void*>(8); + metric_objs->store (0, jlabels); + metric_objs->store (1, clock_list); + metric_objs->store (2, excl_list); + metric_objs->store (3, ep_list); + metric_objs->store (4, incl_list); + metric_objs->store (5, ip_list); + metric_objs->store (6, vtype); + + int last_init = -1; + for (int i = 0; i < objs->size (); i++) + { + Histable *obj = objs->fetch (i); + // Get the data to be displayed + Hist_data *data = dbev->get_hist_data (prop_mlist, obj->get_type (), subtype, + Hist_data::SELF, obj, dbev->sel_binctx, objs); + + if (data->get_status () != Hist_data::SUCCESS) + { + if (type != DSP_DLAYOUT) + { // For data_layout, rows with zero metrics are OK + delete data; + continue; + } + } + TValue *values = NULL; + if (data->get_status () == Hist_data::SUCCESS) + { + Hist_data::HistItem *hi = data->fetch (0); + if (hi) + values = hi->value; + } + Hist_data::HistItem *total = data->get_totals (); + int index2 = 0; + char *tstr = GTXT (" Time"); + char *estr = GTXT ("Exclusive "); + size_t len = strlen (estr); + + // get the metric list from the data + MetricList *mlist = data->get_metric_list (); + int index; + Metric *mitem; + double clock; + Vec_loop (Metric*, mlist->get_items (), index, mitem) + { + if (mitem->get_subtype () == Metric::STATIC) + continue; + if (last_init < index2) + { + last_init = index2; + jlabels->store (index2, NULL); + clock_list->store (index2, 0.0); + excl_list->store (index2, 0.0); + ep_list->store (index2, 0.0); + incl_list->store (index2, 0.0); + ip_list->store (index2, 0.0); + vtype->store (index2, 0); + } + double dvalue = (values != NULL) ? values[index].to_double () : 0.0; + double dtotal = total->value[index].to_double (); + if (mitem->is_time_val ()) + clock = 1.e+6 * dbeSession->get_clock (-1); + else + clock = 0.0; + + clock_list->store (index2, clock); + if ((mitem->get_subtype () == Metric::EXCLUSIVE) || + (mitem->get_subtype () == Metric::DATASPACE)) + { + if (i == 0) + { + char *sstr = mitem->get_name (); + if (!strncmp (sstr, estr, len)) + sstr += len; + char *buf, *lstr = strstr (sstr, tstr); + if (lstr) + buf = dbe_strndup (sstr, lstr - sstr); + else + buf = dbe_strdup (sstr); + jlabels->store (index2, buf); + vtype->store (index2, mitem->get_vtype ()); + } + dvalue += excl_list->fetch (index2); + double percent = dtotal == 0.0 ? dtotal : (dvalue / dtotal) * 100; + excl_list->store (index2, dvalue); + ep_list->store (index2, percent); + } + else + { + dvalue += incl_list->fetch (index2); + if (dvalue > dtotal) + dvalue = dtotal; // temporary correction + double percent = dtotal == 0.0 ? dtotal : (dvalue / dtotal) * 100; + incl_list->store (index2, dvalue); + ip_list->store (index2, percent); + index2++; + } + } + delete data; + } + delete prop_mlist; + Vector<void*> *summary = new Vector<void*>(2); + summary->store (0, name_objs); + summary->store (1, metric_objs); + return summary; +} + +// Get Summary List +Vector<void*> * +dbeGetSummary (int dbevindex, Vector<Obj> *sel_objs, int type, int subtype) +{ + bool is_data, is_mem, is_indx, is_iodata, is_heapdata; + Hist_data::HistItem *total; + MetricList *prop_mlist; // as passed to get_hist_data + MetricList *mlist; // as stored in the data + Metric *mitem; + int i, nname, nitems, index, index2; + TValue *values; + double dvalue, clock; + Hist_data *data; + Vector<double> *percent_scale; + + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + if (sel_objs == NULL || sel_objs->size () == 0) + return NULL; + + is_mem = false; + is_data = false; + is_indx = false; + is_iodata = false; + is_heapdata = false; + nname = SUMMARY_NAME; + Vector<Histable*>*objs = new Vector<Histable*>(sel_objs->size ()); + if (type == DSP_TIMELINE) + objs->append ((Histable *) sel_objs->fetch (0)); + else + { + switch (type) + { + case DSP_FUNCTION: + data = dbev->func_data; + break; + case DSP_LINE: + data = dbev->line_data; + break; + case DSP_PC: + data = dbev->pc_data; + break; + case DSP_SELF: + data = dbev->fitem_data; + break; + case DSP_SOURCE: + case DSP_SOURCE_V2: + data = dbev->src_data; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + data = dbev->dis_data; + break; + case DSP_DLAYOUT: + is_data = true; + nname = LSUMMARY_NAME; + data = dbev->dlay_data; + break; + case DSP_DATAOBJ: + is_data = true; + nname = DSUMMARY_NAME; + data = dbev->dobj_data; + break; + case DSP_MEMOBJ: + is_data = true; + is_mem = true; + nname = IMSUMMARY_NAME; + data = dbev->get_indxobj_data (subtype); + break; + case DSP_INDXOBJ: + is_indx = true; + nname = IMSUMMARY_NAME; + data = dbev->get_indxobj_data (subtype); + break; + case DSP_IOACTIVITY: + is_iodata = true; + nname = IMSUMMARY_NAME; + data = dbev->iofile_data; + break; + case DSP_IOVFD: + is_iodata = true; + nname = IMSUMMARY_NAME; + data = dbev->iovfd_data; + break; + case DSP_IOCALLSTACK: + is_iodata = true; + nname = IMSUMMARY_NAME; + data = dbev->iocs_data; + break; + case DSP_HEAPCALLSTACK: + is_heapdata = true; + nname = IMSUMMARY_NAME; + data = dbev->heapcs_data; + break; + default: + data = NULL; + break; + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return NULL; + + Hist_data::HistItem *current_item; + for (i = 0; i < sel_objs->size (); i++) + { + int sel_index = (int) sel_objs->fetch (i); + if (type != DSP_IOACTIVITY && type != DSP_IOVFD && + type != DSP_IOCALLSTACK && type != DSP_HEAPCALLSTACK) + { + if (sel_index < 0 || sel_index >= data->size ()) + continue; + current_item = data->fetch (sel_index); + if (current_item->obj == NULL) + continue; + } + else + { + if (sel_index < 0) + continue; + bool found = false; + for (int j = 0; j < data->size (); j++) + { + current_item = data->fetch (j); + if ((current_item->obj != NULL) && (current_item->obj->id == sel_index)) + { + found = true; + break; + } + } + if (!found) + continue; + } + char *nm = current_item->obj->get_name (); + if (streq (nm, NTXT ("<Total>"))) + { + // Special case for 'Total'. + // Multi selection which includes 'Total' is only 'Total' + objs->reset (); + objs->append (current_item->obj); + break; + } + objs->append (current_item->obj); + } + } + if (objs->size () == 0) + return NULL; + + // Set name area + Vector<int> *saligns = new Vector<int>(nname); + Vector<char>*mnemonic = new Vector<char>(nname); + Vector<char*> *jlabels = new Vector<char*>(nname); + Vector<char*> *jvalues = new Vector<char*>(nname); + Vector<void*> *name_objs = new Vector<void*>(4); + name_objs->store (0, saligns); + name_objs->store (1, mnemonic); + name_objs->store (2, jlabels); + name_objs->store (3, jvalues); + if (is_mem) + setMemSummary (objs, saligns, mnemonic, jlabels, jvalues); + else if (is_indx) + setIndxSummary (objs, saligns, mnemonic, jlabels, jvalues); + else if (is_data) + setDataSummary (objs, saligns, mnemonic, jlabels, jvalues); + else if (is_iodata) + setIOActivitySummary (objs, saligns, mnemonic, jlabels, jvalues); + else if (is_heapdata) + setHeapActivitySummary (objs, saligns, mnemonic, jlabels, jvalues); + else + setSummary (objs, saligns, mnemonic, jlabels, jvalues); + + // Get the reference metrics + if (is_data) + prop_mlist = new MetricList (dbev->get_metric_ref (MET_DATA)); + else if (is_indx) + prop_mlist = new MetricList (dbev->get_metric_ref (MET_INDX)); + else if (is_iodata) + prop_mlist = new MetricList (dbev->get_metric_ref (MET_IO)); + else if (is_heapdata) + prop_mlist = new MetricList (dbev->get_metric_ref (MET_HEAP)); + else + prop_mlist = new MetricList (dbev->get_metric_ref (MET_NORMAL)); + + // XXXX a workaround to avoid aggregated data for compare mode, only show base experiment data + if (prop_mlist && dbev->comparingExperiments ()) + prop_mlist = dbev->get_compare_mlist (prop_mlist, 0); + nitems = prop_mlist->get_items ()->size (); + + // Set the metrics area + jlabels = new Vector<char*>(nitems); + Vector<double> *clock_list = new Vector<double>(nitems); + Vector<double> *excl_list = new Vector<double>(nitems); + Vector<double> *ep_list = new Vector<double>(nitems); + Vector<double> *incl_list = new Vector<double>(nitems); + Vector<double> *ip_list = new Vector<double>(nitems); + Vector<int> *vtype = new Vector<int>(nitems); + + // Initialize Java String array + Vector<void*> *metric_objs = new Vector<void*>(8); + metric_objs->store (0, jlabels); + metric_objs->store (1, clock_list); + metric_objs->store (2, excl_list); + metric_objs->store (3, ep_list); + metric_objs->store (4, incl_list); + metric_objs->store (5, ip_list); + metric_objs->store (6, vtype); + percent_scale = new Vector<double>(); + int last_init = -1; + for (i = 0; i < objs->size (); i++) + { + Histable *current_obj = objs->fetch (i); + // Get the data to be displayed + data = dbev->get_hist_data (prop_mlist, current_obj->get_type (), subtype, + Hist_data::SELF, current_obj, dbev->sel_binctx, objs); + if (data->get_status () != Hist_data::SUCCESS) + { + if (type != DSP_DLAYOUT) + { // For data_layout, rows with zero metrics are OK + delete data; + continue; + } + } + Hist_data::HistItem *hi = data->fetch (0); + values = hi ? hi->value : NULL; + total = data->get_totals (); + index2 = 0; + + // get the metric list from the data + mlist = data->get_metric_list (); + + // We loop over the metrics in mlist. + // We construct index2, which tells us + // the corresponding entry in the metric_objs lists. + // We need this mapping multiple times. + // So, if you change the looping in any way here, + // do so as well in other similar loops. + // All such loops are marked so: + // See discussion on "mlist-to-index2 mapping". + + Vec_loop (Metric*, mlist->get_items (), index, mitem) + { + if (mitem->get_subtype () == Metric::STATIC) + continue; + if (last_init < index2) + { + last_init = index2; + jlabels->store (index2, NULL); + clock_list->store (index2, 0.0); + excl_list->store (index2, 0.0); + ep_list->store (index2, 0.0); + incl_list->store (index2, 0.0); + ip_list->store (index2, 0.0); + vtype->store (index2, 0); + } + dvalue = (values != NULL) ? values[index].to_double () : 0.0; + double dtotal = total->value[index].to_double (); + percent_scale->store (index, dtotal == 0. ? 0. : 100. / dtotal); + if (mitem->is_time_val ()) + clock = 1.e+6 * dbeSession->get_clock (-1); + else + clock = 0.0; + + clock_list->store (index2, clock); + if (mitem->get_subtype () == Metric::EXCLUSIVE || + mitem->get_subtype () == Metric::DATASPACE) + { + if (i == 0) + { + char *sstr = mitem->get_username (); + char *buf = dbe_strdup (sstr); + jlabels->store (index2, buf); + vtype->store (index2, mitem->get_vtype ()); + } + dvalue += excl_list->fetch (index2); + double percent = dvalue * percent_scale->fetch (index); + excl_list->store (index2, dvalue); + ep_list->store (index2, percent); + if (is_data || is_indx || is_iodata || is_heapdata) + // move to next row, except if there's inclusive data, too + index2++; + } + else + { + dvalue += incl_list->fetch (index2); + if (dvalue > dtotal && mitem->get_type () != BaseMetric::DERIVED) + dvalue = dtotal; // temporary correction + double percent = dvalue * percent_scale->fetch (index); + incl_list->store (index2, dvalue); + ip_list->store (index2, percent); + index2++; + } + } + delete data; + } + + // for multi-selection, we have to recompute the derived metrics + if (objs->size () > 1 && + dbev->get_derived_metrics () != NULL && + dbev->get_derived_metrics ()->get_items () != NULL && + dbev->get_derived_metrics ()->get_items ()->size () > 0) + { + // See discussion on "mlist-to-index2 mapping". + Vector<Metric*> *mvec = new Vector<Metric*>(nitems); + index2 = 0; + Vec_loop (Metric*, prop_mlist->get_items (), index, mitem) + { + if (mitem->get_subtype () == Metric::STATIC) + continue; + if (mitem->get_subtype () == Metric::EXCLUSIVE || + mitem->get_subtype () == Metric::DATASPACE) + { + mvec->store (index2, mitem); + if (is_data || is_indx || is_iodata || is_heapdata) + index2++; + } + else + { + assert (strcmp (mvec->fetch (index2)->get_cmd (), mitem->get_cmd ()) == 0); + index2++; + } + } + int *map = dbev->get_derived_metrics ()->construct_map (mvec, BaseMetric::EXCLUSIVE, mvec->fetch (0)->get_expr_spec ()); + if (map != NULL) + { + int nmetrics = mvec->size (); + double *evalues = (double *) malloc (nmetrics * sizeof (double)); + double *ivalues = (double *) malloc (nmetrics * sizeof (double)); + for (index2 = 0; index2 < nmetrics; index2++) + { + evalues[index2] = excl_list->fetch (index2); + ivalues[index2] = incl_list->fetch (index2); + } + + // evaluate derived metrics + dbev->get_derived_metrics ()->eval (map, evalues); + dbev->get_derived_metrics ()->eval (map, ivalues); + for (index2 = 0; index2 < nmetrics; index2++) + { + excl_list->store (index2, evalues[index2]); + incl_list->store (index2, ivalues[index2]); + } + + // recompute percentages for derived metrics EUGENE maybe all percentage computations should be moved here + // See discussion on "mlist-to-index2 mapping". + index2 = 0; + Vec_loop (Metric*, prop_mlist->get_items (), index, mitem) + { + if (mitem->get_subtype () == Metric::STATIC) + continue; + if (mitem->get_subtype () == Metric::EXCLUSIVE || + mitem->get_subtype () == Metric::DATASPACE) + { + if (mitem->get_type () == BaseMetric::DERIVED) + ep_list->store (index2, excl_list->fetch (index2) * percent_scale->fetch (index)); + if (is_data || is_indx || is_iodata || is_heapdata) + index2++; + } + else + { + if (mitem->get_type () == BaseMetric::DERIVED) + ip_list->store (index2, incl_list->fetch (index2) * percent_scale->fetch (index)); + index2++; + } + } + free (evalues); + free (ivalues); + free (map); + } + delete mvec; + } + delete prop_mlist; + Vector<void*> *summary = new Vector<void*>(2); + summary->store (0, name_objs); + summary->store (1, metric_objs); + delete objs; + delete percent_scale; + return summary; +} + +char * +dbeGetExpName (int /*dbevindex*/, char *dir_name) +{ + char *ret; + char *warn; + if (col_ctr == NULL) + col_ctr = new Coll_Ctrl (1); // Potential race condition? + if (dir_name != NULL) + { + ret = col_ctr->set_directory (dir_name, &warn); + // note that the warning and error msgs are written to stderr, not returned to caller + if (warn != NULL) + fprintf (stderr, NTXT ("%s"), warn); + if (ret != NULL) + fprintf (stderr, NTXT ("%s"), ret); + } + return dbe_strdup (col_ctr->get_expt ()); +} + +// === CollectDialog HWC info === + +Vector<Vector<char*>*> * +dbeGetHwcSets (int /*dbevindex*/, bool forKernel) +{ + Vector<Vector<char*>*> *list = new Vector<Vector<char*>*>(2); + char * defctrs = hwc_get_default_cntrs2 (forKernel, 1); + Vector<char*> *i18n = new Vector<char*>(1); // User name + Vector<char*> *name = new Vector<char*>(1); // Internal name + if (NULL != defctrs) + { + i18n->store (0, strdup (defctrs)); + name->store (0, strdup (NTXT ("default"))); + } + list->store (0, i18n); + list->store (1, name); + return list; +} + +static Vector<void*> * +dbeGetHwcs (Hwcentry **hwcs) +{ + int sz; + for (sz = 0; hwcs && hwcs[sz]; sz++) + ; + Vector<void*> *list = new Vector<void*>(9); + Vector<char*> *i18n = new Vector<char*>(sz); + Vector<char*> *name = new Vector<char*>(sz); + Vector<char*> *int_name = new Vector<char*>(sz); + Vector<char*> *metric = new Vector<char*>(sz); + Vector<long long> *val = new Vector<long long>(sz); + Vector<int> *timecvt = new Vector<int>(sz); + Vector<int> *memop = new Vector<int>(sz); + Vector<char*> *short_desc = new Vector<char*>(sz); + Vector<Vector<int>*> *reglist_v = new Vector<Vector<int>*>(sz); + Vector<bool> *supportsAttrs = new Vector<bool>(sz); + Vector<bool> *supportsMemspace = new Vector<bool>(sz); + + for (int i = 0; i < sz; i++) + { + Hwcentry *ctr = hwcs[i]; + Vector<int> *registers = new Vector<int>(MAX_PICS); + regno_t *reglist = ctr->reg_list; + for (int k = 0; !REG_LIST_EOL (reglist[k]) && k < MAX_PICS; k++) + registers->store (k, reglist[k]); + + i18n->store (i, dbe_strdup (hwc_i18n_metric (ctr))); + name->store (i, dbe_strdup (ctr->name)); + int_name->store (i, dbe_strdup (ctr->int_name)); + metric->store (i, dbe_strdup (ctr->metric)); + val->store (i, ctr->val); // signed promotion from int + timecvt->store (i, ctr->timecvt); + memop->store (i, ctr->memop); + reglist_v->store (i, registers); + short_desc->store (i, dbe_strdup (ctr->short_desc)); + supportsAttrs->store (i, true); + supportsMemspace->store (i, ABST_MEMSPACE_ENABLED (ctr->memop)); + } + list->store (0, i18n); + list->store (1, name); + list->store (2, int_name); + list->store (3, metric); + list->store (4, val); + list->store (5, timecvt); + list->store (6, memop); + list->store (7, short_desc); + list->store (8, reglist_v); + list->store (9, supportsAttrs); + list->store (10, supportsMemspace); + return list; +} + +Vector<void *> * +dbeGetHwcsAll (int /*dbevindex*/, bool forKernel) +{ + Vector<void*> *list = new Vector<void*>(2); + list->store (0, dbeGetHwcs (hwc_get_std_ctrs (forKernel))); + list->store (1, dbeGetHwcs (hwc_get_raw_ctrs (forKernel))); + return list; +} + +Vector<char*> * +dbeGetHwcHelp (int /*dbevindex*/, bool forKernel) +{ + Vector<char*> *strings = new Vector<char*>(32); + FILE *f = tmpfile (); + hwc_usage_f (forKernel, f, "", 0, 0, 1); // writes to f + fflush (f); + fseek (f, 0, SEEK_SET); +#define MAX_LINE_LEN 2048 + char buff[MAX_LINE_LEN]; + int ii = 0; + while (fgets (buff, MAX_LINE_LEN, f)) + strings->store (ii++, dbe_strdup (buff)); + fclose (f); + return strings; +} + +Vector<char*> * +dbeGetHwcAttrList (int /*dbevindex*/, bool forKernel) +{ + char ** attr_list = hwc_get_attrs (forKernel); // Get Attribute list + int size; + for (size = 0; attr_list && attr_list[size]; size++) + ; + + Vector<char*> *name = new Vector<char*>(size); + for (int i = 0; i < size; i++) + name->store (i, dbe_strdup (attr_list[i])); + return name; +} + +//Get maximum number of simultaneous counters +int +dbeGetHwcMaxConcurrent (int /*dbevindex*/, bool forKernel) +{ + return hwc_get_max_concurrent (forKernel); +} + +// === End CollectDialog HWC info === + + +// Instruction-frequency data +Vector<char*> * +dbeGetIfreqData (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + if (!dbeSession->is_ifreq_available ()) + return NULL; + int size = dbeSession->nexps (); + if (size == 0) + return NULL; + + // Initialize Java String array + Vector<char*> *list = new Vector<char*>(); + for (int i = 0; i < size; i++) + { + Experiment *exp = dbeSession->get_exp (i); + if (exp->broken || !dbev->get_exp_enable (i) || !exp->ifreqavail) + continue; + // write a header for the experiment + list->append (dbe_sprintf (GTXT ("Instruction frequency data from experiment %s\n\n"), + exp->get_expt_name ())); + // add its instruction frequency messages + char *ifreq = pr_mesgs (exp->fetch_ifreq (), NTXT (""), NTXT ("")); + list->append (ifreq); + } + return list; +} + +// LeakList related methods +// +Vector<void*> * +dbeGetLeakListInfo (int dbevindex, bool leakflag) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + MetricList *origmlist = dbev->get_metric_list (MET_NORMAL); + MetricList *nmlist = new MetricList (origmlist); + if (leakflag) + nmlist->set_metrics (NTXT ("e.heapleakbytes:e.heapleakcnt:name"), true, + dbev->get_derived_metrics ()); + else + nmlist->set_metrics (NTXT ("e.heapallocbytes:e.heapalloccnt:name"), true, + dbev->get_derived_metrics ()); + MetricList *mlist = new MetricList (nmlist); + delete nmlist; + + CStack_data *lam = dbev->get_cstack_data (mlist); + if (lam == NULL || lam->size () == 0) + { + delete lam; + delete mlist; + return NULL; + } + Vector<Vector<Obj>*> *evalue = new Vector<Vector<Obj>*>(lam->size ()); + Vector<Vector<Obj>*> *pcstack = new Vector<Vector<Obj>*>(lam->size ()); + Vector<Vector<Obj>*> *offstack = new Vector<Vector<Obj>*>(lam->size ()); + Vector<Vector<Obj>*> *fpcstack = new Vector<Vector<Obj>*>(lam->size ()); + Vector<Vector<Obj>*> *sumval = new Vector<Vector<Obj>*>(lam->size ()); + + int index; + CStack_data::CStack_item *lae; + Vec_loop (CStack_data::CStack_item*, lam->cstack_items, index, lae) + { + Vector<Obj> *jivals = NULL; + if (lae != NULL) + { + jivals = new Vector<Obj>(4); + jivals->store (0, (Obj) (index + 1)); + jivals->store (1, (Obj) lae->value[1].ll); + jivals->store (2, (Obj) lae->value[0].ll); + jivals->store (3, (Obj) (leakflag ? 1 : 2)); + } + evalue->store (index, jivals); + int snum = lae->stack->size (); + Vector<Obj> *jivals1 = new Vector<Obj>(snum); + Vector<Obj> *jivals2 = new Vector<Obj>(snum); + Vector<Obj> *jivals3 = new Vector<Obj>(snum); + if (lae->stack != NULL) + { + for (int i = lae->stack->size () - 1; i >= 0; i--) + { + DbeInstr *instr = lae->stack->fetch (i); + jivals1->store (i, (Obj) instr); + jivals2->store (i, (Obj) instr->func); + jivals3->store (i, (Obj) instr->addr); + } + } + fpcstack->store (index, jivals1); + pcstack->store (index, jivals2); + offstack->store (index, jivals3); + lae++; + } + Vector<Obj> *jivals4 = new Vector<Obj>(3); + jivals4->store (0, (Obj) lam->size ()); + jivals4->store (1, (Obj) lam->total->value[1].ll); + jivals4->store (2, (Obj) lam->total->value[0].ll); + sumval->store (0, jivals4); + delete lam; + delete mlist; + Vector<void*> *earray = new Vector<void*>(5); + earray->store (0, evalue); + earray->store (1, pcstack); + earray->store (2, offstack); + earray->store (3, fpcstack); + earray->store (4, sumval); + return earray; +} + +// Map timeline address to function instr +// +Obj +dbeGetObject (int dbevindex, Obj sel_func, Obj sel_pc) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + if (sel_pc) + return sel_pc; + return sel_func; +} + +char * +dbeGetName (int /*dbevindex*/, int exp_id) +// This function's name is not descriptive enough - it returns a string +// containing the full experiment name with path, process name, and PID. +// There are various dbe functions that provide experiment name and experiment +// details, and they should probably be consolidated/refactored. (TBR) +// For another example of similar output formatting, see dbeGetExpName(). +{ + int id = (exp_id < 0) ? 0 : exp_id; + Experiment *exp = dbeSession->get_exp (id); + if (exp == NULL) + return NULL; + char *buf = + dbe_sprintf (NTXT ("%s [%s, PID %d]"), + exp->get_expt_name (), + exp->utargname != NULL ? exp->utargname : GTXT ("(unknown)"), + exp->getPID ()); + return buf; +} + +Vector<char*> * +dbeGetExpVerboseName (Vector<int> *exp_ids) +{ + int len = exp_ids->size (); + Vector<char*> *list = new Vector<char*>(len); + for (int i = 0; i < len; i++) + { + char * verboseName = dbeGetName (0, exp_ids->fetch (i)); // no strdup() + list->store (i, verboseName); + } + return list; +} + +long long +dbeGetStartTime (int /*dbevindex*/, int exp_id) +{ + int id = (exp_id < 0) ? 0 : exp_id; + Experiment *exp = dbeSession->get_exp (id); + return exp ? exp->getStartTime () : (long long) 0; +} + +long long +dbeGetRelativeStartTime (int /*dbevindex*/, int exp_id) +{ + int id = (exp_id < 0) ? 0 : exp_id; + Experiment *exp = dbeSession->get_exp (id); + return exp ? exp->getRelativeStartTime () : (long long) 0; +} + +long long +dbeGetEndTime (int /*dbevindex*/, int exp_id) +{ + int id = (exp_id < 0) ? 0 : exp_id; + Experiment *exp = dbeSession->get_exp (id); + + // Experiment::getEndTime was initially implemented as + // returning exp->last_event. To preserve the semantics + // new Experiment::getLastEvent() is used here. + return exp ? exp->getLastEvent () : (long long) 0; +} + +int +dbeGetClock (int /*dbevindex*/, int exp_id) +{ + return dbeSession->get_clock (exp_id); +} + +long long +dbeGetWallStartSec (int /*dbevindex*/, int exp_id) +{ + int id = (exp_id < 0) ? 0 : exp_id; + Experiment *exp = dbeSession->get_exp (id); + return exp ? exp->getWallStartSec () : 0ll; +} + +char * +dbeGetHostname (int /*dbevindex*/, int exp_id) +{ + int id = (exp_id < 0) ? 0 : exp_id; + Experiment *exp = dbeSession->get_exp (id); + return exp ? dbe_strdup (exp->hostname) : NULL; +} + +static DataView * +getTimelinePackets (int dbevindex, int exp_id, int data_id, int entity_prop_id) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + const int sortprop_count = 3; + const int sortprops[sortprop_count] = { + PROP_HWCTAG, // aux + entity_prop_id, + PROP_TSTAMP + }; + DataView *packets = dbev->get_filtered_events (exp_id, data_id, + sortprops, sortprop_count); + return packets; +} + +static long +getIdxByVals (DataView * packets, int aux, int entity_prop_val, + uint64_t time, DataView::Relation rel) +{ + const int sortprop_count = 3; + Datum tval[sortprop_count]; + tval[0].setUINT32 (aux); + tval[1].setUINT32 (entity_prop_val); //CPUID, LWPID, THRID are downsized to 32 + tval[2].setUINT64 (time); + long idx = packets->getIdxByVals (tval, rel); + return idx; +} + +static bool +isValidIdx (DataView * packets, int entity_prop_id, + int aux, int entity_prop_val, long idx) +{ + if (idx < 0 || idx >= packets->getSize ()) + return false; + int pkt_aux = packets->getIntValue (PROP_HWCTAG, idx); + if (pkt_aux != aux) + return false; + if (entity_prop_id == PROP_EXPID) + return true; // not a packet property; we know the packet is in this experiment + if (entity_prop_id == PROP_NONE) + return true; // not a packet property; we know the packet is in this experiment + int pkt_ent = packets->getIntValue (entity_prop_id, idx); + if (pkt_ent != entity_prop_val) + return false; + return true; +} + +static bool +hasInvisbleTLEvents (Experiment *exp, VMode view_mode) +{ + if (exp->has_java && view_mode == VMODE_USER) + return true; + return false; +} + +static bool +isVisibleTLEvent (Experiment *exp, VMode view_mode, DataView* packets, long idx) +{ + if (hasInvisbleTLEvents (exp, view_mode)) + { + JThread *jthread = (JThread*) packets->getObjValue (PROP_JTHREAD, idx); + if (jthread == JTHREAD_NONE || (jthread != NULL && jthread->is_system ())) + return false; + } + return true; +} + +static long +getTLVisibleIdxByStepping (Experiment *exp, VMode view_mode, int entity_prop_id, + DataView * packets, int aux, int entity_prop_val, + long idx, long move_count, int direction) +{ + assert (move_count >= 0); + assert (direction == 1 || direction == -1 || direction == 0); + if (direction == 0 /* precise hit required */) + move_count = 0; + do + { + if (!isValidIdx (packets, entity_prop_id, aux, entity_prop_val, idx)) + return -1; + if (isVisibleTLEvent (exp, view_mode, packets, idx)) + { + if (move_count <= 0) + break; + move_count--; + } + if (direction == 0) + return -1; + idx += direction; + } + while (1); + return idx; +} + +static long +getTLVisibleIdxByVals (Experiment *exp, VMode view_mode, int entity_prop_id, + DataView * packets, + int aux, int entity_prop_val, uint64_t time, DataView::Relation rel) +{ + long idx = getIdxByVals (packets, aux, entity_prop_val, time, rel); + if (!hasInvisbleTLEvents (exp, view_mode)) + return idx; + if (idx < 0) + return idx; + if (rel == DataView::REL_EQ) + return -1; // would require bi-directional search... not supported for now + int direction = (rel == DataView::REL_LT || rel == DataView::REL_LTEQ) ? -1 : 1; + idx = getTLVisibleIdxByStepping (exp, view_mode, entity_prop_id, packets, + aux, entity_prop_val, + idx, 0 /* first match */, direction); + return idx; +} + +// In thread mode, the entity name for non Java thread should be the 1st func +// from the current thread's stack. See #4961315 +static char* +getThreadRootFuncName (int, int, int, int, VMode) +{ + return NULL; // until we figure out what we want to show... YXXX +} + +Vector<void*> * +dbeGetEntityProps (int dbevindex) //YXXX TBD, should this be exp-specific? +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Vector<int> *prop_id = new Vector<int>(); + Vector<char*> *prop_name = new Vector<char*>(); + Vector<char*> *prop_uname = new Vector<char*>(); + Vector<char*> *prop_cname = new Vector<char*>(); //must match TLModeCmd vals! + + prop_id->append (PROP_NONE); + prop_name->append (dbe_strdup (GTXT ("NONE"))); + prop_uname->append (dbe_strdup (GTXT ("Unknown"))); + prop_cname->append (dbe_strdup (NTXT ("unknown"))); + + prop_id->append (PROP_LWPID); + prop_name->append (dbe_strdup (GTXT ("LWPID"))); + prop_uname->append (dbe_strdup (GTXT ("LWP"))); + prop_cname->append (dbe_strdup (NTXT ("lwp"))); + + prop_id->append (PROP_THRID); + prop_name->append (dbe_strdup (GTXT ("THRID"))); + prop_uname->append (dbe_strdup (GTXT ("Thread"))); + prop_cname->append (dbe_strdup (NTXT ("thread"))); + + prop_id->append (PROP_CPUID); + prop_name->append (dbe_strdup (GTXT ("CPUID"))); + prop_uname->append (dbe_strdup (GTXT ("CPU"))); + prop_cname->append (dbe_strdup (NTXT ("cpu"))); + + prop_id->append (PROP_EXPID); + prop_name->append (dbe_strdup (GTXT ("EXPID"))); + prop_uname->append (dbe_strdup (GTXT ("Process"))); // placeholder... + // ...until we finalize how to expose user-level Experiments, descendents + prop_cname->append (dbe_strdup (NTXT ("experiment"))); + Vector<void*> *darray = new Vector<void*>(); + darray->store (0, prop_id); + darray->store (1, prop_name); + darray->store (2, prop_uname); + darray->store (3, prop_cname); + return darray; +} + +Vector<void*> * +dbeGetEntities (int dbevindex, int exp_id, int entity_prop_id) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Experiment *exp = dbeSession->get_exp (exp_id); + if (exp == NULL) + return NULL; + + // Recognize and skip faketime experiments + if (exp->timelineavail == false) + return NULL; + Vector<Histable*> *tagObjs = exp->getTagObjs ((Prop_type) entity_prop_id); + int total_nelem; + if (tagObjs) + total_nelem = (int) tagObjs->size (); + else + total_nelem = 0; + const VMode view_mode = dbev->get_view_mode (); + bool show_java_threadnames = (entity_prop_id == PROP_THRID && + view_mode != VMODE_MACHINE); + // allocate the structures for the return + Vector<int> *entity_prop_vals = new Vector<int>(); + Vector<char*> *jthr_names = new Vector<char*>(); + Vector<char*> *jthr_g_names = new Vector<char*>(); + Vector<char*> *jthr_p_names = new Vector<char*>(); + + // now walk the tagObjs from the experiment, and check for filtering + for (int tagObjsIdx = 0; tagObjsIdx < total_nelem; tagObjsIdx++) + { + int entity_prop_val = (int) ((Other *) tagObjs->fetch (tagObjsIdx))->tag; + entity_prop_vals->append (entity_prop_val); + char *jname, *jgname, *jpname; + JThread *jthread = NULL; + bool has_java_threadnames = false; + if (show_java_threadnames) + { + jthread = exp->get_jthread (entity_prop_val); + has_java_threadnames = (jthread != JTHREAD_DEFAULT + && jthread != JTHREAD_NONE); + } + if (!has_java_threadnames) + { + jname = jgname = jpname = NULL; + if (entity_prop_id == PROP_THRID || entity_prop_id == PROP_LWPID) + // if non Java thread, set thread name to the 1st func + // from the current thread's stack. see #4961315 + jname = getThreadRootFuncName (dbevindex, exp_id, entity_prop_id, + entity_prop_val, view_mode); + } + else + { + jname = dbe_strdup (jthread->name); + jgname = dbe_strdup (jthread->group_name); + jpname = dbe_strdup (jthread->parent_name); + } + jthr_names->append (jname); + jthr_g_names->append (jgname); + jthr_p_names->append (jpname); + } + Vector<char*> *entity_prop_name_v = new Vector<char*>(); + char* entity_prop_name = dbeSession->getPropName (entity_prop_id); + entity_prop_name_v->append (entity_prop_name); + Vector<void*> *darray = new Vector<void*>(5); + darray->store (0, entity_prop_vals); + darray->store (1, jthr_names); + darray->store (2, jthr_g_names); + darray->store (3, jthr_p_names); + darray->store (4, entity_prop_name_v); // vector only has 1 element + return darray; +} + +// TBR: dbeGetEntities() can be set to private now that we have dbeGetEntitiesV2() +Vector<void*> * +dbeGetEntitiesV2 (int dbevindex, Vector<int> *exp_ids, int entity_prop_id) +{ + int sz = exp_ids->size (); + Vector<void*> *res = new Vector<void*>(sz); + for (int ii = 0; ii < sz; ii++) + { + int expIdx = exp_ids->fetch (ii); + Vector<void*>* ents = dbeGetEntities (dbevindex, expIdx, entity_prop_id); + res->store (ii, ents); + } + return res; +} + +//YXXX old-tl packets still used for details +static Vector<void*> * +getTLDetailValues (int dbevindex, Experiment * exp, int data_id, + VMode view_mode, DataView *packets, long idx) +{ + Vector<long long> *value = new Vector<long long>(15); + long i = idx; + if (data_id == DATA_SAMPLE || data_id == DATA_GCEVENT) + { + //YXXX DATA_SAMPLE not handled but could be. + } + Obj stack = (unsigned long) getStack (view_mode, packets, i); + Vector<Obj> *funcs = stack ? dbeGetStackFunctions (dbevindex, stack) : NULL; + Function *func = (Function*) + getStackPC (0, view_mode, packets, i)->convertto (Histable::FUNCTION); + // Fill common data + value->store (0, packets->getIntValue (PROP_LWPID, i)); + value->store (1, packets->getIntValue (PROP_THRID, i)); + value->store (2, packets->getIntValue (PROP_CPUID, i)); + value->store (3, packets->getLongValue (PROP_TSTAMP, i)); + value->store (4, (unsigned long) stack); + value->store (5, (unsigned long) func); + + // Fill specific data + switch (data_id) + { + case DATA_CLOCK: + value->store (6, packets->getIntValue (PROP_MSTATE, i)); + { + hrtime_t interval = exp->get_params ()->ptimer_usec * 1000LL // nanoseconds + * packets->getLongValue (PROP_NTICK, i); + value->store (7, interval); + } + value->store (8, packets->getIntValue (PROP_OMPSTATE, i)); + value->store (9, packets->getLongValue (PROP_EVT_TIME, i)); // visual duration + break; + case DATA_SYNCH: + value->store (6, packets->getLongValue (PROP_EVT_TIME, i)); + value->store (7, packets->getLongValue (PROP_SOBJ, i)); + break; + case DATA_HWC: + value->store (6, packets->getLongValue (PROP_HWCINT, i)); + value->store (7, packets->getLongValue (PROP_VADDR, i)); // data vaddr + value->store (8, packets->getLongValue (PROP_PADDR, i)); // data paddr + value->store (9, packets->getLongValue (PROP_VIRTPC, i)); // pc paddr + value->store (10, packets->getLongValue (PROP_PHYSPC, i)); // pc vaddr + break; + case DATA_RACE: + value->store (6, packets->getIntValue (PROP_RTYPE, i)); + value->store (7, packets->getIntValue (PROP_RID, i)); + value->store (8, packets->getLongValue (PROP_RVADDR, i)); + break; + case DATA_DLCK: + value->store (6, packets->getIntValue (PROP_DTYPE, i)); + value->store (7, packets->getIntValue (PROP_DLTYPE, i)); + value->store (8, packets->getIntValue (PROP_DID, i)); + value->store (9, packets->getLongValue (PROP_DVADDR, i)); + break; + case DATA_HEAP: + case DATA_HEAPSZ: + value->store (6, packets->getIntValue (PROP_HTYPE, i)); + value->store (7, packets->getLongValue (PROP_HSIZE, i)); + value->store (8, packets->getLongValue (PROP_HVADDR, i)); + value->store (9, packets->getLongValue (PROP_HOVADDR, i)); + value->store (10, packets->getLongValue (PROP_HLEAKED, i)); + value->store (11, packets->getLongValue (PROP_HFREED, i)); + value->store (12, packets->getLongValue (PROP_HCUR_ALLOCS, i)); // signed int64_t + value->store (13, packets->getLongValue (PROP_HCUR_LEAKS, i)); + break; + case DATA_IOTRACE: + value->store (6, packets->getIntValue (PROP_IOTYPE, i)); + value->store (7, packets->getIntValue (PROP_IOFD, i)); + value->store (8, packets->getLongValue (PROP_IONBYTE, i)); + value->store (9, packets->getLongValue (PROP_EVT_TIME, i)); + value->store (10, packets->getIntValue (PROP_IOVFD, i)); + break; + } + Vector<void*> *result = new Vector<void*>(5); + result->store (0, value); + result->store (1, funcs); // Histable::Function* + result->store (2, funcs ? dbeGetFuncNames (dbevindex, funcs) : 0); // formatted func names + result->store (3, stack ? dbeGetStackPCs (dbevindex, stack) : 0); // Histable::DbeInstr* + result->store (4, stack ? dbeGetStackNames (dbevindex, stack) : 0); // formatted pc names + return result; +} + +Vector<void*> * +dbeGetTLDetails (int dbevindex, int exp_id, int data_id, + int entity_prop_id, Obj event_id) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Experiment *exp = dbeSession->get_exp (exp_id < 0 ? 0 : exp_id); + if (exp == NULL) + return NULL; + DataView *packets = + getTimelinePackets (dbevindex, exp_id, data_id, entity_prop_id); + if (!packets) + return NULL; + + VMode view_mode = dbev->get_view_mode (); + long idx = (long) event_id; + Vector<void*> *values = getTLDetailValues (dbevindex, exp, data_id, view_mode, packets, idx); + return values; +} + +Vector<Obj> * +dbeGetStackFunctions (int dbevindex, Obj stack) +{ + Vector<Obj> *instrs = dbeGetStackPCs (dbevindex, stack); + if (instrs == NULL) + return NULL; + int stsize = instrs->size (); + Vector<Obj> *jivals = new Vector<Obj>(stsize); + for (int i = 0; i < stsize; i++) + { + Histable *obj = (Histable*) instrs->fetch (i); + // if ( obj->get_type() != Histable::LINE ) {//YXXX what is this? + // Remove the above check: why not do this conversion for lines - + // otherwise filtering in timeline by function stack in omp user mode is broken + obj = obj->convertto (Histable::FUNCTION); + jivals->store (i, (Obj) obj); + } + delete instrs; + return jivals; +} + +Vector<void*> * +dbeGetStacksFunctions (int dbevindex, Vector<Obj> *stacks) +{ + long sz = stacks->size (); + Vector<void*> *res = new Vector<void*>(sz); + for (int ii = 0; ii < sz; ii++) + { + Obj stack = stacks->fetch (ii); + Vector<Obj> *jivals = dbeGetStackFunctions (dbevindex, stack); + res->store (ii, jivals); + } + return res; +} + +Vector<Obj> * +dbeGetStackPCs (int dbevindex, Obj stack) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + if (stack == 0) + return NULL; + + bool show_all = dbev->isShowAll (); + Vector<Histable*> *instrs = CallStack::getStackPCs ((void *) stack, !show_all); + int stsize = instrs->size (); + int istart = 0; + bool showAll = dbev->isShowAll (); + for (int i = 0; i < stsize - 1; i++) + { + Function *func = (Function*) instrs->fetch (i)->convertto (Histable::FUNCTION); + int ix = func->module->loadobject->seg_idx; + if (showAll && dbev->get_lo_expand (ix) == LIBEX_API) + // truncate stack here: LIBRARY_VISIBILITY if we are using API only but no hide + istart = i; + } + stsize = stsize - istart; + Vector<Obj> *jlvals = new Vector<Obj>(stsize); + for (int i = 0; i < stsize; i++) + { + Histable *instr = instrs->fetch (i + istart); + jlvals->store (i, (Obj) instr); + } + delete instrs; + return jlvals; +} + +Vector<char*> * +dbeGetStackNames (int dbevindex, Obj stack) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + Vector<Obj> *instrs = dbeGetStackPCs (dbevindex, stack); + if (instrs == NULL) + return NULL; + int stsize = instrs->size (); + Vector<char*> *list = new Vector<char*>(stsize); + bool showAll = dbev->isShowAll (); + for (int i = 0; i < stsize; i++) + { + Histable* instr = (Histable*) instrs->fetch (i); + if (!showAll) + { + // LIBRARY_VISIBILITY + Function *func = (Function*) instr->convertto (Histable::FUNCTION); + LoadObject *lo = ((Function*) func)->module->loadobject; + if (dbev->get_lo_expand (lo->seg_idx) == LIBEX_HIDE) + { + list->store (i, dbe_strdup (lo->get_name ())); + continue; + } + } + list->store (i, dbe_strdup (instr->get_name (dbev->get_name_format ()))); + } + delete instrs; + return list; +} + +Vector<void*> * +dbeGetSamples (int dbevindex, int exp_id, int64_t lo_idx, int64_t hi_idx) +{ + DataView * packets = + getTimelinePackets (dbevindex, exp_id, DATA_SAMPLE, PROP_EXPID); + if (packets == NULL || packets->getSize () == 0) + return NULL; + long lo; + if (lo_idx < 0) + lo = 0; + else + lo = (long) lo_idx; + + long long max = packets->getSize () - 1; + long hi; + if (hi_idx < 0 || hi_idx > max) + hi = (long) max; + else + hi = (long) hi_idx; + + Vector<Vector<long long>*> *sarray = new Vector<Vector<long long>*>; + Vector<long long>* starts = new Vector<long long>; + Vector<long long>* ends = new Vector<long long>; + Vector<long long>* rtimes = new Vector<long long>; + Vector<char*> *startNames = new Vector<char*>; + Vector<char*> *endNames = new Vector<char*>; + Vector<int> *sampId = new Vector<int>; + + for (long index = lo; index <= hi; index++) + { + Sample *sample = (Sample*) packets->getObjValue (PROP_SMPLOBJ, index); + PrUsage *prusage = sample->get_usage (); + if (prusage == NULL) + prusage = new PrUsage; + Vector<long long> *states = prusage->getMstateValues (); + sarray->append (states); + starts->append (sample->get_start_time ()); + ends->append (sample->get_end_time ()); + rtimes->append (prusage->pr_rtime); + startNames->append (dbe_strdup (sample->get_start_label ())); + endNames->append (dbe_strdup (sample->get_end_label ())); + sampId->append (sample->get_number ()); + } + Vector<void *> *res = new Vector<void*>(6); + res->store (0, sarray); + res->store (1, starts); + res->store (2, ends); + res->store (3, rtimes); + res->store (4, startNames); + res->store (5, endNames); + res->store (6, sampId); + return res; +} + +Vector<void*> * +dbeGetGCEvents (int dbevindex, int exp_id, int64_t lo_idx, int64_t hi_idx) +{ + DataView *packets = + getTimelinePackets (dbevindex, exp_id, DATA_GCEVENT, PROP_EXPID); + if (packets == NULL || packets->getSize () == 0) + return NULL; + + long lo; + if (lo_idx < 0) + lo = 0; + else + lo = (long) lo_idx; + long long max = packets->getSize () - 1; + long hi; + if (hi_idx < 0 || hi_idx > max) + hi = (long) max; + else + hi = (long) hi_idx; + + Vector<long long>* starts = new Vector<long long>; + Vector<long long>* ends = new Vector<long long>; + Vector<int> *eventId = new Vector<int>; + for (long index = lo; index <= hi; index++) + { + GCEvent *gcevent = (GCEvent*) packets->getObjValue (PROP_GCEVENTOBJ, index); + if (gcevent) + { + starts->append (gcevent->start); + ends->append (gcevent->end); + eventId->append (gcevent->id); + } + } + Vector<void *> *res = new Vector<void*>(3); + res->store (0, starts); + res->store (1, ends); + res->store (2, eventId); + return res; +} + +Vector<Vector<char*>*>* +dbeGetIOStatistics (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + Hist_data *hist_data; + Hist_data::HistItem *hi; + FileData *fDataTotal; + + hist_data = dbev->iofile_data; + if (hist_data == NULL) + return NULL; + hi = hist_data->fetch (0); + fDataTotal = (FileData*) hi->obj; + + Vector<char*> *writeStat = new Vector<char*>; + Vector<char*> *readStat = new Vector<char*>; + Vector<char*> *otherStat = new Vector<char*>; + Vector<char*> *errorStat = new Vector<char*>; + + writeStat->append (dbe_strdup (GTXT ("Write Statistics"))); + readStat->append (dbe_strdup (GTXT ("Read Statistics"))); + otherStat->append (dbe_strdup (GTXT ("Other I/O Statistics"))); + errorStat->append (dbe_strdup (GTXT ("I/O Error Statistics"))); + + StringBuilder sb; + if (fDataTotal->getWriteCnt () > 0) + { + if (fDataTotal->getW0KB1KBCnt () > 0) + { + sb.sprintf (GTXT ("0KB - 1KB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW0KB1KBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW1KB8KBCnt () > 0) + { + sb.sprintf (GTXT ("1KB - 8KB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW1KB8KBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW8KB32KBCnt () > 0) + { + sb.sprintf (GTXT ("8KB - 32KB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW8KB32KBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW32KB128KBCnt () > 0) + { + sb.sprintf (GTXT ("32KB - 128KB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW32KB128KBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW128KB256KBCnt () > 0) + { + sb.sprintf (GTXT ("128KB - 256KB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW128KB256KBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW256KB512KBCnt () > 0) + { + sb.sprintf (GTXT ("256KB - 512KB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW256KB512KBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW512KB1000KBCnt () > 0) + { + sb.sprintf (GTXT ("512KB - 1000KB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW512KB1000KBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW1000KB10MBCnt () > 0) + { + sb.sprintf (GTXT ("1000KB - 10MB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW1000KB10MBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW10MB100MBCnt () > 0) + { + sb.sprintf (GTXT ("10MB - 100MB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW10MB100MBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW100MB1GBCnt () > 0) + { + sb.sprintf (GTXT ("100MB - 1GB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW100MB1GBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW1GB10GBCnt () > 0) + { + sb.sprintf (GTXT ("1GB - 10GB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW1GB10GBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW10GB100GBCnt () > 0) + { + sb.sprintf (GTXT ("10GB - 100GB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW10GB100GBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW100GB1TBCnt () > 0) + { + sb.sprintf (GTXT ("100GB - 1TB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW100GB1TBCnt ()); + writeStat->append (sb.toString ()); + } + if (fDataTotal->getW1TB10TBCnt () > 0) + { + sb.sprintf (GTXT ("1TB - 10TB")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getW1TB10TBCnt ()); + writeStat->append (sb.toString ()); + } + + sb.sprintf (GTXT ("Longest write")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%.6f (secs.)"), + (double) (fDataTotal->getWSlowestBytes () / (double) NANOSEC)); + writeStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Smallest write bytes")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (fDataTotal->getWSmallestBytes ())); + writeStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Largest write bytes")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (fDataTotal->getWLargestBytes ())); + writeStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total time")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%.6f (secs.)"), + (double) (fDataTotal->getWriteTime () / (double) NANOSEC)); + writeStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total calls")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (fDataTotal->getWriteCnt ())); + writeStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total bytes")); + writeStat->append (sb.toString ()); + sb.sprintf (NTXT ("%lld"), (long long) (fDataTotal->getWriteBytes ())); + writeStat->append (sb.toString ()); + } + + if (fDataTotal->getReadCnt () > 0) + { + if (fDataTotal->getR0KB1KBCnt () > 0) + { + sb.sprintf (GTXT ("0KB - 1KB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR0KB1KBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR1KB8KBCnt () > 0) + { + sb.sprintf (GTXT ("1KB - 8KB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR1KB8KBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR8KB32KBCnt () > 0) + { + sb.sprintf (GTXT ("8KB - 32KB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR8KB32KBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR32KB128KBCnt () > 0) + { + sb.sprintf (GTXT ("32KB - 128KB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR32KB128KBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR128KB256KBCnt () > 0) + { + sb.sprintf (GTXT ("128KB - 256KB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR128KB256KBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR256KB512KBCnt () > 0) + { + sb.sprintf (GTXT ("256KB - 512KB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR256KB512KBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR512KB1000KBCnt () > 0) + { + sb.sprintf (GTXT ("512KB - 1000KB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR512KB1000KBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR1000KB10MBCnt () > 0) + { + sb.sprintf (GTXT ("1000KB - 10MB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR1000KB10MBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR10MB100MBCnt () > 0) + { + sb.sprintf (GTXT ("10MB - 100MB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR10MB100MBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR100MB1GBCnt () > 0) + { + sb.sprintf (GTXT ("100MB - 1GB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR100MB1GBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR1GB10GBCnt () > 0) + { + sb.sprintf (GTXT ("1GB - 10GB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR1GB10GBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR10GB100GBCnt () > 0) + { + sb.sprintf (GTXT ("10GB - 100GB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR10GB100GBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR100GB1TBCnt () > 0) + { + sb.sprintf (GTXT ("100GB - 1TB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR100GB1TBCnt ()); + readStat->append (sb.toString ()); + } + if (fDataTotal->getR1TB10TBCnt () > 0) + { + sb.sprintf (GTXT ("1TB - 10TB")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), fDataTotal->getR1TB10TBCnt ()); + readStat->append (sb.toString ()); + } + + sb.sprintf (GTXT ("Longest read")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%.6f (secs.)"), + (double) (fDataTotal->getRSlowestBytes () / (double) NANOSEC)); + readStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Smallest read bytes")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (fDataTotal->getRSmallestBytes ())); + readStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Largest read bytes")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (fDataTotal->getRLargestBytes ())); + readStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total time")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%.6f (secs.)"), + (double) (fDataTotal->getReadTime () / (double) NANOSEC)); + readStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total calls")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (fDataTotal->getReadCnt ())); + readStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total bytes")); + readStat->append (sb.toString ()); + sb.sprintf (NTXT ("%lld"), (long long) (fDataTotal->getReadBytes ())); + readStat->append (sb.toString ()); + } + + if (fDataTotal->getOtherCnt () > 0) + { + sb.sprintf (GTXT ("Total time")); + otherStat->append (sb.toString ()); + sb.sprintf (NTXT ("%.6f (secs.)"), + (double) (fDataTotal->getOtherTime () / (double) NANOSEC)); + otherStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total calls")); + otherStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (fDataTotal->getOtherCnt ())); + otherStat->append (sb.toString ()); + } + + if (fDataTotal->getErrorCnt () > 0) + { + sb.sprintf (GTXT ("Total time")); + errorStat->append (sb.toString ()); + sb.sprintf (NTXT ("%.6f (secs.)"), + (double) (fDataTotal->getErrorTime () / (double) NANOSEC)); + errorStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total calls")); + errorStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (fDataTotal->getErrorCnt ())); + errorStat->append (sb.toString ()); + } + Vector<Vector<char*>*>* statisticsData = new Vector<Vector<char*>*>(4); + statisticsData->store (0, writeStat); + statisticsData->store (1, readStat); + statisticsData->store (2, otherStat); + statisticsData->store (3, errorStat); + return statisticsData; +} + +Vector<Vector<char*>*>* +dbeGetHeapStatistics (int dbevindex) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + Hist_data *hist_data; + Hist_data::HistItem *hi; + HeapData *hDataTotal; + hist_data = dbev->heapcs_data; + if (hist_data == NULL) + return NULL; + + hi = hist_data->fetch (0); + hDataTotal = (HeapData*) hi->obj; + Vector<char*> *memoryUsage = new Vector<char*>; + Vector<char*> *allocStat = new Vector<char*>; + Vector<char*> *leakStat = new Vector<char*>; + + memoryUsage->append (dbe_strdup (GTXT ("Process With Highest Peak Memory Usage"))); + allocStat->append (dbe_strdup (GTXT ("Memory Allocations Statistics"))); + leakStat->append (dbe_strdup (GTXT ("Memory Leaks Statistics"))); + StringBuilder sb; + if (hDataTotal->getPeakMemUsage () > 0) + { + sb.sprintf (GTXT ("Heap size bytes")); + memoryUsage->append (sb.toString ()); + sb.sprintf (NTXT ("%lld"), (long long) (hDataTotal->getPeakMemUsage ())); + memoryUsage->append (sb.toString ()); + + sb.sprintf (GTXT ("Experiment Id")); + memoryUsage->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (hDataTotal->getUserExpId ())); + memoryUsage->append (sb.toString ()); + + sb.sprintf (GTXT ("Process Id")); + memoryUsage->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (hDataTotal->getPid ())); + memoryUsage->append (sb.toString ()); + + Vector<hrtime_t> *pTimestamps; + pTimestamps = hDataTotal->getPeakTimestamps (); + if (pTimestamps != NULL) + { + for (int i = 0; i < pTimestamps->size (); i++) + { + sb.sprintf (GTXT ("Time of peak")); + memoryUsage->append (sb.toString ()); + sb.sprintf (NTXT ("%.3f (secs.)"), (double) (pTimestamps->fetch (i) / (double) NANOSEC)); + memoryUsage->append (sb.toString ()); + } + } + } + + if (hDataTotal->getAllocCnt () > 0) + { + if (hDataTotal->getA0KB1KBCnt () > 0) + { + sb.sprintf (GTXT ("0KB - 1KB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA0KB1KBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA1KB8KBCnt () > 0) + { + sb.sprintf (GTXT ("1KB - 8KB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA1KB8KBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA8KB32KBCnt () > 0) + { + sb.sprintf (GTXT ("8KB - 32KB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA8KB32KBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA32KB128KBCnt () > 0) + { + sb.sprintf (GTXT ("32KB - 128KB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA32KB128KBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA128KB256KBCnt () > 0) + { + sb.sprintf (GTXT ("128KB - 256KB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA128KB256KBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA256KB512KBCnt () > 0) + { + sb.sprintf (GTXT ("256KB - 512KB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA256KB512KBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA512KB1000KBCnt () > 0) + { + sb.sprintf (GTXT ("512KB - 1000KB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA512KB1000KBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA1000KB10MBCnt () > 0) + { + sb.sprintf (GTXT ("1000KB - 10MB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA1000KB10MBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA10MB100MBCnt () > 0) + { + sb.sprintf (GTXT ("10MB - 100MB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA10MB100MBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA100MB1GBCnt () > 0) + { + sb.sprintf (GTXT ("100MB - 1GB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA100MB1GBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA1GB10GBCnt () > 0) + { + sb.sprintf (GTXT ("1GB - 10GB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA1GB10GBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA10GB100GBCnt () > 0) + { + sb.sprintf (GTXT ("10GB - 100GB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA10GB100GBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA100GB1TBCnt () > 0) + { + sb.sprintf (GTXT ("100GB - 1TB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA100GB1TBCnt ()); + allocStat->append (sb.toString ()); + } + if (hDataTotal->getA1TB10TBCnt () > 0) + { + sb.sprintf (GTXT ("1TB - 10TB")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getA1TB10TBCnt ()); + allocStat->append (sb.toString ()); + } + + sb.sprintf (GTXT ("Smallest allocation bytes")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (hDataTotal->getASmallestBytes ())); + allocStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Largest allocation bytes")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (hDataTotal->getALargestBytes ())); + allocStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total allocations")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (hDataTotal->getAllocCnt ())); + allocStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total bytes")); + allocStat->append (sb.toString ()); + sb.sprintf (NTXT ("%lld"), (long long) (hDataTotal->getAllocBytes ())); + allocStat->append (sb.toString ()); + } + + if (hDataTotal->getLeakCnt () > 0) + { + if (hDataTotal->getL0KB1KBCnt () > 0) + { + sb.sprintf (GTXT ("0KB - 1KB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL0KB1KBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL1KB8KBCnt () > 0) + { + sb.sprintf (GTXT ("1KB - 8KB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL1KB8KBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL8KB32KBCnt () > 0) + { + sb.sprintf (GTXT ("8KB - 32KB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL8KB32KBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL32KB128KBCnt () > 0) + { + sb.sprintf (GTXT ("32KB - 128KB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL32KB128KBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL128KB256KBCnt () > 0) + { + sb.sprintf (GTXT ("128KB - 256KB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL128KB256KBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL256KB512KBCnt () > 0) + { + sb.sprintf (GTXT ("256KB - 512KB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL256KB512KBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL512KB1000KBCnt () > 0) + { + sb.sprintf (GTXT ("512KB - 1000KB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL512KB1000KBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL1000KB10MBCnt () > 0) + { + sb.sprintf (GTXT ("1000KB - 10MB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL1000KB10MBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL10MB100MBCnt () > 0) + { + sb.sprintf (GTXT ("10MB - 100MB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL10MB100MBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL100MB1GBCnt () > 0) + { + sb.sprintf (GTXT ("100MB - 1GB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL100MB1GBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL1GB10GBCnt () > 0) + { + sb.sprintf (GTXT ("1GB - 10GB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL1GB10GBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL10GB100GBCnt () > 0) + { + sb.sprintf (GTXT ("10GB - 100GB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL10GB100GBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL100GB1TBCnt () > 0) + { + sb.sprintf (GTXT ("100GB - 1TB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL100GB1TBCnt ()); + leakStat->append (sb.toString ()); + } + if (hDataTotal->getL1TB10TBCnt () > 0) + { + sb.sprintf (GTXT ("1TB - 10TB")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), hDataTotal->getL1TB10TBCnt ()); + leakStat->append (sb.toString ()); + } + + sb.sprintf (GTXT ("Smallest leaked bytes")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (hDataTotal->getLSmallestBytes ())); + leakStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Largest leaked bytes")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (hDataTotal->getLLargestBytes ())); + leakStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total leaked")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%d"), (int) (hDataTotal->getLeakCnt ())); + leakStat->append (sb.toString ()); + + sb.sprintf (GTXT ("Total bytes")); + leakStat->append (sb.toString ()); + sb.sprintf (NTXT ("%lld"), (long long) (hDataTotal->getLeakBytes ())); + leakStat->append (sb.toString ()); + } + Vector<Vector<char*>*>* statisticsData = new Vector<Vector<char*>*>(3); + statisticsData->store (0, memoryUsage); + statisticsData->store (1, allocStat); + statisticsData->store (2, leakStat); + return statisticsData; +} + +Vector<char*> * +dbeGetFuncNames (int dbevindex, Vector<Obj> *funcs) +{ + int len = funcs->size (); + Vector<char*> *list = new Vector<char*>(len); + for (int i = 0; i < len; i++) + list->store (i, dbeGetFuncName (dbevindex, funcs->fetch (i))); // no strdup() + return list; +} + +Vector<char*> * +dbeGetObjNamesV2 (int dbevindex, Vector<uint64_t> *ids) +{ + int len = ids->size (); + Vector<char*> *list = new Vector<char*>(len); + for (int i = 0; i < len; i++) + list->store (i, dbeGetObjNameV2 (dbevindex, ids->fetch (i))); // no strdup() + return list; +} + +char * +dbeGetFuncName (int dbevindex, Obj func) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + if (func == 0) + return NULL; + char *fname; + fname = ((Histable *) func)->get_name (dbev->get_name_format ()); + return fname ? dbe_strdup (fname) : NULL; +} + +Vector<uint64_t> * +dbeGetFuncIds (int dbevindex, Vector<Obj> *funcs) +{ + int len = funcs->size (); + Vector<uint64_t> *list = new Vector<uint64_t>(len); + for (int i = 0; i < len; i++) + list->store (i, dbeGetFuncId (dbevindex, funcs->fetch (i))); + return list; +} + +uint64_t +dbeGetFuncId (int dbevindex, Obj func) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + if (func == 0) + return 0; + uint64_t id = ((Histable *) func)->id; + return id; +} + +char * +dbeGetObjNameV2 (int dbevindex, uint64_t id) +{ + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Histable *obj = dbeSession->findObjectById (id); + if (obj == NULL) + return NULL; + char *fname = obj->get_name (dbev->get_name_format ()); + return fname ? dbe_strdup (fname) : NULL; +} + +char * +dbeGetDataspaceTypeDesc (int /*dbevindex*/, Obj stack) +{ + if (stack == 0) + return NULL; + Histable *hist = CallStack::getStackPC ((void *) stack, 0); + DbeInstr *instr; + Histable::Type type = hist->get_type (); + if (type != Histable::INSTR) + return NULL; + else + instr = (DbeInstr *) hist; + char *descriptor = instr->get_descriptor (); + return descriptor ? dbe_strdup (descriptor) : NULL; +} + +Vector<void*> * +dbeGetDataDescriptorsV2 (int exp_id) +{ + Experiment *exp = dbeSession->get_exp (exp_id); + if (exp == NULL) + return NULL; + Vector<int> *dataId = new Vector<int>; + Vector<char*> *dataName = new Vector<char*>; + Vector<char*> *dataUName = new Vector<char*>; + Vector<int> *auxProp = new Vector<int>; + Vector<DataDescriptor*> *ddscr = exp->getDataDescriptors (); + for (int i = 0; i < ddscr->size (); i++) + { + DataDescriptor *dataDscr = ddscr->fetch (i); + if (dataDscr->getFlags () & DDFLAG_NOSHOW) + continue; + int data_id = dataDscr->getId (); + int aux_prop_id = (data_id == DATA_HWC) ? PROP_HWCTAG : PROP_NONE; + dataId->append (data_id); + dataName->append (strdup (dataDscr->getName ())); + dataUName->append (strdup (dataDscr->getUName ())); + auxProp->append (aux_prop_id); + } + delete ddscr; + Vector<void*> *res = new Vector<void*>(3); + res->store (0, dataId); + res->store (1, dataName); + res->store (2, dataUName); + res->store (3, auxProp); + return res; +} + +Vector<void*> * +dbeGetDataPropertiesV2 (int exp_id, int data_id) +{ + Experiment *exp = dbeSession->get_exp (exp_id); + if (exp == NULL) + return NULL; + DataDescriptor *dataDscr = exp->get_raw_events (data_id); + if (dataDscr == NULL) + return NULL; + Vector<PropDescr*> *props = dataDscr->getProps (); + Vector<int> *propId = new Vector<int>(props->size ()); + Vector<char*> *propUName = new Vector<char*>(props->size ()); + Vector<int> *propTypeId = new Vector<int>(props->size ()); + Vector<char*> *propTypeName = new Vector<char*>(props->size ()); + Vector<int> *propFlags = new Vector<int>(props->size ()); + Vector<char*> *propName = new Vector<char*>(props->size ()); + Vector<void*> *propStateNames = new Vector<void*>(props->size ()); + Vector<void*> *propStateUNames = new Vector<void*>(props->size ()); + + for (int i = 0; i < props->size (); i++) + { + PropDescr *prop = props->fetch (i); + char *pname = prop->name; + if (pname == NULL) + pname = NTXT (""); + char *uname = prop->uname; + if (uname == NULL) + uname = pname; + int vtypeNum = prop->vtype; + if (vtypeNum < 0 || vtypeNum >= TYPE_LAST) + vtypeNum = TYPE_NONE; + const char * vtypeNames[] = VTYPE_TYPE_NAMES; + const char *vtype = vtypeNames[prop->vtype]; + Vector<char*> *stateNames = NULL; + Vector<char*> *stateUNames = NULL; + int nStates = prop->getMaxState (); + if (nStates > 0) + { + stateNames = new Vector<char*>(nStates); + stateUNames = new Vector<char*>(nStates); + for (int kk = 0; kk < nStates; kk++) + { + const char * stateName = prop->getStateName (kk); + stateNames->store (kk, dbe_strdup (stateName)); + const char * Uname = prop->getStateUName (kk); + stateUNames->store (kk, dbe_strdup (Uname)); + } + } + propId->store (i, prop->propID); + propUName->store (i, dbe_strdup (uname)); + propTypeId->store (i, prop->vtype); + propTypeName->store (i, dbe_strdup (vtype)); + propFlags->store (i, prop->flags); + propName->store (i, dbe_strdup (pname)); + propStateNames->store (i, stateNames); + propStateUNames->store (i, stateUNames); + } + Vector<void*> *res = new Vector<void*>(7); + res->store (0, propId); + res->store (1, propUName); + res->store (2, propTypeId); + res->store (3, propTypeName); + res->store (4, propFlags); + res->store (5, propName); + res->store (6, propStateNames); + res->store (7, propStateUNames); + return res; +} + +Vector<void *> * +dbeGetExperimentTimeInfo (Vector<int> *exp_ids) +{ + int sz = exp_ids->size (); + Vector<long long> *offset_time = new Vector<long long> (sz); + Vector<long long> *start_time = new Vector<long long> (sz); + Vector<long long> *end_time = new Vector<long long> (sz); + Vector<long long> *start_wall_sec = new Vector<long long> (sz); + Vector<char* > *hostname = new Vector<char*> (sz); + Vector<int> *cpu_freq = new Vector<int> (sz); + for (int ii = 0; ii < sz; ii++) + { + int expIdx = exp_ids->fetch (ii); + { // update end_time by forcing fetch of experiment data + // workaround until dbeGetEndTime() is more robust + int id = (expIdx < 0) ? 0 : expIdx; + Experiment *exp = dbeSession->get_exp (id); + if (exp) + { + Vector<DataDescriptor*> *ddscr = exp->getDataDescriptors (); + delete ddscr; + } + } + offset_time->store (ii, dbeGetRelativeStartTime (0, expIdx)); + start_time->store (ii, dbeGetStartTime (0, expIdx)); + end_time->store (ii, dbeGetEndTime (0, expIdx)); + start_wall_sec->store (ii, dbeGetWallStartSec (0, expIdx)); + hostname->store (ii, dbeGetHostname (0, expIdx)); + cpu_freq->store (ii, dbeGetClock (0, expIdx)); + } + Vector<void*> *res = new Vector<void*>(4); + res->store (0, offset_time); + res->store (1, start_time); + res->store (2, end_time); + res->store (3, start_wall_sec); + res->store (4, hostname); + res->store (5, cpu_freq); + return res; +} + +Vector<void *> * +dbeGetExperimentDataDescriptors (Vector<int> *exp_ids) +{ + int sz = exp_ids->size (); + Vector<void*> *exp_dscr_info = new Vector<void*> (sz); + Vector<void*> *exp_dscr_props = new Vector<void*> (sz); + + for (int ii = 0; ii < sz; ii++) + { + int expIdx = exp_ids->fetch (ii); + Vector<void*> *ddscrInfo = dbeGetDataDescriptorsV2 (expIdx); + Vector<void*> *ddscrProps = new Vector<void*> (); // one entry per ddscrInfo + if (ddscrInfo) + { + Vector<int> *dataId = (Vector<int>*)ddscrInfo->fetch (0); + if (dataId) + { + // loop thru data descriptors + int ndata = dataId->size (); + for (int j = 0; j < ndata; ++j) + { + Vector<void*> *props = dbeGetDataPropertiesV2 (expIdx, dataId->fetch (j)); + ddscrProps->store (j, props); + } + } + } + exp_dscr_info->store (ii, ddscrInfo); + exp_dscr_props->store (ii, ddscrProps); + } + Vector<void*> *res = new Vector<void*>(2); + res->store (0, exp_dscr_info); + res->store (1, exp_dscr_props); + return res; +} + +static Vector<void *> * +dbeGetTLDataRepVals (VMode view_mode, hrtime_t start_ts, hrtime_t delta, + int numDeltas, DataView*packets, + Vector<long> *representativeEvents, bool showDuration); + +static bool +dbeHasTLData (int dbevindex, int exp_id, int data_id, int entity_prop_id, + int entity_prop_value, int aux) +{ + DataView *packets = + getTimelinePackets (dbevindex, exp_id, data_id, entity_prop_id); + if (!packets || packets->getSize () == 0) + return false; + long start_ind = getIdxByVals (packets, aux, entity_prop_value, + 0, DataView::REL_GTEQ); // time >= 0 + if (start_ind < 0) + return false; + + DbeView *dbev = dbeSession->getView (dbevindex); + VMode view_mode = dbev->get_view_mode (); + Experiment *exp = dbeSession->get_exp (exp_id); + if (!hasInvisbleTLEvents (exp, view_mode)) + return true; // all events are visible, no further checking required + long end_ind = getIdxByVals (packets, aux, entity_prop_value, + MAX_TIME, DataView::REL_LTEQ); + for (long ii = start_ind; ii <= end_ind; ii++) + { + if (!isVisibleTLEvent (exp, view_mode, packets, ii)) + continue; + return true; // first visible packet => has data + } + return false; +} + +Vector<bool> * +dbeHasTLData (int dbev_index, Vector<int> *exp_ids, Vector<int> *data_ids, + Vector<int> *entity_prop_ids, // LWP,CPU,THR, etc + Vector<int> *entity_prop_values, Vector<int> *auxs) +{ + DbeView *dbev = dbeSession->getView (dbev_index); + if (!dbev->isShowAll () && (dbev->isShowHideChanged () + || dbev->isNewViewMode ())) + { + // LIBRARY_VISIBILITY + dbev->resetAndConstructShowHideStacks (); + if (dbev->isNewViewMode ()) + dbev->resetNewViewMode (); + if (dbev->isShowHideChanged ()) + dbev->resetShowHideChanged (); + } + + int sz = exp_ids->size (); + Vector<bool> *hasVec = new Vector<bool>(sz); + for (int ii = 0; ii < sz; ii++) + { + bool hasData = dbeHasTLData (dbev_index, exp_ids->fetch (ii), + data_ids->fetch (ii), + entity_prop_ids->fetch (ii), + entity_prop_values->fetch (ii), + auxs->fetch (ii)); + hasVec->store (ii, hasData); + } + return hasVec; +} + +/* + * dbeGetTLData implements: + * FROM data_id + * DURATION >= delta AND ( start_ts <= TSTAMP < start_ts+num*delta OR + * start_ts <= TSTAMP-DURATION < start_ts+num*delta ) + * OR + * FAIR( DURATION < delta AND ( start_ts <= TSTAMP < start_ts+num*delta ) ) + * WHERE lfilter + */ + +Vector<void *> * +dbeGetTLData ( + int dbevindex, + int exp_id, + int data_id, // DATA_* + int entity_prop_id, // Show PROP_LWPID, PROP_CPUID, PROP_THRID, PROP_EXPID, or N/A + int entity_prop_value, // which LWPID, CPUID, THRID, EXPID for this request + int aux, + hrtime_t param_start_ts, + hrtime_t param_delta, + int param_numDeltas, + bool getRepresentatives, // fetch TL representatives + Vector<char *> *chartProps) // calculate sums for these property vals +{ + const hrtime_t start_ts = param_start_ts; + const hrtime_t delta = param_delta; + const int numDeltas = param_numDeltas; + DbeView *dbev = dbeSession->getView (dbevindex); + if (dbev == NULL) + abort (); + Experiment *exp = dbeSession->get_exp (exp_id); + if (exp == NULL) + return NULL; + if (getRepresentatives == false && chartProps == NULL) + return NULL; + if (delta <= 0) + return NULL; + + hrtime_t tmp_ts = start_ts + delta * numDeltas; + if (tmp_ts < start_ts) + tmp_ts = MAX_TIME; + const hrtime_t end_ts = tmp_ts; + if (exp->get_status () == Experiment::INCOMPLETE && + exp->getLastEvent () < end_ts) + exp->update (); + DataView *packets = + getTimelinePackets (dbevindex, exp_id, data_id, entity_prop_id); + if (packets == NULL) + return NULL; // strange, no data view? + + VMode view_mode = dbev->get_view_mode (); // user, expert, machine //YXXX yuck + + // storage for calculating timeline representative events + Vector<long> *representativeEvents = NULL; + // list of representative events to be displayed on TL + Vector<int> *binRepIdx = NULL; + // for each bin, index of current "best" representativeEvent + Vector<void*> *representativeVals = NULL; + // TL representative packets' values + + // storage for calculating charts + Vector<int> *propIds = NULL; // [propIdx], which prop to measure + Vector<void*> *propVals = NULL; // [propIdx][bin], prop vals + Vector<int> *propNumStates = NULL; // [propIdx], how many states for prop? + Vector<bool> *propCumulativeChart = NULL; // [propIdx], data represents cumulative totals + Vector<long long> *propCumulativeRecentBinLastVal = NULL; // [propIdx], most recent value + Vector<long long> *propCumulativeRecentBinHighVal = NULL; // [propIdx], highest value for propCumulativeRecentBin + Vector<int> *propCumulativeRecentBin = NULL; // [propIdx], most recent bin + + // determine when to show duration of events + bool tmp_repsShowDuration = false; + bool tmp_statesUseDuration = false; + bool tmp_extendMicrostates = false; + const hrtime_t ptimerTickDuration = exp->get_params ()->ptimer_usec * 1000LL; // nanoseconds per tick + const bool hasDuration = packets->getProp (PROP_EVT_TIME) ? true : false; + if (hasDuration) + { + switch (entity_prop_id) + { + case PROP_CPUID: + tmp_repsShowDuration = false; + tmp_statesUseDuration = false; + break; + case PROP_THRID: + case PROP_LWPID: + tmp_repsShowDuration = true; + tmp_statesUseDuration = true; + tmp_extendMicrostates = (DATA_CLOCK == data_id) && (ptimerTickDuration < param_delta); + break; + case PROP_EXPID: + case PROP_NONE: // experiment summary row uses this + default: + if (DATA_SAMPLE == data_id) + { + tmp_repsShowDuration = true; + tmp_statesUseDuration = true; + } + else if (DATA_GCEVENT == data_id) + { + tmp_repsShowDuration = true; + tmp_statesUseDuration = true; + } + else if (DATA_CLOCK == data_id) + { + tmp_repsShowDuration = false; + tmp_statesUseDuration = true; + tmp_extendMicrostates = true; + } + else + { + tmp_repsShowDuration = false; + tmp_statesUseDuration = true; + } + break; + } + } + const bool repsShowDuration = tmp_repsShowDuration; // show stretched callstacks + const bool statesUseDuration = tmp_statesUseDuration; // use duration to calculate state charts + const bool extendMicrostates = tmp_extendMicrostates; // we show discrete profiling microstates with + // width=(tick-1), but for computing + // zoomed-out graphs we need to extend to + // account for all ticks, width=(ntick) + const bool reverseScan = repsShowDuration || extendMicrostates; // scan packets in reverse + + // determine range of packet indices (lo_pkt_idx, hi_pkt_idx) + long lo_pkt_idx, hi_pkt_idx; + if (extendMicrostates && !(entity_prop_id == PROP_THRID || entity_prop_id == PROP_LWPID)) + { + // merging data from multiple threads, need to scan all packets with timestamp [start_ts, exp end] + hrtime_t exp_end_time = exp->getLastEvent () + 1; + hi_pkt_idx = getIdxByVals (packets, aux, entity_prop_value, + exp_end_time, DataView::REL_LT); // last item + } + else + hi_pkt_idx = getIdxByVals (packets, aux, entity_prop_value, + end_ts, DataView::REL_LT); + if (repsShowDuration) + { + // There are two issues to deal with + // 1. events that end "off screen" to the right + // 2. overlapping events + + // 1. events that end "off screen" to the right + // For now, we only consistently handle the case where events don't overlap. + // Note that packet timestamps mark end of duration, not start. + // This means that the rightmost event won't be within hi_pkt_idx. + // Solution: Check if end+1 packet _started_ in-range + // Caveat: because we only look ahead by one packet, if there are + // overlapping duration events (e.g. EXPID aggregation)), zoom level + // and panning combo may cause events with TSTAMP>end_ts + // to appear/disappear. A complete solution would involve + // a solution to 2. + + // 2. overlapping events + // For now, we have a simplistic solution that makes "wide" events win. However, + // a future solution for deterministically dealing with overlap might look like this: + // - find all packets that touch the visible time range + // - possibly use two DataViews: one with TSTAMP_HI sort and one with TSTAMP_LO + // sort to allow efficient determination of packets with HI and LO endpoints in-range + // - create buckets to capture "winning" event for each bin (each pixel, that is) + // - sort the new list of packets by TSTAMP_HI (for example) + // - looping thru the packets that are in-range, update every bin it touches with it's id + // - if there is overlap, earlier packets will be kicked out of bins + // - On the GUI side, paint one event at a time, as normal. + // - However, for selections, recognize that duration of event may span many bins + // + long idx; + if (hi_pkt_idx >= 0) + // a packet was found to the left of the end time + idx = hi_pkt_idx + 1; // attempt to go one packet right + else + idx = getIdxByVals (packets, aux, entity_prop_value, + end_ts, DataView::REL_GTEQ); + if (isValidIdx (packets, entity_prop_id, aux, entity_prop_value, idx)) + { + int64_t pkt_ts = packets->getLongValue (PROP_TSTAMP, idx); + int64_t duration = packets->getLongValue (PROP_EVT_TIME, idx); + pkt_ts -= duration; + if (pkt_ts < end_ts) + hi_pkt_idx = idx; + } + } + lo_pkt_idx = getIdxByVals (packets, aux, entity_prop_value, + start_ts, DataView::REL_GTEQ); + + // allocate structs that return chart data + bool hasCumulativeCharts = false; + if (chartProps && chartProps->size () > 0) + { + int nprops = chartProps->size (); + // pre-allocate storage + propIds = new Vector<int> (nprops); + propVals = new Vector<void*>(nprops); + propNumStates = new Vector<int> (nprops); + propCumulativeChart = new Vector<bool>(nprops); + propCumulativeRecentBinLastVal = new Vector<long long>(nprops); + propCumulativeRecentBinHighVal = new Vector<long long>(nprops); + propCumulativeRecentBin = new Vector<int>(nprops); + for (int propNum = 0; propNum < nprops; propNum++) + { + const char* propStr = chartProps->fetch (propNum); + int items_per_prop = 0; + int prop_id = PROP_NONE; + if (!strcmp (propStr, "EVT_COUNT")) + items_per_prop = 1; // use PROP_NONE for counting packets + else + { + int lookup_prop_id = dbeSession->getPropIdByName (propStr); + PropDescr *propDscr = packets->getProp (lookup_prop_id); + if (propDscr != NULL) + { + switch (propDscr->vtype) + { + case TYPE_INT32: + case TYPE_UINT32: + case TYPE_INT64: + case TYPE_UINT64: + items_per_prop = propDscr->getMaxState () + 1; + // add extra slot to store values with out-of-range idx + prop_id = lookup_prop_id; + break; + case TYPE_DOUBLE: + break; // not implemented yet + case TYPE_STRING: + case TYPE_OBJ: + case TYPE_DATE: + default: + break; + } + } + } + void *vals; + if (!items_per_prop) + vals = NULL; + else if (items_per_prop == 1) + { + Vector<long long> *longVals = new Vector<long long> (); + longVals->store (numDeltas - 1, 0); // initialize all elements + vals = longVals; + } + else + { + Vector<Vector<long long>*> *stateVals = + new Vector<Vector<long long>*> (); + vals = stateVals; + // initialize only on-demand, some may not be needed + } + + bool isCumulativeChart; +#define YXXX_HEAP_VS_TIME 1 // YXXX add data meaning to properties? +#if YXXX_HEAP_VS_TIME + isCumulativeChart = (prop_id == PROP_HCUR_LEAKS || prop_id == PROP_HCUR_ALLOCS); +#endif + if (isCumulativeChart) + hasCumulativeCharts = true; + propIds->store (propNum, prop_id); + propVals->store (propNum, vals); + propNumStates->store (propNum, items_per_prop); + propCumulativeRecentBinLastVal->store (propNum, 0); + propCumulativeRecentBinHighVal->store (propNum, 0); + propCumulativeRecentBin->store (propNum, 0); + propCumulativeChart->store (propNum, isCumulativeChart); + } + } + + // Adjust idx range for calculating 'cumulative charts' e.g. heap size + if (hasCumulativeCharts) + { + // set initial values if earlier packet exists + long lo_idx; + if (lo_pkt_idx >= 0) + // packet was found to the right of start + lo_idx = lo_pkt_idx - 1; // attempt to go left by one event + else + // no packet was to the right of start, look left of start + lo_idx = getIdxByVals (packets, aux, entity_prop_value, + start_ts, DataView::REL_LT); + if (isValidIdx (packets, entity_prop_id, aux, entity_prop_value, lo_idx)) + { + // preceding packet found + // update initial values + int nprops = propCumulativeChart->size (); + for (int propNum = 0; propNum < nprops; propNum++) + { + if (!propCumulativeChart->fetch (propNum)) + continue; + int propId = propIds->fetch (propNum); + long long value = packets->getLongValue (propId, lo_idx); + propCumulativeRecentBinLastVal->store (propNum, value); + propCumulativeRecentBinHighVal->store (propNum, value); + } + // update indices used for iterating + lo_pkt_idx = lo_idx; + if (hi_pkt_idx < lo_pkt_idx) + hi_pkt_idx = lo_pkt_idx; + } + } + if (lo_pkt_idx < 0 || hi_pkt_idx < 0) + goto dbeGetTLData_done; // no data; return empty vectors, not null + + // representative events (subset of callstacks to represent on TL) + if (getRepresentatives) + { + representativeEvents = new Vector<long>(numDeltas); + // per-bin, longest event's index + binRepIdx = new Vector<int>(numDeltas); + for (int ii = 0; ii < numDeltas; ++ii) + binRepIdx->append (-1); + } + // While packets are sorted by _end_ timestamp (TSTAMP), + // after calculating start times for non-zero durations, + // start times are not guaranteed be monotonically increasing. + // For packets with duration, we'll scan them in reverse order to + // take advantage of the monotonically decreasing _end_ timestamps. + long start_idx, idx_inc; + if (!reverseScan) + { + start_idx = lo_pkt_idx; + idx_inc = 1; + } + else + { + start_idx = hi_pkt_idx; + idx_inc = -1; + } + for (long ii = start_idx; ii >= lo_pkt_idx && ii <= hi_pkt_idx; ii += idx_inc) + { + if (!isVisibleTLEvent (exp, view_mode, packets, ii) && !hasCumulativeCharts) + continue; + + // determine packet time duration and start bin + int tmp_start_bin; // packet start bin + int tmp_end_bin; // packet end bin (inclusive) + const hrtime_t pkt_end_ts = packets->getLongValue (PROP_TSTAMP, ii); + const hrtime_t pkt_dur = packets->getLongValue (PROP_EVT_TIME, ii); + const hrtime_t pkt_start_ts = pkt_end_ts - pkt_dur; + if (pkt_end_ts < start_ts && !hasCumulativeCharts) + continue; // weird, should not happen + if (pkt_start_ts >= end_ts) + continue; // could happen + hrtime_t bin_end_ts = pkt_end_ts; + if (bin_end_ts >= end_ts) + bin_end_ts = end_ts - 1; + tmp_end_bin = (int) ((bin_end_ts - start_ts) / delta); + hrtime_t bin_start_ts = pkt_start_ts; + if (bin_start_ts < start_ts) + bin_start_ts = start_ts; // event truncated to left. + tmp_start_bin = (int) ((bin_start_ts - start_ts) / delta); + // By definition + // (end_ts - start_ts) == delta * numDeltas + // and we know + // pkt_start < end_ts + // therefore + // (pkt_start - start_ts) < delta * numDeltas + // (pkt_start - start_ts) / delta < numDeltas + // bin < numDeltas + assert (tmp_end_bin < numDeltas); + assert (tmp_start_bin < numDeltas); + const bool is_offscreen = tmp_end_bin < 0 ? true : false; + if (tmp_end_bin < 0) + tmp_end_bin = 0; + const int pkt_end_bin = tmp_end_bin; // packet end bin (inclusive) + const int pkt_start_bin = tmp_start_bin; + if (getRepresentatives && !is_offscreen) + { // find best representative + // Note: for events with duration, we're scanning packets in order + // of decreasing end-timestamp. This means that the first packet + // that hits a particular _start_ bin will have the longest duration + // of any later packet that might hit that start bin. The + // the first packet will be the best (longest) packet. + const int bin = reverseScan ? pkt_start_bin : pkt_end_bin; + int eventIdx = binRepIdx->fetch (bin); + if (eventIdx == -1) + { + eventIdx = representativeEvents->size (); // append to end + representativeEvents->append (ii); + binRepIdx->store (bin, eventIdx); + } + } + if (propIds) + { // per-bin chart: sum across filtered packets + for (int propNum = 0; propNum < propIds->size (); propNum++) + { + void *thisProp = propVals->fetch (propNum); + if (thisProp == NULL) + continue; // no valid data + if (is_offscreen && !propCumulativeChart->fetch (propNum)) + continue; // offscreen events are only processed for cumulative charts + int propId = propIds->fetch (propNum); + long long val; + if (propId == PROP_NONE) + val = 1; // count + else + val = packets->getLongValue (propId, ii); + long nitems = propNumStates->fetch (propNum); + if (nitems < 1) + continue; + else if (nitems == 1) + { + // chart is not based on not multiple states + Vector<long long>* thisPropVals = + (Vector<long long>*)thisProp; + if (thisPropVals->size () == 0) + thisPropVals->store (numDeltas - 1, 0); + const int bin = statesUseDuration ? pkt_start_bin : pkt_end_bin; + if (!propCumulativeChart->fetch (propNum)) + { + val += thisPropVals->fetch (bin); + thisPropVals->store (bin, val); + } + else + { + // propCumulativeChart + long long high_value = propCumulativeRecentBinHighVal->fetch (propNum); + int last_bin = propCumulativeRecentBin->fetch (propNum); + if (last_bin < bin) + { + // backfill from previous event + // last_bin: store largest value (in case of multiple events) + thisPropVals->store (last_bin, high_value); + // propagate forward the bin's last value + long long last_value = propCumulativeRecentBinLastVal->fetch (propNum); + for (int kk = last_bin + 1; kk < bin; kk++) + thisPropVals->store (kk, last_value); + // prepare new bin for current event + high_value = 0; // high value of next bin is 0. + propCumulativeRecentBinHighVal->store (propNum, high_value); + propCumulativeRecentBin->store (propNum, bin); + } + long long this_value = packets->getLongValue (propId, ii); + propCumulativeRecentBinLastVal->store (propNum, this_value); + if (high_value < this_value) + { + // record the max + high_value = this_value; + propCumulativeRecentBinHighVal->store (propNum, high_value); + } + if (ii == hi_pkt_idx) + { + // bin: show largest value (in case of multiple events + thisPropVals->store (bin, high_value); + //forward fill remaining bins + for (int kk = bin + 1; kk < numDeltas; kk++) + thisPropVals->store (kk, this_value); + } + } + } + else + { + // means val is actually a state # + Vector<Vector<long long>*>* thisPropStateVals = + (Vector<Vector<long long>*>*)thisProp; + if (thisPropStateVals->size () == 0) + thisPropStateVals->store (numDeltas - 1, 0); + long stateNum; + if (val >= 0 && val < nitems) + stateNum = (long) val; + else + stateNum = nitems - 1; // out of range, use last slot + hrtime_t graph_pkt_dur = pkt_dur; + hrtime_t graph_pkt_start_ts = pkt_start_ts; + int tmp2_start_bin = pkt_start_bin; + if (propId == PROP_MSTATE) + { + if (statesUseDuration && extendMicrostates) + { + // microstate stacks are shown and filtered with width=NTICK-1 + // but for microstate graph calcs use width=NTICK. + graph_pkt_dur += ptimerTickDuration; + graph_pkt_start_ts -= ptimerTickDuration; + hrtime_t bin_start_ts = graph_pkt_start_ts; + if (bin_start_ts < start_ts) + bin_start_ts = start_ts; // event truncated to left. + tmp2_start_bin = (int) ((bin_start_ts - start_ts) / delta); + } + } + const int graph_pkt_start_bin = statesUseDuration ? tmp2_start_bin : pkt_end_bin; + + // We will distribute the state's presence evenly over duration of the event. + // When only a 'partial bin' is touched by an event, adjust accordingly. + long long value_per_bin; // weight to be applied to each bin + { + long long weight; + if (propId == PROP_MSTATE) // ticks to nanoseconds + weight = packets->getLongValue (PROP_NTICK, ii) * ptimerTickDuration; + else if (graph_pkt_dur) + weight = graph_pkt_dur; // nanoseconds + else + weight = 1; // no duration; indicate presence + if (graph_pkt_start_bin != pkt_end_bin) + { + // spans multiple bins + double nbins = (double) graph_pkt_dur / delta; + value_per_bin = weight / nbins; + } + else + value_per_bin = weight; + } + for (int evtbin = graph_pkt_start_bin; evtbin <= pkt_end_bin; evtbin++) + { + Vector<long long>* stateValues = + (Vector<long long>*) thisPropStateVals->fetch (evtbin); + if (stateValues == NULL) + { + // on-demand storage + stateValues = new Vector<long long>(nitems); + stateValues->store (nitems - 1, 0); // force memset of full vector + thisPropStateVals->store (evtbin, stateValues); + } + long long new_val = stateValues->fetch (stateNum); + if (graph_pkt_start_bin == pkt_end_bin || + (evtbin > graph_pkt_start_bin && evtbin < pkt_end_bin)) + { + new_val += value_per_bin; + } + else + { + // partial bin + const hrtime_t bin_start = start_ts + evtbin * delta; + const hrtime_t bin_end = start_ts + (evtbin + 1) * delta - 1; + if (evtbin == graph_pkt_start_bin) + { + // leftmost bin + if (graph_pkt_start_ts < bin_start) + new_val += value_per_bin; + else + { + double percent = (double) (bin_end - graph_pkt_start_ts) / delta; + new_val += value_per_bin*percent; + } + } + else + { + // rightmost bin + if (pkt_end_ts > bin_end) + new_val += value_per_bin; + else + { + double percent = (double) (pkt_end_ts - bin_start) / delta; + new_val += value_per_bin*percent; + } + } + } + stateValues->store (stateNum, new_val); + } + } + } + } + } + delete binRepIdx; + delete propIds; + delete propCumulativeChart; + delete propCumulativeRecentBinLastVal; + delete propCumulativeRecentBinHighVal; + delete propCumulativeRecentBin; + if (representativeEvents != NULL && reverseScan) + { + if (repsShowDuration) + { + //YXXX for now prune here, but in the future, let gui decide what to show + // Prune events that are completely obscured long duration events. + // Note: representativeEvents is sorted by decreasing _end_ timestamps. + Vector<long> *prunedEvents = new Vector<long>(numDeltas); + hrtime_t prev_start_ts = MAX_TIME; + long repCnt = representativeEvents->size (); + for (long kk = 0; kk < repCnt; kk++) + { + long ii = representativeEvents->fetch (kk); + hrtime_t tmp_end_ts = packets->getLongValue (PROP_TSTAMP, ii); + hrtime_t tmp_dur = packets->getLongValue (PROP_EVT_TIME, ii); + hrtime_t tmp_start_ts = tmp_end_ts - tmp_dur; + if (tmp_start_ts >= prev_start_ts) + // this event would be completely hidden + // (because of sorting, we know tmp_end_ts <= prev_end_ts) + continue; + prev_start_ts = tmp_start_ts; + prunedEvents->append (ii); + } + // invert order to to get increasing _end_ timestamps + representativeEvents->reset (); + for (long kk = prunedEvents->size () - 1; kk >= 0; kk--) + { + long packet_idx = prunedEvents->fetch (kk); + representativeEvents->append (packet_idx); + } + delete prunedEvents; + } + else + { // !repsShowDuration + // Note: representativeEvents is sorted by decreasing _end_ timestamps. + // Reverse the order: + long hi_idx = representativeEvents->size () - 1; + long lo_idx = 0; + while (hi_idx > lo_idx) + { + // swap + long lo = representativeEvents->fetch (lo_idx); + long hi = representativeEvents->fetch (hi_idx); + representativeEvents->store (lo_idx, hi); + representativeEvents->store (hi_idx, lo); + hi_idx--; + lo_idx++; + } + } + } + +dbeGetTLData_done: + if (getRepresentatives) + { + representativeVals = dbeGetTLDataRepVals (view_mode, start_ts, delta, + numDeltas, packets, representativeEvents, repsShowDuration); + delete representativeEvents; + } + Vector<void*> *results = new Vector<void*> (2); + results->store (0, representativeVals); + results->store (1, propVals); + return results; +} + +// add representative events to return buffer + +static Vector<void *> * +dbeGetTLDataRepVals (VMode view_mode, hrtime_t start_ts, hrtime_t delta, + int numDeltas, DataView*packets, + Vector<long> *representativeEvents, bool showDuration) +{ + int numrecs = representativeEvents ? representativeEvents->size () : 0; + // allocate storage for results + Vector<int> *startBins = new Vector<int>(numrecs); + Vector<int> *numBins = new Vector<int>(numrecs); + Vector<Obj> *eventIdxs = new Vector<Obj>(numrecs); + Vector<Obj> *stackIds = NULL; + if (packets->getProp (PROP_FRINFO)) + stackIds = new Vector<Obj>(numrecs); + Vector<int> *mstates = NULL; + if (packets->getProp (PROP_MSTATE)) + mstates = new Vector<int>(numrecs); + Vector<Vector<long long>*> *sampleVals = NULL; + if (packets->getProp (PROP_SMPLOBJ)) + sampleVals = new Vector<Vector<long long>*>(numrecs); + Vector<long long> *timeStart = new Vector<long long>(numrecs); + Vector<long long> *timeEnd = new Vector<long long>(numrecs); + int prevEndBin = -1; // make sure we don't overlap bins + for (int eventIdx = 0; eventIdx < numrecs; eventIdx++) + { + long packetIdx = representativeEvents->fetch (eventIdx); + // long eventId = packets->getIdByIdx( packetIdx ); + const hrtime_t pkt_tstamp = packets->getLongValue (PROP_TSTAMP, packetIdx); + const hrtime_t pkt_dur = showDuration ? packets->getLongValue (PROP_EVT_TIME, packetIdx) : 0; + timeStart->store (eventIdx, pkt_tstamp - pkt_dur); + timeEnd->store (eventIdx, pkt_tstamp); + + // calc startBin + int startBin = (int) ((pkt_tstamp - pkt_dur - start_ts) / delta); + if (startBin <= prevEndBin) + startBin = prevEndBin + 1; + // calc binCnt + int endBin = (int) ((pkt_tstamp - start_ts) / delta); + if (endBin >= numDeltas) + endBin = numDeltas - 1; + int binCnt = endBin - startBin + 1; + prevEndBin = endBin; + startBins->store (eventIdx, startBin); + numBins->store (eventIdx, binCnt); + eventIdxs->store (eventIdx, packetIdx); // store packet's idx + if (stackIds != NULL) + { + void* stackId = getStack (view_mode, packets, packetIdx); + stackIds->store (eventIdx, (Obj) (unsigned long) stackId); + } + if (mstates != NULL) + { + int mstate = packets->getIntValue (PROP_MSTATE, packetIdx); + mstates->store (eventIdx, mstate); + } + if (sampleVals != NULL) + { + Sample* sample = (Sample*) packets->getObjValue (PROP_SMPLOBJ, packetIdx); + if (!sample || !sample->get_usage ()) + sample = sample; + else + { + PrUsage* prusage = sample->get_usage (); + Vector<long long> *mstateVals = prusage->getMstateValues (); + sampleVals->store (eventIdx, mstateVals); + } + } + } + // caller responsible for: delete representativeEvents; + Vector<void*> *results = new Vector<void*> (8); + results->store (0, startBins); + results->store (1, numBins); + results->store (2, eventIdxs); + results->store (3, stackIds); + results->store (4, mstates); + results->store (5, sampleVals); + results->store (6, timeStart); + results->store (7, timeEnd); + return results; +} + +// starting from <event_id> packet idx, step <move_count> visible events +// return the resulting idx and that packet's center time, or null if no event. +Vector<long long> * +dbeGetTLEventCenterTime (int dbevindex, int exp_id, int data_id, + int entity_prop_id, int entity_prop_val, int aux, + long long event_id, long long move_count) +{ + DataView *packets = getTimelinePackets (dbevindex, exp_id, data_id, + entity_prop_id); + if (packets == NULL) + return NULL; + long idx = (long) event_id; + + DbeView *dbev = dbeSession->getView (dbevindex); + VMode view_mode = dbev->get_view_mode (); + Experiment *exp = dbeSession->get_exp (exp_id); + int direction; + if (move_count == 0) + direction = 0; + else if (move_count < 0) + { + move_count = -move_count; + direction = -1; + } + else + direction = 1; + idx = getTLVisibleIdxByStepping (exp, view_mode, entity_prop_id, packets, aux, + entity_prop_val, idx, move_count, direction); + if (idx >= 0) + { + long long ts = packets->getLongValue (PROP_TSTAMP, idx); + long long dur = packets->getLongValue (PROP_EVT_TIME, idx); + long long center = ts - dur / 2; + Vector<long long> *results = new Vector<long long> (2); + results->store (0, idx); // result idx + results->store (1, center); // result timestamp + return results; + } + return NULL; +} + +long long +dbeGetTLEventIdxNearTime (int dbevindex, int exp_id, int data_id, + int entity_prop_id, int entity_prop_val, int aux, + int searchDirection, long long tstamp) +{ + DataView *packets = getTimelinePackets (dbevindex, exp_id, data_id, + entity_prop_id); + if (packets == NULL) + return -1; + DbeView *dbev = dbeSession->getView (dbevindex); + VMode view_mode = dbev->get_view_mode (); + Experiment *exp = dbeSession->get_exp (exp_id); + if (searchDirection < 0) + { + int idx = getTLVisibleIdxByVals (exp, view_mode, entity_prop_id, + packets, aux, entity_prop_val, tstamp, + DataView::REL_LTEQ); + if (idx != -1) + return idx; + searchDirection = 1; // couldn't find to left, try to right + } + if (searchDirection > 0) + { + int idx = getTLVisibleIdxByVals (exp, view_mode, entity_prop_id, + packets, aux, entity_prop_val, tstamp, + DataView::REL_GTEQ); + if (idx != -1) + return idx; + // couldn't find to right, fall through to generic + } + // search left and right of timestamp + long idx1, idx2; + idx1 = getTLVisibleIdxByVals (exp, view_mode, entity_prop_id, + packets, aux, entity_prop_val, tstamp, + DataView::REL_LT); + idx2 = getTLVisibleIdxByVals (exp, view_mode, entity_prop_id, + packets, aux, entity_prop_val, tstamp, + DataView::REL_GTEQ); + if (idx1 == -1) + return idx2; + else if (idx2 == -1) + return idx1; + + // both valid, so need to compare to see which is closer + long long t1 = packets->getLongValue (PROP_TSTAMP, idx1); + long long t2 = packets->getLongValue (PROP_TSTAMP, idx2); + long long t2dur = packets->getLongValue (PROP_EVT_TIME, idx2); + long long delta1 = tstamp - t1; // should always be positive + long long delta2 = (t2 - t2dur) - tstamp; // if negative, overlaps idx1 + if (delta1 > delta2) + return idx2; + else + return idx1; +} + +enum Aggr_type +{ + AGGR_NONE, + AGGR_FAIR, + AGGR_MAX, + AGGR_MIN, + AGGR_CNT, + AGGR_SUM, + AGGR_AVG +}; + +static Aggr_type +getAggrFunc (char *aname) +{ + Aggr_type agrfn = AGGR_NONE; + if (aname == NULL) + return agrfn; + if (strcmp (aname, NTXT ("FAIR")) == 0) + agrfn = AGGR_FAIR; + else if (strcmp (aname, NTXT ("MAX")) == 0) + agrfn = AGGR_MAX; + else if (strcmp (aname, NTXT ("MIN")) == 0) + agrfn = AGGR_MIN; + else if (strcmp (aname, NTXT ("CNT")) == 0) + agrfn = AGGR_CNT; + else if (strcmp (aname, NTXT ("SUM")) == 0) + agrfn = AGGR_SUM; + else if (strcmp (aname, NTXT ("AVG")) == 0) + agrfn = AGGR_AVG; + return agrfn; +} + +static long long +computeAggrVal (DefaultMap<long long, long long> *fval_map, Aggr_type agrfn) +{ + long long aval = 0; + long cnt = 0; + Vector<long long> *fvals = fval_map->values (); + long nvals = fvals->size (); + for (int i = 0; i < nvals; ++i) + { + long long val = fvals->fetch (i); + switch (agrfn) + { + case AGGR_FAIR: + aval = val; + break; + case AGGR_MAX: + if (aval < val || cnt == 0) + aval = val; + break; + case AGGR_MIN: + if (aval > val || cnt == 0) + aval = val; + break; + case AGGR_CNT: + aval = cnt + 1; + break; + case AGGR_SUM: + case AGGR_AVG: + aval += val; + break; + case AGGR_NONE: + break; + } + if (agrfn == AGGR_FAIR) + break; + cnt += 1; + } + + // Finalize aggregation + if (agrfn == AGGR_AVG) + if (cnt > 0) + aval = (aval + cnt / 2) / cnt; + delete fvals; + return aval; +} + +Vector<long long> * +dbeGetAggregatedValue (int data_id, // data table id + char *lfilter, // local filter + char *fexpr, // function expression + char *pname_ts, // property name for timestamp + hrtime_t start_ts, // start of the first time interval + hrtime_t delta, // time interval length + int num, // number of time intervals + char *pname_key, // property name for aggregation key + char *aggr_func) // aggregation function +{ + Vector<long long> *res = new Vector<long long>; + Experiment *exp = dbeSession->get_exp (0); + if (exp == NULL) + return res; + hrtime_t end_ts = start_ts + delta * num; + if (end_ts < start_ts) // check overflow + end_ts = MAX_TIME; + + if (exp->get_status () == Experiment::INCOMPLETE + && exp->getLastEvent () < end_ts) + exp->update (); + + DataDescriptor *dataDscr = exp->get_raw_events (data_id); + if (dataDscr == NULL) + return res; + + // Process timestamp argument + int prop_ts = dbeSession->getPropIdByName (pname_ts); + if (prop_ts == PROP_NONE) + return res; + assert (prop_ts == -1); + + // Parse all expressions + Expression *flt_expr = NULL; + if (lfilter != NULL) + flt_expr = dbeSession->ql_parse (lfilter); + Expression *func_expr = NULL; + if (fexpr != NULL) + func_expr = dbeSession->ql_parse (fexpr); + if (func_expr == NULL) // Not specified or malformed + return res; + + // Process aggregation key argument + int prop_key = PROP_NONE; + Data *data_key = NULL; + if (pname_key != NULL) + { + prop_key = dbeSession->getPropIdByName (pname_key); + data_key = dataDscr->getData (prop_key); + if (data_key == NULL) // Specified but not found + return res; + } + + // Process aggregation function argument + Aggr_type agrfn = AGGR_FAIR; + if (aggr_func != NULL) + { + agrfn = getAggrFunc (aggr_func); + if (agrfn == AGGR_NONE) // Specified but not recognized + return res; + } + DefaultMap<long long, long long> * + fval_map = new DefaultMap<long long, long long>; // key_val -> func_val + Vector<long long> *key_set = NULL; + assert (key_set != NULL); + if (key_set == NULL) + { + key_set = new Vector<long long>; + key_set->append (0L); + } + DefaultMap<long long, int> *key_seen = new DefaultMap<long long, int>; + long idx_prev = -1; + for (int tidx = 0; tidx < num; ++tidx) + { + long idx_cur = -1; + assert (idx_cur != -1); + int left = key_set->size (); + key_seen->clear (); + for (long idx = idx_cur; idx > idx_prev; --idx) + { + long id = 0; + assert (id != 0); + + // Pre-create expression context + Expression::Context ctx (dbeSession->getView (0), exp, NULL, id); + // First use the filter + if (flt_expr != NULL) + if (flt_expr->eval (&ctx) == 0) + continue; + + // Calculate the key + // keys are limited to integral values + long long key = 0; + if (data_key != NULL) + key = data_key->fetchLong (id); + + // Check if already seen + if (key_seen->get (key) == 1) + continue; + key_seen->put (key, 1); + left -= 1; + + // Calculate function value + // function values are limited to integral values + long long fval = func_expr->eval (&ctx); + fval_map->put (key, fval); + if (left == 0) + break; + } + idx_prev = idx_cur; + long long aval = computeAggrVal (fval_map, agrfn); + res->store (tidx, aval); + } + delete key_seen; + delete fval_map; + delete flt_expr; + delete func_expr; + return res; +} + +Vector<char*> * +dbeGetLineInfo (Obj pc) +{ + DbeInstr *instr = (DbeInstr*) pc; + if (instr == NULL || instr->get_type () != Histable::INSTR) + return NULL; + DbeLine *dbeline = (DbeLine*) instr->convertto (Histable::LINE); + const char *fname = dbeline ? dbeline->sourceFile->get_name () : NTXT (""); + char lineno[16]; + *lineno = '\0'; + if (dbeline != NULL) + snprintf (lineno, sizeof (lineno), NTXT ("%d"), dbeline->lineno); + Vector<char*> *res = new Vector<char*>(2); + res->store (0, strdup (fname)); + res->store (1, strdup (lineno)); + return res; +} + +int +dbeSetAlias (char *name, char *uname, char *expr) +{ + char *res = dbeSession->indxobj_define (name, uname, expr, NULL, NULL); + return res == NULL ? 0 : 1; +} + +Vector<char*> * +dbeGetAlias (char *name) +{ + Vector<char*> *res = new Vector<char*>; + int idx = dbeSession->findIndexSpaceByName (name); + if (idx >= 0) + { + char *str = dbeSession->getIndexSpaceDescr (idx); + res->append (dbe_strdup (str)); + str = dbeSession->getIndexSpaceExprStr (idx); + res->append (dbe_strdup (str)); + } + return res; +} + +static int +key_cmp (const void *p1, const void *p2) +{ + long long ll1 = *(long long*) p1; + long long ll2 = *(long long*) p2; + return ll1 < ll2 ? -1 : ll1 > ll2 ? 1 : 0; +} + +Vector<Vector<long long>*> * +dbeGetXYPlotData ( + int data_id, // data table id + char *lfilter, // local filter expression + char *arg, // name for the argument + char *func1, // expression for the first axis (x) + char *aggr1, // aggregation function for func1: "SUM","CNT",... + char *func2, // expression for the second axis (y) + char *aggr2, // aggregation function for func2 + char *func3, // expression for the third axis (color) + char *aggr3) // aggregation function for func3 +{ + Vector<Vector<long long>*> *res = new Vector<Vector<long long>*>; + Experiment *exp = dbeSession->get_exp (0); + if (exp == NULL) + return res; + if (exp->get_status () == Experiment::INCOMPLETE) + exp->update (); + + DataDescriptor *dataDscr = exp->get_raw_events (data_id); + if (dataDscr == NULL) + return res; + + // Parse all expressions + Vector<Expression*> *funcs = new Vector<Expression*>; + Vector<Aggr_type> *aggrs = new Vector<Aggr_type>; + Vector<DefaultMap<long long, long long>*> *fval_maps = + new Vector<DefaultMap<long long, long long>*>; + Vector<DefaultMap<long long, long>*> *cnt_maps = + new Vector<DefaultMap<long long, long>*>; + if (func1 != NULL) + { + Expression *expr = dbeSession->ql_parse (func1); + funcs->append (expr); + aggrs->append (getAggrFunc (aggr1)); + fval_maps->append (new DefaultMap<long long, long long>); + cnt_maps->append (new DefaultMap<long long, long>); + res->append (new Vector<long long>); + if (func2 != NULL) + { + expr = dbeSession->ql_parse (func2); + funcs->append (expr); + aggrs->append (getAggrFunc (aggr2)); + fval_maps->append (new DefaultMap<long long, long long>); + cnt_maps->append (new DefaultMap<long long, long>); + res->append (new Vector<long long>); + if (func3 != NULL) + { + expr = dbeSession->ql_parse (func3); + funcs->append (expr); + aggrs->append (getAggrFunc (aggr3)); + fval_maps->append (new DefaultMap<long long, long long>); + cnt_maps->append (new DefaultMap<long long, long>); + res->append (new Vector<long long>); + } + } + } + if (funcs->size () == 0) + { + funcs->destroy (); + delete funcs; + fval_maps->destroy (); + delete fval_maps; + cnt_maps->destroy (); + delete cnt_maps; + delete aggrs; + return res; + } + Expression *arg_expr = NULL; + if (arg != NULL) + arg_expr = dbeSession->ql_parse (arg); + if (arg_expr == NULL) + { + funcs->destroy (); + delete funcs; + fval_maps->destroy (); + delete fval_maps; + cnt_maps->destroy (); + delete cnt_maps; + delete aggrs; + return res; + } + Expression *flt_expr = NULL; + if (lfilter != NULL) + flt_expr = dbeSession->ql_parse (lfilter); + Vector<long long> *kidx_map = new Vector<long long>(); // key_idx -> key_val + for (long i = 0; i < dataDscr->getSize (); i++) + { + Expression::Context ctx (dbeSession->getView (0), exp, NULL, i); + // First use the filter + if (flt_expr != NULL) + if (flt_expr->eval (&ctx) == 0) + continue; + + // Compute the argument + long long key = arg_expr->eval (&ctx); + if (kidx_map->find (key) == -1) + kidx_map->append (key); + for (long j = 0; j < funcs->size (); ++j) + { + Expression *func = funcs->fetch (j); + Aggr_type aggr = aggrs->fetch (j); + DefaultMap<long long, long long> *fval_map = fval_maps->fetch (j); + DefaultMap<long long, long> *cnt_map = cnt_maps->fetch (j); + long long fval = func->eval (&ctx); + long long aval = fval_map->get (key); + long cnt = cnt_map->get (key); + switch (aggr) + { + case AGGR_NONE: + case AGGR_FAIR: + if (cnt == 0) + aval = fval; + break; + case AGGR_MAX: + if (aval < fval || cnt == 0) + aval = fval; + break; + case AGGR_MIN: + if (aval > fval || cnt == 0) + aval = fval; + break; + case AGGR_CNT: + aval = cnt + 1; + break; + case AGGR_SUM: + case AGGR_AVG: + aval += fval; + break; + } + cnt_map->put (key, cnt + 1); + fval_map->put (key, aval); + } + } + kidx_map->sort (key_cmp); + + // Finalize aggregation, prepare result + for (long j = 0; j < funcs->size (); ++j) + { + Aggr_type aggr = aggrs->fetch (j); + Vector<long long> *resj = res->fetch (j); + DefaultMap<long long, long long> * + fval_map = fval_maps->fetch (j); + DefaultMap<long long, long> * + cnt_map = cnt_maps->fetch (j); + for (int kidx = 0; kidx < kidx_map->size (); ++kidx) + { + long long key = kidx_map->fetch (kidx); + long long aval = fval_map->get (key); + if (aggr == AGGR_AVG) + { + long cnt = cnt_map->get (key); + if (cnt > 0) + aval = (aval + cnt / 2) / cnt; + } + resj->append (aval); + } + } + delete flt_expr; + funcs->destroy (); + delete funcs; + delete aggrs; + delete arg_expr; + delete kidx_map; + fval_maps->destroy (); + delete fval_maps; + cnt_maps->destroy (); + delete cnt_maps; + return res; +} + +/* ********************************************************************* */ +/* Routines for use by Collector GUI */ +/** + * Returns signal value for provided name. Example of name: "SIGUSR1" + * @param signal + * @return value + */ +int +dbeGetSignalValue (char *signal) +{ + int ret = -1; + if (signal == NULL) + return ret; + if (strcmp (signal, "SIGUSR1") == 0) + return (SIGUSR1); + if (strcmp (signal, "SIGUSR2") == 0) + return (SIGUSR2); + if (strcmp (signal, "SIGPROF") == 0) + return (SIGPROF); + return ret; +} + +char * +dbeSendSignal (pid_t p, int signum) +{ + int ret = kill (p, signum); + if (p == 0 || p == -1) + return (dbe_sprintf (GTXT ("kill of process %d not supported\n"), p)); + if (ret == 0) + return NULL; + char *msg = dbe_sprintf (GTXT ("kill(%d, %d) failed: %s\n"), p, signum, + strerror (errno)); + return msg; +} + +char * +dbeGetCollectorControlValue (char *control) +{ + if (control == NULL) + return NULL; + if (col_ctr == NULL) + col_ctr = new Coll_Ctrl (1); + char *msg = col_ctr->get (control); + return msg; +} + +char * +dbeSetCollectorControlValue (char *control, char * value) +{ + if (control == NULL) + return NULL; + if (col_ctr == NULL) + col_ctr = new Coll_Ctrl (1); + char *msg = col_ctr->set (control, value); + return msg; +} + +char * +dbeUnsetCollectorControlValue (char *control) +{ + if (control == NULL) + return NULL; + if (col_ctr == NULL) + col_ctr = new Coll_Ctrl (1); + char *msg = col_ctr->unset (control); + return msg; +} + +void +dbeSetLocation (const char *fname, const char *location) +{ + Vector<SourceFile*> *sources = dbeSession->get_sources (); + for (long i = 0, sz = sources ? sources->size () : 0; i < sz; i++) + { + SourceFile *src = sources->get (i); + DbeFile *df = src->dbeFile; + if (df && (strcmp (fname, df->get_name ()) == 0)) + { + df->find_file ((char *) location); + break; + } + } +} + +void +dbeSetLocations (Vector<const char *> *fnames, Vector<const char *> *locations) +{ + if (fnames == NULL || locations == NULL + || fnames->size () != locations->size ()) + return; + for (long i = 0, sz = fnames->size (); i < sz; i++) + dbeSetLocation (fnames->get (i), locations->get (i)); +} + +Vector<void*> * +dbeResolvedWith_setpath (const char *path) +{ + Vector<char*> *names = new Vector<char*>(); + Vector<char*> *pathes = new Vector<char*>(); + Vector<long long> *ids = new Vector<long long>(); + Vector<SourceFile*> *sources = dbeSession->get_sources (); + for (long i = 0, sz = sources ? sources->size () : 0; i < sz; i++) + { + SourceFile *src = sources->get (i); + DbeFile *df = src->dbeFile; + if (df == NULL || (df->filetype & DbeFile::F_FICTION) != 0) + continue; + char *fnm = df->get_name (); + if ((df->filetype & (DbeFile::F_JAVACLASS | DbeFile::F_JAVA_SOURCE)) != 0) + { + char *jnm = dbe_sprintf (NTXT ("%s/%s"), path, fnm); + if (df->check_access (jnm) == DbeFile::F_FILE) + { + names->append (dbe_strdup (fnm)); + pathes->append (jnm); + ids->append (src->id); + continue; + } + free (jnm); + } + char *nm = dbe_sprintf (NTXT ("%s/%s"), path, get_basename (fnm)); + if (df->check_access (nm) == DbeFile::F_FILE) + { + names->append (dbe_strdup (fnm)); + pathes->append (nm); + ids->append (src->id); + continue; + } + free (nm); + } + if (names->size () != 0) + { + Vector<void*> *data = new Vector<void*>(3); + data->append (names); + data->append (pathes); + data->append (ids); + return data; + } + return NULL; +} + +Vector<void*> * +dbeResolvedWith_pathmap (const char *old_prefix, const char *new_prefix) +{ + size_t len = strlen (old_prefix); + Vector<char*> *names = new Vector<char*>(); + Vector<char*> *pathes = new Vector<char*>(); + Vector<long long> *ids = new Vector<long long>(); + Vector<SourceFile*> *sources = dbeSession->get_sources (); + for (long i = 0, sz = sources ? sources->size () : 0; i < sz; i++) + { + SourceFile *src = sources->get (i); + DbeFile *df = src->dbeFile; + if (df == NULL || (df->filetype & DbeFile::F_FICTION) != 0) + continue; + char *fnm = df->get_name (); + if (strncmp (old_prefix, fnm, len) == 0 + && (fnm[len] == '/' || fnm[len] == '\0')) + { + char *nm = dbe_sprintf (NTXT ("%s/%s"), new_prefix, fnm + len); + if (df->check_access (nm) == DbeFile::F_FILE) + { + names->append (dbe_strdup (fnm)); + pathes->append (nm); + ids->append (src->id); + continue; + } + if ((df->filetype & DbeFile::F_JAVA_SOURCE) != 0) + { + free (nm); + nm = dbe_sprintf (NTXT ("%s/%s"), new_prefix, fnm); + if (df->check_access (nm) == DbeFile::F_FILE) + { + names->append (dbe_strdup (fnm)); + pathes->append (nm); + ids->append (src->id); + continue; + } + } + free (nm); + } + } + if (names->size () != 0) + { + Vector<void*> *data = new Vector<void*>(3); + data->append (names); + data->append (pathes); + data->append (ids); + return data; + } + return NULL; +} + +void +dbe_archive (Vector<long long> *ids, Vector<const char *> *locations) +{ + if (ids == NULL || locations == NULL || ids->size () != locations->size ()) + return; + Experiment *exp = dbeSession->get_exp (0); + if (exp == NULL) + return; + Vector<SourceFile*> *sources = dbeSession->get_sources (); + for (long i1 = 0, sz1 = ids->size (); i1 < sz1; i1++) + { + long long id = ids->get (i1); + for (long i = 0, sz = sources ? sources->size () : 0; i < sz; i++) + { + SourceFile *src = sources->get (i); + if (src->id == id) + { + DbeFile *df = src->dbeFile; + if (df) + { + char *fnm = df->find_file ((char *) locations->get (i1)); + if (fnm) + { + char *nm = df->get_name (); + char *anm = exp->getNameInArchive (nm, false); + exp->copy_file (fnm, anm, true); + free (anm); + } + } + } + } + } +} + +/* ************************************************************************ */ + +/* Routines to check connection between Remote Analyzer Client and er_print */ +char * +dbeCheckConnection (char *str) +{ + return dbe_strdup (str); +} diff --git a/gprofng/src/Dbe.h b/gprofng/src/Dbe.h new file mode 100644 index 0000000..f811096 --- /dev/null +++ b/gprofng/src/Dbe.h @@ -0,0 +1,294 @@ +/* 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. */ + +#ifndef _DBE_H_ +#define _DBE_H_ + +#include <stdio.h> +#include "enums.h" + +class MetricList; +template <class ITEM> class Vector; +typedef long long Obj; + +Vector<char*> *dbeGetInitMessages (void); +Vector<char*> *dbeGetExpPreview (int dbevindex, char *exp_name); +char *dbeGetExpParams (int dbevindex, char *exp_name); +char *dbeCreateDirectories (const char *dirname); +char *dbeDeleteFile (const char *pathname); +Vector<char*> *dbeReadFile (const char *pathname); +int dbeWriteFile (const char *pathname, const char *contents); +char *dbeGetFileAttributes (const char *filename, const char *format); +char *dbeGetFiles (const char *dirname, const char *format); +char *dbeGetRunningProcesses (const char *format); +char *dbeOpenExperimentList (int dbevindex, Vector<Vector<char*>*> *groups, + bool sessionRestart); +char *dbeReadRCFile (int dbevindex, char* path); +char *dbeSetExperimentsGroups (Vector<Vector<char*>*> *groups); +Vector<Vector<char*>*> *dbeGetExperimensGroups (); +char *dbeDropExperiment (int dbevindex, Vector<int> *drop_index); +Vector<char*> *dbeGetExpsProperty (const char *prop_name); +Vector<char*> *dbeGetExpName (int dbevindex); +Vector<int> *dbeGetExpState (int dbevindex); +Vector<bool> *dbeGetExpEnable (int dbevindex); +bool dbeSetExpEnable (int dbevindex, Vector<bool> *enable); +Vector<char*> *dbeGetExpInfo (int dbevindex); +bool dbeGetViewModeEnable (); +bool dbeGetJavaEnable (); +int dbeUpdateNotes (int dbevindex, int exp_id, int type, char* text, + bool handle_file); +Vector<void*> *dbeGetTabListInfo (int dbevindex); +Vector<bool> *dbeGetTabSelectionState (int dbevindex); +void dbeSetTabSelectionState (int dbevindex, Vector<bool> *selected); +Vector<bool> *dbeGetMemTabSelectionState (int dbevindex); +void dbeSetMemTabSelectionState (int dbevindex, Vector<bool> *selected); +Vector<bool> *dbeGetIndxTabSelectionState (int dbevindex); +void dbeSetIndxTabSelectionState (int dbevindex, Vector<bool> *selected); +Vector<char*> *dbeGetLoadObjectName (int dbevindex); +Vector<void *> *dbeGetLoadObjectList (int dbevindex); +Vector<char*> *dbeGetSearchPath (int dbevindex); +void dbeSetSearchPath (int dbevindex, Vector<char*> *path); +Vector<void*> *dbeGetPathmaps (int dbevindex); +char *dbeSetPathmaps (Vector<char*> *from, Vector<char*> *to); +char *dbeAddPathmap (int dbevindex, char *from, char *to); +char *dbeGetMsg (int dbevindex, int type); +int dbeInitView (int index, int cloneindex); +void dbeDeleteView (int dbevindex); + +// methods concerning metrics +MetricList *dbeGetMetricListV2 (int dbevindex, MetricType mtype, + Vector<int> *type, Vector<int> *subtype, + Vector<bool> *sort, Vector<int> *vis, + Vector<char*> *aux, Vector<char*> *expr_spec, + Vector<char*> *legends); +Vector<void*> *dbeGetRefMetricsV2 (); +Vector<void*> *dbeGetCurMetricsV2 (int dbevindex, MetricType mtype); +void dbeSetSort (int dbevindex, int sort_index, MetricType mtype, bool reverse); + +// methods concerning metrics for Overview Tab +Vector<void*> *dbeGetRefMetricTree (int dbevindex, bool include_unregistered); +Vector<void*> *dbeGetRefMetricTreeValues (int dbevindex, Vector<char *> *met_cmds, + Vector<char *> *non_met_cmds); +Vector<char*> *dbeGetOverviewText (int dbevindex); +Vector<int> *dbeGetAnoValue (int dbevindex); +void dbeSetAnoValue (int dbevindex, Vector<int> *set); +int dbeGetNameFormat (int dbevindex); +bool dbeGetSoName (int dbevindex); +void dbeSetNameFormat (int dbevindex, int fnames, bool soname); +int dbeGetViewMode (int dbevindex); +void dbeSetViewMode (int dbevindex, int nmode); +Vector<void*> *dbeGetTLValue (int dbevindex); +void dbeSetTLValue (int dbevindex, const char *tldata_cmd, + int entitiy_prop_id, int stackalign, int stackdepth); +Vector<void*> *dbeGetExpFounderDescendants (); +Vector<void*> *dbeGetExpSelection (int dbevindex); +Vector<void*> *dbeGetSampleStatus (int dbevindex, int nselected, + Vector<bool> *selected); +Vector<unsigned> *dbeGetSampleSize (int dbevindex, Vector<bool> *selected); +char *dbeCheckPattern (int dbevindex, Vector<bool> *selected, char *pattern, + int type); +char *dbeSetFilterStr (int dbevindex, char *filter_str); +char *dbeGetFilterStr (int dbevindex); +int dbeValidateFilterExpression (char *str_expr); +Vector<void*> *dbeGetFilterKeywords (int dbevindex); +Vector<void*> *dbeGetFilters (int dbevindex, int nexp); +bool dbeUpdateFilters (int dbevindex, Vector<bool> *selected, + Vector<char*> *pattern_str); +char *dbeComposeFilterClause (int dbevindex, int type, int subtype, + Vector<int>*selections); +Vector<int> *dbeGetLoadObjectState (int dbevindex); +void dbeSetLoadObjectState (int dbevindex, Vector<int> *selected); +void dbeSetLoadObjectDefaults (int dbevindex); +Vector<void*> *dbeGetMemObjects (int dbevindex); +char *dbeDefineMemObj (char *name, char *index_expr, char *_machmodel, + char *sdesc, char *ldesc); +char *dbeDeleteMemObj (char *name); +Vector<char*> *dbeGetCPUVerMachineModel (int dbevindex); +char *dbeLoadMachineModel (char *name); +char *dbeGetMachineModel (); +Vector<char*> *dbeListMachineModels (); +void dbeDetectLoadMachineModel (int dbevindex); +Vector<void*> *dbeGetIndxObjDescriptions (int dbevindex); +Vector<void*> *dbeGetCustomIndxObjects (int dbevindex); +char *dbeDefineIndxObj (char *name, char *index_expr, char *sdesc, char *ldesc); +void dbeSetSelObj (int dbevindex, Obj sel_obj, int type, int subtype); +void dbeSetSelObjV2 (int dbevindex, uint64_t id); +Obj dbeGetSelObj (int dbevindex, int type, int subtype); +uint64_t dbeGetSelObjV2 (int dbevindex, char *typeStr); +int dbeGetSelIndex (int dbevindex, Obj sel_obj, int type, int subtype); +Vector<uint64_t> *dbeGetSelObjsIO (int dbevindex, Vector<uint64_t> *ids, int type); +Vector<uint64_t> *dbeGetSelObjIO (int dbevindex, uint64_t id, int type); +uint64_t dbeGetSelObjHeapTimestamp (int dbevindex, uint64_t id); +int dbeGetSelObjHeapUserExpId (int dbevindex, uint64_t id); +char *dbeSetPrintLimit (int dbevindex, int limit); +int dbeGetPrintLimit (int dbevindex); +char *dbeSetPrintMode (int dbevindex, char *printmode); +int dbeGetPrintMode (int dbevindex); +char *dbeGetPrintModeString (int dbevindex); +char dbeGetPrintDelim (int dbevindex); +Vector<void*> *dbeGetTotals (int dbevindex, int dsptype, int subtype); +Vector<void*> *dbeGetHotMarks (int dbevindex, int type); +Vector<void*> *dbeGetHotMarksInc (int dbevindex, int type); +Vector<void*> *dbeGetSummaryHotMarks (int dbevindex, Vector<Obj> *sel_objs, int type); +Vector<uint64_t> *dbeGetFuncId (int dbevindex, int type, int begin, int length); +Vector<void*> *dbeGetFuncCalleeInfo (int dbevindex, int type, Vector<int>* idxs, int groupId); +Vector<void*> *dbeGetFuncCallerInfo (int dbevindex, int type, Vector<int>* idxs, int groupId); +Vector<void*> *dbeGetFuncCalleeInfoById (int dbevindex, int type, int idx); +Vector<void*> *dbeGetFuncCallerInfoById (int dbevindex, int type, int idx); +char *dbePrintData (int dbevindex, int type, int subtype, char *printer, + char *fname, FILE *outfile); +int dbeSetFuncData (int dbevindex, Obj sel_obj, int type, int subtype); +Vector<void*> *dbeGetFuncList (int dbevindex, int type, int subtype); +Vector<void*> *dbeGetFuncListV2 (int dbevindex, int mtype, Obj sel_obj, int type, int subtype); +Vector<void*> *dbeGetFuncListMini (int dbevindex, int type, int subtype); +Vector<Obj> *dbeGetComparableObjsV2 (int dbevindex, Obj sel_obj, int type); +Obj dbeConvertSelObj (Obj obj, int type); +Vector<int> *dbeGetGroupIds (int dbevindex); +Vector<void*> *dbeGetTableDataV2 (int dbevindex, char *mlistStr, char *modeStr, + char *typeStr, char *subtypeStr, Vector<uint64_t> *ids); + +int dbeGetCallTreeNumLevels (int dbevindex); +Vector<void*> *dbeGetCallTreeLevel (int dbevindex, char *mcmd, int level); +Vector<void*> *dbeGetCallTreeLevels (int dbevindex, char *mcmd); +Vector<void*> *dbeGetCallTreeChildren (int dbevindex, char *mcmd, Vector<int /*NodeIdx*/>*nodes); +Vector<void*> *dbeGetCallTreeLevelFuncs (int dbevindex, int level_start, int level_end); +Vector<void*> *dbeGetCallTreeFuncs (int dbevindex); +Vector<char*> *dbeGetNames (int dbevindex, int type, Obj sel_obj); +Vector<void*> *dbeGetTotalMax (int dbevindex, int type, int subtype); +Vector<void*> *dbeGetStatisOverviewList (int dbevindex); +Vector<void*> *dbeGetStatisList (int dbevindex); +Vector<void*> *dbeGetSummary (int dbevindex, Vector<Obj> *objs, int type, int subtype); +Vector<void*> *dbeGetSummaryV2 (int dbevindex, Vector<Obj> *objs, int type, int subtype); +Vector<int> *dbeGetFounderExpId (Vector<int> *expIds); +Vector<int> *dbeGetUserExpId (Vector<int> *expIds); // filter "user visible" experiment id +Vector<int> *dbeGetExpGroupId (Vector<int> *expIds); +char *dbeGetExpName (int dbevindex, char *dir_name); +Vector<char*> *dbeGetHwcHelp (int dbevindex, bool forKernel); +Vector<Vector<char*>*> *dbeGetHwcSets (int dbevindex, bool forKernel); +Vector<void*> *dbeGetHwcsAll (int dbevindex, bool forKernel); +Vector<char*> *dbeGetHwcAttrList (int dbevindex, bool forKernel); +int dbeGetHwcMaxConcurrent (int dbevindex, bool forKernel); +int dbeGetHwcMaxReg (int dbevindex); // TBR? + +Vector<char*> *dbeGetIfreqData (int dbevindex); +Vector<void*> *dbeGetLeakListInfo (int dbevindex, bool leakflag); +Vector<void*> *dbeMpviewGetTlFuncReps (int dbevindex, int exp_id, + long long binSizeTime, long long startTime, long long endTime, + long long binSizeRank, long long startRank, long long endRank); +Vector<void*> *dbeMpviewGetTlMsgReps (int dbevindex, int exp_id, int throttle, + long long binSizeTime, long long startTime, long long endTime, + long long binSizeRank, long long startRank, long long endRank); +Vector<long long> *dbeMpviewGetAxisRange (int dbevindex, int exp_id, + int chart_type, int axis_type); +Vector<char*> *dbeMpviewGetAxisDiscreteLabels (int dbevindex, int exp_id, + int chart_type, int axis_type); +Vector<void*> *dbeMpviewGetFuncDetails (int dbevindex, int exp_id, Obj funcId); +Vector<void*> *dbeMpviewGetMesgDetails (int dbevindex, int exp_id, Obj mesgId); +Vector<long long> *dbeMpviewGetChartData (int dbevindex, int exp_id, int ctype, + int attr1, long long start1, + long long end1, int nbins1, + int attr2, long long start2, + long long end2, int nbins2, + int metric, int reduction); +void dbeMpviewFilterSet (int dbevindex, int exp_id, Vector<int> *ctid, + Vector<int > *axid, Vector<long long> *startVal, + Vector<long long> *endVal); +void dbeMpviewLoadStacks (int dbevindex, int exp_id); + + +Obj dbeGetObject (int dbevindex, Obj sel_func, Obj sel_pc); +char *dbeGetName (int dbevindex, int exp_id); +Vector<char*> *dbeGetExpVerboseName (Vector<int> *exp_ids); +long long dbeGetStartTime (int dbevindex, int exp_id); +long long dbeGetRelativeStartTime (int dbevindex, int exp_id); +long long dbeGetEndTime (int dbevindex, int exp_id); +int dbeGetClock (int dbevindex, int exp_id); +long long dbeGetWallStartSec (int dbevindex, int exp_id); +char *dbeGetHostname (int dbevindex, int exp_id); +Vector<void*> *dbeGetEntityProps (int dbevindex); +Vector<void*> *dbeGetEntities (int dbevindex, int exp_id, int ekind); +Vector<void*> *dbeGetEntitiesV2 (int dbevindex, Vector<int> *exp_ids, int ekind); +Vector<void*> *dbeGetTLDetails (int dbevindex, int exp_id, int data_id, + int entity_prop_id, Obj event_id); +Vector<Obj> *dbeGetStackFunctions (int dbevindex, Obj stack); +Vector<void*> *dbeGetStacksFunctions (int dbevindex, Vector<Obj> *stacks); +Vector<Obj> *dbeGetStackPCs (int dbevindex, Obj stack); +Vector<char*> *dbeGetStackNames (int dbevindex, Obj stack); +Vector<void*> *dbeGetSamples (int dbevindex, int exp_id, int64_t lo, int64_t hi); +Vector<void*> *dbeGetGCEvents (int dbevindex, int exp_id, int64_t lo, int64_t hi); +Vector<Vector<char*>*>* dbeGetIOStatistics (int dbevindex); +Vector<Vector<char*>*>* dbeGetHeapStatistics (int dbevindex); +Vector<char*> *dbeGetFuncNames (int dbevindex, Vector<Obj> *funcs); +Vector<char*> *dbeGetObjNamesV2 (int dbevindex, Vector<uint64_t> *ids); +char *dbeGetFuncName (int dbevindex, Obj func); +char *dbeGetObjNameV2 (int dbevindex, uint64_t id); +Vector<uint64_t> *dbeGetFuncIds (int dbevindex, Vector<Obj> *funcs); +uint64_t dbeGetFuncId (int dbevindex, Obj func); +char *dbeGetDataspaceTypeDesc (int dbevindex, Obj stack); +Vector<void*> *dbeGetDataDescriptorsV2 (int exp_id); +Vector<void*> *dbeGetDataPropertiesV2 (int exp_id, int data_id); +Vector<void*> *dbeGetExperimentTimeInfo (Vector<int> *exp_ids); +Vector<void*> *dbeGetExperimentDataDescriptors (Vector<int> *exp_ids); + +/* New Timeline Interface */ +Vector<long long> *dbeGetAggregatedValue (int data_id, char *lfilter, char *fexpr, + char *pname_ts, hrtime_t start_ts, + hrtime_t delta, int num, + char *pname_key, char *aggr_func); +Vector<char*> *dbeGetLineInfo (Obj pc); +int dbeSetAlias (char *name, char *uname, char *expr); +Vector<char*> *dbeGetAlias (char *name); +Vector<Vector<long long>*> *dbeGetXYPlotData (int data_id, char *lfilter, + char *arg, char *func1, char *aggr1, + char *func2, char *aggr2, + char *func3, char *aggr3); +Vector<bool> *dbeHasTLData (int dbev_index, Vector<int> *exp_ids, + Vector<int> *data_ids, // DATA_* + Vector<int> *entity_prop_ids, // LWP,CPU,THR, etc + Vector<int> *entity_prop_values, + Vector<int> *auxs); +Vector<void*> *dbeGetTLData (int dbevindex, int exp_id, int data_id, + int entity_prop_id, int entity_prop_val, int aux, + hrtime_t start_ts, hrtime_t delta, int num, + bool getRepresentatives, Vector<char*> *chartProperties); +Vector<long long> *dbeGetTLEventCenterTime (int dbevindex, int exp_id, + int data_id, int entity_prop_id, + int entity_prop_val, int aux, + long long event_idx, long long move_count); +long long dbeGetTLEventIdxNearTime (int dbevindex, int exp_id, + int data_id, + int entity_prop_id, int entity_prop_val, int aux, + int searchDirection, + long long timestamp); + +/* Interface for use by Collector GUI */ +int dbeGetSignalValue (char *); +char *dbeSendSignal (pid_t, int); +char *dbeGetCollectorControlValue (char *); +char *dbeSetCollectorControlValue (char *, char *); +char *dbeUnsetCollectorControlValue (char *); +char *dbeCheckConnection (char *); +void dbe_archive (Vector<long long> *ids, Vector<const char *> *locations); +void dbeSetLocation (const char *fname, const char *location); +void dbeSetLocations (Vector<const char *> *fnames, Vector<const char *> *locations); +Vector<void*> *dbeResolvedWith_setpath (const char *path); +Vector<void*> *dbeResolvedWith_pathmap (const char *old_prefix, const char *new_prefix); + +#endif /* _DBE_H_ */ diff --git a/gprofng/src/DbeApplication.cc b/gprofng/src/DbeApplication.cc new file mode 100644 index 0000000..382bd45 --- /dev/null +++ b/gprofng/src/DbeApplication.cc @@ -0,0 +1,113 @@ +/* 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 <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include "DbeSession.h" +#include "DbeApplication.h" +#include "LoadObject.h" +#include "Experiment.h" +#include "PreviewExp.h" +#include "Function.h" +#include "Hist_data.h" +#include "Module.h" +#include "DataObject.h" +#include "Sample.h" +#include "CallStack.h" +#include "Print.h" +#include "util.h" +#include "libgen.h" +#include "i18n.h" + +DbeApplication *theDbeApplication; + +DbeApplication::DbeApplication (int argc, char *argv[], char* _run_dir) +: Application (argc, argv, _run_dir) +{ + theDbeApplication = this; + ipcMode = false; + rdtMode = false; + if (argc > 1) + if (strcmp (argv[1], NTXT ("-IPC")) == 0 + || strcmp (argv[1], NTXT ("-DIPC")) == 0) + ipcMode = true; + lic_found = 0; + lic_err = NULL; + + // Instantiate a session + (void) new DbeSession (settings, ipcMode, rdtMode); +} + +DbeApplication::~DbeApplication () +{ + delete dbeSession; + theDbeApplication = NULL; +} + +Vector<char*> * +DbeApplication::initApplication (char *fdhome, char *licpath, ProgressFunc func) +{ + // set the home directory + if (fdhome != NULL) + set_run_dir (fdhome); + + // Set progress function + set_progress_func (func); + + // Get license + char *license_err = NULL; + char *sts; + if (licpath != NULL) + { + lic_found = 0; + if (lic_found == 0) + { + lic_err = dbe_strdup (DbeApplication::get_version ()); + sts = dbe_strdup (GTXT ("OK")); + } + else if (lic_found == 2) + { + lic_err = dbe_strdup (license_err); + sts = dbe_strdup (GTXT ("WARN")); + } + else if (lic_found == 3) + { + lic_err = dbe_strdup (license_err); + sts = dbe_strdup (GTXT ("FATAL")); + } + else + { + lic_err = dbe_strdup (license_err); + sts = dbe_strdup (GTXT ("ERROR")); + } + } + else + { + lic_err = dbe_strdup (DbeApplication::get_version ()); + sts = dbe_strdup (GTXT ("OK")); + } + Vector<char*> *data = new Vector<char*>(2); + data->store (0, sts); + data->store (1, lic_err); + return data; +} diff --git a/gprofng/src/DbeApplication.h b/gprofng/src/DbeApplication.h new file mode 100644 index 0000000..b6bdaaf --- /dev/null +++ b/gprofng/src/DbeApplication.h @@ -0,0 +1,50 @@ +/* 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. */ + +/* + * The DbeApplication class is the base class for all C++ executables + * that read Experiments + * + * It is derived from the Application class, and differs from it + * only in that it instantiates a DbeSession (q.v.) to manage + * the reading and processing of Experiments + */ + +#ifndef _DBEAPPLICATION_H +#define _DBEAPPLICATION_H + +#include "Application.h" + +template <class ITEM> class Vector; + +class DbeApplication : public Application +{ +public: + DbeApplication (int argc, char *argv[], char *_run_dir = NULL); + ~DbeApplication (); + Vector<char*> *initApplication (char *fdhome, char *licpath, ProgressFunc func); + + bool rdtMode; + bool ipcMode; +}; + +extern DbeApplication *theDbeApplication; + +#endif /* _DBEAPPLICATION_H */ diff --git a/gprofng/src/DbeArray.h b/gprofng/src/DbeArray.h new file mode 100644 index 0000000..af7c36e --- /dev/null +++ b/gprofng/src/DbeArray.h @@ -0,0 +1,99 @@ +/* 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. */ + +#ifndef _DbeArray_H +#define _DbeArray_H + +template <typename ITEM> class DbeArray +{ +public: + + DbeArray (long sz) + { + count = 0; + limit = sz; + data = new ITEM[limit]; + }; + + virtual + ~DbeArray () + { + delete[] data; + } + + int + append (const ITEM &item) + { + int n = allocate (1); + ITEM *p = get (n); + *p = item; + return n; + }; + + ITEM * + get (long index) + { + return (index < count && index >= 0) ? data + index : (ITEM *) NULL; + }; + + int + allocate (int cnt) + { + count += cnt; + resize (count); + return count - cnt; + }; + + int + size () + { + return (int) count; + }; + + void + reset () + { + count = 0; + }; + +private: + + void + resize (long cnt) + { + if (limit <= cnt) + { + limit *= 2; + if (limit < cnt) + limit = cnt + 1; + ITEM *d = new ITEM[limit]; + if (count > 0) + memcpy (d, data, sizeof (ITEM) * count); + delete[] data; + data = d; + } + }; + + ITEM *data; // Pointer to data vector + long count; // Number of items + long limit; // Array length +}; + +#endif /* _DbeArray_H */ diff --git a/gprofng/src/DbeCacheMap.h b/gprofng/src/DbeCacheMap.h new file mode 100644 index 0000000..4c51a34 --- /dev/null +++ b/gprofng/src/DbeCacheMap.h @@ -0,0 +1,109 @@ +/* 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. */ + +/* + * Dbe Cache Map implementation. + * + * Simple Cache Map makes the following assumptions: + * - Cache Map is used for very fast but not guaranteed mapping; + * - No Relations can be used; + * - all objects used as keys or values has to be managed + * outside CacheMap (f.e. deletion); + */ + +#ifndef _DbeCacheMap_h +#define _DbeCacheMap_h + +#include <Map.h> + +template <typename Key_t, class ITEM> +class DbeCacheMap : public Map<Key_t, ITEM *> +{ +public: + + DbeCacheMap (int _size = DefaultSize) + { // _size should be 2 ** N + size = _size; + table = new DbeCache_T[size]; + memset (table, 0, size * sizeof (DbeCache_T)); + }; + + ~DbeCacheMap () + { + delete[] table; + }; + + void + put (Key_t key, ITEM *val) + { + int ind = get_hash (key); + table[ind].key = key; + table[ind].value = val; + }; + + ITEM * + get (Key_t key) + { + int ind = get_hash (key); + if (table[ind].key == key) + return table[ind].value; + return (ITEM *) NULL; + }; + + ITEM * + remove (Key_t key) + { + int ind = get_hash (key); + ITEM *v = table[ind].value; + table[ind].value = (ITEM *) NULL; + return v; + }; + + ITEM * + get (Key_t /* key */, typename Map<Key_t, ITEM *>::Relation /* rel */) + { + return (ITEM *) NULL; + }; + +private: + + enum + { + DefaultSize = (1 << 13) + }; + + typedef struct DbeCache_S + { + Key_t key; + ITEM *value; + } DbeCache_T; + DbeCache_T *table; + int size; + + int + get_hash (Key_t key) + { + unsigned long long h = (unsigned long long) key; + h ^= (h >> 20) ^ (h >> 12); + return (h ^ (h >> 7) ^ (h >> 4)) & (size - 1); + } +}; + +#endif diff --git a/gprofng/src/DbeFile.cc b/gprofng/src/DbeFile.cc new file mode 100644 index 0000000..9e1fe1c --- /dev/null +++ b/gprofng/src/DbeFile.cc @@ -0,0 +1,541 @@ +/* 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 "util.h" +#include "DbeSession.h" +#include "Experiment.h" +#include "DbeFile.h" +#include "ExpGroup.h" +#include "DbeJarFile.h" + +DbeFile::DbeFile (const char *filename) +{ + filetype = 0; + name = dbe_strdup (filename); + name = canonical_path (name); + orig_location = NULL; + location = NULL; + location_info = NULL; + jarFile = NULL; + container = NULL; + need_refind = true; + inArchive = false; + sbuf.st_atim.tv_sec = 0; + experiment = NULL; +} + +DbeFile::~DbeFile () +{ + free (name); + free (location); + free (orig_location); + free (location_info); +} + +void +DbeFile::set_need_refind (bool val) +{ + if (val != need_refind) + { + free (location_info); + location_info = NULL; + need_refind = val; + } +} + +void +DbeFile::set_location (const char *filename) +{ + free (location); + location = NULL; + if (filename) + { + if (strncmp (filename, NTXT ("./"), 2) == 0) + filename += 2; + location = canonical_path (dbe_strdup (filename)); + } + free (location_info); + location_info = NULL; + set_need_refind (false); +} + +char * +DbeFile::get_location_info () +{ + if (location_info == NULL) + { + char *fnm = get_name (); + char *loc = get_location (); + Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::get_location_info: %s %s\n"), + STR (fnm), STR (loc)); + if (loc == NULL) + { + if (filetype & F_FICTION) + location_info = dbe_strdup (fnm); + else + location_info = dbe_sprintf (GTXT ("%s (not found)"), + get_relative_path (fnm)); + } + else + { + char *r_fnm = get_relative_path (fnm); + char *r_loc = get_relative_path (loc); + if (strcmp (r_fnm, r_loc) == 0) + location_info = dbe_strdup (r_fnm); + else + { + char *bname = get_basename (r_fnm); + if (strcmp (bname, r_loc) == 0) // found in current directory + location_info = dbe_strdup (bname); + else + location_info = dbe_sprintf (GTXT ("%s (found as %s)"), bname, r_loc); + } + } + } + return location_info; +} + +char * +DbeFile::getResolvedPath () +{ + if (get_location ()) + return location; + return name; +} + +DbeFile * +DbeFile::getJarDbeFile (char *fnm, int sym) +{ + Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::getJarDbeFile: %s fnm='%s' sym=%d\n"), + STR (name), STR (fnm), sym); + DbeFile *df = NULL; + if (sym) + { + char *s = strchr (fnm, sym); + if (s) + { + s = dbe_strndup (fnm, s - fnm); + df = dbeSession->getDbeFile (s, F_JAR_FILE | F_FILE); + free (s); + } + } + if (df == NULL) + df = dbeSession->getDbeFile (fnm, F_JAR_FILE | F_FILE); + if (df && (df->experiment == NULL)) + df->experiment = experiment; + return df; +} + +char * +DbeFile::get_location (bool find_needed) +{ + Dprintf (DEBUG_DBE_FILE, NTXT ("get_location 0x%x %s\n"), filetype, STR (name)); + if (!find_needed) + return need_refind ? NULL : location; + if (location || !need_refind) + return location; + set_need_refind (false); + if ((filetype & F_FICTION) != 0) + return NULL; + if (filetype == F_DIR_OR_JAR) + { + find_in_archives (name); + if (location) + { + filetype |= F_JAR_FILE | F_FILE; + return location; + } + find_in_pathmap (name); + if (location) + return location; + if (check_access (name) == F_DIRECTORY) + { + filetype |= F_DIRECTORY; + set_location (name); + return location; + } + } + + if ((filetype & F_FILE) != 0) + { + if (experiment) + { + char *fnm = experiment->checkFileInArchive (name, false); + if (fnm) + { + set_location (fnm); + inArchive = true; + sbuf.st_mtime = 0; // Don't check timestamps + free (fnm); + return location; + } + if ((filetype & F_JAVACLASS) != 0) + { + if (orig_location) + { + Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::get_location:%d name='%s' orig_location='%s'\n"), + (int) __LINE__, name, orig_location); + // Parse a fileName attribute. There are 4 possibilities: + // file:<Class_Name> + // file:<name_of_jar_or_zip_file> + // jar:file:<name_of_jar_or_zip_file>!<Class_Name> + // zip:<name_of_jar_or_zip_file>!<Class_Name> + DbeFile *jar_df = NULL; + if (strncmp (orig_location, NTXT ("zip:"), 4) == 0) + jar_df = getJarDbeFile (orig_location + 4, '!'); + else if (strncmp (orig_location, NTXT ("jar:file:"), 9) == 0) + jar_df = getJarDbeFile (orig_location + 9, '!'); + else if (strncmp (orig_location, NTXT ("file:"), 5) == 0 + && isJarOrZip (orig_location + 5)) + jar_df = getJarDbeFile (orig_location + 5, 0); + if (jar_df) + { + if (find_in_jar_file (name, jar_df->get_jar_file ())) + { + Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::get_location:%d FOUND name='%s' location='%s' jar='%s'\n"), + (int) __LINE__, name, STR (location), STR (jar_df->get_location ())); + inArchive = jar_df->inArchive; + container = jar_df; + return location; + } + } + if (strncmp (orig_location, NTXT ("file:"), 5) == 0 + && !isJarOrZip (orig_location + 5)) + { + DbeFile *df = new DbeFile (orig_location + 5); + df->filetype = DbeFile::F_FILE; + df->experiment = experiment; + fnm = df->get_location (); + if (fnm) + { + set_location (fnm); + inArchive = df->inArchive; + sbuf.st_mtime = df->sbuf.st_mtime; + Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::get_location:%d FOUND name='%s' orig_location='%s' location='%s'\n"), + (int) __LINE__, name, orig_location, fnm); + delete df; + return location; + } + delete df; + } + } + fnm = dbe_sprintf (NTXT ("%s/%s/%s"), experiment->get_expt_name (), SP_DYNAMIC_CLASSES, name); + if (find_file (fnm)) + { + inArchive = true; + sbuf.st_mtime = 0; // Don't check timestamps + Dprintf (DEBUG_DBE_FILE, NTXT ("DbeFile::get_location:%d FOUND name='%s' location='%s'\n"), + (int) __LINE__, name, fnm); + free (fnm); + return location; + } + free (fnm); + } + } + } + + if (dbeSession->archive_mode) + { + find_file (name); + if (location) + return location; + } + + bool inPathMap = find_in_pathmap (name); + if (location) + return location; + find_in_setpath (name, dbeSession->get_search_path ()); + if (location) + return location; + if ((filetype & (F_JAVACLASS | F_JAVA_SOURCE)) != 0) + { + find_in_classpath (name, dbeSession->get_classpath ()); + if (location) + return location; + } + if (!inPathMap) + find_file (name); + Dprintf (DEBUG_DBE_FILE && (location == NULL), + "DbeFile::get_location:%d NOT FOUND name='%s'\n", __LINE__, name); + return location; +} + +int +DbeFile::check_access (const char *filename) +{ + if (filename == NULL) + return F_NOT_FOUND; + int st = dbe_stat (filename, &sbuf); + Dprintf (DEBUG_DBE_FILE, NTXT ("check_access: %d 0x%x %s\n"), st, filetype, filename); + if (st == 0) + { + if (S_ISDIR (sbuf.st_mode)) + return F_DIRECTORY; + else if (S_ISREG (sbuf.st_mode)) + return F_FILE; + return F_UNKNOWN; // Symbolic link or unknown type of file + } + sbuf.st_atim.tv_sec = 0; + sbuf.st_mtime = 0; // Don't check timestamps + return F_NOT_FOUND; // File not found +} + +bool +DbeFile::isJarOrZip (const char *fnm) +{ + size_t len = strlen (fnm) - 4; + return len > 0 && (strcmp (fnm + len, NTXT (".jar")) == 0 + || strcmp (fnm + len, NTXT (".zip")) == 0); +} + +char * +DbeFile::find_file (const char *filename) +{ + switch (check_access (filename)) + { + case F_DIRECTORY: + if (filetype == F_DIR_OR_JAR) + filetype |= F_DIRECTORY; + if ((filetype & F_DIRECTORY) != 0) + set_location (filename); + break; + case F_FILE: + if (filetype == F_DIR_OR_JAR) + { + filetype |= F_FILE; + if (isJarOrZip (filename)) + filetype |= F_JAR_FILE; + } + if ((filetype & F_DIRECTORY) == 0) + set_location (filename); + break; + } + return location; +} + +DbeJarFile * +DbeFile::get_jar_file () +{ + if (jarFile == NULL) + { + char *fnm = get_location (); + if (fnm) + jarFile = dbeSession->get_JarFile (fnm); + } + return jarFile; +} + +char * +DbeFile::find_package_name (const char *filename, const char *dirname) +{ + char *nm = dbe_sprintf (NTXT ("%s/%s"), dirname, filename); + if (!find_in_pathmap (nm)) + find_file (nm); + free (nm); + return location; +} + +char * +DbeFile::find_in_directory (const char *filename, const char *dirname) +{ + if (filename && dirname) + { + char *nm = dbe_sprintf (NTXT ("%s/%s"), dirname, filename); + find_file (nm); + free (nm); + } + return location; +} + +char * +DbeFile::find_in_jar_file (const char *filename, DbeJarFile *jfile) +{ + // Read .jar or .zip + if (jfile == NULL) + return NULL; + int entry = jfile->get_entry (filename); + if (entry >= 0) + { + char *fnm = dbeSession->get_tmp_file_name (filename, true); + long long fsize = jfile->copy (fnm, entry); + if (fsize >= 0) + { + dbeSession->tmp_files->append (fnm); + set_location (fnm); + sbuf.st_size = fsize; + sbuf.st_mtime = 0; // Don't check timestamps + fnm = NULL; + } + free (fnm); + } + return location; +} + +bool +DbeFile::find_in_pathmap (char *filename) +{ + Vector<pathmap_t*> *pathmaps = dbeSession->get_pathmaps (); + bool inPathMap = false; + if (strncmp (filename, NTXT ("./"), 2) == 0) + filename += 2; + for (int i = 0, sz = pathmaps ? pathmaps->size () : 0; i < sz; i++) + { + pathmap_t *pmp = pathmaps->fetch (i); + size_t len = strlen (pmp->old_prefix); + if (strncmp (pmp->old_prefix, filename, len) == 0 + && (filename[len] == '/' || filename[len] == '\0')) + { + inPathMap = true; + if (find_in_directory (filename + len, pmp->new_prefix)) + { + return inPathMap; + } + } + } + return inPathMap; +} + +void +DbeFile::find_in_archives (char *filename) +{ + for (int i1 = 0, sz1 = dbeSession->expGroups->size (); i1 < sz1; i1++) + { + ExpGroup *gr = dbeSession->expGroups->fetch (i1); + if (gr->founder) + { + char *nm = gr->founder->checkFileInArchive (filename, false); + if (nm) + { + find_file (nm); + if (location) + { + sbuf.st_mtime = 0; // Don't check timestamps + return; + } + } + } + } +} + +void +DbeFile::find_in_setpath (char *filename, Vector<char*> *searchPath) +{ + char *base = get_basename (filename); + for (int i = 0, sz = searchPath ? searchPath->size () : 0; i < sz; i++) + { + char *spath = searchPath->fetch (i); + // Check file in each experiment directory + if (streq (spath, "$") || streq (spath, NTXT ("$expts"))) + { + // find only in founders and only LoadObj. + for (int i1 = 0, sz1 = dbeSession->expGroups->size (); i1 < sz1; i1++) + { + ExpGroup *gr = dbeSession->expGroups->fetch (i1); + char *exp_name = gr->founder->get_expt_name (); + if (gr->founder) + { + if ((filetype & (F_JAVACLASS | F_JAVA_SOURCE)) != 0) + { + // Find with the package name + if (find_in_directory (filename, exp_name)) + return; + } + if (find_in_directory (base, exp_name)) + return; + } + } + continue; + } + DbeFile *df = dbeSession->getDbeFile (spath, DbeFile::F_DIR_OR_JAR); + if (df->get_location () == NULL) + continue; + if ((filetype & (F_JAVACLASS | F_JAVA_SOURCE)) != 0) + { + if ((df->filetype & F_JAR_FILE) != 0) + { + if (find_in_jar_file (filename, df->get_jar_file ())) + { + container = df; + return; + } + continue; + } + else if ((df->filetype & F_DIRECTORY) != 0) + // Find with the package name + if (find_package_name (filename, spath)) + return; + } + if ((df->filetype & F_DIRECTORY) != 0) + if (find_in_directory (base, df->get_location ())) + return; + } +} + +void +DbeFile::find_in_classpath (char *filename, Vector<DbeFile*> *classPath) +{ + for (int i = 0, sz = classPath ? classPath->size () : 0; i < sz; i++) + { + DbeFile *df = classPath->fetch (i); + if (df->get_location () == NULL) + continue; + if ((df->filetype & F_JAR_FILE) != 0) + { + if (find_in_jar_file (filename, df->get_jar_file ())) + { + container = df; + return; + } + } + else if ((df->filetype & F_DIRECTORY) != 0) + // Find with the package name + if (find_package_name (filename, df->get_name ())) + return; + } +} + +struct stat64 * +DbeFile::get_stat () +{ + if (sbuf.st_atim.tv_sec == 0) + { + int st = check_access (get_location (false)); + if (st == F_NOT_FOUND) + return NULL; + } + return &sbuf; +} + +bool +DbeFile::compare (DbeFile *df) +{ + if (df == NULL) + return false; + struct stat64 *st1 = get_stat (); + struct stat64 *st2 = df->get_stat (); + if (st1 == NULL || st2 == NULL) + return false; + if (st1->st_size != st2->st_size) + return false; + if (st1->st_mtim.tv_sec != st2->st_mtim.tv_sec) + return false; + return true; +} diff --git a/gprofng/src/DbeFile.h b/gprofng/src/DbeFile.h new file mode 100644 index 0000000..abd7180 --- /dev/null +++ b/gprofng/src/DbeFile.h @@ -0,0 +1,103 @@ +/* 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. */ + +#ifndef _DBE_FILE_H +#define _DBE_FILE_H + +#include <fcntl.h> + +class DbeJarFile; +class Experiment; +template <class ITEM> class Vector; + +class DbeFile +{ +public: + + enum + { + F_NOT_FOUND = 0, + F_FICTION = 1, + F_LOADOBJ = 2, + F_SOURCE = 4, + F_JAVACLASS = 8, + F_JAVA_SOURCE = 16, + F_DOT_O = 32, + F_DEBUG_FILE = 64, + F_DOT_A_LIB = 128, + F_DIR_OR_JAR = 256, + F_DIRECTORY = 512, + F_FILE = 1024, + F_JAR_FILE = 2048, + F_UNKNOWN = 65536 + }; + + DbeFile (const char *filename); + ~DbeFile (); + + char * + get_name () + { + return name; + }; + + bool + get_need_refind () + { + return need_refind; + }; + + char *get_location (bool find_needed = true); + char *getResolvedPath (); + char *get_location_info (); + struct stat64 *get_stat (); + bool compare (DbeFile *df); + void set_need_refind (bool val); + void set_location (const char *filename); + int check_access (const char *filename); + char *find_file (const char *filename); + DbeFile *getJarDbeFile (char *fnm, int sym); + char *find_in_jar_file (const char *filename, DbeJarFile *jfile); + DbeJarFile *get_jar_file (); + + bool inArchive; + int filetype; + struct stat64 sbuf; + DbeFile *container; + char *orig_location; + Experiment *experiment; + +protected: + static bool isJarOrZip (const char *fnm); + char *find_package_name (const char *filename, const char *dirname); + char *find_in_directory (const char *filename, const char *dirname); + bool find_in_pathmap (char *filename); + void find_in_archives (char *filename); + void find_in_setpath (char *filename, Vector<char*> *searchPath); + void find_in_classpath (char *filename, Vector<DbeFile*> *classPath); + + char *name; + char *location; + char *location_info; + bool need_refind; + DbeJarFile *jarFile; +}; + +#endif /* _DBE_FILE_H */ diff --git a/gprofng/src/DbeJarFile.cc b/gprofng/src/DbeJarFile.cc new file mode 100644 index 0000000..9029512 --- /dev/null +++ b/gprofng/src/DbeJarFile.cc @@ -0,0 +1,505 @@ +/* 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 <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +#include "zlib.h" +#include "util.h" +#include "DbeJarFile.h" +#include "Data_window.h" +#include "vec.h" + +static uint32_t +get_u1 (unsigned char *b) +{ + return (uint32_t) ((b)[0]); +} + +static uint32_t +get_u2 (unsigned char *b) +{ + return (get_u1 (b + 1) << 8) | get_u1 (b); +} + +static uint32_t +get_u4 (unsigned char *b) +{ + return (get_u2 (b + 2) << 16) | get_u2 (b); +} + +static uint64_t +get_u8 (unsigned char *b) +{ + return (((uint64_t) get_u4 (b + 4)) << 32) | get_u4 (b); +} + +enum +{ + END_CENT_DIR_SIZE = 22, + LOC_FILE_HEADER_SIZE = 30, + CENT_FILE_HEADER_SIZE = 46, + ZIP64_LOCATOR_SIZE = 20, + ZIP64_CENT_DIR_SIZE = 56, + ZIP_BUF_SIZE = 65536 +}; + +struct EndCentDir +{ + uint64_t count; + uint64_t size; + uint64_t offset; +}; + +class ZipEntry +{ +public: + + ZipEntry () + { + name = NULL; + data_offset = 0; + } + + ~ZipEntry () + { + free (name); + } + + int + compare (ZipEntry *ze) + { + return dbe_strcmp (name, ze->name); + } + + char *name; // entry name + int time; // modification time + int64_t size; // size of uncompressed data + int64_t csize; // size of compressed data (zero if uncompressed) + uint32_t compressionMethod; + int64_t offset; // offset of LOC header + int64_t data_offset; +}; + +static int +cmp_names (const void *a, const void *b) +{ + ZipEntry *e1 = *((ZipEntry **) a); + ZipEntry *e2 = *((ZipEntry **) b); + return e1->compare (e2); +} + +template<> void Vector<ZipEntry *>::dump (const char *msg) +{ + Dprintf (1, NTXT ("Vector<ZipEntry *> %s [%lld]\n"), msg ? msg : NTXT (""), (long long) size ()); + for (long i = 0, sz = size (); i < sz; i++) + { + ZipEntry *ze = get (i); + Dprintf (1, NTXT (" %lld offset:%lld (0x%llx) size: %lld --> %lld %s\n"), + (long long) i, (long long) ze->offset, (long long) ze->offset, + (long long) ze->csize, (long long) ze->size, STR (ze->name)); + } +} + +DbeJarFile::DbeJarFile (const char *jarName) +{ + name = strdup (jarName); + fnames = NULL; + dwin = new Data_window (name); + get_entries (); +} + +DbeJarFile::~DbeJarFile () +{ + free (name); + delete fnames; +} + +void +DbeJarFile::get_entries () +{ + Dprintf (DUMP_JAR_FILE, NTXT ("\nArchive: %s\n"), STR (name)); + if (dwin->not_opened ()) + { + append_msg (CMSG_ERROR, GTXT ("Cannot open file `%s'"), name); + return; + } + struct EndCentDir endCentDir; + if (get_EndCentDir (&endCentDir) == 0) + return; + + if (endCentDir.count == 0) + { + append_msg (CMSG_WARN, GTXT ("No files in %s"), name); + return; + } + unsigned char *b = (unsigned char *) dwin->bind (endCentDir.offset, endCentDir.size); + if (b == NULL) + { + append_msg (CMSG_ERROR, GTXT ("%s: cannot read the central directory record"), name); + return; + } + + fnames = new Vector<ZipEntry*>(endCentDir.count); + for (uint64_t i = 0, offset = endCentDir.offset, last = endCentDir.offset + endCentDir.size; i < endCentDir.count; i++) + { + if ((last - offset) < CENT_FILE_HEADER_SIZE) + { + append_msg (CMSG_ERROR, GTXT ("%s: cannot read the central file header (%lld (from %lld), offset=0x%016llx last=0x%016llx"), + name, (long long) i, (long long) endCentDir.count, (long long) offset, (long long) last); + break; + } + b = (unsigned char *) dwin->bind (offset, CENT_FILE_HEADER_SIZE); + // Central file header + // Offset Bytes Description + // 0 4 central file header signature = 0x02014b50 + // 4 2 version made by + // 6 2 version needed to extract + // 8 2 general purpose bit flag + // 10 2 compression method + // 12 2 last mod file time + // 14 2 last mod file date + // 16 4 crc-32 + // 20 4 compressed size + // 24 4 uncompressed size + // 28 2 file name length + // 30 2 extra field length + // 32 2 file comment length + // 34 2 disk number start + // 36 2 internal file attributes + // 38 4 external file attributes + // 42 4 relative offset of local header + // 46 file name (variable size) + // extra field (variable size) + // file comment (variable size) + uint32_t signature = get_u4 (b); + if (signature != 0x02014b50) + { + append_msg (CMSG_ERROR, GTXT ("%s: wrong header signature (%lld (total %lld), offset=0x%016llx last=0x%016llx"), + name, (long long) i, (long long) endCentDir.count, (long long) offset, (long long) last); + break; + } + ZipEntry *ze = new ZipEntry (); + fnames->append (ze); + uint32_t name_len = get_u2 (b + 28); + uint32_t extra_len = get_u2 (b + 30); + uint32_t comment_len = get_u2 (b + 32); + ze->compressionMethod = get_u2 (b + 10); + ze->csize = get_u4 (b + 20); + ze->size = get_u4 (b + 24); + ze->offset = get_u4 (b + 42); + char *nm = (char *) dwin->bind (offset + 46, name_len); + if (nm) + { + ze->name = (char *) malloc (name_len + 1); + strncpy (ze->name, nm, name_len); + ze->name[name_len] = 0; + } + offset += CENT_FILE_HEADER_SIZE + name_len + extra_len + comment_len; + } + fnames->sort (cmp_names); + if (DUMP_JAR_FILE) + fnames->dump (get_basename (name)); +} + +int +DbeJarFile::get_entry (const char *fname) +{ + if (fnames == NULL) + return -1; + ZipEntry zipEntry, *ze = &zipEntry; + ze->name = (char *) fname; + int ind = fnames->bisearch (0, -1, &ze, cmp_names); + ze->name = NULL; + return ind; +} + +long long +DbeJarFile::copy (char *toFileNname, int fromEntryNum) +{ + if (fromEntryNum < 0 || fromEntryNum >= VecSize (fnames)) + return -1; + ZipEntry *ze = fnames->get (fromEntryNum); + if (ze->data_offset == 0) + { + // Local file header + // Offset Bytes Description + // 0 4 local file header signature = 0x04034b50 + // 4 2 version needed to extract + // 6 2 general purpose bit flag + // 8 2 compression method + // 10 2 last mod file time + // 12 2 last mod file date + // 14 4 crc-32 + // 18 4 compressed size + // 22 4 uncompressed size + // 26 2 file name length + // 28 2 extra field length + // 30 2 file name (variable size) + // extra field (variable size) + unsigned char *b = (unsigned char *) dwin->bind (ze->offset, LOC_FILE_HEADER_SIZE); + if (b == NULL) + { + append_msg (CMSG_ERROR, + GTXT ("%s: Cannot read a local file header (%s offset=0x%lld"), + name, STR (ze->name), (long long) ze->offset); + return -1; + } + uint32_t signature = get_u4 (b); + if (signature != 0x04034b50) + { + append_msg (CMSG_ERROR, + GTXT ("%s: wrong local header signature ('%s' offset=%lld (0x%llx)"), + name, STR (ze->name), (long long) ze->offset, + (long long) ze->offset); + return -1; + } + ze->data_offset = ze->offset + LOC_FILE_HEADER_SIZE + get_u2 (b + 26) + get_u2 (b + 28); + } + + if (ze->compressionMethod == 0) + { + int fd = open (toFileNname, O_CREAT | O_WRONLY | O_LARGEFILE, 0644); + if (fd == -1) + { + append_msg (CMSG_ERROR, GTXT ("Cannot create file %s (%s)"), toFileNname, STR (strerror (errno))); + return -1; + } + long long len = dwin->copy_to_file (fd, ze->data_offset, ze->size); + close (fd); + if (len != ze->size) + { + append_msg (CMSG_ERROR, GTXT ("%s: Cannot write %lld bytes (only %lld)"), + toFileNname, (long long) ze->size, (long long) len); + unlink (toFileNname); + return -1; + } + return len; + } + + unsigned char *b = (unsigned char *) dwin->bind (ze->data_offset, ze->csize); + if (b == NULL) + { + append_msg (CMSG_ERROR, + GTXT ("%s: Cannot extract file %s (offset=0x%lld csize=%lld)"), + name, STR (ze->name), (long long) ze->offset, + (long long) ze->csize); + return -1; + } + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.next_in = Z_NULL; + strm.avail_in = 0; + if (inflateInit2 (&strm, -MAX_WBITS) != Z_OK) + { + append_msg (CMSG_ERROR, GTXT ("%s: inflateInit2 failed (%s)"), STR (ze->name), STR (strm.msg)); + return -1; + } + strm.avail_in = ze->csize; + strm.next_in = b; + int retval = ze->size; + unsigned char *buf = (unsigned char *) malloc (ze->size); + for (;;) + { + strm.next_out = buf; + strm.avail_out = ze->size; + int ret = inflate (&strm, Z_SYNC_FLUSH); + if ((ret == Z_NEED_DICT) || (ret == Z_DATA_ERROR) || (ret == Z_MEM_ERROR) || (ret == Z_STREAM_ERROR)) + { + append_msg (CMSG_ERROR, GTXT ("%s: inflate('%s') error %d (%s)"), name, STR (ze->name), ret, STR (strm.msg)); + retval = -1; + break; + } + if (strm.avail_out != 0) + break; + } + inflateEnd (&strm); + if (retval != -1) + { + int fd = open (toFileNname, O_CREAT | O_WRONLY | O_LARGEFILE, 0644); + if (fd == -1) + { + append_msg (CMSG_ERROR, GTXT ("Cannot create file %s (%s)"), toFileNname, STR (strerror (errno))); + retval = -1; + } + else + { + long long len = write (fd, buf, ze->size); + if (len != ze->size) + { + append_msg (CMSG_ERROR, GTXT ("%s: Cannot write %lld bytes (only %lld)"), + toFileNname, (long long) strm.avail_out, (long long) len); + retval = -1; + } + close (fd); + } + } + free (buf); + return retval; +} + +int +DbeJarFile::get_EndCentDir (struct EndCentDir *endCentDir) +{ + int64_t fsize = dwin->get_fsize (); + int64_t sz = (fsize < ZIP_BUF_SIZE) ? fsize : ZIP_BUF_SIZE; + + // Find the end of central directory record: + unsigned char *b = (unsigned char *) dwin->bind (fsize - sz, sz); + if (b == NULL) + { + append_msg (CMSG_ERROR, GTXT ("%s: cannot find the central directory record (fsize=%lld)"), + name, (long long) fsize); + return 0; + } + + // End of central directory record: + // Offset Bytes Description + // 0 4 end of central directory signature = 0x06054b50 + // 4 2 number of this disk + // 6 2 disk where central directory starts + // 8 2 number of central directory records on this disk + // 10 2 total number of central directory records + // 12 4 size of central directory(bytes) + // 16 4 offset of start of central directory, relative to start of archive + // 20 2 comment length(n) + // 22 n comment + + endCentDir->count = 0; + endCentDir->size = 0; + endCentDir->offset = 0; + int64_t ecdrOffset = fsize; + for (int64_t i = END_CENT_DIR_SIZE; i < sz; i++) + { + b = (unsigned char *) dwin->bind (fsize - i, END_CENT_DIR_SIZE); + if (b == NULL) + { + append_msg (CMSG_ERROR, GTXT ("%s: read failed (offset:0x%llx bytes:%lld"), + name, (long long) (fsize - i), (long long) END_CENT_DIR_SIZE); + break; + } + uint32_t signature = get_u4 (b); + if (signature == 0x06054b50) + { + int64_t len_comment = get_u2 (b + 20); + if (i != (len_comment + END_CENT_DIR_SIZE)) + continue; + ecdrOffset = fsize - i; + endCentDir->count = get_u2 (b + 10); + endCentDir->size = get_u4 (b + 12); + endCentDir->offset = get_u4 (b + 16); + Dprintf (DUMP_JAR_FILE, + " Zip archive file size: %10lld (0x%016llx)\n" + " end-cent-dir record offset: %10lld (0x%016llx)\n" + " cent-dir offset: %10lld (0x%016llx)\n" + " cent-dir size: %10lld (0x%016llx)\n" + " cent-dir entries: %10lld\n", + (long long) fsize, (long long) fsize, + (long long) ecdrOffset, (long long) ecdrOffset, + (long long) endCentDir->offset, (long long) endCentDir->offset, + (long long) endCentDir->size, (long long) endCentDir->size, + (long long) endCentDir->count); + break; + } + } + if (ecdrOffset == fsize) + { + append_msg (CMSG_ERROR, + GTXT ("%s: cannot find the central directory record"), name); + return 0; + } + if (endCentDir->count == 0xffff || endCentDir->offset == 0xffffffff + || endCentDir->size == 0xffffffff) + { + // Zip64 format: + // Zip64 end of central directory record + // Zip64 end of central directory locator ( Can be absent ) + // End of central directory record + b = (unsigned char *) dwin->bind (ecdrOffset - ZIP64_LOCATOR_SIZE, + ZIP64_LOCATOR_SIZE); + if (b == NULL) + { + append_msg (CMSG_ERROR, + GTXT ("%s: cannot find the Zip64 central directory record"), name); + return 0; + } + uint32_t signature = get_u4 (b); + if (signature == 0x07064b50) + { // Get an offset from the Zip64 cent-dir locator + // Zip64 end of central directory locator + // Offset Bytes Description + // 0 4 Zip64 end of central dir locator signature = 0x07064b50 + // 4 4 number of the disk with the start of the zip64 end of central directory + // 8 8 relative offset of the Zip64 end of central directory record + // 12 4 total number of disks + Dprintf (DUMP_JAR_FILE, " cent-dir locator offset %10lld (0x%016llx)\n", + (long long) (ecdrOffset - ZIP64_LOCATOR_SIZE), (long long) (ecdrOffset - ZIP64_LOCATOR_SIZE)); + ecdrOffset = get_u8 (b + 8); + } + else // the Zip64 end of central directory locator is absent + ecdrOffset -= ZIP64_CENT_DIR_SIZE; + Dprintf (DUMP_JAR_FILE, NTXT (" Zip64 end-cent-dir record offset: %10lld (0x%016llx)\n"), + (long long) ecdrOffset, (long long) ecdrOffset); + + b = (unsigned char *) dwin->bind (ecdrOffset, ZIP64_CENT_DIR_SIZE); + if (b == NULL) + { + append_msg (CMSG_ERROR, + GTXT ("%s: cannot find the Zip64 central directory record"), name); + return 0; + } + // Zip64 end of central directory record + // Offset Bytes Description + // 0 4 Zip64 end of central dir signature = 0x06064b50 + // 4 8 size of zip64 end of central directory record + // 12 2 version made by + // 14 2 version needed to extract + // 16 4 number of this disk + // 20 4 number of the disk with the start of the central directory + // 24 8 total number of entries in the central directory on this disk + // 32 8 total number of entries in the central directory + // 40 8 size of the central directory + // 48 8 offset of start of centraldirectory with respect to the starting disk number + // 56 Zip64 extensible data sector (variable size) + signature = get_u4 (b); + if (signature != 0x06064b50) + { + append_msg (CMSG_ERROR, GTXT ("%s: cannot find the Zip64 central directory record"), name); + return 0; + } + endCentDir->count = get_u8 (b + 32); + endCentDir->size = get_u8 (b + 40); + endCentDir->offset = get_u8 (b + 48); + Dprintf (DUMP_JAR_FILE, + NTXT (" cent-dir offset: %10lld (0x%016llx)\n" + " cent-dir size: %10lld (0x%016llx)\n" + " cent-dir entries: %10lld\n"), + (long long) endCentDir->offset, (long long) endCentDir->offset, + (long long) endCentDir->size, (long long) endCentDir->size, + (long long) endCentDir->count); + } + return 1; +} + diff --git a/gprofng/src/DbeJarFile.h b/gprofng/src/DbeJarFile.h new file mode 100644 index 0000000..f0b4f7b --- /dev/null +++ b/gprofng/src/DbeJarFile.h @@ -0,0 +1,46 @@ +/* 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. */ + +#ifndef _DBE_JAR_FILE_H +#define _DBE_JAR_FILE_H + +#include "Emsg.h" + +class Data_window; +class ZipEntry; +template <class ITEM> class Vector; + +class DbeJarFile : public DbeMessages +{ +public: + DbeJarFile (const char *jarName); + ~DbeJarFile (); + + int get_entry (const char *fname); + long long copy (char *toFileNname, int fromEntryNum); + +private: + void get_entries (); + int get_EndCentDir (struct EndCentDir *endCentDir); + char *name; + Vector<ZipEntry *> *fnames; + Data_window *dwin; +}; +#endif /* _DBE_JAR_FILE_H */ diff --git a/gprofng/src/DbeLinkList.h b/gprofng/src/DbeLinkList.h new file mode 100644 index 0000000..760cc67 --- /dev/null +++ b/gprofng/src/DbeLinkList.h @@ -0,0 +1,73 @@ +/* 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. */ + +#ifndef _DbeLinkList_h +#define _DbeLinkList_h + +template <typename ITEM> class DbeLinkList +{ +public: + + DbeLinkList (ITEM _item) + { + item = _item; + next = NULL; + }; + + ~DbeLinkList () { }; + + ITEM + get_item () + { + return item; + } + + DbeLinkList<ITEM> * + get_next () + { + return next; + } + + void + set_next (DbeLinkList<ITEM> *p) + { + next = p; + } + + void + destroy (bool deleteItem = false) + { + for (DbeLinkList<ITEM> *p = next; p;) + { + DbeLinkList<ITEM> *p1 = p->get_next (); + if (deleteItem) + delete p->get_item (); + delete p; + p = p1; + } + next = NULL; + } + +private: + ITEM item; + DbeLinkList<ITEM> *next; +}; + +#endif /* _DbeLinkList_h */ diff --git a/gprofng/src/DbeLock.cc b/gprofng/src/DbeLock.cc new file mode 100644 index 0000000..14a1eb3 --- /dev/null +++ b/gprofng/src/DbeLock.cc @@ -0,0 +1,41 @@ +/* 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 "DbeLock.h" + +DbeLock::DbeLock () +{ + pthread_mutex_init (&lock, NULL); +} + +DbeLock::~DbeLock () { } + +void +DbeLock::aquireLock () +{ + pthread_mutex_lock (&lock); +} + +void +DbeLock::releaseLock () +{ + pthread_mutex_unlock (&lock); +} diff --git a/gprofng/src/DbeLock.h b/gprofng/src/DbeLock.h new file mode 100644 index 0000000..28dfb6e --- /dev/null +++ b/gprofng/src/DbeLock.h @@ -0,0 +1,38 @@ +/* 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. */ + +#ifndef _DBE_LOCK_H +#define _DBE_LOCK_H + +#include <pthread.h> + +class DbeLock +{ +public: + DbeLock (); + ~DbeLock (); + void aquireLock (); + void releaseLock (); + +private: + pthread_mutex_t lock; +}; + +#endif /* _DBE_LOCK_H */ diff --git a/gprofng/src/DbeSession.cc b/gprofng/src/DbeSession.cc new file mode 100644 index 0000000..4370114 --- /dev/null +++ b/gprofng/src/DbeSession.cc @@ -0,0 +1,3527 @@ +/* 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 <stdio.h> +#include <dirent.h> +#include <unistd.h> +#include <sys/types.h> +#include <errno.h> +#include <sys/param.h> + +#include "util.h" +#include "Application.h" +#include "Experiment.h" +#include "ExpGroup.h" +#include "Expression.h" +#include "DataObject.h" +#include "Elf.h" +#include "Function.h" +#include "DbeSession.h" +#include "LoadObject.h" +#include "DbeSyncMap.h" +#include "DbeThread.h" +#include "ClassFile.h" +#include "IndexObject.h" +#include "PathTree.h" +#include "Print.h" +#include "QLParser.tab.hh" +#include "DbeView.h" +#include "MemorySpace.h" +#include "Module.h" +#include "SourceFile.h" +#include "StringBuilder.h" +#include "BaseMetric.h" +#include "BaseMetricTreeNode.h" +#include "Command.h" +#include "UserLabel.h" +#include "StringMap.h" +#include "DbeFile.h" +#include "DbeJarFile.h" +#include "IOActivity.h" +#include "HeapActivity.h" + +// This is a universal List structure to organize objects +// of various types, even if different. +struct List +{ + List *next; + void *val; +}; + +struct Countable +{ + Countable (void *_item) + { + item = _item; + ref_count = 0; + } + + void *item; + int ref_count; +}; + +Platform_t DbeSession::platform = +#if ARCH(SPARC) + Sparc; +#elif ARCH(Aarch64) + Aarch64; +#else // ARCH(Intel) + Intel; +#endif + +// This constant determines the size of the data object name hash table. +static const int HTableSize = 8192; +static int DEFAULT_TINY_THRESHOLD = -1; + +unsigned int mpmt_debug_opt = 0; +DbeSession *dbeSession = NULL; + +DbeSession::DbeSession (Settings *_settings, bool _ipc_mode, bool _rdt_mode) +{ + dbeSession = this; + ipc_mode = _ipc_mode; + rdt_mode = _rdt_mode; + settings = new Settings (_settings); + views = new Vector<DbeView*>; + exps = new Vector<Experiment*>; + lobjs = new Vector<LoadObject*>; + objs = new Vector<Histable*>; + dobjs = new Vector<DataObject*>; + metrics = new Vector<Countable*>; + reg_metrics = new Vector<BaseMetric*>; + hwcentries = NULL; + reg_metrics_tree = NULL; // BaseMetric() requires DbeSession::ql_parse + idxobjs = new Vector<HashMap<uint64_t, Histable*>*>; + tmp_files = new Vector<char*>; + search_path = new Vector<char*>; + classpath = new Vector<char*>; + classpath_df = NULL; + expGroups = new Vector<ExpGroup*>; + sourcesMap = new HashMap<char*, SourceFile*>; + sources = new Vector<SourceFile*>; + comp_lobjs = new HashMap<char*, LoadObject*>; + comp_dbelines = new HashMap<char*, DbeLine*>; + comp_sources = new HashMap<char*, SourceFile*>; + loadObjMap = new DbeSyncMap<LoadObject>; + f_special = new Vector<Function*>(LastSpecialFunction); + omp_functions = new Vector<Function*>(OMP_LAST_STATE); + interactive = false; + lib_visibility_used = false; + + // Define all known property names + propNames = new Vector<PropDescr*>; + propNames_name_store (PROP_NONE, NTXT ("")); + propNames_name_store (PROP_ATSTAMP, NTXT ("ATSTAMP")); + propNames_name_store (PROP_ETSTAMP, NTXT ("ETSTAMP")); + propNames_name_store (PROP_TSTAMP, NTXT ("TSTAMP")); + propNames_name_store (PROP_THRID, NTXT ("THRID")); + propNames_name_store (PROP_LWPID, NTXT ("LWPID")); + propNames_name_store (PROP_CPUID, NTXT ("CPUID")); + propNames_name_store (PROP_FRINFO, NTXT ("FRINFO")); + propNames_name_store (PROP_EVT_TIME, NTXT ("EVT_TIME")); + + // Samples + propNames_name_store (PROP_SMPLOBJ, NTXT ("SMPLOBJ")); + propNames_name_store (PROP_SAMPLE, NTXT ("SAMPLE")); + + // GCEvents + propNames_name_store (PROP_GCEVENTOBJ, NTXT ("GCEVENTOBJ")); + propNames_name_store (PROP_GCEVENT, NTXT ("GCEVENT")); + + // Metadata used by some packet types + propNames_name_store (PROP_VOIDP_OBJ, NTXT ("VOIDP_OBJ"), + NULL, TYPE_UINT64, DDFLAG_NOSHOW); + + // Clock profiling properties + propNames_name_store (PROP_UCPU, NTXT ("UCPU")); + propNames_name_store (PROP_SCPU, NTXT ("SCPU")); + propNames_name_store (PROP_TRAP, NTXT ("TRAP")); + propNames_name_store (PROP_TFLT, NTXT ("TFLT")); + propNames_name_store (PROP_DFLT, NTXT ("DFLT")); + propNames_name_store (PROP_KFLT, NTXT ("KFLT")); + propNames_name_store (PROP_ULCK, NTXT ("ULCK")); + propNames_name_store (PROP_TSLP, NTXT ("TSLP")); + propNames_name_store (PROP_WCPU, NTXT ("WCPU")); + propNames_name_store (PROP_TSTP, NTXT ("TSTP")); + + propNames_name_store (PROP_MSTATE, NTXT ("MSTATE")); + propNames_name_store (PROP_NTICK, NTXT ("NTICK")); + propNames_name_store (PROP_OMPSTATE, NTXT ("OMPSTATE")); + + // Synchronization tracing properties + propNames_name_store (PROP_SRQST, NTXT ("SRQST")); + propNames_name_store (PROP_SOBJ, NTXT ("SOBJ")); + + // Hardware counter profiling properties + propNames_name_store (PROP_HWCTAG, NTXT ("HWCTAG")); + propNames_name_store (PROP_HWCINT, NTXT ("HWCINT")); + propNames_name_store (PROP_VADDR, NTXT ("VADDR")); + propNames_name_store (PROP_PADDR, NTXT ("PADDR")); + propNames_name_store (PROP_VIRTPC, NTXT ("VIRTPC")); + propNames_name_store (PROP_PHYSPC, NTXT ("PHYSPC")); + propNames_name_store (PROP_LWP_LGRP_HOME, NTXT ("LWP_LGRP_HOME")); + propNames_name_store (PROP_PS_LGRP_HOME, NTXT ("PS_LGRP_HOME")); + propNames_name_store (PROP_EA_PAGESIZE, NTXT ("EA_PAGESIZE")); + propNames_name_store (PROP_EA_LGRP, NTXT ("EA_LGRP")); + propNames_name_store (PROP_PC_PAGESIZE, NTXT ("PC_PAGESIZE")); + propNames_name_store (PROP_PC_LGRP, NTXT ("PC_LGRP")); + propNames_name_store (PROP_HWCDOBJ, NTXT ("HWCDOBJ")); + propNames_name_store (PROP_MEM_LAT, NTXT ("MEM_LAT")); + propNames_name_store (PROP_MEM_SRC, NTXT ("MEM_SRC")); + + // Heap tracing properties + propNames_name_store (PROP_HTYPE, NTXT ("HTYPE")); + propNames_name_store (PROP_HSIZE, NTXT ("HSIZE")); + propNames_name_store (PROP_HVADDR, NTXT ("HVADDR")); + propNames_name_store (PROP_HOVADDR, NTXT ("HOVADDR")); + propNames_name_store (PROP_HLEAKED, NTXT ("HLEAKED"), + GTXT ("Leaked bytes"), TYPE_UINT64, 0); + propNames_name_store (PROP_HMEM_USAGE, NTXT ("HMEM_USAGE")); + propNames_name_store (PROP_HFREED, NTXT ("HFREED"), + GTXT ("Freed bytes"), TYPE_UINT64, 0); + propNames_name_store (PROP_HCUR_ALLOCS, NTXT ("HCUR_ALLOCS"), + GTXT ("Current allocations"), TYPE_INT64, 0); + propNames_name_store (PROP_HCUR_NET_ALLOC, NTXT ("HCUR_NET_ALLOC"), + NULL, TYPE_INT64, DDFLAG_NOSHOW); + propNames_name_store (PROP_HCUR_LEAKS, NTXT ("HCUR_LEAKS"), + GTXT ("Current leaks"), TYPE_UINT64, 0); + propNames_name_store (PROP_DDSCR_LNK, NTXT ("DDSCR_LNK"), + NULL, TYPE_UINT64, DDFLAG_NOSHOW); + + // IO tracing properties + propNames_name_store (PROP_IOTYPE, NTXT ("IOTYPE")); + propNames_name_store (PROP_IOFD, NTXT ("IOFD")); + propNames_name_store (PROP_IONBYTE, NTXT ("IONBYTE")); + propNames_name_store (PROP_IORQST, NTXT ("IORQST")); + propNames_name_store (PROP_IOOFD, NTXT ("IOOFD")); + propNames_name_store (PROP_IOFNAME, NTXT ("IOFNAME")); + propNames_name_store (PROP_IOVFD, NTXT ("IOVFD")); + propNames_name_store (PROP_IOFSTYPE, NTXT ("IOFSTYPE")); + + // omptrace raw properties + propNames_name_store (PROP_CPRID, NTXT ("CPRID")); + propNames_name_store (PROP_PPRID, NTXT ("PPRID")); + propNames_name_store (PROP_TSKID, NTXT ("TSKID")); + propNames_name_store (PROP_PTSKID, NTXT ("PTSKID")); + propNames_name_store (PROP_PRPC, NTXT ("PRPC")); + + // Data race detection properties + propNames_name_store (PROP_RID, NTXT ("RID")); + propNames_name_store (PROP_RTYPE, NTXT ("RTYPE")); + propNames_name_store (PROP_LEAFPC, NTXT ("LEAFPC")); + propNames_name_store (PROP_RVADDR, NTXT ("RVADDR")); + propNames_name_store (PROP_RCNT, NTXT ("RCNT")); + + // Deadlock detection properties + propNames_name_store (PROP_DID, NTXT ("DID")); + propNames_name_store (PROP_DLTYPE, NTXT ("DLTYPE")); + propNames_name_store (PROP_DTYPE, NTXT ("DTYPE")); + propNames_name_store (PROP_DVADDR, NTXT ("DVADDR")); + + // Synthetic properties (queries only) + propNames_name_store (PROP_STACK, NTXT ("STACK")); + propNames_name_store (PROP_MSTACK, NTXT ("MSTACK")); + propNames_name_store (PROP_USTACK, NTXT ("USTACK")); + propNames_name_store (PROP_XSTACK, NTXT ("XSTACK")); + propNames_name_store (PROP_HSTACK, NTXT ("HSTACK")); + propNames_name_store (PROP_STACKID, NTXT ("STACKID")); + //propNames_name_store( PROP_CPRID, NTXT("CPRID") ); + //propNames_name_store( PROP_TSKID, NTXT("TSKID") ); + propNames_name_store (PROP_JTHREAD, NTXT ("JTHREAD"), + GTXT ("Java thread number"), TYPE_UINT64, 0); + + propNames_name_store (PROP_LEAF, NTXT ("LEAF")); + propNames_name_store (PROP_DOBJ, NTXT ("DOBJ")); + propNames_name_store (PROP_SAMPLE_MAP, NTXT ("SAMPLE_MAP")); + propNames_name_store (PROP_GCEVENT_MAP, NTXT ("GCEVENT_MAP")); + propNames_name_store (PROP_PID, NTXT ("PID"), + GTXT ("Process id"), TYPE_UINT64, 0); + propNames_name_store (PROP_EXPID, NTXT ("EXPID"), + GTXT ("Experiment id"), TYPE_UINT64, DDFLAG_NOSHOW); + propNames_name_store (PROP_EXPID_CMP, NTXT ("EXPID_CMP"), + GTXT ("Comparable Experiment Id"), TYPE_UINT64, + DDFLAG_NOSHOW); //YXXX find better description + propNames_name_store (PROP_EXPGRID, NTXT ("EXPGRID"), + GTXT ("Comparison Group id"), TYPE_UINT64, 0); + propNames_name_store (PROP_PARREG, NTXT ("PARREG")); + propNames_name_store (PROP_TSTAMP_LO, NTXT ("TSTAMP_LO"), + GTXT ("Start Timestamp (nanoseconds)"), TYPE_UINT64, 0); + propNames_name_store (PROP_TSTAMP_HI, NTXT ("TSTAMP_HI"), + GTXT ("End Timestamp (nanoseconds)"), TYPE_UINT64, 0); + propNames_name_store (PROP_TSTAMP2, NTXT ("TSTAMP2"), + GTXT ("End Timestamp (nanoseconds)"), TYPE_UINT64, + DDFLAG_NOSHOW); + propNames_name_store (PROP_FREQ_MHZ, NTXT ("FREQ_MHZ"), + GTXT ("CPU Frequency, MHz"), TYPE_UINT32, 0); + propNames_name_store (PROP_NTICK_USEC, NTXT ("NTICK_USEC"), + GTXT ("Clock Profiling Interval, Microseconds"), + TYPE_UINT64, 0); + + propNames_name_store (PROP_IOHEAPBYTES, NTXT ("IOHEAPBYTES")); + + propNames_name_store (PROP_STACKL, NTXT ("STACKL")); + propNames_name_store (PROP_MSTACKL, NTXT ("MSTACKL")); + propNames_name_store (PROP_USTACKL, NTXT ("USTACKL")); + propNames_name_store (PROP_XSTACKL, NTXT ("XSTACKL")); + + propNames_name_store (PROP_STACKI, NTXT ("STACKI")); + propNames_name_store (PROP_MSTACKI, NTXT ("MSTACKI")); + propNames_name_store (PROP_USTACKI, NTXT ("USTACKI")); + propNames_name_store (PROP_XSTACKI, NTXT ("XSTACKI")); + + // Make sure predefined names are not used for dynamic properties + propNames_name_store (PROP_LAST, NTXT ("")); + + localized_SP_UNKNOWN_NAME = GTXT ("(unknown)"); + + // define Index objects + dyn_indxobj = new Vector<IndexObjType_t*>(); + dyn_indxobj_indx = 0; + char *s = dbe_sprintf (NTXT ("((EXPID_CMP<<%llu) | THRID)"), + (unsigned long long) IndexObject::INDXOBJ_EXPID_SHIFT); + indxobj_define (NTXT ("Threads"), GTXT ("Threads"), s, NULL, NULL); + free (s); + indxobj_define (NTXT ("CPUs"), GTXT ("CPUs"), NTXT ("(CPUID)"), NULL, NULL); + indxobj_define (NTXT ("Samples"), GTXT ("Samples"), NTXT ("(SAMPLE_MAP)"), + NULL, NULL); + indxobj_define (NTXT ("GCEvents"), GTXT ("GCEvents"), NTXT ("(GCEVENT_MAP)"), + NULL, NULL); + indxobj_define (NTXT ("Seconds"), GTXT ("Seconds"), + NTXT ("(TSTAMP/1000000000)"), NULL, NULL); + indxobj_define (NTXT ("Processes"), GTXT ("Processes"), NTXT ("(EXPID_CMP)"), + NULL, NULL); + s = dbe_sprintf (NTXT ("((EXPGRID<<%llu) | (EXPID<<%llu))"), + (unsigned long long) IndexObject::INDXOBJ_EXPGRID_SHIFT, + (unsigned long long) IndexObject::INDXOBJ_EXPID_SHIFT); + indxobj_define (NTXT ("Experiment_IDs"), GTXT ("Experiment_IDs"), s, NULL, NULL); + free (s); + indxobj_define (NTXT ("Datasize"), GTXT ("Datasize"), + "(IOHEAPBYTES==0?0:" + "((IOHEAPBYTES<=(1<<0)?(1<<0):" + "((IOHEAPBYTES<=(1<<2)?(1<<2):" + "((IOHEAPBYTES<=(1<<4)?(1<<4):" + "((IOHEAPBYTES<=(1<<6)?(1<<6):" + "((IOHEAPBYTES<=(1<<8)?(1<<8):" + "((IOHEAPBYTES<=(1<<10)?(1<<10):" + "((IOHEAPBYTES<=(1<<12)?(1<<12):" + "((IOHEAPBYTES<=(1<<14)?(1<<14):" + "((IOHEAPBYTES<=(1<<16)?(1<<16):" + "((IOHEAPBYTES<=(1<<18)?(1<<18):" + "((IOHEAPBYTES<=(1<<20)?(1<<20):" + "((IOHEAPBYTES<=(1<<22)?(1<<22):" + "((IOHEAPBYTES<=(1<<24)?(1<<24):" + "((IOHEAPBYTES<=(1<<26)?(1<<26):" + "((IOHEAPBYTES<=(1<<28)?(1<<28):" + "((IOHEAPBYTES<=(1<<30)?(1<<30):" + "((IOHEAPBYTES<=(1<<32)?(1<<32):" + "((IOHEAPBYTES<=(1<<34)?(1<<34):" + "((IOHEAPBYTES<=(1<<36)?(1<<36):" + "((IOHEAPBYTES<=(1<<38)?(1<<38):" + "((IOHEAPBYTES<=(1<<40)?(1<<40):" + "((IOHEAPBYTES<=(1<<42)?(1<<42):" + "((IOHEAPBYTES<=(1<<44)?(1<<44):" + "((IOHEAPBYTES<=(1<<46)?(1<<46):" + "((IOHEAPBYTES<=(1<<48)?(1<<48):" + "((IOHEAPBYTES<=(1<<50)?(1<<50):" + "(IOHEAPBYTES==-1?-1:(1<<50|1)" + "))))))))))))))))))))))))))))))))))))))))))))))))))))))", + NULL, NULL); + indxobj_define (NTXT ("Duration"), GTXT ("Duration"), + "((TSTAMP_HI-TSTAMP_LO)==0?0:" + "(((TSTAMP_HI-TSTAMP_LO)<=1000?1000:" + "(((TSTAMP_HI-TSTAMP_LO)<=10000?10000:" + "(((TSTAMP_HI-TSTAMP_LO)<=100000?100000:" + "(((TSTAMP_HI-TSTAMP_LO)<=1000000?1000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=10000000?10000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=100000000?100000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=1000000000?1000000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=10000000000?10000000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=100000000000?100000000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=1000000000000?1000000000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=10000000000000?10000000000000:" + "(10000000000001))))))))))))))))))))))))", NULL, NULL); + dyn_indxobj_indx_fixed = dyn_indxobj_indx; + Elf::elf_init (); + defExpName = NULL; + mach_model_loaded = NULL; + tmp_dir_name = NULL; + settings->read_rc (ipc_mode || rdt_mode); + + init (); +} + +DbeSession::~DbeSession () +{ + Destroy (views); + Destroy (exps); + Destroy (dobjs); + Destroy (metrics); + Destroy (search_path); + Destroy (classpath); + Destroy (propNames); + Destroy (expGroups); + Destroy (userLabels); + if (hwcentries) + { + for (long i = 0, sz = hwcentries->size (); i < sz; i++) + { + Hwcentry *h = hwcentries->get (i); + free (h->int_name); + free (h->name); + delete h; + } + delete hwcentries; + } + + if (idxobjs) + { + for (int i = 0; i < idxobjs->size (); ++i) + { + HashMap<uint64_t, Histable*> *hMap = idxobjs->get (i); + if (hMap) + { + hMap->values ()->destroy (); + delete hMap; + } + } + delete idxobjs; + } + + for (int i = 0; i < HTableSize; i++) + { + List *list = dnameHTable[i]; + while (list) + { + List *tmp = list; + list = list->next; + delete tmp; + } + } + delete[] dnameHTable; + delete classpath_df; + Destroy (objs); + Destroy (reg_metrics); + Destroy (dyn_indxobj); + delete lobjs; + delete f_special; + destroy_map (DbeFile *, dbeFiles); + destroy_map (DbeJarFile *, dbeJarFiles); + delete loadObjMap; + delete omp_functions; + delete sourcesMap; + delete sources; + delete comp_lobjs; + delete comp_dbelines; + delete comp_sources; + delete reg_metrics_tree; + delete settings; + free (mach_model_loaded); + + if (defExpName != NULL) + { + StringBuilder *sb = new StringBuilder (); + sb->append (NTXT ("/bin/rm -rf ")); + sb->append (defExpName); + char *cmd = sb->toString (); + system (cmd); + free (cmd); + delete sb; + free (defExpName); + } + unlink_tmp_files (); + delete tmp_files; + dbeSession = NULL; +} + +void +DbeSession::unlink_tmp_files () +{ + if (tmp_files) + { + for (int i = 0, sz = tmp_files->size (); i < sz; i++) + unlink (tmp_files->fetch (i)); + tmp_files->destroy (); + delete tmp_files; + tmp_files = NULL; + } + if (tmp_dir_name) + { + char *cmd = dbe_sprintf (NTXT ("/bin/rm -rf %s"), tmp_dir_name); + system (cmd); + free (cmd); + free (tmp_dir_name); + tmp_dir_name = NULL; + } +} + +char * +DbeSession::get_tmp_file_name (const char *nm, bool for_java) +{ + if (tmp_dir_name == NULL) + { + tmp_dir_name = dbe_sprintf (NTXT ("/tmp/analyzer.%llu.%lld"), + (unsigned long long) getuid (), (long long) getpid ()); + mkdir (tmp_dir_name, S_IRWXU); + } + char *fnm = dbe_sprintf (NTXT ("%s/%s"), tmp_dir_name, nm); + if (for_java) + for (char *s = fnm + strlen (tmp_dir_name) + 1; *s; s++) + if (*s == '/') + *s = '.'; + return fnm; +} + +void +DbeSession::init () +{ + user_exp_id_counter = 0; + status_ompavail = 0; + archive_mode = 0; + +#if DEBUG + char *s = getenv (NTXT ("MPMT_DEBUG")); + if (s) + mpmt_debug_opt = atoi (s); +#endif /* DEBUG */ + dbeFiles = new StringMap<DbeFile*>(); + dbeJarFiles = new StringMap<DbeJarFile*>(128, 128); + + // set up the initial (after .rc file reading) search path + set_search_path (settings->str_search_path, true); + userLabels = NULL; + + // Preset all objects as they may reuse each other + lo_unknown = NULL; + f_unknown = NULL; + j_unknown = NULL; + lo_total = NULL; + sf_unknown = NULL; + f_total = NULL; + f_jvm = NULL; + d_total = NULL; + d_scalars = NULL; + d_unknown = NULL; + expGroups->destroy (); + f_special->reset (); + for (int i = 0; i < LastSpecialFunction; i++) + f_special->append (NULL); + + lo_omp = NULL; + omp_functions->reset (); + for (int i = 0; i < OMP_LAST_STATE; i++) + omp_functions->append (NULL); + + // make sure the metric list is initialized + register_metric (Metric::SIZES); + register_metric (Metric::ADDRESS); + register_metric (Metric::ONAME); + + // This is needed only to maintain loadobject id's + // for <Total> and <Unknown> in tests + (void) get_Unknown_LoadObject (); + (void) get_Total_LoadObject (); + + // Create the data object name hash table. + dnameHTable = new List*[HTableSize]; + for (int i = 0; i < HTableSize; i++) + dnameHTable[i] = NULL; + + d_total = createDataObject (); + d_total->set_name (NTXT ("<Total>")); + + // XXXX <Scalars> only appropriate for Program/Data-oriented analyses + d_scalars = createDataObject (); + d_scalars->set_name (GTXT ("<Scalars>")); + + d_unknown = createDataObject (); + d_unknown->set_name (GTXT ("<Unknown>")); + + // assign d_unknown's children so data_olayout has consistent sorting + for (unsigned pp_code = 1; pp_code < NUM_ABS_PP_CODES + 2; pp_code++) + { + char *errcode; + DataObject* dobj = createDataObject (); + switch (pp_code) + { + case NUM_ABS_PP_CODES + 1: + errcode = PTXT (DOBJ_UNDETERMINED); + break; + case NUM_ABS_PP_CODES: + errcode = PTXT (DOBJ_UNSPECIFIED); + break; + case NUM_ABS_PP_CODES - 1: + errcode = PTXT (DOBJ_UNIDENTIFIED); + break; + default: + errcode = PTXT (ABS_PP_CODES[pp_code]); + } + dobj->parent = d_unknown; + dobj->set_dobjname (errcode, NULL); // dobj->parent must already be set + } + + for (unsigned rt_code = 1; rt_code < NUM_ABS_RT_CODES - 1; rt_code++) + { + DataObject* dobj = createDataObject (); + dobj->parent = d_unknown; + dobj->set_dobjname (PTXT (ABS_RT_CODES[rt_code]), NULL); // dobj->parent must already be set + } +} + +void +DbeSession::reset_data () +{ + for (long i = 0, sz = VecSize (idxobjs); i < sz; ++i) + if (idxobjs->get (i)) + idxobjs->get (i)->reset (); +} + +void +DbeSession::reset () +{ + loadObjMap->reset (); + DbeView *dbev; + int index; + + Vec_loop (DbeView*, views, index, dbev) + { + dbev->reset (); + } + + destroy_map (DbeFile *, dbeFiles); + destroy_map (DbeJarFile *, dbeJarFiles); + exps->destroy (); + lobjs->reset (); // all LoadObjects belong to objs + dobjs->destroy (); // deletes d_unknown and d_total as well + objs->destroy (); + comp_lobjs->clear (); + comp_dbelines->clear (); + comp_sources->clear (); + sourcesMap->clear (); + sources->reset (); + + // Delete the data object name hash table. + for (int i = 0; i < HTableSize; i++) + { + List *list = dnameHTable[i]; + while (list) + { + List *tmp = list; + list = list->next; + delete tmp; + } + } + delete[] dnameHTable; + + // IndexObect definitions remain, objects themselves may go + for (int i = 0; i < idxobjs->size (); ++i) + { + HashMap<uint64_t, Histable*> *v = idxobjs->fetch (i); + if (v != NULL) + { + v->values ()->destroy (); + v->clear (); + } + } + init (); +} + +Vector<SourceFile*> * +DbeSession::get_sources () +{ + return sources; +} + +DbeFile * +DbeSession::getDbeFile (char *filename, int filetype) +{ + Dprintf (DEBUG_DBE_FILE, NTXT ("DbeSession::getDbeFile filetype=0x%x %s\n"), filetype, filename); + if (strncmp (filename, NTXT ("./"), 2) == 0) + filename += 2; + DbeFile *dbeFile = dbeFiles->get (filename); + if (dbeFile == NULL) + { + dbeFile = new DbeFile (filename); + dbeFiles->put (filename, dbeFile); + } + dbeFile->filetype |= filetype; + return dbeFile; +} + +LoadObject * +DbeSession::get_Total_LoadObject () +{ + if (lo_total == NULL) + { + lo_total = createLoadObject (NTXT ("<Total>")); + lo_total->dbeFile->filetype |= DbeFile::F_FICTION; + } + return lo_total; +} + +Function * +DbeSession::get_Total_Function () +{ + if (f_total == NULL) + { + f_total = createFunction (); + f_total->flags |= FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET; + f_total->set_name (NTXT ("<Total>")); + Module *mod = get_Total_LoadObject ()->noname; + f_total->module = mod; + mod->functions->append (f_total); + } + return f_total; +} + +LoadObject * +DbeSession::get_Unknown_LoadObject () +{ + if (lo_unknown == NULL) + { + lo_unknown = createLoadObject (GTXT ("<Unknown>")); + lo_unknown->type = LoadObject::SEG_TEXT; // makes it expandable + lo_unknown->dbeFile->filetype |= DbeFile::F_FICTION; + + // force creation of the <Unknown> function + (void) get_Unknown_Function (); + } + return lo_unknown; +} + +SourceFile * +DbeSession::get_Unknown_Source () +{ + if (sf_unknown == NULL) + { + sf_unknown = createSourceFile (localized_SP_UNKNOWN_NAME); + sf_unknown->dbeFile->filetype |= DbeFile::F_FICTION; + sf_unknown->flags |= SOURCE_FLAG_UNKNOWN; + } + return sf_unknown; +} + +Function * +DbeSession::get_Unknown_Function () +{ + if (f_unknown == NULL) + { + f_unknown = createFunction (); + f_unknown->flags |= FUNC_FLAG_SIMULATED; + f_unknown->set_name (GTXT ("<Unknown>")); + Module *mod = get_Unknown_LoadObject ()->noname; + f_unknown->module = mod; + mod->functions->append (f_unknown); + } + return f_unknown; +} + +// LIBRARY_VISIBILITY + +Function * +DbeSession::create_hide_function (LoadObject *lo) +{ + Function *h_function = createFunction (); + h_function->set_name (lo->get_name ()); + h_function->module = lo->noname; + h_function->isHideFunc = true; + lo->noname->functions->append (h_function); + return h_function; +} + +Function * +DbeSession::get_JUnknown_Function () +{ + if (j_unknown == NULL) + { + j_unknown = createFunction (); + j_unknown->flags |= FUNC_FLAG_SIMULATED; + j_unknown->set_name (GTXT ("<no Java callstack recorded>")); + Module *mod = get_Unknown_LoadObject ()->noname; + j_unknown->module = mod; + mod->functions->append (j_unknown); + } + return j_unknown; +} + +Function * +DbeSession::get_jvm_Function () +{ + if (f_jvm == NULL) + { + f_jvm = createFunction (); + f_jvm->flags |= FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET; + f_jvm->set_name (GTXT ("<JVM-System>")); + + // Find the JVM LoadObject + LoadObject *jvm = get_Unknown_LoadObject (); + for (int i = 0; i < lobjs->size (); ++i) + { + LoadObject *lo = lobjs->fetch (i); + if (lo->flags & SEG_FLAG_JVM) + { + jvm = lo; + break; + } + } + Module *mod = jvm->noname; + f_jvm->module = mod; + mod->functions->append (f_jvm); + // XXXX is it required? no consistency among all special functions + // jvm->functions->append( f_jvm ); + } + return f_jvm; +} + +Function * +DbeSession::getSpecialFunction (SpecialFunction kind) +{ + if (kind < 0 || kind >= LastSpecialFunction) + return NULL; + + Function *func = f_special->fetch (kind); + if (func == NULL) + { + char *fname; + switch (kind) + { + case TruncatedStackFunc: + fname = GTXT ("<Truncated-stack>"); + break; + case FailedUnwindFunc: + fname = GTXT ("<Stack-unwind-failed>"); + break; + default: + return NULL; + } + func = createFunction (); + func->flags |= FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET; + Module *mod = get_Total_LoadObject ()->noname; + func->module = mod; + mod->functions->append (func); + func->set_name (fname); + f_special->store (kind, func); + } + return func; +} + +LoadObject * +DbeSession::get_OMP_LoadObject () +{ + if (lo_omp == NULL) + { + for (int i = 0, sz = lobjs->size (); i < sz; i++) + { + LoadObject *lo = lobjs->fetch (i); + if (lo->flags & SEG_FLAG_OMP) + { + lo_omp = lo; + return lo_omp; + } + } + lo_omp = createLoadObject (GTXT ("<OMP>")); + lo_omp->type = LoadObject::SEG_TEXT; + lo_omp->dbeFile->filetype |= DbeFile::F_FICTION; + } + return lo_omp; +} + +Function * +DbeSession::get_OMP_Function (int n) +{ + if (n < 0 || n >= OMP_LAST_STATE) + return NULL; + + Function *func = omp_functions->fetch (n); + if (func == NULL) + { + char *fname; + switch (n) + { + case OMP_OVHD_STATE: + fname = GTXT ("<OMP-overhead>"); + break; + case OMP_IDLE_STATE: + fname = GTXT ("<OMP-idle>"); + break; + case OMP_RDUC_STATE: + fname = GTXT ("<OMP-reduction>"); + break; + case OMP_IBAR_STATE: + fname = GTXT ("<OMP-implicit_barrier>"); + break; + case OMP_EBAR_STATE: + fname = GTXT ("<OMP-explicit_barrier>"); + break; + case OMP_LKWT_STATE: + fname = GTXT ("<OMP-lock_wait>"); + break; + case OMP_CTWT_STATE: + fname = GTXT ("<OMP-critical_section_wait>"); + break; + case OMP_ODWT_STATE: + fname = GTXT ("<OMP-ordered_section_wait>"); + break; + case OMP_ATWT_STATE: + fname = GTXT ("<OMP-atomic_wait>"); + break; + default: + return NULL; + } + func = createFunction (); + func->flags |= FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET; + func->set_name (fname); + + LoadObject *omp = get_OMP_LoadObject (); + func->module = omp->noname; + omp->noname->functions->append (func); + omp->functions->append (func); + omp_functions->store (n, func); + } + return func; +} + +// Divide the original createExperiment() into two steps +// In part1, we just create the data structure, in part2, if +// we decide to keep the experiment around, add it to various +// lists in DbeSession +Experiment * +DbeSession::createExperimentPart1 () +{ + Experiment *exp = new Experiment (); + return exp; +} + +void +DbeSession::createExperimentPart2 (Experiment *exp) +{ + int ind = expGroups->size (); + if (ind > 0) + { + ExpGroup *gr = expGroups->fetch (ind - 1); + exp->groupId = gr->groupId; + gr->append (exp); + } + exp->setExpIdx (exps->size ()); + exp->setUserExpId (++user_exp_id_counter); + exps->append (exp); +} + +Experiment * +DbeSession::createExperiment () +{ + Experiment *exp = new Experiment (); + append (exp); + return exp; +} + +void +DbeSession::append (Experiment *exp) +{ + exp->setExpIdx (exps->size ()); + exp->setUserExpId (++user_exp_id_counter); + exps->append (exp); + if (exp->founder_exp) + { + if (exp->founder_exp->children_exps == NULL) + exp->founder_exp->children_exps = new Vector<Experiment *>; + exp->founder_exp->children_exps->append (exp); + if (exp->founder_exp->groupId > 0) + { + exp->groupId = exp->founder_exp->groupId; + expGroups->get (exp->groupId - 1)->append (exp); + } + } + if (exp->groupId == 0) + { + long ind = VecSize (expGroups); + if (ind > 0) + { + ExpGroup *gr = expGroups->get (ind - 1); + exp->groupId = gr->groupId; + gr->append (exp); + } + } +} + +void +DbeSession::append (Hwcentry *h) +{ + if (hwcentries == NULL) + hwcentries = new Vector<Hwcentry*>; + hwcentries->append (h); +} + +int +DbeSession::ngoodexps () +{ + return exps->size (); +} + +int +DbeSession::createView (int index, int cloneindex) +{ + // ensure that there is no view with that index + DbeView *dbev = getView (index); + if (dbev != NULL) + abort (); + + // find the view to be cloned + dbev = getView (cloneindex); + DbeView *newview; + if (dbev == NULL) + newview = new DbeView (theApplication, settings, index); + else + newview = new DbeView (dbev, index); + views->append (newview); + return index; +} + +DbeView * +DbeSession::getView (int index) +{ + int i; + DbeView *dbev; + Vec_loop (DbeView*, views, i, dbev) + { + if (dbev->vindex == index) + return dbev; + } + return NULL; +} + +void +DbeSession::dropView (int index) +{ + int i; + DbeView *dbev; + + Vec_loop (DbeView*, views, i, dbev) + { + if (dbev->vindex == index) + { + views->remove (i); + delete dbev; + return; + } + } + // view not found; ignore for now +} + +Vector<char*> * +DbeSession::get_group_or_expt (char *path) +{ + Vector<char*> *exp_list = new Vector<char*>; + FILE *fptr; + char *new_path, buf[MAXPATHLEN], name[MAXPATHLEN]; + + fptr = fopen (path, NTXT ("r")); + if (!fptr || !fgets (buf, (int) sizeof (buf), fptr) + || strncmp (buf, SP_GROUP_HEADER, strlen (SP_GROUP_HEADER))) + { + // it's not an experiment group + new_path = dbe_strdup (path); + new_path = canonical_path (new_path); + exp_list->append (new_path); + } + else + { + // it is an experiment group, read the list to get them all + while (fgets (buf, (int) sizeof (buf), fptr)) + { + if ((*buf != '#') && (sscanf (buf, NTXT ("%s"), name) == 1)) + { + new_path = dbe_strdup (name); + new_path = canonical_path (new_path); + exp_list->append (new_path); + } + } + } + if (fptr) + fclose (fptr); + return exp_list; +} + +#define GET_INT_VAL(v, s, len) \ + for (v = len = 0; isdigit(*s); s++, len++) { v = v * 10 + (*s -'0'); } + +static int +dir_name_cmp (const void *a, const void *b) +{ + char *s1 = *((char **) a); + char *s2 = *((char **) b); + while (*s1) + { + if (isdigit (*s1) && isdigit (*s2)) + { + int v1, v2, len1, len2; + GET_INT_VAL (v1, s1, len1); + GET_INT_VAL (v2, s2, len2); + if (v1 != v2) + return v1 - v2; + if (len1 != len2) + return len2 - len1; + continue; + } + if (*s1 != *s2) + break; + s1++; + s2++; + } + return *s1 - *s2; +} + +static int +read_experiment_data_in_parallel (void *arg) +{ + exp_ctx *ctx = (exp_ctx *) arg; + Experiment *dexp = ctx->exp; + bool read_ahead = ctx->read_ahead; + dexp->read_experiment_data (read_ahead); + free (ctx); + return 0; +} + +void +DbeSession::open_experiment (Experiment *exp, char *path) +{ + exp->open (path); + if (exp->get_status () != Experiment::FAILURE) + exp->read_experiment_data (false); + exp->open_epilogue (); + + // Update all DbeViews + for (int i = 0, sz = views->size (); i < sz; i++) + { + DbeView *dbev = views->fetch (i); + dbev->add_experiment (exp->getExpIdx (), true); + } + + if (exp->get_status () == Experiment::FAILURE) + { + check_tab_avail (); + return; + } + + char *discard_tiny = getenv (NTXT ("SP_ANALYZER_DISCARD_TINY_EXPERIMENTS")); + int user_specified_tiny_threshold = DEFAULT_TINY_THRESHOLD; // in milliseconds + if (discard_tiny != NULL) + { + user_specified_tiny_threshold = (atoi (discard_tiny)); + if (user_specified_tiny_threshold < 0) + user_specified_tiny_threshold = DEFAULT_TINY_THRESHOLD; + } + + // Open descendant experiments + DIR *exp_dir = opendir (path); + if (exp_dir == NULL) + { + check_tab_avail (); + return; + } + + Vector<char*> *exp_names = new Vector<char*>(); + struct dirent *entry = NULL; + while ((entry = readdir (exp_dir)) != NULL) + { + if (entry->d_name[0] != '_') + continue; + size_t len = strlen (entry->d_name); + if (len < 3 || strcmp (entry->d_name + len - 3, NTXT (".er")) != 0) + continue; + exp_names->append (dbe_strdup (entry->d_name)); + } + closedir (exp_dir); + exp_names->sort (dir_name_cmp); + Experiment **t_exp_list = new Experiment *[exp_names->size ()]; + int nsubexps = 0; + + for (int j = 0, jsz = exp_names->size (); j < jsz; j++) + { + t_exp_list[j] = NULL; + + char *lineage_name = exp_names->fetch (j); + struct stat64 sbuf; + char *dpath = dbe_sprintf (NTXT ("%s/%s"), path, lineage_name); + + // look for experiments with no profile collected + if (user_specified_tiny_threshold == DEFAULT_TINY_THRESHOLD) + { + char *frinfoname = dbe_sprintf (NTXT ("%s/%s"), dpath, "data." SP_FRINFO_FILE); + int st = dbe_stat (frinfoname, &sbuf); + free (frinfoname); + if (st == 0) + { + // if no profile/trace data do not process this experiment any further + if (sbuf.st_size == 0) + { + free (dpath); + continue; + } + } + } + else + { // check if dpath is a directory + if (dbe_stat (dpath, &sbuf) != 0) + { + free (dpath); + continue; + } + else if (!S_ISDIR (sbuf.st_mode)) + { + free (dpath); + continue; + } + } + size_t lineage_name_len = strlen (lineage_name); + lineage_name[lineage_name_len - 3] = 0; /* remove .er */ + Experiment *dexp = new Experiment (); + dexp->founder_exp = exp; + if (user_specified_tiny_threshold > DEFAULT_TINY_THRESHOLD) + { + dexp->setTinyThreshold (user_specified_tiny_threshold); + dexp->open (dpath); + if (dexp->isDiscardedTinyExperiment ()) + { + delete dexp; + free (dpath); + continue; + } + } + else + dexp->open (dpath); + append (dexp); + t_exp_list[j] = dexp; + nsubexps++; + dexp->set_clock (exp->clock); + + // DbeView add_experiment() is split into two parts + // add_subexperiment() is called repeeatedly for + // all sub_experiments, later add_experiment_epilogue() finishes up the task + for (int i = 0, sz = views->size (); i < sz; i++) + { + DbeView *dbev = views->fetch (i); + bool enabled = settings->check_en_desc (lineage_name, dexp->utargname); + dbev->add_subexperiment (dexp->getExpIdx (), enabled); + } + free (dpath); + } + + for (int i = 0, sz = views->size (); i < sz; i++) + { + DbeView *dbev = views->fetch (i); + dbev->add_experiment_epilogue (); + } + + DbeThreadPool * threadPool = new DbeThreadPool (-1); + for (int j = 0, jsz = exp_names->size (); j < jsz; j++) + { + if (t_exp_list[j] == NULL) continue; + Experiment *dexp = t_exp_list[j]; + exp_ctx *new_ctx = (exp_ctx*) malloc (sizeof (exp_ctx)); + new_ctx->path = NULL; + new_ctx->exp = dexp; + new_ctx->ds = this; + new_ctx->read_ahead = true; + DbeQueue *q = new DbeQueue (read_experiment_data_in_parallel, new_ctx); + threadPool->put_queue (q); + } + threadPool->wait_queues (); + delete threadPool; + + for (long j = 0, jsz = exp_names->size (); j < jsz; j++) + { + if (t_exp_list[j] == NULL) continue; + Experiment *dexp = t_exp_list[j]; + dexp->open_epilogue (); + } + exp_names->destroy (); + delete[] t_exp_list; + delete exp_names; + + // update setting for leaklist and dataspace + check_tab_avail (); +} + +void +DbeSession::append_mesgs (StringBuilder *sb, char *path, Experiment *exp) +{ + if (exp->fetch_errors () != NULL) + { + // yes, there were errors + char *ststr = pr_mesgs (exp->fetch_errors (), NTXT (""), NTXT ("")); + sb->append (path); + sb->append (NTXT (": ")); + sb->append (ststr); + free (ststr); + } + + Emsg *m = exp->fetch_warnings (); + if (m != NULL) + { + sb->append (path); + sb->append (NTXT (": ")); + if (!is_interactive ()) + sb->append (GTXT ("Experiment has warnings, see header for details\n")); + else + sb->append (GTXT ("Experiment has warnings, see experiment panel for details\n")); + } + + // Check for descendant experiments that are not loaded + int num_desc = VecSize (exp->children_exps); + if ((num_desc > 0) && !settings->check_en_desc (NULL, NULL)) + { + char *s; + if (!is_interactive ()) + s = dbe_sprintf (GTXT ("Has %d descendant(s), use commands controlling selection to load descendant data\n"), num_desc); + else + s = dbe_sprintf (GTXT ("Has %d descendant(s), use filter panel to load descendant data\n"), num_desc); + sb->append (path); + sb->append (NTXT (": ")); + sb->append (s); + free (s); + } +} + +Experiment * +DbeSession::get_exp (int exp_ind) +{ + if (exp_ind < 0 || exp_ind >= exps->size ()) + return NULL; + Experiment *exp = exps->fetch (exp_ind); + exp->setExpIdx (exp_ind); + return exp; +} + +Vector<Vector<char*>*> * +DbeSession::getExperimensGroups () +{ + if (dbeSession->expGroups == NULL || dbeSession->expGroups->size () == 0) + return NULL; + bool compare_mode = expGroups->size () > 1; + Vector<Vector<char*>*> *groups = new Vector<Vector<char*>*> ( + compare_mode ? expGroups->size () : 1); + for (int i = 0; i < expGroups->size (); i++) + { + ExpGroup *grp = expGroups->fetch (i); + Vector<Experiment*> *founders = grp->get_founders (); + if (founders && founders->size () != 0) + { + Vector<char *> *names = new Vector<char*> (founders->size ()); + for (int j = 0; j < founders->size (); j++) + { + Experiment *exp = founders->fetch (j); + names->append (dbe_strdup (exp->get_expt_name ())); + } + if (compare_mode || groups->size () == 0) + groups->append (names); + else + groups->fetch (0)->addAll (names); + } + delete founders; + } + return groups; +} + +char * +DbeSession::setExperimentsGroups (Vector<Vector<char*>*> *groups) +{ + StringBuilder sb; + for (int i = 0; i < groups->size (); i++) + { + Vector<char *> *names = groups->fetch (i); + ExpGroup *grp; + if (names->size () == 1) + grp = new ExpGroup (names->fetch (0)); + else + { + char *nm = dbe_sprintf (GTXT ("Group %d"), i + 1); + grp = new ExpGroup (nm); + free (nm); + } + expGroups->append (grp); + grp->groupId = expGroups->size (); + + for (int j = 0; j < names->size (); j++) + { + char *path = names->fetch (j); + size_t len = strlen (path); + if ((len > 4) && !strcmp (path + len - 4, NTXT (".erg"))) + { + Vector<char*> *lst = get_group_or_expt (path); + for (int j1 = 0; j1 < lst->size (); j1++) + { + Experiment *exp = new Experiment (); + append (exp); + open_experiment (exp, lst->get (j1)); + if (exp->get_status () == Experiment::FAILURE) + append_mesgs (&sb, path, exp); + } + lst->destroy (); + delete lst; + } + else + { + Experiment *exp = new Experiment (); + append (exp); + open_experiment (exp, path); + if (exp->get_status () == Experiment::FAILURE) + append_mesgs (&sb, path, exp); + } + } + } + + for (int i = 0, sz = views->size (); i < sz; i++) + { + DbeView *dbev = views->fetch (i); + dbev->update_advanced_filter (); + int cmp = dbev->get_settings ()->get_compare_mode (); + dbev->set_compare_mode (CMP_DISABLE); + dbev->set_compare_mode (cmp); + } + return sb.length () == 0 ? NULL : sb.toString (); +} + +char * +DbeSession::drop_experiment (int exp_ind) +{ + DbeView *dbev; + int index; + Experiment *exp2; + + status_ompavail = -1; + Experiment *exp = exps->fetch (exp_ind); + + // If this is a sub experiment, don't do it + if (exp->founder_exp != NULL) // this is a sub experiment; don't do it + return (dbe_strdup (GTXT ("Can not drop subexperiments"))); + + if (VecSize (exp->children_exps) > 0) + for (;;) + { + // search the list of experiments to find all that have this one as founder + bool found = false; + Vec_loop (Experiment*, exps, index, exp2) + { + if (exp2->founder_exp == exp) + { + exp2->founder_exp = NULL; + drop_experiment (index); + found = true; + break; + } + } + if (found == false) + break; + } + + // then proceed to finish the drop + Vec_loop (DbeView*, views, index, dbev) + { + dbev->drop_experiment (exp_ind); + } + + int old_cnt = expGroups->size (); + for (int i = 0; i < old_cnt; i++) + { + ExpGroup *gr = expGroups->fetch (i); + if (gr->groupId == exp->groupId) + { + gr->drop_experiment (exp); + if ((gr->founder == NULL) && (gr->exps->size () == 0)) + { + delete gr; + expGroups->remove (i); + } + break; + } + } + delete exps->remove (exp_ind); + if (old_cnt != expGroups->size ()) + { + for (int i = 0, sz = expGroups->size (); i < sz; i++) + { + ExpGroup *gr = expGroups->fetch (i); + gr->groupId = i + 1; + Vector<Experiment*> *expList = gr->exps; + for (int i1 = 0, sz1 = expList->size (); i1 < sz1; i1++) + expList->fetch (i1)->groupId = gr->groupId; + } + for (int i = 0, sz = views->size (); i < sz; i++) + { + dbev = views->fetch (i); + int cmp = dbev->get_compare_mode (); + dbev->set_compare_mode (CMP_DISABLE); + dbev->set_compare_mode (cmp); + } + } + check_tab_avail (); // update tab availability + return NULL; +} + +int +DbeSession::find_experiment (char *path) +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (strcmp (exp->get_expt_name (), path) == 0) + return exp->getExpIdx (); + } + return -1; +} + +LoadObject * +DbeSession::createLoadObject (const char *nm, int64_t cksum) +{ + return loadObjMap->sync_create_item (nm, cksum); +} + +LoadObject * +DbeSession::createLoadObject (const char *nm, const char *runTimePath, DbeFile *df) +{ + return loadObjMap->sync_create_item (nm, runTimePath, df); +} + +void +DbeSession::append (LoadObject *lobj) +{ + Histable *obj = lobj; // workaround for a C++ problem + objs->append (obj); + lobj->id = objs->size () - 1; + lobjs->append (lobj); + lobj->seg_idx = lobjs->size () - 1; + char *loname = lobj->get_pathname (); + dbeFiles->put (loname, lobj->dbeFile); +} + +DbeJarFile * +DbeSession::get_JarFile (const char *name) +{ + DbeJarFile *jf = dbeJarFiles->get (name); + if (jf == NULL) + { + jf = new DbeJarFile (name); + dbeJarFiles->put (name, jf); + } + return jf; +} + +Module * +DbeSession::createModule (LoadObject *lo, const char *nm) +{ + Module *mod = new Module (); + Histable *obj = mod; // workaround for a C++ problem + objs->append (obj); + mod->id = objs->size () - 1; + mod->loadobject = lo; + mod->set_name (dbe_strdup (nm ? nm : localized_SP_UNKNOWN_NAME)); + lo->seg_modules->append (mod); + return mod; +} + +Module * +DbeSession::createUnknownModule (LoadObject *lo) +{ + Module *mod = createModule (lo, localized_SP_UNKNOWN_NAME); + mod->flags |= MOD_FLAG_UNKNOWN; + mod->set_file_name (dbe_strdup (localized_SP_UNKNOWN_NAME)); + return mod; +} + +SourceFile * +DbeSession::createSourceFile (const char *_path) +{ + char *path = (char *) _path; + if (strncmp (path, NTXT ("./"), 2) == 0) + path += 2; + SourceFile *source = sourcesMap->get (path); + if (source == NULL) + { + source = new SourceFile (path); + (void) sourcesMap->put (path, source); + append (source); + } + return source; +} + +Function * +DbeSession::createFunction () +{ + Function *func = new Function (objs->size ()); + Histable *obj = func; // workaround for a C++ problem + objs->append (obj); + return func; +} + +JMethod * +DbeSession::createJMethod () +{ + JMethod *jmthd = new JMethod (objs->size ()); + Histable *obj = jmthd; // workaround for a C++ problem + objs->append (obj); + return jmthd; +} + +Module * +DbeSession::createClassFile (char *className) +{ + ClassFile *cls = new ClassFile (); + cls->set_name (className); + char *clpath = cls->get_java_file_name (className, true); + cls->dbeFile = getDbeFile (clpath, DbeFile::F_JAVACLASS); + free (clpath); + Histable *obj = cls; // workaround for a C++ problem + objs->append (obj); + cls->id = objs->size () - 1; + return cls; +} + +Histable * +DbeSession::createHistObject (Histable::Type type) +{ + switch (type) + { + case Histable::DOBJECT: + { + DataObject *dataobj = new DataObject (); + dobjs->append (dataobj); + dataobj->id = dobjs->size () - 1; + return dataobj; + } + default: + assert (0); + } + return NULL; +} + +DataObject * +DbeSession::createDataObject () +{ + DataObject *dataobj = new DataObject (); + dobjs->append (dataobj); + dataobj->id = dobjs->size () - 1; + return dataobj; +} + +DataObject * +DbeSession::createDataObject (DataObject *dobj, DataObject *parent) +{ + DataObject *dataobj = new DataObject (); + dataobj->size = dobj->size; + dataobj->offset = dobj->offset; + dataobj->parent = parent; + dataobj->set_dobjname (dobj->get_typename (), dobj->get_instname ()); + dobjs->append (dataobj); + dataobj->id = dobjs->size () - 1; + return dataobj; +} + +DataObject * +DbeSession::createMasterDataObject (DataObject *dobj) +{ + DataObject *parent = NULL; + if (dobj->parent) + { // define master parent first + parent = find_dobj_master (dobj->parent); + if (!parent) + { // clone master from this dataobject parent + parent = createDataObject (dobj->parent); + parent->scope = NULL; // master is scope-less + Dprintf (DEBUG_DATAOBJ, + "Master DataObject(%llu) cloned from (%llu) %s\n", + (ull_t) parent->id, (ull_t) dobj->parent->id, + dobj->parent->get_name ()); + // clone master DataObject elements + Vector<DataObject*> *delem = get_dobj_elements (dobj->parent); + int element_index = 0; + DataObject *element = NULL; + Vec_loop (DataObject*, delem, element_index, element) + { + DataObject *master_element = createDataObject (element, parent); + master_element->scope = NULL; // master is scope-less + Dprintf (DEBUG_DATAOBJ, + "Member DataObject(%llu) cloned from (%llu) %s\n", + (ull_t) master_element->id, (ull_t) element->id, + element->get_name ()); + } + } + else + Dprintf (DEBUG_DATAOBJ, "Master DataObject(%llu) clone found (%llu) %s\n", + (ull_t) parent->id, (ull_t) dobj->parent->id, + dobj->parent->get_name ()); + } + + DataObject *master = find_dobj_master (dobj); + if (!master) + { // clone master from this dataobject + master = createDataObject (dobj, parent); + master->scope = NULL; // master is scope-less + Dprintf (DEBUG_DATAOBJ, "Master DataObject(%llu) cloned from (%llu) %s\n", + (ull_t) master->id, (ull_t) dobj->id, dobj->get_name ()); + } + else + Dprintf (DEBUG_DATAOBJ, "Master DataObject(%llu) clone found (%llu) %s\n", + (ull_t) master->id, (ull_t) dobj->id, dobj->get_name ()); + return master; +} + +void +DbeSession::insert_metric (BaseMetric *mtr, Vector<BaseMetric*> *mlist) +{ + if ((mtr->get_flavors () & Metric::STATIC) == 0) + { + // insert in front of the first STATIC + for (int i = 0, mlist_sz = mlist->size (); i < mlist_sz; i++) + { + BaseMetric *m = mlist->fetch (i); + if (m->get_flavors () & Metric::STATIC) + { + mlist->insert (i, mtr); + return; + } + } + } + mlist->append (mtr); +} + +BaseMetricTreeNode* +DbeSession::get_reg_metrics_tree () +{ + if (reg_metrics_tree == NULL) + // Can't init earlier because BaseMetric() requires DbeSession::ql_parse + reg_metrics_tree = new BaseMetricTreeNode (); + return reg_metrics_tree; +} + +void +DbeSession::update_metric_tree (BaseMetric *m) +{ + get_reg_metrics_tree ()->register_metric (m); +} + +BaseMetric * +DbeSession::register_metric_expr (BaseMetric::Type type, char *cmd, char *expr_spec) +{ + BaseMetric *m = find_metric (type, cmd, expr_spec); + if (m) + return m; + BaseMetric *bm = find_metric (type, cmd, NULL); // clone this version + m = new BaseMetric (*bm); + m->set_expr_spec (expr_spec); + insert_metric (m, reg_metrics); + return m; +} + +BaseMetric * +DbeSession::register_metric (BaseMetric::Type type) +{ + BaseMetric *m = find_metric (type, NULL, NULL); + if (m) + return m; + m = new BaseMetric (type); + insert_metric (m, reg_metrics); + update_metric_tree (m); + return m; +} + +BaseMetric * +DbeSession::register_metric (Hwcentry *ctr, const char* aux, const char* username) +{ + BaseMetric *m = find_metric (BaseMetric::HWCNTR, aux, NULL); + if (m) + // That may be a problem when metrics aren't an exact match. + // For example, memoryspace is disabled in one experiment and not in another. + return m; + if (ctr->timecvt) + { + char *time_cmd = dbe_sprintf (NTXT ("t%s"), aux); + char *time_username = dbe_sprintf (GTXT ("%s Time"), + ctr->metric ? ctr->metric : + (ctr->name ? ctr->name : ctr->int_name)); + BaseMetric *m1; + if (ipc_mode) + { + // Two visible metrics are presented in GUI + m1 = new BaseMetric (ctr, aux, time_cmd, time_username, VAL_TIMEVAL); + insert_metric (m1, reg_metrics); + update_metric_tree (m1); + m = new BaseMetric (ctr, aux, username, VAL_VALUE, m1); + } + else + { + // Only one visible metric is presented in er_print + m1 = new BaseMetric (ctr, aux, time_cmd, time_username, VAL_TIMEVAL | VAL_INTERNAL); + insert_metric (m1, reg_metrics); + m = new BaseMetric (ctr, aux, username, VAL_TIMEVAL | VAL_VALUE, m1); + } + free (time_cmd); + free (time_username); + } + else + m = new BaseMetric (ctr, aux, username, VAL_VALUE); + insert_metric (m, reg_metrics); + update_metric_tree (m); + return m; +} + +BaseMetric * +DbeSession::register_metric (char *name, char *username, char *_def) +{ + BaseMetric *m = find_metric (BaseMetric::DERIVED, name, NULL); + if (m) + return m; + Definition *p = Definition::add_definition (_def); + if (p == NULL) + return NULL; + m = new BaseMetric (name, username, p); + insert_metric (m, reg_metrics); + update_metric_tree (m); + return m; +} + +void +DbeSession::drop_metric (BaseMetric *mtr) +{ + Countable *cnt; + int index; + + Vec_loop (Countable*, metrics, index, cnt) + { + if (mtr == (BaseMetric *) cnt->item) + { + cnt->ref_count--; + if (cnt->ref_count == 0) + { + // Remove this metric from all views + DbeView *dbev; + int index2; + Vec_loop (DbeView*, views, index2, dbev) + { + dbev->reset_metrics (); + } + delete metrics->remove (index); + delete mtr; + return; + } + } + } +} + +BaseMetric * +DbeSession::find_metric (BaseMetric::Type type, const char *cmd, const char *expr_spec) +{ + for (int i = 0, sz = reg_metrics->size (); i < sz; i++) + { + BaseMetric *bm = reg_metrics->fetch (i); + if (bm->get_type () == type && dbe_strcmp (bm->get_expr_spec (), expr_spec) == 0) + { + if ((type == BaseMetric::DERIVED || type == BaseMetric::HWCNTR) + && dbe_strcmp (bm->get_cmd (), cmd) != 0) + continue; + return bm; + } + } + return NULL; +} + +BaseMetric * +DbeSession::find_base_reg_metric (char * mcmd) +{ + for (int i = 0, sz = reg_metrics->size (); i < sz; i++) + { + BaseMetric *bm = reg_metrics->fetch (i); + if (bm->get_expr_spec () != NULL) + continue; // skip compare metrics + if (dbe_strcmp (bm->get_cmd (), mcmd) == 0) + return bm; + } + return NULL; +} + +Vector<BaseMetric*> * +DbeSession::get_base_reg_metrics () +{ + Vector<BaseMetric*> *mlist = new Vector<BaseMetric*>; + Vector<BaseMetric*> *ml = get_all_reg_metrics (); + for (int i = 0, sz = ml->size (); i < sz; i++) + { + BaseMetric *m = ml->fetch (i); + if (m->get_expr_spec () == NULL) + mlist->append (m); + } + return mlist; +} + +void +DbeSession::check_tab_avail () +{ + DbeView *dbev; + int index; + // tell the views to update their tab lists + Vec_loop (DbeView*, views, index, dbev) + { + dbev->get_settings ()->updateTabAvailability (); + } +} + +bool +DbeSession::is_datamode_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->dataspaceavail) + return true; + } + return false; +} + +bool +DbeSession::is_leaklist_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->leaklistavail) + return true; + } + return false; +} + +bool +DbeSession::is_heapdata_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->heapdataavail) + return true; + } + return false; +} + +bool +DbeSession::is_iodata_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->iodataavail) + return true; + } + return false; +} + +bool +DbeSession::is_racelist_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->racelistavail) + return true; + } + return false; +} + +bool +DbeSession::is_deadlocklist_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->deadlocklistavail) + return true; + } + return false; +} + +bool +DbeSession::is_timeline_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->timelineavail) + return true; + } + return false; +} + +bool +DbeSession::is_ifreq_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->ifreqavail) + return true; + } + return false; +} + +bool +DbeSession::is_omp_available () +{ + if (status_ompavail == -1) + { + status_ompavail = 0; + for (int i = 0, sz = exps ? exps->size () : 0; i < sz; i++) + { + Experiment *exp = exps->fetch (i); + if (exp->ompavail) + { + status_ompavail = 1; + break; + } + } + } + return status_ompavail == 1; +} + +bool +DbeSession::has_java () +{ + int status_has_java = 0; + for (int i = 0, sz = exps ? exps->size () : 0; i < sz; i++) + { + Experiment *exp = exps->fetch (i); + if (exp->has_java) + { + status_has_java = 1; + break; + } + } + return status_has_java == 1; +} + +bool +DbeSession::has_ompavail () +{ + int status_has_ompavail = 0; + for (int i = 0, sz = exps ? exps->size () : 0; i < sz; i++) + { + Experiment *exp = exps->fetch (i); + if (exp->ompavail) + { + status_has_ompavail = 1; + break; + } + } + return status_has_ompavail == 1; +} + +int +DbeSession::get_clock (int whichexp) +{ + // XXXX clock frequency should be an attribute of each CPU, + // XXX and not a property of the session + // if whichexp is -1, pick the first exp that has a clock + // otherwise return the clock from the numbered experiment + Experiment *exp; + if (whichexp != -1) + { + exp = get_exp (whichexp); + if (exp != NULL) + return exp->clock; + return 0; + } + int n = nexps (); + for (int i = 0; i < n; i++) + { + exp = get_exp (i); + if (exp != NULL && exp->clock != 0) + return exp->clock; + } + return 0; +} + +LoadObject * +DbeSession::find_lobj_by_name (const char *lobj_name, int64_t cksum) +{ + return loadObjMap->get (lobj_name, cksum); +} + +static unsigned +hash (char *s) +{ + unsigned res = 0; + for (int i = 0; i < 64 && *s; i++) + res = res * 13 + *s++; + return res; +} + +// This method is introduced to fix performance +// problems with the data space profiling in the +// current release. A better design is desired. +void +DbeSession::dobj_updateHT (DataObject *dobj) +{ + unsigned index = hash (dobj->get_unannotated_name ()) % HTableSize; + List *list = new List; + list->val = (void*) dobj; + list->next = dnameHTable[index]; + dnameHTable[index] = list; +} + +DataObject * +DbeSession::find_dobj_by_name (char *dobj_name) +{ + unsigned index = hash (dobj_name) % HTableSize; + List *list = dnameHTable[index]; + for (; list; list = list->next) + { + DataObject *d = (DataObject*) list->val; + if (strcmp (d->get_unannotated_name (), dobj_name) == 0) + return d; + } + return (DataObject *) NULL; +} + +DataObject * +DbeSession::find_dobj_match (DataObject *dobj) +{ + char *dobj_name = dobj->get_unannotated_name (); + unsigned index = hash (dobj_name) % HTableSize; + List *list = dnameHTable[index]; + for (; list; list = list->next) + { + DataObject *d = (DataObject*) list->val; + if (strcmp (d->get_unannotated_name (), dobj_name) == 0 + && d->size == dobj->size && d->offset == dobj->offset + && d->scope == dobj->scope) + return d; + } + return (DataObject *) NULL; +} + +DataObject * +DbeSession::find_dobj_master (DataObject *dobj) +{ + char *dobj_name = dobj->get_unannotated_name (); + unsigned index = hash (dobj_name) % HTableSize; + List *list = dnameHTable[index]; + for (; list; list = list->next) + { + DataObject *d = (DataObject*) list->val; + // XXXX should parent also match? + if (strcmp (d->get_unannotated_name (), dobj_name) == 0 + && d->size == dobj->size && d->offset == dobj->offset + && d->master == NULL && d->scope == NULL) + return d; + } + return (DataObject *) NULL; +} + +Vector<DataObject*>* +DbeSession::get_dobj_elements (DataObject *dobj) +{ + DataObject *d; + int index; + Vector<DataObject*> *elements = new Vector<DataObject*>; + if (dobj == d_total) + return elements; + Vec_loop (DataObject*, dobjs, index, d) + { + if (d->get_parent () && d->get_parent () == dobj) + elements->append (d); + } + return elements; +} + +Vector<LoadObject*>* +DbeSession::get_text_segments () +{ + LoadObject *lo; + int index; + Vector<LoadObject*> *tlobjs = new Vector<LoadObject*>; + Vec_loop (LoadObject*, lobjs, index, lo) + { + if (lo->type == LoadObject::SEG_TEXT) + tlobjs->append (lo); + } + return tlobjs; +} + +static long long +getNumber (const char *s, char * &last) +{ + long long val; + char *sp; + errno = 0; + val = strtoll (s, &sp, 0); + if (errno == EINVAL) + last = NULL; + else + { + while (isspace (*sp)) + sp++; + last = sp; + } + return (val); +} + +bool +DbeSession::find_obj (FILE *dis_file, FILE *inp_file, Histable *&obj, + char *name, const char *sel, Histable::Type type, bool xdefault) +{ + Vector<Histable*> *obj_lst; + int which = -1; + char *last = NULL; + if (type != Histable::FUNCTION && sel) + { + // check that a number has been provided + which = (int) getNumber (sel, last); + if (last == NULL || *last != '\0') + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + sel = NULL; + which = 0; + } + which--; + } + obj_lst = new Vector<Histable*>; + switch (type) + { + case Histable::FUNCTION: + obj = map_NametoFunction (name, obj_lst, sel); + break; + case Histable::MODULE: + obj = map_NametoModule (name, obj_lst, which); + break; + case Histable::LOADOBJECT: + obj = map_NametoLoadObject (name, obj_lst, which); + break; + case Histable::DOBJECT: + obj = map_NametoDataObject (name, obj_lst, which); + break; + default: + abort (); // unexpected Histable! + } + + if ((obj == NULL) && (obj_lst->size () > 0)) + { + if (obj_lst->size () == 1) + which = 0; + else + { + if (sel && (which < 0 || which >= obj_lst->size ())) + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + if (xdefault) + { + fprintf (stderr, GTXT ("Default selection \"1\" made\n")); + which = 0; + } + else + { + which = ask_which (dis_file, inp_file, obj_lst, name); + if (which == -1) + { + delete obj_lst; + return false; + } + } + } + obj = obj_lst->fetch (which); + } + delete obj_lst; + return true; +} + +int +DbeSession::ask_which (FILE *dis_file, FILE *inp_file, + Vector<Histable*> *list, char *name) +{ + Histable *hitem; + Function *func; + Module *module; + int which, index, index1; + char *item_name, *lo_name, *fname, *last; + char buf[BUFSIZ]; + for (;;) + { + fprintf (dis_file, GTXT ("Available name list:\n")); + fprintf (dis_file, GTXT ("%8d) Cancel\n"), 0); + Vec_loop (Histable*, list, index, hitem) + { + index1 = index + 1; + item_name = hitem->get_name (); + switch (hitem->get_type ()) + { + case Histable::FUNCTION: + func = (Function *) hitem; + module = func->module; + + // id == -1 indicates er_src invocation + if (module == NULL || (module->lang_code == Sp_lang_java + && module->loadobject->id == -1)) + fprintf (dis_file, NTXT ("%8d) %s\n"), index1, item_name); + else + { + lo_name = module->loadobject->get_pathname (); + fname = (module->file_name && *module->file_name) ? + module->file_name : module->get_name (); + if (fname && *fname) + fprintf (dis_file, NTXT ("%8d) %s %s:0x%llx (%s)\n"), index1, + item_name, lo_name, (ull_t) func->img_offset, fname); + else + fprintf (dis_file, NTXT ("%8d) %s %s:0x%llx\n"), index1, + item_name, lo_name, (ull_t) func->img_offset); + } + break; + case Histable::MODULE: + module = (Module *) hitem; + lo_name = module->loadobject->get_pathname (); + if (name[strlen (name) - 1] == + module->file_name[strlen (module->file_name) - 1]) + fprintf (dis_file, NTXT ("%8d) %s(%s)\n"), index1, + module->file_name, lo_name); + else + fprintf (dis_file, NTXT ("%8d) %s(%s)\n"), index1, item_name, + lo_name); + break; + default: + fprintf (dis_file, NTXT ("%8d) %s\n"), index1, item_name); + break; + } + } + if (inp_file != stdin) + return -1; + fprintf (dis_file, GTXT ("Enter selection: ")); + if (fgets (buf, (int) sizeof (buf), inp_file) == NULL) + { + fprintf (stderr, GTXT ("Error: Invalid number entered:\n")); + return -1; + } + which = (int) getNumber (buf, last); + if (last && *last == '\0') + if (which >= 0 && which <= list->size ()) + return which - 1; + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), buf); + } +} + +static bool +match_basename (char *name, char *full_name, int len = -1) +{ + if (full_name == NULL) + return false; + if (!strchr (name, '/')) + full_name = get_basename (full_name); + if (len == -1) + return streq (name, full_name); + return strncmp (name, full_name, len) == 0; +} + +LoadObject * +DbeSession::map_NametoLoadObject (char *name, Vector<Histable*> *list, int which) +{ + // Search the tree to find the first module whose module name + // matches "name" or whose source file name matches "name" + // Issues: is the name a pathname, or a base name? + // Should we look at suffix to disambiguate? + LoadObject *loitem; + int index; + Vec_loop (LoadObject*, lobjs, index, loitem) + { + // try pathname first + // if failed, try object name next + if (match_basename (name, loitem->get_pathname ()) || + match_basename (name, loitem->get_name ())) + { + if (which == list->size ()) + return loitem; + list->append (loitem); + } + } + return (LoadObject *) NULL; +} + +Module * +DbeSession::map_NametoModule (char *name, Vector<Histable*> *list, int which) +{ + // Search the tree to find the first loadobject whose loadobject name + // matches "name". + + // Issues: is the name a pathname, or a base name? + // Should we look at suffix to disambiguate? + LoadObject *loitem; + Module *mitem; + int index1, index2; + Vec_loop (LoadObject*, lobjs, index1, loitem) + { + Vec_loop (Module*, loitem->seg_modules, index2, mitem) + { + // try source name first + // if failed, try object name next + if (match_basename (name, mitem->file_name) || + match_basename (name, mitem->get_name ())) + { + if (which == list->size ()) + return mitem; + list->append (mitem); + } + } + } + return (Module *) NULL; +} + +Function * +DbeSession::map_NametoFunction (char *name, Vector<Histable*> *list, + const char *sel) +{ + // Search the tree to find the first function whose + // name matches "name". + // Issues: is the name a full name, or a short name? + // Is it a demangled name? If so, what about spaces + // within the name? + // Is there a way to return all names that match? + // How can the user specify a particular function of that name? + LoadObject *loitem; + Function *fitem, *main_func = NULL; + Module *mitem, *main_mod = NULL; + int index1, index2, index3, which = -1; + if (sel) + { + char *last = NULL; + if (*sel == '@') + { // 'sel' is "@seg_num:address" + which = (int) getNumber (sel + 1, last); + if (last == NULL || *last != ':' || (which < 0) || (which >= lobjs->size ())) + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + return NULL; + } + uint64_t address = getNumber (last + 1, last); + if (last == NULL || *last != '\0') + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + return NULL; + } + loitem = lobjs->fetch (which); + Vec_loop (Module*, loitem->seg_modules, index2, mitem) + { + Vec_loop (Function*, mitem->functions, index3, fitem) + { + if (address == fitem->img_offset && match_FName (name, fitem)) + return fitem; + } + } + return NULL; + } + + which = (int) getNumber (sel, last); + if (last == NULL || *last != '\0') + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + return NULL; + } + which--; + } + + int len_path = 0; + char *with_path = name; + name = StrRchr (name, '`'); + if (name != with_path) + len_path = (int) (name - with_path); + else + with_path = NULL; + + Vec_loop (LoadObject*, lobjs, index1, loitem) + { + Vec_loop (Module*, loitem->seg_modules, index2, mitem) + { + if (with_path) + { // with file name + // try source name first + // if failed, try object name next + if (!match_basename (with_path, mitem->file_name, len_path) && + !match_basename (with_path, mitem->get_name (), len_path)) + continue; + } + Vec_loop (Function*, mitem->functions, index3, fitem) + { + if (match_FName (name, fitem)) + { + if (which == list->size ()) + return fitem; + list->append (fitem); + continue; + } + if (streq (fitem->get_name (), NTXT ("MAIN_")) && mitem->is_fortran ()) + { + main_func = fitem; + main_mod = mitem; + } + } + } + } + + if (main_mod && main_func) + { + main_mod->read_stabs (); + if (streq (main_func->get_match_name (), name) && which <= 1) + return main_func; + } + return (Function *) NULL; +} + +DataObject * +DbeSession::map_NametoDataObject (char *name, Vector<Histable*> *list, + int which) +{ + // Search master list to find dataobjects whose names match "name" + // selecting only the entry corresponding to "which" if it is not -1. + // Issues: is the name fully qualified or only partially? + DataObject *ditem = NULL; + int index; + char *full_name; + Vec_loop (DataObject*, dobjs, index, ditem) + { + if (ditem->scope) continue; // skip non-master dataobjects + + // try fully-qualified dataobject name first + if ((full_name = ditem->get_name ()) != NULL) + { + if (streq (name, full_name)) + { + if (which == list->size ()) + return ditem; + list->append (ditem); + } + } + } + if (list->size () > 0) + return ditem; // return fully-qualified match + + // if fully-qualified name doesn't match anything, try a partial match + Vec_loop (DataObject*, dobjs, index, ditem) + { + if (ditem->scope) continue; // skip non-master dataobjects + + // try fully-qualified dataobject name first + if ((full_name = ditem->get_name ()) != NULL) + { + if (strstr (full_name, name)) + { + if (which == list->size ()) + return ditem; + list->append (ditem); + } + } + } + return (DataObject *) NULL; +} + +bool +DbeSession::match_FName (char *name, Function *func) +{ + size_t len; + char buf[MAXDBUF]; + char *full_name; + if (streq (func->get_name (), name)) // try full name comparison + return true; + if (streq (func->get_mangled_name (), name)) // try mangled name + return true; + if (streq (func->get_match_name (), name)) // try match name + return true; + + Module *md = func->module; // try FORTRAN name + if (md && md->is_fortran ()) + { + char *mangled_name = func->get_mangled_name (); + len = strlen (name); + if (((len + 1) == strlen (mangled_name)) && + (strncmp (name, mangled_name, len) == 0)) + return true; + } + snprintf (buf, sizeof (buf), NTXT ("%s"), func->get_name ()); + full_name = buf; + char *arg = NULL; // find modifier and C++ class name + int i = get_paren (buf); + if (i >= 0) + { + arg = buf + i; + *arg = '\0'; + } + + char *mod = strchr (full_name, ' '); + char *cls = strchr (full_name, ':'); + + if (mod) + { + len = mod - full_name + 1; + if (!strncmp (full_name, name, len)) + name += len; + full_name += len; + if (streq (full_name, name)) // try without modifier + return true; + } + + size_t len_cmp = strlen (name); + if (arg) + { + *arg = '('; + len = arg - full_name; // try without 'args' + if (len_cmp == len && !strncmp (full_name, name, len)) + return true; + if (cls) + { + len = arg - cls - 2; // and without 'class name' + if ((len_cmp == len) && !strncmp (cls + 2, name, len)) + return true; + } + } + + if (cls) + { + len = cls - full_name; // try C++ class name only + if (len_cmp == len && !strncmp (full_name, name, len)) + return true; + if (streq (cls + 2, name)) // try without 'class name' + return true; + } + return false; +} + +bool +DbeSession::add_path (char *path) +{ + return add_path (path, get_search_path ()); +} + +bool +DbeSession::add_classpath (char *path) +{ + return add_path (path, classpath); +} + +Vector<DbeFile*> * +DbeSession::get_classpath () +{ + if (classpath_df == NULL) + classpath_df = new Vector<DbeFile*>; + for (int i = classpath_df->size (), sz = classpath->size (); i < sz; i++) + classpath_df->store (i, getDbeFile (classpath->fetch (i), + DbeFile::F_DIR_OR_JAR)); + return classpath_df; +} + +bool +DbeSession::add_path (char *path, Vector<char*> *pathes) +{ + bool result = false; + Vector <char *> *tokens = split_str (path, ':'); + for (long j = 0, jsz = VecSize (tokens); j < jsz; j++) + { + char *spath = tokens->get (j); + // Don't append path if it's already there + bool got = false; + for (int i = 0, sz = pathes->size (); i < sz; i++) + { + char *nm = pathes->get (i); + if (streq (nm, spath)) + { + got = true; + break; + } + } + if (!got) + { + pathes->append (spath); + result = true; + } + else + free (spath); + } + delete tokens; + return result; +} + +void +DbeSession::set_need_refind () +{ + Vector<DbeFile*> *f_list = dbeFiles->values (); + for (long i = 0, sz = f_list == NULL ? 0 : f_list->size (); i < sz; i++) + { + DbeFile *f = f_list->get (i); + f->set_need_refind (true); + } + delete f_list; + for (long i = 0, sz = sources == NULL ? 0 : sources->size (); i < sz; i++) + { + SourceFile *f = sources->get (i); + if (f && f->dbeFile) + f->dbeFile->set_need_refind (true); + } +} + +void +DbeSession::set_search_path (Vector<char*> *path, bool reset) +{ + if (reset) + search_path->destroy (); + for (int i = 0, sz = path == NULL ? 0 : path->size (); i < sz; i++) + { + char *name = path->fetch (i); + if (add_path (name)) + reset = true; + } + if (reset) + { + set_need_refind (); + + // now reset the string setting for it + StringBuilder sb; + for (int i = 0, sz = search_path == NULL ? 0 : search_path->size (); i < sz; i++) + { + char *name = search_path->fetch (i); + if (sb.length () != 0) + sb.append (':'); + sb.append (name); + } + free (settings->str_search_path); + settings->str_search_path = sb.toString (); + } +} + +void +DbeSession::set_search_path (char *_lpath, bool reset) +{ + Vector<char *> *path = new Vector<char*>; + char *lpath = dbe_strdup (_lpath); + for (char *s = lpath; s;) + { + path->append (s); + s = strchr (s, ':'); + if (s) + { + *s = 0; + s++; + } + } + set_search_path (path, reset); + delete path; + free (lpath); +} + +void +DbeSession::set_pathmaps (Vector<pathmap_t*> *newPathMap) +{ + set_need_refind (); + settings->set_pathmaps (newPathMap); +} + +Vector<pathmap_t*> * +DbeSession::get_pathmaps () +{ + return settings->pathmaps; +} + +void +DbeSession::mobj_define (MemObjType_t *mobj) +{ + settings->mobj_define (mobj, false); + DbeView *dbev; + int index; + Vec_loop (DbeView*, views, index, dbev) + { + dbev->get_settings ()->mobj_define (mobj, false); + } +} + +void +DbeSession::dump_segments (FILE *out) +{ + int index; + LoadObject *loitem; + Vec_loop (LoadObject*, lobjs, index, loitem) + { + fprintf (out, NTXT ("Segment %d -- %s -- %s\n\n"), + index, loitem->get_name (), loitem->get_pathname ()); + loitem->dump_functions (out); + fprintf (out, NTXT ("\n End Segment %d -- %s -- %s\n\n"), + index, loitem->get_name (), loitem->get_pathname ()); + } +} + +void +DbeSession::dump_dataobjects (FILE *out) +{ + DataObject *ditem; + int index; + + fprintf (out, NTXT ("\nMaster list of DataObjects:\n")); + Vec_loop (DataObject*, dobjs, index, ditem) + { + Histable* scope = ditem->get_scope (); + DataObject* parent = ditem->get_parent (); + DataObject* master = ditem->get_master (); + if (parent != NULL) + fprintf (out, "id %6lld: [%4lld] parent = %6lld, offset = %+4lld %s\n", + (ll_t) ditem->id, (ll_t) ditem->get_size (), + (ll_t) parent->id, (ll_t) ditem->get_offset (), + ditem->get_name ()); + else + { + // parent is NULL + fprintf (out, NTXT ("id %6lld: [%4lld] %s "), + (ll_t) ditem->id, (ll_t) ditem->get_size (), + ditem->get_name ()); + if (master != NULL) + fprintf (out, NTXT (" master=%lld "), (ll_t) master->id); + else if (scope != NULL) + fprintf (out, NTXT (" master=?? ")); + else + fprintf (out, NTXT (" MASTER ")); +#if DEBUG + if (scope != NULL) + { + switch (scope->get_type ()) + { + case Histable::LOADOBJECT: + case Histable::FUNCTION: + fprintf (out, NTXT ("%s"), scope->get_name ()); + break; + case Histable::MODULE: + { + char *filename = get_basename (scope->get_name ()); + fprintf (out, NTXT ("%s"), filename); + break; + } + default: + fprintf (out, NTXT (" Unexpected scope %d:%s"), + scope->get_type (), scope->get_name ()); + } + } +#endif + fprintf (out, NTXT ("\n")); + } + } +} + +void +DbeSession::dump_map (FILE *out) +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + exp->dump_map (out); + } +} + +void +DbeSession::dump_stacks (FILE *outfile) +{ + Experiment *exp; + int n = nexps (); + FILE *f = (outfile == NULL ? stderr : outfile); + for (int i = 0; i < n; i++) + { + exp = get_exp (i); + fprintf (f, GTXT ("Experiment %d -- %s\n"), i, exp->get_expt_name ()); + exp->dump_stacks (f); + } +} + +void +DbeSession::propNames_name_store (int propId, const char *propName) +{ + PropDescr *prop = new PropDescr (propId, propName); + prop->flags = PRFLAG_NOSHOW; // do not show descriptions + propNames->store (propId, prop); +} + +void +DbeSession::propNames_name_store (int propId, const char* propName, + const char* propUname, VType_type dataType, + int flags) +{ + PropDescr *prop = new PropDescr (propId, propName); + prop->vtype = dataType; + prop->uname = dbe_strdup (propUname); + prop->flags = flags; + propNames->store (propId, prop); +} + +char * +DbeSession::propNames_name_fetch (int i) +{ + PropDescr *prop = propNames->fetch (i); + if (prop) + return prop->name; + return NULL; +} + +int +DbeSession::registerPropertyName (const char *name) +{ + if (name == NULL) + return PROP_NONE; + for (int i = 0; i < propNames->size (); i++) + { + char *pname = propNames_name_fetch (i); + if (pname && strcasecmp (pname, name) == 0) + return i; + } + int propId = propNames->size (); + propNames_name_store (propId, name); + return propId; +} + +int +DbeSession::getPropIdByName (const char *name) +{ + if (name == NULL) + return PROP_NONE; + for (int i = 0; i < propNames->size (); i++) + { + char *pname = propNames_name_fetch (i); + if (pname && strcasecmp (pname, name) == 0) + return i; + } + return PROP_NONE; +} + +char * +DbeSession::getPropName (int propId) +{ + if (!propNames) + return NULL; + if (propId < 0 || propId >= propNames->size ()) + return NULL; + return dbe_strdup (propNames_name_fetch (propId)); +} + +char * +DbeSession::getPropUName (int propId) +{ + if (!propNames) + return NULL; + if (propId < 0 || propId >= propNames->size ()) + return NULL; + PropDescr *prop = propNames->fetch (propId); + if (prop) + return dbe_strdup (prop->uname); + return NULL; +} + +void +DbeSession::append (UserLabel *lbl) +{ + if (lbl->expr) + { + if (userLabels == NULL) + userLabels = new Vector<UserLabel*> (); + userLabels->append (lbl); + } +} + +void +DbeSession::append (SourceFile *sf) +{ + sources->append (sf); + objs->append (sf); +} + +UserLabel * +DbeSession::findUserLabel (char *name) +{ + for (int i = 0, sz = userLabels ? userLabels->size () : 0; i < sz; i++) + { + UserLabel *lbl = userLabels->fetch (i); + if (strcasecmp (lbl->name, name) == 0) + return lbl; + } + return NULL; +} + +Expression * +DbeSession::findObjDefByName (char *name) +{ + Expression *expr = NULL; + + MemObjType_t *mot = MemorySpace::findMemSpaceByName (name); + if (mot != NULL) + { + char *index_expr_str = mot->index_expr; + expr = ql_parse (index_expr_str); + } + + if (expr == NULL) + { + int indxtype = findIndexSpaceByName (name); + expr = getIndexSpaceExpr (indxtype); + } + if (expr == NULL) + { + UserLabel *ulbl = findUserLabel (name); + if (ulbl) + expr = ulbl->expr; + } + return expr; +} + +Expression * +DbeSession::ql_parse (const char *expr_spec) +{ + /* (This slight duplication means we don't need to worry about copy + constructors for the QL::Result, nor about the lifetime of the + expr_spec.) */ + if (expr_spec != NULL) + { + QL::Result result (expr_spec); + QL::Parser qlparser (result); + if (qlparser () != 0) + return NULL; + return result (); + } + else + { + QL::Result result; + QL::Parser qlparser (result); + if (qlparser () != 0) + return NULL; + return result (); + } +} + +Vector<void*> * +DbeSession::getIndxObjDescriptions () +{ + int size = dyn_indxobj_indx; + if (size == 0) + return NULL; + Vector<int> *type = new Vector<int>(dyn_indxobj_indx); + Vector<char*> *desc = new Vector<char*>(dyn_indxobj_indx); + Vector<char*> *i18ndesc = new Vector<char*>(dyn_indxobj_indx); + Vector<char> *mnemonic = new Vector<char>(dyn_indxobj_indx); + Vector<int> *orderList = new Vector<int>(dyn_indxobj_indx); + Vector<char*> *exprList = new Vector<char*>(dyn_indxobj_indx); + Vector<char*> *sdesc = new Vector<char*>(dyn_indxobj_indx); + Vector<char*> *ldesc = new Vector<char*>(dyn_indxobj_indx); + + for (long i = 0, sz = VecSize (dyn_indxobj); i < sz; i++) + { + IndexObjType_t *tot = dyn_indxobj->get (i); + if (tot->memObj == NULL) + { + type->append ((int) tot->type); + desc->append (dbe_strdup (tot->name)); + i18ndesc->append (dbe_strdup (tot->i18n_name)); + sdesc->append (dbe_strdup (tot->short_description)); + ldesc->append (dbe_strdup (tot->long_description)); + mnemonic->append (tot->mnemonic); + orderList->append (settings->indx_tab_order->fetch (i)); + exprList->append (dbe_strdup (tot->index_expr_str)); + } + } + Vector<void*> *res = new Vector<void*>(8); + res->store (0, type); + res->store (1, desc); + res->store (2, mnemonic); + res->store (3, i18ndesc); + res->store (4, orderList); + res->store (5, exprList); + res->store (6, sdesc); + res->store (7, ldesc); + return (res); +} + +// Static function to get a vector of custom index object definitions +Vector<void*> * +DbeSession::getCustomIndxObjects () +{ + Vector<char*> *name = new Vector<char*>; + Vector<char*> *formula = new Vector<char*>; + for (long i = dyn_indxobj_indx_fixed, sz = VecSize (dyn_indxobj); i < sz; i++) + { + IndexObjType_t *tot = dyn_indxobj->get (i); + if (tot->memObj == NULL) + { + name->append (dbe_strdup (tot->name)); + formula->append (dbe_strdup (tot->index_expr_str)); + } + } + Vector<void*> *res = new Vector<void*>(2); + res->store (0, name); + res->store (1, formula); + return (res); +} + +// Static function to define a new index object type +char * +DbeSession::indxobj_define (const char *mname, char *i18nname, const char *index_expr_str, char *short_description, char *long_description) +{ + if (mname == NULL) + return dbe_strdup (GTXT ("No index object type name has been specified.")); + if (isalpha ((int) (mname[0])) == 0) + return dbe_sprintf (GTXT ("Index Object type name %s does not begin with an alphabetic character"), + mname); + const char *p = mname; + while (*p != 0) + { + if ((isalnum ((int) (*p)) == 0) && (*p != '_')) + return dbe_sprintf (GTXT ("Index Object type name %s contains a non-alphanumeric character"), + mname); + p++; + } + + // make sure the name is not in use + if (MemorySpace::findMemSpaceByName (mname) != NULL) + return dbe_sprintf (GTXT ("Memory/Index Object type name %s is already defined"), + mname); + + int idxx = findIndexSpaceByName (mname); + if (idxx >= 0) + { + IndexObjType_t *mt = dyn_indxobj->fetch (idxx); + if (strcmp (mt->index_expr_str, index_expr_str) == 0) + // It's a redefinition, but the new definition is the same + return NULL; + return dbe_sprintf (GTXT ("Memory/Index Object type name %s is already defined"), + mname); + } + if (index_expr_str == NULL) + return dbe_strdup (GTXT ("No index-expr has been specified.")); + if (strlen (index_expr_str) == 0) + return dbe_sprintf (GTXT ("Index Object index expression is invalid: %s"), + index_expr_str); + + // verify that the index expression parses correctly + char *expr_str = dbe_strdup (index_expr_str); + Expression *expr = ql_parse (expr_str); + if (expr == NULL) + return dbe_sprintf (GTXT ("Index Object index expression is invalid: %s"), + expr_str); + + // It's OK, create the new table entry + IndexObjType_t *tot = new IndexObjType_t; + tot->type = dyn_indxobj_indx++; + tot->name = dbe_strdup (mname); + tot->i18n_name = dbe_strdup (i18nname); + tot->short_description = dbe_strdup (short_description); + tot->long_description = dbe_strdup (long_description); + tot->index_expr_str = expr_str; + tot->index_expr = expr; + tot->mnemonic = mname[0]; + + // add it to the list + dyn_indxobj->append (tot); + idxobjs->append (new HashMap<uint64_t, Histable*>); + + // tell the session + settings->indxobj_define (tot->type, false); + + DbeView *dbev; + int index; + Vec_loop (DbeView*, views, index, dbev) + { + dbev->addIndexSpace (tot->type); + } + return NULL; +} + +char * +DbeSession::getIndexSpaceName (int index) +{ + if (index < 0 || index >= dyn_indxobj->size ()) + return NULL; + return dyn_indxobj->fetch (index)->name; +} + +char * +DbeSession::getIndexSpaceDescr (int index) +{ + if (index < 0 || index >= dyn_indxobj->size ()) + return NULL; + return dyn_indxobj->fetch (index)->i18n_name; +} + +Expression * +DbeSession::getIndexSpaceExpr (int index) +{ + if (index < 0 || index >= dyn_indxobj->size ()) + return NULL; + return dyn_indxobj->fetch (index)->index_expr; +} + +char * +DbeSession::getIndexSpaceExprStr (int index) +{ + if (index < 0 || index >= dyn_indxobj->size ()) + return NULL; + return dyn_indxobj->fetch (index)->index_expr_str; +} + +int +DbeSession::findIndexSpaceByName (const char *mname) +{ + int idx; + IndexObjType_t *mt; + Vec_loop (IndexObjType_t*, dyn_indxobj, idx, mt) + { + if (strcasecmp (mt->name, mname) == 0) + return idx; + } + return -1; +} + +void +DbeSession::removeIndexSpaceByName (const char *mname) +{ + IndexObjType_t *indObj = findIndexSpace (mname); + if (indObj) + indObj->name[0] = 0; +} + +IndexObjType_t * +DbeSession::getIndexSpace (int index) +{ + return ((index < 0) || (index >= VecSize (dyn_indxobj))) ? NULL : dyn_indxobj->get (index); +} + +IndexObjType_t * +DbeSession::findIndexSpace (const char *mname) +{ + return getIndexSpace (findIndexSpaceByName (mname)); +} + +void +DbeSession::get_filter_keywords (Vector<void*> *res) +{ + Vector <char*> *kwCategory = (Vector<char*>*) res->fetch (0); + Vector <char*> *kwCategoryI18N = (Vector<char*>*) res->fetch (1); + Vector <char*> *kwDataType = (Vector<char*>*) res->fetch (2); + Vector <char*> *kwKeyword = (Vector<char*>*) res->fetch (3); + Vector <char*> *kwFormula = (Vector<char*>*) res->fetch (4); + Vector <char*> *kwDescription = (Vector<char*>*) res->fetch (5); + Vector <void*> *kwEnumDescs = (Vector<void*>*) res->fetch (6); + + char *vtypeNames[] = VTYPE_TYPE_NAMES; + for (long i = 0, sz = userLabels ? userLabels->size () : 0; i < sz; i++) + { + UserLabel *lbl = userLabels->fetch (i); + kwCategory->append (dbe_strdup (NTXT ("FK_LABEL"))); + kwCategoryI18N->append (dbe_strdup (GTXT ("Labels"))); + kwDataType->append (dbe_strdup (vtypeNames[TYPE_BOOL])); + kwKeyword->append (dbe_strdup (lbl->name)); + kwFormula->append (dbe_strdup (lbl->str_expr)); + kwDescription->append (dbe_strdup (lbl->comment)); + kwEnumDescs->append (NULL); + } + + for (long i = 0, sz = propNames ? propNames->size () : 0; i < sz; i++) + { + PropDescr *prop = propNames->fetch (i); + char *pname = prop ? prop->name : NULL; + if (pname == NULL || *pname == 0 || prop->flags & PRFLAG_NOSHOW) + continue; + int vtypeNum = prop->vtype; + if (vtypeNum < 0 || vtypeNum >= TYPE_LAST) + vtypeNum = TYPE_NONE; + kwCategory->append (dbe_strdup (NTXT ("FK_EVTPROP"))); //Event Property + kwCategoryI18N->append (dbe_strdup (GTXT ("Misc. Definitions"))); + kwDataType->append (dbe_strdup (vtypeNames[vtypeNum])); + kwKeyword->append (dbe_strdup (pname)); + kwFormula->append (NULL); + kwDescription->append (dbe_strdup (prop->uname)); + kwEnumDescs->append (NULL); + } + + for (long i = 0, sz = dyn_indxobj ? dyn_indxobj->size () : 0; i < sz; i++) + { + IndexObjType_t *obj = dyn_indxobj->get (i); + if (obj->memObj) + continue; + kwCategory->append (dbe_strdup (NTXT ("FK_IDXOBJ"))); + kwCategoryI18N->append (dbe_strdup (GTXT ("Index Object Definitions"))); + kwDataType->append (dbe_strdup (vtypeNames[TYPE_INT64])); + kwKeyword->append (dbe_strdup (obj->name)); + kwFormula->append (dbe_strdup (obj->index_expr_str)); + kwDescription->append (dbe_strdup (obj->i18n_name)); + kwEnumDescs->append (NULL); + } +} + +Histable * +DbeSession::findIndexObject (int idxtype, uint64_t idx) +{ + HashMap<uint64_t, Histable*> *iobjs = idxobjs->fetch (idxtype); + return iobjs->get (idx); +} + +Histable * +DbeSession::createIndexObject (int idxtype, int64_t idx) +{ + HashMap<uint64_t, Histable*> *iobjs = idxobjs->fetch (idxtype); + + Histable *idxobj = iobjs->get (idx); + if (idxobj == NULL) + { + idxobj = new IndexObject (idxtype, idx); + if (idx == -1) + idxobj->set_name (dbe_strdup (GTXT ("<Unknown>"))); + iobjs->put (idx, idxobj); + } + + return idxobj; +} + +Histable * +DbeSession::createIndexObject (int idxtype, Histable *hobj) +{ + HashMap<uint64_t, Histable*> *iobjs = idxobjs->fetch (idxtype); + int64_t idx = hobj ? hobj->id : -1; + Histable *idxobj = iobjs->get (idx); + if (idxobj == NULL) + { + idxobj = new IndexObject (idxtype, hobj); + if (idx == -1) + idxobj->set_name (dbe_strdup (GTXT ("<Unknown>"))); + iobjs->put (idx, idxobj); + } + + return idxobj; +} + +Histable * +DbeSession::findObjectById (Histable::Type type, int subtype, uint64_t id) +{ + switch (type) + { + case Histable::FUNCTION: + case Histable::MODULE: + case Histable::LOADOBJECT: + return ( id < (uint64_t) objs->size ()) ? objs->fetch ((int) id) : NULL; + case Histable::INDEXOBJ: + return findIndexObject (subtype, id); + // ignoring the following cases + case Histable::INSTR: + case Histable::LINE: + case Histable::EADDR: + case Histable::MEMOBJ: + case Histable::PAGE: + case Histable::DOBJECT: + case Histable::SOURCEFILE: + case Histable::IOACTFILE: + case Histable::IOACTVFD: + case Histable::IOCALLSTACK: + case Histable::HEAPCALLSTACK: + case Histable::OTHER: + case Histable::EXPERIMENT: + break; + } + return NULL; +} + +// return a vector of Functions that match the regular expression input string +Vector<JThread *> * +DbeSession::match_java_threads (char *ustr, int matchParent, + Vector<uint64_t> * &grids, + Vector<uint64_t> * &expids) +{ + if (ustr == NULL) + return NULL; + + char *str = dbe_sprintf (NTXT ("^%s$"), ustr); + regex_t regex_desc; + int rc = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + if (rc) // syntax error in parsing string + return NULL; + + // allocate the new vector + Vector<JThread *> *ret = new Vector<JThread*>; + grids = new Vector<uint64_t>; + expids = new Vector<uint64_t>; + + int index; + JThread *jthread; + int expid; + Experiment* exp; + Vec_loop (Experiment*, exps, expid, exp) + { + + Vec_loop (JThread*, exp->get_jthreads (), index, jthread) + { + const char * name; + if (matchParent) + name = jthread->parent_name; + else + name = jthread->group_name; + if (name == NULL) + name = ""; + if (!regexec (®ex_desc, name, 0, NULL, 0)) + { + // this one matches + ret->append (jthread); + grids->append (exp->groupId); + expids->append (exp->getUserExpId ()); + } + } + } + + regfree (®ex_desc); + return ret; +} + +// return a vector of Functions that match the regular expression input string +Vector<Function *> * +DbeSession::match_func_names (const char *ustr, Histable::NameFormat nfmt) +{ + if (ustr == NULL) + return NULL; + char *str = dbe_sprintf (NTXT ("^%s$"), ustr); + regex_t regex_desc; + int rc = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + if (rc) // syntax error in parsing string + return NULL; + + // allocate the new vector + Vector<Function *> *ret = new Vector<Function*>; + + int index; + Histable *obj; + Vec_loop (Histable*, objs, index, obj) + { + if (obj->get_type () == Histable::FUNCTION) + { + Function *func = (Function*) obj; + if (!regexec (®ex_desc, func->get_name (nfmt), 0, NULL, 0)) + // this one matches + ret->append (func); + } + } + regfree (®ex_desc); + return ret; +} + +// return a vector of Functions that match the regular expression input string +Vector<FileData *> * +DbeSession::match_file_names (char *ustr, Histable::NameFormat nfmt) +{ + if (ustr == NULL) + return NULL; + char *str = dbe_sprintf (NTXT ("^%s$"), ustr); + regex_t regex_desc; + int rc = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + if (rc) // syntax error in parsing string + return NULL; + + // allocate the new vector + Vector<FileData *> *ret = new Vector<FileData*>; + int numExps = nexps (); + DefaultMap<int64_t, FileData*>* fDataMap; + Vector<FileData *> *fDataObjs; + FileData *fData; + int size; + for (int i = 0; i < numExps; i++) + { + Experiment *exp = get_exp (i); + fDataMap = exp->getFDataMap (); + fDataObjs = fDataMap->values (); + size = fDataObjs->size (); + for (int j = 0; j < size; j++) + { + fData = fDataObjs->fetch (j); + if (fData + && !regexec (®ex_desc, fData->get_raw_name (nfmt), 0, NULL, 0)) + // this one matches + ret->append (fData); + } + } + regfree (®ex_desc); + return ret; +} + +// return a vector of DataObjects that match the regular expression input string +Vector<DataObject *> * +DbeSession::match_dobj_names (char *ustr) +{ + if (ustr == NULL) + return NULL; + char *str = dbe_sprintf (NTXT ("^%s$"), ustr); + regex_t regex_desc; + int rc = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + if (rc) // syntax error in parsing string + return NULL; + + // allocate the new vector + Vector<DataObject *> *ret = new Vector<DataObject*>; + int index; + DataObject *ditem; + Vec_loop (DataObject*, dobjs, index, ditem) + { + // does this one match + if (!regexec (®ex_desc, ditem->get_name (), 0, NULL, 0)) + // this one matches + ret->append (ditem); + } + regfree (®ex_desc); + return ret; +} + +void +DbeSession::dump (char *msg, Vector<BaseMetric*> *mlist) +{ + if (msg) + fprintf (stderr, "%s\n", msg); + int sz = mlist ? mlist->size () : -1; + for (int i = 0; i < sz; i++) + { + BaseMetric *m = mlist->fetch (i); + char *s = m->dump (); + fprintf (stderr, "%2d %s\n", i, s); + free (s); + } + fprintf (stderr, "======END of mlist[%d] =========\n", sz); +} + +void +DbeSession::dump (char *msg, Vector<Metric*> *mlist) +{ + if (msg) + fprintf (stderr, "%s\n", msg); + int sz = mlist ? mlist->size () : -1; + for (int i = 0; i < sz; i++) + { + Metric *m = mlist->fetch (i); + char *s = m->dump (); + fprintf (stderr, "%2d %s\n", i, s); + free (s); + } + fprintf (stderr, "======END of mlist[%d] =========\n", sz); +} diff --git a/gprofng/src/DbeSession.cc.1 b/gprofng/src/DbeSession.cc.1 new file mode 100644 index 0000000..7d635d2 --- /dev/null +++ b/gprofng/src/DbeSession.cc.1 @@ -0,0 +1,3531 @@ +/* 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 <stdio.h> +#include <dirent.h> +#include <unistd.h> +#include <sys/types.h> +#include <errno.h> +#include <sys/param.h> + +#include "util.h" +#include "Application.h" +#include "Experiment.h" +#include "ExpGroup.h" +#include "Expression.h" +#include "DataObject.h" +#include "Elf.h" +#include "Function.h" +#include "DbeSession.h" +#include "LoadObject.h" +#include "DbeSyncMap.h" +#include "DbeThread.h" +#include "ClassFile.h" +#include "IndexObject.h" +#include "PathTree.h" +#include "Print.h" +#include "QLParser.tab.hh" +#include "DbeView.h" +#include "MemorySpace.h" +#include "Module.h" +#include "SourceFile.h" +#include "StringBuilder.h" +#include "BaseMetric.h" +#include "BaseMetricTreeNode.h" +#include "Command.h" +#include "UserLabel.h" +#include "StringMap.h" +#include "DbeFile.h" +#include "DbeJarFile.h" +#include "IOActivity.h" +#include "HeapActivity.h" + +// This is a universal List structure to organize objects +// of various types, even if different. +struct List +{ + List *next; + void *val; +}; + +struct Countable +{ + Countable (void *_item) + { + item = _item; + ref_count = 0; + } + + void *item; + int ref_count; +}; + +Platform_t DbeSession::platform = +#if ARCH(SPARC) + Sparc; +#elif ARCH(Aarch64) + Aarch64; +#else // ARCH(Intel) + Intel; +#endif + +// This constant determines the size of the data object name hash table. +static const int HTableSize = 8192; +static int DEFAULT_TINY_THRESHOLD = -1; + +unsigned int mpmt_debug_opt = 0; +DbeSession *dbeSession = NULL; + +DbeSession::DbeSession (Settings *_settings, bool _ipc_mode, bool _rdt_mode) +{ + dbeSession = this; + ipc_mode = _ipc_mode; + rdt_mode = _rdt_mode; + settings = new Settings (_settings); + views = new Vector<DbeView*>; + exps = new Vector<Experiment*>; + lobjs = new Vector<LoadObject*>; + objs = new Vector<Histable*>; + dobjs = new Vector<DataObject*>; + metrics = new Vector<Countable*>; + reg_metrics = new Vector<BaseMetric*>; + hwcentries = NULL; + reg_metrics_tree = NULL; // BaseMetric() requires DbeSession::ql_parse + idxobjs = new Vector<HashMap<uint64_t, Histable*>*>; + tmp_files = new Vector<char*>; + search_path = new Vector<char*>; + classpath = new Vector<char*>; + classpath_df = NULL; + expGroups = new Vector<ExpGroup*>; + sourcesMap = new HashMap<char*, SourceFile*>; + sources = new Vector<SourceFile*>; + comp_lobjs = new HashMap<char*, LoadObject*>; + comp_dbelines = new HashMap<char*, DbeLine*>; + comp_sources = new HashMap<char*, SourceFile*>; + loadObjMap = new DbeSyncMap<LoadObject>; + f_special = new Vector<Function*>(LastSpecialFunction); + omp_functions = new Vector<Function*>(OMP_LAST_STATE); + interactive = false; + lib_visibility_used = false; + + // Define all known property names + propNames = new Vector<PropDescr*>; + propNames_name_store (PROP_NONE, NTXT ("")); + propNames_name_store (PROP_ATSTAMP, NTXT ("ATSTAMP")); + propNames_name_store (PROP_ETSTAMP, NTXT ("ETSTAMP")); + propNames_name_store (PROP_TSTAMP, NTXT ("TSTAMP")); + propNames_name_store (PROP_THRID, NTXT ("THRID")); + propNames_name_store (PROP_LWPID, NTXT ("LWPID")); + propNames_name_store (PROP_CPUID, NTXT ("CPUID")); + propNames_name_store (PROP_FRINFO, NTXT ("FRINFO")); + propNames_name_store (PROP_EVT_TIME, NTXT ("EVT_TIME")); + + // Samples + propNames_name_store (PROP_SMPLOBJ, NTXT ("SMPLOBJ")); + propNames_name_store (PROP_SAMPLE, NTXT ("SAMPLE")); + + // GCEvents + propNames_name_store (PROP_GCEVENTOBJ, NTXT ("GCEVENTOBJ")); + propNames_name_store (PROP_GCEVENT, NTXT ("GCEVENT")); + + // Metadata used by some packet types + propNames_name_store (PROP_VOIDP_OBJ, NTXT ("VOIDP_OBJ"), + NULL, TYPE_UINT64, DDFLAG_NOSHOW); + + // Clock profiling properties + propNames_name_store (PROP_UCPU, NTXT ("UCPU")); + propNames_name_store (PROP_SCPU, NTXT ("SCPU")); + propNames_name_store (PROP_TRAP, NTXT ("TRAP")); + propNames_name_store (PROP_TFLT, NTXT ("TFLT")); + propNames_name_store (PROP_DFLT, NTXT ("DFLT")); + propNames_name_store (PROP_KFLT, NTXT ("KFLT")); + propNames_name_store (PROP_ULCK, NTXT ("ULCK")); + propNames_name_store (PROP_TSLP, NTXT ("TSLP")); + propNames_name_store (PROP_WCPU, NTXT ("WCPU")); + propNames_name_store (PROP_TSTP, NTXT ("TSTP")); + + propNames_name_store (PROP_MSTATE, NTXT ("MSTATE")); + propNames_name_store (PROP_NTICK, NTXT ("NTICK")); + propNames_name_store (PROP_OMPSTATE, NTXT ("OMPSTATE")); + + // Synchronization tracing properties + propNames_name_store (PROP_SRQST, NTXT ("SRQST")); + propNames_name_store (PROP_SOBJ, NTXT ("SOBJ")); + + // Hardware counter profiling properties + propNames_name_store (PROP_HWCTAG, NTXT ("HWCTAG")); + propNames_name_store (PROP_HWCINT, NTXT ("HWCINT")); + propNames_name_store (PROP_VADDR, NTXT ("VADDR")); + propNames_name_store (PROP_PADDR, NTXT ("PADDR")); + propNames_name_store (PROP_VIRTPC, NTXT ("VIRTPC")); + propNames_name_store (PROP_PHYSPC, NTXT ("PHYSPC")); + propNames_name_store (PROP_LWP_LGRP_HOME, NTXT ("LWP_LGRP_HOME")); + propNames_name_store (PROP_PS_LGRP_HOME, NTXT ("PS_LGRP_HOME")); + propNames_name_store (PROP_EA_PAGESIZE, NTXT ("EA_PAGESIZE")); + propNames_name_store (PROP_EA_LGRP, NTXT ("EA_LGRP")); + propNames_name_store (PROP_PC_PAGESIZE, NTXT ("PC_PAGESIZE")); + propNames_name_store (PROP_PC_LGRP, NTXT ("PC_LGRP")); + propNames_name_store (PROP_HWCDOBJ, NTXT ("HWCDOBJ")); + propNames_name_store (PROP_MEM_LAT, NTXT ("MEM_LAT")); + propNames_name_store (PROP_MEM_SRC, NTXT ("MEM_SRC")); + + // Heap tracing properties + propNames_name_store (PROP_HTYPE, NTXT ("HTYPE")); + propNames_name_store (PROP_HSIZE, NTXT ("HSIZE")); + propNames_name_store (PROP_HVADDR, NTXT ("HVADDR")); + propNames_name_store (PROP_HOVADDR, NTXT ("HOVADDR")); + propNames_name_store (PROP_HLEAKED, NTXT ("HLEAKED"), + GTXT ("Leaked bytes"), TYPE_UINT64, 0); + propNames_name_store (PROP_HMEM_USAGE, NTXT ("HMEM_USAGE")); + propNames_name_store (PROP_HFREED, NTXT ("HFREED"), + GTXT ("Freed bytes"), TYPE_UINT64, 0); + propNames_name_store (PROP_HCUR_ALLOCS, NTXT ("HCUR_ALLOCS"), + GTXT ("Current allocations"), TYPE_INT64, 0); + propNames_name_store (PROP_HCUR_NET_ALLOC, NTXT ("HCUR_NET_ALLOC"), + NULL, TYPE_INT64, DDFLAG_NOSHOW); + propNames_name_store (PROP_HCUR_LEAKS, NTXT ("HCUR_LEAKS"), + GTXT ("Current leaks"), TYPE_UINT64, 0); + propNames_name_store (PROP_DDSCR_LNK, NTXT ("DDSCR_LNK"), + NULL, TYPE_UINT64, DDFLAG_NOSHOW); + + // IO tracing properties + propNames_name_store (PROP_IOTYPE, NTXT ("IOTYPE")); + propNames_name_store (PROP_IOFD, NTXT ("IOFD")); + propNames_name_store (PROP_IONBYTE, NTXT ("IONBYTE")); + propNames_name_store (PROP_IORQST, NTXT ("IORQST")); + propNames_name_store (PROP_IOOFD, NTXT ("IOOFD")); + propNames_name_store (PROP_IOFNAME, NTXT ("IOFNAME")); + propNames_name_store (PROP_IOVFD, NTXT ("IOVFD")); + propNames_name_store (PROP_IOFSTYPE, NTXT ("IOFSTYPE")); + + // omptrace raw properties + propNames_name_store (PROP_CPRID, NTXT ("CPRID")); + propNames_name_store (PROP_PPRID, NTXT ("PPRID")); + propNames_name_store (PROP_TSKID, NTXT ("TSKID")); + propNames_name_store (PROP_PTSKID, NTXT ("PTSKID")); + propNames_name_store (PROP_PRPC, NTXT ("PRPC")); + + // Data race detection properties + propNames_name_store (PROP_RID, NTXT ("RID")); + propNames_name_store (PROP_RTYPE, NTXT ("RTYPE")); + propNames_name_store (PROP_LEAFPC, NTXT ("LEAFPC")); + propNames_name_store (PROP_RVADDR, NTXT ("RVADDR")); + propNames_name_store (PROP_RCNT, NTXT ("RCNT")); + + // Deadlock detection properties + propNames_name_store (PROP_DID, NTXT ("DID")); + propNames_name_store (PROP_DLTYPE, NTXT ("DLTYPE")); + propNames_name_store (PROP_DTYPE, NTXT ("DTYPE")); + propNames_name_store (PROP_DVADDR, NTXT ("DVADDR")); + + // Synthetic properties (queries only) + propNames_name_store (PROP_STACK, NTXT ("STACK")); + propNames_name_store (PROP_MSTACK, NTXT ("MSTACK")); + propNames_name_store (PROP_USTACK, NTXT ("USTACK")); + propNames_name_store (PROP_XSTACK, NTXT ("XSTACK")); + propNames_name_store (PROP_HSTACK, NTXT ("HSTACK")); + propNames_name_store (PROP_STACKID, NTXT ("STACKID")); + //propNames_name_store( PROP_CPRID, NTXT("CPRID") ); + //propNames_name_store( PROP_TSKID, NTXT("TSKID") ); + propNames_name_store (PROP_JTHREAD, NTXT ("JTHREAD"), + GTXT ("Java thread number"), TYPE_UINT64, 0); + + propNames_name_store (PROP_LEAF, NTXT ("LEAF")); + propNames_name_store (PROP_DOBJ, NTXT ("DOBJ")); + propNames_name_store (PROP_SAMPLE_MAP, NTXT ("SAMPLE_MAP")); + propNames_name_store (PROP_GCEVENT_MAP, NTXT ("GCEVENT_MAP")); + propNames_name_store (PROP_PID, NTXT ("PID"), + GTXT ("Process id"), TYPE_UINT64, 0); + propNames_name_store (PROP_EXPID, NTXT ("EXPID"), + GTXT ("Experiment id"), TYPE_UINT64, DDFLAG_NOSHOW); + propNames_name_store (PROP_EXPID_CMP, NTXT ("EXPID_CMP"), + GTXT ("Comparable Experiment Id"), TYPE_UINT64, + DDFLAG_NOSHOW); //YXXX find better description + propNames_name_store (PROP_EXPGRID, NTXT ("EXPGRID"), + GTXT ("Comparison Group id"), TYPE_UINT64, 0); + propNames_name_store (PROP_PARREG, NTXT ("PARREG")); + propNames_name_store (PROP_TSTAMP_LO, NTXT ("TSTAMP_LO"), + GTXT ("Start Timestamp (nanoseconds)"), TYPE_UINT64, 0); + propNames_name_store (PROP_TSTAMP_HI, NTXT ("TSTAMP_HI"), + GTXT ("End Timestamp (nanoseconds)"), TYPE_UINT64, 0); + propNames_name_store (PROP_TSTAMP2, NTXT ("TSTAMP2"), + GTXT ("End Timestamp (nanoseconds)"), TYPE_UINT64, + DDFLAG_NOSHOW); + propNames_name_store (PROP_FREQ_MHZ, NTXT ("FREQ_MHZ"), + GTXT ("CPU Frequency, MHz"), TYPE_UINT32, 0); + propNames_name_store (PROP_NTICK_USEC, NTXT ("NTICK_USEC"), + GTXT ("Clock Profiling Interval, Microseconds"), + TYPE_UINT64, 0); + + propNames_name_store (PROP_IOHEAPBYTES, NTXT ("IOHEAPBYTES")); + + propNames_name_store (PROP_STACKL, NTXT ("STACKL")); + propNames_name_store (PROP_MSTACKL, NTXT ("MSTACKL")); + propNames_name_store (PROP_USTACKL, NTXT ("USTACKL")); + propNames_name_store (PROP_XSTACKL, NTXT ("XSTACKL")); + + propNames_name_store (PROP_STACKI, NTXT ("STACKI")); + propNames_name_store (PROP_MSTACKI, NTXT ("MSTACKI")); + propNames_name_store (PROP_USTACKI, NTXT ("USTACKI")); + propNames_name_store (PROP_XSTACKI, NTXT ("XSTACKI")); + + // Make sure predefined names are not used for dynamic properties + propNames_name_store (PROP_LAST, NTXT ("")); + + localized_SP_UNKNOWN_NAME = GTXT ("(unknown)"); + + // define Index objects + dyn_indxobj = new Vector<IndexObjType_t*>(); + dyn_indxobj_indx = 0; + char *s = dbe_sprintf (NTXT ("((EXPID_CMP<<%llu) | THRID)"), + (unsigned long long) IndexObject::INDXOBJ_EXPID_SHIFT); + indxobj_define (NTXT ("Threads"), GTXT ("Threads"), s, NULL, NULL); + free (s); + indxobj_define (NTXT ("CPUs"), GTXT ("CPUs"), NTXT ("(CPUID)"), NULL, NULL); + indxobj_define (NTXT ("Samples"), GTXT ("Samples"), NTXT ("(SAMPLE_MAP)"), + NULL, NULL); + indxobj_define (NTXT ("GCEvents"), GTXT ("GCEvents"), NTXT ("(GCEVENT_MAP)"), + NULL, NULL); + indxobj_define (NTXT ("Seconds"), GTXT ("Seconds"), + NTXT ("(TSTAMP/1000000000)"), NULL, NULL); + indxobj_define (NTXT ("Processes"), GTXT ("Processes"), NTXT ("(EXPID_CMP)"), + NULL, NULL); + s = dbe_sprintf (NTXT ("((EXPGRID<<%llu) | (EXPID<<%llu))"), + (unsigned long long) IndexObject::INDXOBJ_EXPGRID_SHIFT, + (unsigned long long) IndexObject::INDXOBJ_EXPID_SHIFT); + indxobj_define (NTXT ("Experiment_IDs"), GTXT ("Experiment_IDs"), s, NULL, NULL); + free (s); + indxobj_define (NTXT ("Datasize"), GTXT ("Datasize"), + "(IOHEAPBYTES==0?0:" + "((IOHEAPBYTES<=(1<<0)?(1<<0):" + "((IOHEAPBYTES<=(1<<2)?(1<<2):" + "((IOHEAPBYTES<=(1<<4)?(1<<4):" + "((IOHEAPBYTES<=(1<<6)?(1<<6):" + "((IOHEAPBYTES<=(1<<8)?(1<<8):" + "((IOHEAPBYTES<=(1<<10)?(1<<10):" + "((IOHEAPBYTES<=(1<<12)?(1<<12):" + "((IOHEAPBYTES<=(1<<14)?(1<<14):" + "((IOHEAPBYTES<=(1<<16)?(1<<16):" + "((IOHEAPBYTES<=(1<<18)?(1<<18):" + "((IOHEAPBYTES<=(1<<20)?(1<<20):" + "((IOHEAPBYTES<=(1<<22)?(1<<22):" + "((IOHEAPBYTES<=(1<<24)?(1<<24):" + "((IOHEAPBYTES<=(1<<26)?(1<<26):" + "((IOHEAPBYTES<=(1<<28)?(1<<28):" + "((IOHEAPBYTES<=(1<<30)?(1<<30):" + "((IOHEAPBYTES<=(1<<32)?(1<<32):" + "((IOHEAPBYTES<=(1<<34)?(1<<34):" + "((IOHEAPBYTES<=(1<<36)?(1<<36):" + "((IOHEAPBYTES<=(1<<38)?(1<<38):" + "((IOHEAPBYTES<=(1<<40)?(1<<40):" + "((IOHEAPBYTES<=(1<<42)?(1<<42):" + "((IOHEAPBYTES<=(1<<44)?(1<<44):" + "((IOHEAPBYTES<=(1<<46)?(1<<46):" + "((IOHEAPBYTES<=(1<<48)?(1<<48):" + "((IOHEAPBYTES<=(1<<50)?(1<<50):" + "(IOHEAPBYTES==-1?-1:(1<<50|1)" + "))))))))))))))))))))))))))))))))))))))))))))))))))))))", + NULL, NULL); + indxobj_define (NTXT ("Duration"), GTXT ("Duration"), + "((TSTAMP_HI-TSTAMP_LO)==0?0:" + "(((TSTAMP_HI-TSTAMP_LO)<=1000?1000:" + "(((TSTAMP_HI-TSTAMP_LO)<=10000?10000:" + "(((TSTAMP_HI-TSTAMP_LO)<=100000?100000:" + "(((TSTAMP_HI-TSTAMP_LO)<=1000000?1000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=10000000?10000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=100000000?100000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=1000000000?1000000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=10000000000?10000000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=100000000000?100000000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=1000000000000?1000000000000:" + "(((TSTAMP_HI-TSTAMP_LO)<=10000000000000?10000000000000:" + "(10000000000001))))))))))))))))))))))))", NULL, NULL); + dyn_indxobj_indx_fixed = dyn_indxobj_indx; + Elf::elf_init (); + defExpName = NULL; + mach_model_loaded = NULL; + tmp_dir_name = NULL; + settings->read_rc (ipc_mode || rdt_mode); + + init (); +} + +DbeSession::~DbeSession () +{ + Destroy (views); + Destroy (exps); + Destroy (dobjs); + Destroy (metrics); + Destroy (search_path); + Destroy (classpath); + Destroy (propNames); + Destroy (expGroups); + Destroy (userLabels); + if (hwcentries) + { + for (long i = 0, sz = hwcentries->size (); i < sz; i++) + { + Hwcentry *h = hwcentries->get (i); + free (h->int_name); + free (h->name); + delete h; + } + delete hwcentries; + } + + if (idxobjs) + { + for (int i = 0; i < idxobjs->size (); ++i) + { + HashMap<uint64_t, Histable*> *hMap = idxobjs->get (i); + if (hMap) + { + hMap->values ()->destroy (); + delete hMap; + } + } + delete idxobjs; + } + + for (int i = 0; i < HTableSize; i++) + { + List *list = dnameHTable[i]; + while (list) + { + List *tmp = list; + list = list->next; + delete tmp; + } + } + delete[] dnameHTable; + delete classpath_df; + Destroy (objs); + Destroy (reg_metrics); + Destroy (dyn_indxobj); + delete lobjs; + delete f_special; + destroy_map (DbeFile *, dbeFiles); + destroy_map (DbeJarFile *, dbeJarFiles); + delete loadObjMap; + delete omp_functions; + delete sourcesMap; + delete sources; + delete comp_lobjs; + delete comp_dbelines; + delete comp_sources; + delete reg_metrics_tree; + delete settings; + free (mach_model_loaded); + + if (defExpName != NULL) + { + StringBuilder *sb = new StringBuilder (); + sb->append (NTXT ("/bin/rm -rf ")); + sb->append (defExpName); + char *cmd = sb->toString (); + system (cmd); + free (cmd); + delete sb; + free (defExpName); + } + unlink_tmp_files (); + delete tmp_files; + dbeSession = NULL; +} + +void +DbeSession::unlink_tmp_files () +{ + if (tmp_files) + { + for (int i = 0, sz = tmp_files->size (); i < sz; i++) + unlink (tmp_files->fetch (i)); + tmp_files->destroy (); + delete tmp_files; + tmp_files = NULL; + } + if (tmp_dir_name) + { + char *cmd = dbe_sprintf (NTXT ("/bin/rm -rf %s"), tmp_dir_name); + system (cmd); + free (cmd); + free (tmp_dir_name); + tmp_dir_name = NULL; + } +} + +char * +DbeSession::get_tmp_file_name (const char *nm, bool for_java) +{ + if (tmp_dir_name == NULL) + { + tmp_dir_name = dbe_sprintf (NTXT ("/tmp/analyzer.%llu.%lld"), + (unsigned long long) getuid (), (long long) getpid ()); + mkdir (tmp_dir_name, S_IRWXU); + } + char *fnm = dbe_sprintf (NTXT ("%s/%s"), tmp_dir_name, nm); + if (for_java) + for (char *s = fnm + strlen (tmp_dir_name) + 1; *s; s++) + if (*s == '/') + *s = '.'; + return fnm; +} + +void +DbeSession::init () +{ + user_exp_id_counter = 0; + status_ompavail = 0; + archive_mode = 0; + +#if DEBUG + char *s = getenv (NTXT ("MPMT_DEBUG")); + if (s) + mpmt_debug_opt = atoi (s); +#endif /* DEBUG */ + dbeFiles = new StringMap<DbeFile*>(); + dbeJarFiles = new StringMap<DbeJarFile*>(128, 128); + + // set up the initial (after .rc file reading) search path + set_search_path (settings->str_search_path, true); + userLabels = NULL; + + // Preset all objects as they may reuse each other + lo_unknown = NULL; + f_unknown = NULL; + j_unknown = NULL; + lo_total = NULL; + sf_unknown = NULL; + f_total = NULL; + f_jvm = NULL; + d_total = NULL; + d_scalars = NULL; + d_unknown = NULL; + expGroups->destroy (); + f_special->reset (); + for (int i = 0; i < LastSpecialFunction; i++) + f_special->append (NULL); + + lo_omp = NULL; + omp_functions->reset (); + for (int i = 0; i < OMP_LAST_STATE; i++) + omp_functions->append (NULL); + + // make sure the metric list is initialized + register_metric (Metric::SIZES); + register_metric (Metric::ADDRESS); + register_metric (Metric::ONAME); + + // This is needed only to maintain loadobject id's + // for <Total> and <Unknown> in tests + (void) get_Unknown_LoadObject (); + (void) get_Total_LoadObject (); + + // Create the data object name hash table. + dnameHTable = new List*[HTableSize]; + for (int i = 0; i < HTableSize; i++) + dnameHTable[i] = NULL; + + d_total = createDataObject (); + d_total->set_name (NTXT ("<Total>")); + + // XXXX <Scalars> only appropriate for Program/Data-oriented analyses + d_scalars = createDataObject (); + d_scalars->set_name (GTXT ("<Scalars>")); + + d_unknown = createDataObject (); + d_unknown->set_name (GTXT ("<Unknown>")); + + // assign d_unknown's children so data_olayout has consistent sorting + for (unsigned pp_code = 1; pp_code < NUM_ABS_PP_CODES + 2; pp_code++) + { + char *errcode; + DataObject* dobj = createDataObject (); + switch (pp_code) + { + case NUM_ABS_PP_CODES + 1: + errcode = PTXT (DOBJ_UNDETERMINED); + break; + case NUM_ABS_PP_CODES: + errcode = PTXT (DOBJ_UNSPECIFIED); + break; + case NUM_ABS_PP_CODES - 1: + errcode = PTXT (DOBJ_UNIDENTIFIED); + break; + default: + errcode = PTXT (ABS_PP_CODES[pp_code]); + } + dobj->parent = d_unknown; + dobj->set_dobjname (errcode, NULL); // dobj->parent must already be set + } + + for (unsigned rt_code = 1; rt_code < NUM_ABS_RT_CODES - 1; rt_code++) + { + DataObject* dobj = createDataObject (); + dobj->parent = d_unknown; + dobj->set_dobjname (PTXT (ABS_RT_CODES[rt_code]), NULL); // dobj->parent must already be set + } +} + +void +DbeSession::reset_data () +{ + for (long i = 0, sz = VecSize (idxobjs); i < sz; ++i) + if (idxobjs->get (i)) + idxobjs->get (i)->reset (); +} + +void +DbeSession::reset () +{ + loadObjMap->reset (); + DbeView *dbev; + int index; + + Vec_loop (DbeView*, views, index, dbev) + { + dbev->reset (); + } + + destroy_map (DbeFile *, dbeFiles); + destroy_map (DbeJarFile *, dbeJarFiles); + exps->destroy (); + lobjs->reset (); // all LoadObjects belong to objs + dobjs->destroy (); // deletes d_unknown and d_total as well + objs->destroy (); + comp_lobjs->clear (); + comp_dbelines->clear (); + comp_sources->clear (); + sourcesMap->clear (); + sources->reset (); + + // Delete the data object name hash table. + for (int i = 0; i < HTableSize; i++) + { + List *list = dnameHTable[i]; + while (list) + { + List *tmp = list; + list = list->next; + delete tmp; + } + } + delete[] dnameHTable; + + // IndexObect definitions remain, objects themselves may go + for (int i = 0; i < idxobjs->size (); ++i) + { + HashMap<uint64_t, Histable*> *v = idxobjs->fetch (i); + if (v != NULL) + { + v->values ()->destroy (); + v->clear (); + } + } + init (); +} + +Vector<SourceFile*> * +DbeSession::get_sources () +{ + return sources; +} + +DbeFile * +DbeSession::getDbeFile (char *filename, int filetype) +{ + Dprintf (DEBUG_DBE_FILE, NTXT ("DbeSession::getDbeFile filetype=0x%x %s\n"), filetype, filename); + if (strncmp (filename, NTXT ("./"), 2) == 0) + filename += 2; + DbeFile *dbeFile = dbeFiles->get (filename); + if (dbeFile == NULL) + { + dbeFile = new DbeFile (filename); + dbeFiles->put (filename, dbeFile); + } + dbeFile->filetype |= filetype; + return dbeFile; +} + +LoadObject * +DbeSession::get_Total_LoadObject () +{ + if (lo_total == NULL) + { + lo_total = createLoadObject (NTXT ("<Total>")); + lo_total->dbeFile->filetype |= DbeFile::F_FICTION; + } + return lo_total; +} + +Function * +DbeSession::get_Total_Function () +{ + if (f_total == NULL) + { + f_total = createFunction (); + f_total->flags |= FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET; + f_total->set_name (NTXT ("<Total>")); + Module *mod = get_Total_LoadObject ()->noname; + f_total->module = mod; + mod->functions->append (f_total); + } + return f_total; +} + +LoadObject * +DbeSession::get_Unknown_LoadObject () +{ + if (lo_unknown == NULL) + { + lo_unknown = createLoadObject (GTXT ("<Unknown>")); + lo_unknown->type = LoadObject::SEG_TEXT; // makes it expandable + lo_unknown->dbeFile->filetype |= DbeFile::F_FICTION; + + // force creation of the <Unknown> function + (void) get_Unknown_Function (); + } + return lo_unknown; +} + +SourceFile * +DbeSession::get_Unknown_Source () +{ + if (sf_unknown == NULL) + { + sf_unknown = createSourceFile (localized_SP_UNKNOWN_NAME); + sf_unknown->dbeFile->filetype |= DbeFile::F_FICTION; + sf_unknown->flags |= SOURCE_FLAG_UNKNOWN; + } + return sf_unknown; +} + +Function * +DbeSession::get_Unknown_Function () +{ + if (f_unknown == NULL) + { + f_unknown = createFunction (); + f_unknown->flags |= FUNC_FLAG_SIMULATED; + f_unknown->set_name (GTXT ("<Unknown>")); + Module *mod = get_Unknown_LoadObject ()->noname; + f_unknown->module = mod; + mod->functions->append (f_unknown); + } + return f_unknown; +} + +// LIBRARY_VISIBILITY + +Function * +DbeSession::create_hide_function (LoadObject *lo) +{ + Function *h_function = createFunction (); + h_function->set_name (lo->get_name ()); + h_function->module = lo->noname; + h_function->isHideFunc = true; + lo->noname->functions->append (h_function); + return h_function; +} + +Function * +DbeSession::get_JUnknown_Function () +{ + if (j_unknown == NULL) + { + j_unknown = createFunction (); + j_unknown->flags |= FUNC_FLAG_SIMULATED; + j_unknown->set_name (GTXT ("<no Java callstack recorded>")); + Module *mod = get_Unknown_LoadObject ()->noname; + j_unknown->module = mod; + mod->functions->append (j_unknown); + } + return j_unknown; +} + +Function * +DbeSession::get_jvm_Function () +{ + if (f_jvm == NULL) + { + f_jvm = createFunction (); + f_jvm->flags |= FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET; + f_jvm->set_name (GTXT ("<JVM-System>")); + + // Find the JVM LoadObject + LoadObject *jvm = get_Unknown_LoadObject (); + for (int i = 0; i < lobjs->size (); ++i) + { + LoadObject *lo = lobjs->fetch (i); + if (lo->flags & SEG_FLAG_JVM) + { + jvm = lo; + break; + } + } + Module *mod = jvm->noname; + f_jvm->module = mod; + mod->functions->append (f_jvm); + // XXXX is it required? no consistency among all special functions + // jvm->functions->append( f_jvm ); + } + return f_jvm; +} + +Function * +DbeSession::getSpecialFunction (SpecialFunction kind) +{ + if (kind < 0 || kind >= LastSpecialFunction) + return NULL; + + Function *func = f_special->fetch (kind); + if (func == NULL) + { + char *fname; + switch (kind) + { + case TruncatedStackFunc: + fname = GTXT ("<Truncated-stack>"); + break; + case FailedUnwindFunc: + fname = GTXT ("<Stack-unwind-failed>"); + break; + default: + return NULL; + } + func = createFunction (); + func->flags |= FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET; + Module *mod = get_Total_LoadObject ()->noname; + func->module = mod; + mod->functions->append (func); + func->set_name (fname); + f_special->store (kind, func); + } + return func; +} + +LoadObject * +DbeSession::get_OMP_LoadObject () +{ + if (lo_omp == NULL) + { + for (int i = 0, sz = lobjs->size (); i < sz; i++) + { + LoadObject *lo = lobjs->fetch (i); + if (lo->flags & SEG_FLAG_OMP) + { + lo_omp = lo; + return lo_omp; + } + } + lo_omp = createLoadObject (GTXT ("<OMP>")); + lo_omp->type = LoadObject::SEG_TEXT; + lo_omp->dbeFile->filetype |= DbeFile::F_FICTION; + } + return lo_omp; +} + +Function * +DbeSession::get_OMP_Function (int n) +{ + if (n < 0 || n >= OMP_LAST_STATE) + return NULL; + + Function *func = omp_functions->fetch (n); + if (func == NULL) + { + char *fname; + switch (n) + { + case OMP_OVHD_STATE: + fname = GTXT ("<OMP-overhead>"); + break; + case OMP_IDLE_STATE: + fname = GTXT ("<OMP-idle>"); + break; + case OMP_RDUC_STATE: + fname = GTXT ("<OMP-reduction>"); + break; + case OMP_IBAR_STATE: + fname = GTXT ("<OMP-implicit_barrier>"); + break; + case OMP_EBAR_STATE: + fname = GTXT ("<OMP-explicit_barrier>"); + break; + case OMP_LKWT_STATE: + fname = GTXT ("<OMP-lock_wait>"); + break; + case OMP_CTWT_STATE: + fname = GTXT ("<OMP-critical_section_wait>"); + break; + case OMP_ODWT_STATE: + fname = GTXT ("<OMP-ordered_section_wait>"); + break; + case OMP_ATWT_STATE: + fname = GTXT ("<OMP-atomic_wait>"); + break; + default: + return NULL; + } + func = createFunction (); + func->flags |= FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET; + func->set_name (fname); + + LoadObject *omp = get_OMP_LoadObject (); + func->module = omp->noname; + omp->noname->functions->append (func); + omp->functions->append (func); + omp_functions->store (n, func); + } + return func; +} + +// Divide the original createExperiment() into two steps +// In part1, we just create the data structure, in part2, if +// we decide to keep the experiment around, add it to various +// lists in DbeSession +Experiment * +DbeSession::createExperimentPart1 () +{ + Experiment *exp = new Experiment (); + return exp; +} + +void +DbeSession::createExperimentPart2 (Experiment *exp) +{ + int ind = expGroups->size (); + if (ind > 0) + { + ExpGroup *gr = expGroups->fetch (ind - 1); + exp->groupId = gr->groupId; + gr->append (exp); + } + exp->setExpIdx (exps->size ()); + exp->setUserExpId (++user_exp_id_counter); + exps->append (exp); +} + +Experiment * +DbeSession::createExperiment () +{ + Experiment *exp = new Experiment (); + append (exp); + return exp; +} + +void +DbeSession::append (Experiment *exp) +{ + exp->setExpIdx (exps->size ()); + exp->setUserExpId (++user_exp_id_counter); + exps->append (exp); + if (exp->founder_exp) + { + if (exp->founder_exp->children_exps == NULL) + exp->founder_exp->children_exps = new Vector<Experiment *>; + exp->founder_exp->children_exps->append (exp); + if (exp->founder_exp->groupId > 0) + { + exp->groupId = exp->founder_exp->groupId; + expGroups->get (exp->groupId - 1)->append (exp); + } + } + if (exp->groupId == 0) + { + long ind = VecSize (expGroups); + if (ind > 0) + { + ExpGroup *gr = expGroups->get (ind - 1); + exp->groupId = gr->groupId; + gr->append (exp); + } + } +} + +void +DbeSession::append (Hwcentry *h) +{ + if (hwcentries == NULL) + hwcentries = new Vector<Hwcentry*>; + hwcentries->append (h); +} + +int +DbeSession::ngoodexps () +{ + int cnt = 0; + for (long i = 0, sz = VecSize (exps); i < sz; i++) + if (exps->get (i)->get_status () == Experiment::SUCCESS) + cnt++; + return cnt; +} + +int +DbeSession::createView (int index, int cloneindex) +{ + // ensure that there is no view with that index + DbeView *dbev = getView (index); + if (dbev != NULL) + abort (); + + // find the view to be cloned + dbev = getView (cloneindex); + DbeView *newview; + if (dbev == NULL) + newview = new DbeView (theApplication, settings, index); + else + newview = new DbeView (dbev, index); + views->append (newview); + return index; +} + +DbeView * +DbeSession::getView (int index) +{ + int i; + DbeView *dbev; + Vec_loop (DbeView*, views, i, dbev) + { + if (dbev->vindex == index) + return dbev; + } + return NULL; +} + +void +DbeSession::dropView (int index) +{ + int i; + DbeView *dbev; + + Vec_loop (DbeView*, views, i, dbev) + { + if (dbev->vindex == index) + { + views->remove (i); + delete dbev; + return; + } + } + // view not found; ignore for now +} + +Vector<char*> * +DbeSession::get_group_or_expt (char *path) +{ + Vector<char*> *exp_list = new Vector<char*>; + FILE *fptr; + char *new_path, buf[MAXPATHLEN], name[MAXPATHLEN]; + + fptr = fopen (path, NTXT ("r")); + if (!fptr || !fgets (buf, (int) sizeof (buf), fptr) + || strncmp (buf, SP_GROUP_HEADER, strlen (SP_GROUP_HEADER))) + { + // it's not an experiment group + new_path = dbe_strdup (path); + new_path = canonical_path (new_path); + exp_list->append (new_path); + } + else + { + // it is an experiment group, read the list to get them all + while (fgets (buf, (int) sizeof (buf), fptr)) + { + if ((*buf != '#') && (sscanf (buf, NTXT ("%s"), name) == 1)) + { + new_path = dbe_strdup (name); + new_path = canonical_path (new_path); + exp_list->append (new_path); + } + } + } + if (fptr) + fclose (fptr); + return exp_list; +} + +#define GET_INT_VAL(v, s, len) \ + for (v = len = 0; isdigit(*s); s++, len++) { v = v * 10 + (*s -'0'); } + +static int +dir_name_cmp (const void *a, const void *b) +{ + char *s1 = *((char **) a); + char *s2 = *((char **) b); + while (*s1) + { + if (isdigit (*s1) && isdigit (*s2)) + { + int v1, v2, len1, len2; + GET_INT_VAL (v1, s1, len1); + GET_INT_VAL (v2, s2, len2); + if (v1 != v2) + return v1 - v2; + if (len1 != len2) + return len2 - len1; + continue; + } + if (*s1 != *s2) + break; + s1++; + s2++; + } + return *s1 - *s2; +} + +static int +read_experiment_data_in_parallel (void *arg) +{ + exp_ctx *ctx = (exp_ctx *) arg; + Experiment *dexp = ctx->exp; + bool read_ahead = ctx->read_ahead; + dexp->read_experiment_data (read_ahead); + free (ctx); + return 0; +} + +void +DbeSession::open_experiment (Experiment *exp, char *path) +{ + exp->open (path); + if (exp->get_status () != Experiment::FAILURE) + exp->read_experiment_data (false); + exp->open_epilogue (); + + // Update all DbeViews + for (int i = 0, sz = views->size (); i < sz; i++) + { + DbeView *dbev = views->fetch (i); + dbev->add_experiment (exp->getExpIdx (), true); + } + + if (exp->get_status () == Experiment::FAILURE) + { + check_tab_avail (); + return; + } + + char *discard_tiny = getenv (NTXT ("SP_ANALYZER_DISCARD_TINY_EXPERIMENTS")); + int user_specified_tiny_threshold = DEFAULT_TINY_THRESHOLD; // in milliseconds + if (discard_tiny != NULL) + { + user_specified_tiny_threshold = (atoi (discard_tiny)); + if (user_specified_tiny_threshold < 0) + user_specified_tiny_threshold = DEFAULT_TINY_THRESHOLD; + } + + // Open descendant experiments + DIR *exp_dir = opendir (path); + if (exp_dir == NULL) + { + check_tab_avail (); + return; + } + + Vector<char*> *exp_names = new Vector<char*>(); + struct dirent *entry = NULL; + while ((entry = readdir (exp_dir)) != NULL) + { + if (entry->d_name[0] != '_') + continue; + size_t len = strlen (entry->d_name); + if (len < 3 || strcmp (entry->d_name + len - 3, NTXT (".er")) != 0) + continue; + exp_names->append (dbe_strdup (entry->d_name)); + } + closedir (exp_dir); + exp_names->sort (dir_name_cmp); + Experiment **t_exp_list = new Experiment *[exp_names->size ()]; + int nsubexps = 0; + + for (int j = 0, jsz = exp_names->size (); j < jsz; j++) + { + t_exp_list[j] = NULL; + + char *lineage_name = exp_names->fetch (j); + struct stat64 sbuf; + char *dpath = dbe_sprintf (NTXT ("%s/%s"), path, lineage_name); + + // look for experiments with no profile collected + if (user_specified_tiny_threshold == DEFAULT_TINY_THRESHOLD) + { + char *frinfoname = dbe_sprintf (NTXT ("%s/%s"), dpath, "data." SP_FRINFO_FILE); + int st = dbe_stat (frinfoname, &sbuf); + free (frinfoname); + if (st == 0) + { + // if no profile/trace data do not process this experiment any further + if (sbuf.st_size == 0) + { + free (dpath); + continue; + } + } + } + else + { // check if dpath is a directory + if (dbe_stat (dpath, &sbuf) != 0) + { + free (dpath); + continue; + } + else if (!S_ISDIR (sbuf.st_mode)) + { + free (dpath); + continue; + } + } + size_t lineage_name_len = strlen (lineage_name); + lineage_name[lineage_name_len - 3] = 0; /* remove .er */ + Experiment *dexp = new Experiment (); + dexp->founder_exp = exp; + if (user_specified_tiny_threshold > DEFAULT_TINY_THRESHOLD) + { + dexp->setTinyThreshold (user_specified_tiny_threshold); + dexp->open (dpath); + if (dexp->isDiscardedTinyExperiment ()) + { + delete dexp; + free (dpath); + continue; + } + } + else + dexp->open (dpath); + append (dexp); + t_exp_list[j] = dexp; + nsubexps++; + dexp->set_clock (exp->clock); + + // DbeView add_experiment() is split into two parts + // add_subexperiment() is called repeeatedly for + // all sub_experiments, later add_experiment_epilogue() finishes up the task + for (int i = 0, sz = views->size (); i < sz; i++) + { + DbeView *dbev = views->fetch (i); + bool enabled = settings->check_en_desc (lineage_name, dexp->utargname); + dbev->add_subexperiment (dexp->getExpIdx (), enabled); + } + free (dpath); + } + + for (int i = 0, sz = views->size (); i < sz; i++) + { + DbeView *dbev = views->fetch (i); + dbev->add_experiment_epilogue (); + } + + DbeThreadPool * threadPool = new DbeThreadPool (-1); + for (int j = 0, jsz = exp_names->size (); j < jsz; j++) + { + if (t_exp_list[j] == NULL) continue; + Experiment *dexp = t_exp_list[j]; + exp_ctx *new_ctx = (exp_ctx*) malloc (sizeof (exp_ctx)); + new_ctx->path = NULL; + new_ctx->exp = dexp; + new_ctx->ds = this; + new_ctx->read_ahead = true; + DbeQueue *q = new DbeQueue (read_experiment_data_in_parallel, new_ctx); + threadPool->put_queue (q); + } + threadPool->wait_queues (); + delete threadPool; + + for (long j = 0, jsz = exp_names->size (); j < jsz; j++) + { + if (t_exp_list[j] == NULL) continue; + Experiment *dexp = t_exp_list[j]; + dexp->open_epilogue (); + } + exp_names->destroy (); + delete[] t_exp_list; + delete exp_names; + + // update setting for leaklist and dataspace + check_tab_avail (); +} + +void +DbeSession::append_mesgs (StringBuilder *sb, char *path, Experiment *exp) +{ + if (exp->fetch_errors () != NULL) + { + // yes, there were errors + char *ststr = pr_mesgs (exp->fetch_errors (), NTXT (""), NTXT ("")); + sb->append (path); + sb->append (NTXT (": ")); + sb->append (ststr); + free (ststr); + } + + Emsg *m = exp->fetch_warnings (); + if (m != NULL) + { + sb->append (path); + sb->append (NTXT (": ")); + if (!is_interactive ()) + sb->append (GTXT ("Experiment has warnings, see header for details\n")); + else + sb->append (GTXT ("Experiment has warnings, see experiment panel for details\n")); + } + + // Check for descendant experiments that are not loaded + int num_desc = VecSize (exp->children_exps); + if ((num_desc > 0) && !settings->check_en_desc (NULL, NULL)) + { + char *s; + if (!is_interactive ()) + s = dbe_sprintf (GTXT ("Has %d descendant(s), use commands controlling selection to load descendant data\n"), num_desc); + else + s = dbe_sprintf (GTXT ("Has %d descendant(s), use filter panel to load descendant data\n"), num_desc); + sb->append (path); + sb->append (NTXT (": ")); + sb->append (s); + free (s); + } +} + +Experiment * +DbeSession::get_exp (int exp_ind) +{ + if (exp_ind < 0 || exp_ind >= exps->size ()) + return NULL; + Experiment *exp = exps->fetch (exp_ind); + exp->setExpIdx (exp_ind); + return exp; +} + +Vector<Vector<char*>*> * +DbeSession::getExperimensGroups () +{ + if (dbeSession->expGroups == NULL || dbeSession->expGroups->size () == 0) + return NULL; + bool compare_mode = expGroups->size () > 1; + Vector<Vector<char*>*> *groups = new Vector<Vector<char*>*> ( + compare_mode ? expGroups->size () : 1); + for (int i = 0; i < expGroups->size (); i++) + { + ExpGroup *grp = expGroups->fetch (i); + Vector<Experiment*> *founders = grp->get_founders (); + if (founders && founders->size () != 0) + { + Vector<char *> *names = new Vector<char*> (founders->size ()); + for (int j = 0; j < founders->size (); j++) + { + Experiment *exp = founders->fetch (j); + names->append (dbe_strdup (exp->get_expt_name ())); + } + if (compare_mode || groups->size () == 0) + groups->append (names); + else + groups->fetch (0)->addAll (names); + } + delete founders; + } + return groups; +} + +char * +DbeSession::setExperimentsGroups (Vector<Vector<char*>*> *groups) +{ + StringBuilder sb; + for (int i = 0; i < groups->size (); i++) + { + Vector<char *> *names = groups->fetch (i); + ExpGroup *grp; + if (names->size () == 1) + grp = new ExpGroup (names->fetch (0)); + else + { + char *nm = dbe_sprintf (GTXT ("Group %d"), i + 1); + grp = new ExpGroup (nm); + free (nm); + } + expGroups->append (grp); + grp->groupId = expGroups->size (); + + for (int j = 0; j < names->size (); j++) + { + char *path = names->fetch (j); + size_t len = strlen (path); + if ((len > 4) && !strcmp (path + len - 4, NTXT (".erg"))) + { + Vector<char*> *lst = get_group_or_expt (path); + for (int j1 = 0; j1 < lst->size (); j1++) + { + Experiment *exp = new Experiment (); + append (exp); + open_experiment (exp, lst->get (j1)); + if (exp->get_status () == Experiment::FAILURE) + append_mesgs (&sb, path, exp); + } + lst->destroy (); + delete lst; + } + else + { + Experiment *exp = new Experiment (); + append (exp); + open_experiment (exp, path); + if (exp->get_status () == Experiment::FAILURE) + append_mesgs (&sb, path, exp); + } + } + } + + for (int i = 0, sz = views->size (); i < sz; i++) + { + DbeView *dbev = views->fetch (i); + dbev->update_advanced_filter (); + int cmp = dbev->get_settings ()->get_compare_mode (); + dbev->set_compare_mode (CMP_DISABLE); + dbev->set_compare_mode (cmp); + } + return sb.length () == 0 ? NULL : sb.toString (); +} + +char * +DbeSession::drop_experiment (int exp_ind) +{ + DbeView *dbev; + int index; + Experiment *exp2; + + status_ompavail = -1; + Experiment *exp = exps->fetch (exp_ind); + + // If this is a sub experiment, don't do it + if (exp->founder_exp != NULL) // this is a sub experiment; don't do it + return (dbe_strdup (GTXT ("Can not drop subexperiments"))); + + if (VecSize (exp->children_exps) > 0) + for (;;) + { + // search the list of experiments to find all that have this one as founder + bool found = false; + Vec_loop (Experiment*, exps, index, exp2) + { + if (exp2->founder_exp == exp) + { + exp2->founder_exp = NULL; + drop_experiment (index); + found = true; + break; + } + } + if (found == false) + break; + } + + // then proceed to finish the drop + Vec_loop (DbeView*, views, index, dbev) + { + dbev->drop_experiment (exp_ind); + } + + int old_cnt = expGroups->size (); + for (int i = 0; i < old_cnt; i++) + { + ExpGroup *gr = expGroups->fetch (i); + if (gr->groupId == exp->groupId) + { + gr->drop_experiment (exp); + if ((gr->founder == NULL) && (gr->exps->size () == 0)) + { + delete gr; + expGroups->remove (i); + } + break; + } + } + delete exps->remove (exp_ind); + if (old_cnt != expGroups->size ()) + { + for (int i = 0, sz = expGroups->size (); i < sz; i++) + { + ExpGroup *gr = expGroups->fetch (i); + gr->groupId = i + 1; + Vector<Experiment*> *expList = gr->exps; + for (int i1 = 0, sz1 = expList->size (); i1 < sz1; i1++) + expList->fetch (i1)->groupId = gr->groupId; + } + for (int i = 0, sz = views->size (); i < sz; i++) + { + dbev = views->fetch (i); + int cmp = dbev->get_compare_mode (); + dbev->set_compare_mode (CMP_DISABLE); + dbev->set_compare_mode (cmp); + } + } + check_tab_avail (); // update tab availability + return NULL; +} + +int +DbeSession::find_experiment (char *path) +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (strcmp (exp->get_expt_name (), path) == 0) + return exp->getExpIdx (); + } + return -1; +} + +LoadObject * +DbeSession::createLoadObject (const char *nm, int64_t cksum) +{ + return loadObjMap->sync_create_item (nm, cksum); +} + +LoadObject * +DbeSession::createLoadObject (const char *nm, const char *runTimePath, DbeFile *df) +{ + return loadObjMap->sync_create_item (nm, runTimePath, df); +} + +void +DbeSession::append (LoadObject *lobj) +{ + Histable *obj = lobj; // workaround for a C++ problem + objs->append (obj); + lobj->id = objs->size () - 1; + lobjs->append (lobj); + lobj->seg_idx = lobjs->size () - 1; + char *loname = lobj->get_pathname (); + dbeFiles->put (loname, lobj->dbeFile); +} + +DbeJarFile * +DbeSession::get_JarFile (const char *name) +{ + DbeJarFile *jf = dbeJarFiles->get (name); + if (jf == NULL) + { + jf = new DbeJarFile (name); + dbeJarFiles->put (name, jf); + } + return jf; +} + +Module * +DbeSession::createModule (LoadObject *lo, const char *nm) +{ + Module *mod = new Module (); + Histable *obj = mod; // workaround for a C++ problem + objs->append (obj); + mod->id = objs->size () - 1; + mod->loadobject = lo; + mod->set_name (dbe_strdup (nm ? nm : localized_SP_UNKNOWN_NAME)); + lo->seg_modules->append (mod); + return mod; +} + +Module * +DbeSession::createUnknownModule (LoadObject *lo) +{ + Module *mod = createModule (lo, localized_SP_UNKNOWN_NAME); + mod->flags |= MOD_FLAG_UNKNOWN; + mod->set_file_name (dbe_strdup (localized_SP_UNKNOWN_NAME)); + return mod; +} + +SourceFile * +DbeSession::createSourceFile (const char *_path) +{ + char *path = (char *) _path; + if (strncmp (path, NTXT ("./"), 2) == 0) + path += 2; + SourceFile *source = sourcesMap->get (path); + if (source == NULL) + { + source = new SourceFile (path); + (void) sourcesMap->put (path, source); + append (source); + } + return source; +} + +Function * +DbeSession::createFunction () +{ + Function *func = new Function (objs->size ()); + Histable *obj = func; // workaround for a C++ problem + objs->append (obj); + return func; +} + +JMethod * +DbeSession::createJMethod () +{ + JMethod *jmthd = new JMethod (objs->size ()); + Histable *obj = jmthd; // workaround for a C++ problem + objs->append (obj); + return jmthd; +} + +Module * +DbeSession::createClassFile (char *className) +{ + ClassFile *cls = new ClassFile (); + cls->set_name (className); + char *clpath = cls->get_java_file_name (className, true); + cls->dbeFile = getDbeFile (clpath, DbeFile::F_JAVACLASS); + free (clpath); + Histable *obj = cls; // workaround for a C++ problem + objs->append (obj); + cls->id = objs->size () - 1; + return cls; +} + +Histable * +DbeSession::createHistObject (Histable::Type type) +{ + switch (type) + { + case Histable::DOBJECT: + { + DataObject *dataobj = new DataObject (); + dobjs->append (dataobj); + dataobj->id = dobjs->size () - 1; + return dataobj; + } + default: + assert (0); + } + return NULL; +} + +DataObject * +DbeSession::createDataObject () +{ + DataObject *dataobj = new DataObject (); + dobjs->append (dataobj); + dataobj->id = dobjs->size () - 1; + return dataobj; +} + +DataObject * +DbeSession::createDataObject (DataObject *dobj, DataObject *parent) +{ + DataObject *dataobj = new DataObject (); + dataobj->size = dobj->size; + dataobj->offset = dobj->offset; + dataobj->parent = parent; + dataobj->set_dobjname (dobj->get_typename (), dobj->get_instname ()); + dobjs->append (dataobj); + dataobj->id = dobjs->size () - 1; + return dataobj; +} + +DataObject * +DbeSession::createMasterDataObject (DataObject *dobj) +{ + DataObject *parent = NULL; + if (dobj->parent) + { // define master parent first + parent = find_dobj_master (dobj->parent); + if (!parent) + { // clone master from this dataobject parent + parent = createDataObject (dobj->parent); + parent->scope = NULL; // master is scope-less + Dprintf (DEBUG_DATAOBJ, + "Master DataObject(%llu) cloned from (%llu) %s\n", + (ull_t) parent->id, (ull_t) dobj->parent->id, + dobj->parent->get_name ()); + // clone master DataObject elements + Vector<DataObject*> *delem = get_dobj_elements (dobj->parent); + int element_index = 0; + DataObject *element = NULL; + Vec_loop (DataObject*, delem, element_index, element) + { + DataObject *master_element = createDataObject (element, parent); + master_element->scope = NULL; // master is scope-less + Dprintf (DEBUG_DATAOBJ, + "Member DataObject(%llu) cloned from (%llu) %s\n", + (ull_t) master_element->id, (ull_t) element->id, + element->get_name ()); + } + } + else + Dprintf (DEBUG_DATAOBJ, "Master DataObject(%llu) clone found (%llu) %s\n", + (ull_t) parent->id, (ull_t) dobj->parent->id, + dobj->parent->get_name ()); + } + + DataObject *master = find_dobj_master (dobj); + if (!master) + { // clone master from this dataobject + master = createDataObject (dobj, parent); + master->scope = NULL; // master is scope-less + Dprintf (DEBUG_DATAOBJ, "Master DataObject(%llu) cloned from (%llu) %s\n", + (ull_t) master->id, (ull_t) dobj->id, dobj->get_name ()); + } + else + Dprintf (DEBUG_DATAOBJ, "Master DataObject(%llu) clone found (%llu) %s\n", + (ull_t) master->id, (ull_t) dobj->id, dobj->get_name ()); + return master; +} + +void +DbeSession::insert_metric (BaseMetric *mtr, Vector<BaseMetric*> *mlist) +{ + if ((mtr->get_flavors () & Metric::STATIC) == 0) + { + // insert in front of the first STATIC + for (int i = 0, mlist_sz = mlist->size (); i < mlist_sz; i++) + { + BaseMetric *m = mlist->fetch (i); + if (m->get_flavors () & Metric::STATIC) + { + mlist->insert (i, mtr); + return; + } + } + } + mlist->append (mtr); +} + +BaseMetricTreeNode* +DbeSession::get_reg_metrics_tree () +{ + if (reg_metrics_tree == NULL) + // Can't init earlier because BaseMetric() requires DbeSession::ql_parse + reg_metrics_tree = new BaseMetricTreeNode (); + return reg_metrics_tree; +} + +void +DbeSession::update_metric_tree (BaseMetric *m) +{ + get_reg_metrics_tree ()->register_metric (m); +} + +BaseMetric * +DbeSession::register_metric_expr (BaseMetric::Type type, char *cmd, char *expr_spec) +{ + BaseMetric *m = find_metric (type, cmd, expr_spec); + if (m) + return m; + BaseMetric *bm = find_metric (type, cmd, NULL); // clone this version + m = new BaseMetric (*bm); + m->set_expr_spec (expr_spec); + insert_metric (m, reg_metrics); + return m; +} + +BaseMetric * +DbeSession::register_metric (BaseMetric::Type type) +{ + BaseMetric *m = find_metric (type, NULL, NULL); + if (m) + return m; + m = new BaseMetric (type); + insert_metric (m, reg_metrics); + update_metric_tree (m); + return m; +} + +BaseMetric * +DbeSession::register_metric (Hwcentry *ctr, const char* aux, const char* username) +{ + BaseMetric *m = find_metric (BaseMetric::HWCNTR, aux, NULL); + if (m) + // That may be a problem when metrics aren't an exact match. + // For example, memoryspace is disabled in one experiment and not in another. + return m; + if (ctr->timecvt) + { + char *time_cmd = dbe_sprintf (NTXT ("t%s"), aux); + char *time_username = dbe_sprintf (GTXT ("%s Time"), + ctr->metric ? ctr->metric : + (ctr->name ? ctr->name : ctr->int_name)); + BaseMetric *m1; + if (ipc_mode) + { + // Two visible metrics are presented in GUI + m1 = new BaseMetric (ctr, aux, time_cmd, time_username, VAL_TIMEVAL); + insert_metric (m1, reg_metrics); + update_metric_tree (m1); + m = new BaseMetric (ctr, aux, username, VAL_VALUE, m1); + } + else + { + // Only one visible metric is presented in er_print + m1 = new BaseMetric (ctr, aux, time_cmd, time_username, VAL_TIMEVAL | VAL_INTERNAL); + insert_metric (m1, reg_metrics); + m = new BaseMetric (ctr, aux, username, VAL_TIMEVAL | VAL_VALUE, m1); + } + free (time_cmd); + free (time_username); + } + else + m = new BaseMetric (ctr, aux, username, VAL_VALUE); + insert_metric (m, reg_metrics); + update_metric_tree (m); + return m; +} + +BaseMetric * +DbeSession::register_metric (char *name, char *username, char *_def) +{ + BaseMetric *m = find_metric (BaseMetric::DERIVED, name, NULL); + if (m) + return m; + Definition *p = Definition::add_definition (_def); + if (p == NULL) + return NULL; + m = new BaseMetric (name, username, p); + insert_metric (m, reg_metrics); + update_metric_tree (m); + return m; +} + +void +DbeSession::drop_metric (BaseMetric *mtr) +{ + Countable *cnt; + int index; + + Vec_loop (Countable*, metrics, index, cnt) + { + if (mtr == (BaseMetric *) cnt->item) + { + cnt->ref_count--; + if (cnt->ref_count == 0) + { + // Remove this metric from all views + DbeView *dbev; + int index2; + Vec_loop (DbeView*, views, index2, dbev) + { + dbev->reset_metrics (); + } + delete metrics->remove (index); + delete mtr; + return; + } + } + } +} + +BaseMetric * +DbeSession::find_metric (BaseMetric::Type type, const char *cmd, const char *expr_spec) +{ + for (int i = 0, sz = reg_metrics->size (); i < sz; i++) + { + BaseMetric *bm = reg_metrics->fetch (i); + if (bm->get_type () == type && dbe_strcmp (bm->get_expr_spec (), expr_spec) == 0) + { + if ((type == BaseMetric::DERIVED || type == BaseMetric::HWCNTR) + && dbe_strcmp (bm->get_cmd (), cmd) != 0) + continue; + return bm; + } + } + return NULL; +} + +BaseMetric * +DbeSession::find_base_reg_metric (char * mcmd) +{ + for (int i = 0, sz = reg_metrics->size (); i < sz; i++) + { + BaseMetric *bm = reg_metrics->fetch (i); + if (bm->get_expr_spec () != NULL) + continue; // skip compare metrics + if (dbe_strcmp (bm->get_cmd (), mcmd) == 0) + return bm; + } + return NULL; +} + +Vector<BaseMetric*> * +DbeSession::get_base_reg_metrics () +{ + Vector<BaseMetric*> *mlist = new Vector<BaseMetric*>; + Vector<BaseMetric*> *ml = get_all_reg_metrics (); + for (int i = 0, sz = ml->size (); i < sz; i++) + { + BaseMetric *m = ml->fetch (i); + if (m->get_expr_spec () == NULL) + mlist->append (m); + } + return mlist; +} + +void +DbeSession::check_tab_avail () +{ + DbeView *dbev; + int index; + // tell the views to update their tab lists + Vec_loop (DbeView*, views, index, dbev) + { + dbev->get_settings ()->updateTabAvailability (); + } +} + +bool +DbeSession::is_datamode_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->dataspaceavail) + return true; + } + return false; +} + +bool +DbeSession::is_leaklist_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->leaklistavail) + return true; + } + return false; +} + +bool +DbeSession::is_heapdata_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->heapdataavail) + return true; + } + return false; +} + +bool +DbeSession::is_iodata_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->iodataavail) + return true; + } + return false; +} + +bool +DbeSession::is_racelist_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->racelistavail) + return true; + } + return false; +} + +bool +DbeSession::is_deadlocklist_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->deadlocklistavail) + return true; + } + return false; +} + +bool +DbeSession::is_timeline_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->timelineavail) + return true; + } + return false; +} + +bool +DbeSession::is_ifreq_available () +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + if (exp->ifreqavail) + return true; + } + return false; +} + +bool +DbeSession::is_omp_available () +{ + if (status_ompavail == -1) + { + status_ompavail = 0; + for (int i = 0, sz = exps ? exps->size () : 0; i < sz; i++) + { + Experiment *exp = exps->fetch (i); + if (exp->ompavail) + { + status_ompavail = 1; + break; + } + } + } + return status_ompavail == 1; +} + +bool +DbeSession::has_java () +{ + int status_has_java = 0; + for (int i = 0, sz = exps ? exps->size () : 0; i < sz; i++) + { + Experiment *exp = exps->fetch (i); + if (exp->has_java) + { + status_has_java = 1; + break; + } + } + return status_has_java == 1; +} + +bool +DbeSession::has_ompavail () +{ + int status_has_ompavail = 0; + for (int i = 0, sz = exps ? exps->size () : 0; i < sz; i++) + { + Experiment *exp = exps->fetch (i); + if (exp->ompavail) + { + status_has_ompavail = 1; + break; + } + } + return status_has_ompavail == 1; +} + +int +DbeSession::get_clock (int whichexp) +{ + // XXXX clock frequency should be an attribute of each CPU, + // XXX and not a property of the session + // if whichexp is -1, pick the first exp that has a clock + // otherwise return the clock from the numbered experiment + Experiment *exp; + if (whichexp != -1) + { + exp = get_exp (whichexp); + if (exp != NULL) + return exp->clock; + return 0; + } + int n = nexps (); + for (int i = 0; i < n; i++) + { + exp = get_exp (i); + if (exp != NULL && exp->clock != 0) + return exp->clock; + } + return 0; +} + +LoadObject * +DbeSession::find_lobj_by_name (const char *lobj_name, int64_t cksum) +{ + return loadObjMap->get (lobj_name, cksum); +} + +static unsigned +hash (char *s) +{ + unsigned res = 0; + for (int i = 0; i < 64 && *s; i++) + res = res * 13 + *s++; + return res; +} + +// This method is introduced to fix performance +// problems with the data space profiling in the +// current release. A better design is desired. +void +DbeSession::dobj_updateHT (DataObject *dobj) +{ + unsigned index = hash (dobj->get_unannotated_name ()) % HTableSize; + List *list = new List; + list->val = (void*) dobj; + list->next = dnameHTable[index]; + dnameHTable[index] = list; +} + +DataObject * +DbeSession::find_dobj_by_name (char *dobj_name) +{ + unsigned index = hash (dobj_name) % HTableSize; + List *list = dnameHTable[index]; + for (; list; list = list->next) + { + DataObject *d = (DataObject*) list->val; + if (strcmp (d->get_unannotated_name (), dobj_name) == 0) + return d; + } + return (DataObject *) NULL; +} + +DataObject * +DbeSession::find_dobj_match (DataObject *dobj) +{ + char *dobj_name = dobj->get_unannotated_name (); + unsigned index = hash (dobj_name) % HTableSize; + List *list = dnameHTable[index]; + for (; list; list = list->next) + { + DataObject *d = (DataObject*) list->val; + if (strcmp (d->get_unannotated_name (), dobj_name) == 0 + && d->size == dobj->size && d->offset == dobj->offset + && d->scope == dobj->scope) + return d; + } + return (DataObject *) NULL; +} + +DataObject * +DbeSession::find_dobj_master (DataObject *dobj) +{ + char *dobj_name = dobj->get_unannotated_name (); + unsigned index = hash (dobj_name) % HTableSize; + List *list = dnameHTable[index]; + for (; list; list = list->next) + { + DataObject *d = (DataObject*) list->val; + // XXXX should parent also match? + if (strcmp (d->get_unannotated_name (), dobj_name) == 0 + && d->size == dobj->size && d->offset == dobj->offset + && d->master == NULL && d->scope == NULL) + return d; + } + return (DataObject *) NULL; +} + +Vector<DataObject*>* +DbeSession::get_dobj_elements (DataObject *dobj) +{ + DataObject *d; + int index; + Vector<DataObject*> *elements = new Vector<DataObject*>; + if (dobj == d_total) + return elements; + Vec_loop (DataObject*, dobjs, index, d) + { + if (d->get_parent () && d->get_parent () == dobj) + elements->append (d); + } + return elements; +} + +Vector<LoadObject*>* +DbeSession::get_text_segments () +{ + LoadObject *lo; + int index; + Vector<LoadObject*> *tlobjs = new Vector<LoadObject*>; + Vec_loop (LoadObject*, lobjs, index, lo) + { + if (lo->type == LoadObject::SEG_TEXT) + tlobjs->append (lo); + } + return tlobjs; +} + +static long long +getNumber (const char *s, char * &last) +{ + long long val; + char *sp; + errno = 0; + val = strtoll (s, &sp, 0); + if (errno == EINVAL) + last = NULL; + else + { + while (isspace (*sp)) + sp++; + last = sp; + } + return (val); +} + +bool +DbeSession::find_obj (FILE *dis_file, FILE *inp_file, Histable *&obj, + char *name, const char *sel, Histable::Type type, bool xdefault) +{ + Vector<Histable*> *obj_lst; + int which = -1; + char *last = NULL; + if (type != Histable::FUNCTION && sel) + { + // check that a number has been provided + which = (int) getNumber (sel, last); + if (last == NULL || *last != '\0') + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + sel = NULL; + which = 0; + } + which--; + } + obj_lst = new Vector<Histable*>; + switch (type) + { + case Histable::FUNCTION: + obj = map_NametoFunction (name, obj_lst, sel); + break; + case Histable::MODULE: + obj = map_NametoModule (name, obj_lst, which); + break; + case Histable::LOADOBJECT: + obj = map_NametoLoadObject (name, obj_lst, which); + break; + case Histable::DOBJECT: + obj = map_NametoDataObject (name, obj_lst, which); + break; + default: + abort (); // unexpected Histable! + } + + if ((obj == NULL) && (obj_lst->size () > 0)) + { + if (obj_lst->size () == 1) + which = 0; + else + { + if (sel && (which < 0 || which >= obj_lst->size ())) + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + if (xdefault) + { + fprintf (stderr, GTXT ("Default selection \"1\" made\n")); + which = 0; + } + else + { + which = ask_which (dis_file, inp_file, obj_lst, name); + if (which == -1) + { + delete obj_lst; + return false; + } + } + } + obj = obj_lst->fetch (which); + } + delete obj_lst; + return true; +} + +int +DbeSession::ask_which (FILE *dis_file, FILE *inp_file, + Vector<Histable*> *list, char *name) +{ + Histable *hitem; + Function *func; + Module *module; + int which, index, index1; + char *item_name, *lo_name, *fname, *last; + char buf[BUFSIZ]; + for (;;) + { + fprintf (dis_file, GTXT ("Available name list:\n")); + fprintf (dis_file, GTXT ("%8d) Cancel\n"), 0); + Vec_loop (Histable*, list, index, hitem) + { + index1 = index + 1; + item_name = hitem->get_name (); + switch (hitem->get_type ()) + { + case Histable::FUNCTION: + func = (Function *) hitem; + module = func->module; + + // id == -1 indicates er_src invocation + if (module == NULL || (module->lang_code == Sp_lang_java + && module->loadobject->id == -1)) + fprintf (dis_file, NTXT ("%8d) %s\n"), index1, item_name); + else + { + lo_name = module->loadobject->get_pathname (); + fname = (module->file_name && *module->file_name) ? + module->file_name : module->get_name (); + if (fname && *fname) + fprintf (dis_file, NTXT ("%8d) %s %s:0x%llx (%s)\n"), index1, + item_name, lo_name, (ull_t) func->img_offset, fname); + else + fprintf (dis_file, NTXT ("%8d) %s %s:0x%llx\n"), index1, + item_name, lo_name, (ull_t) func->img_offset); + } + break; + case Histable::MODULE: + module = (Module *) hitem; + lo_name = module->loadobject->get_pathname (); + if (name[strlen (name) - 1] == + module->file_name[strlen (module->file_name) - 1]) + fprintf (dis_file, NTXT ("%8d) %s(%s)\n"), index1, + module->file_name, lo_name); + else + fprintf (dis_file, NTXT ("%8d) %s(%s)\n"), index1, item_name, + lo_name); + break; + default: + fprintf (dis_file, NTXT ("%8d) %s\n"), index1, item_name); + break; + } + } + if (inp_file != stdin) + return -1; + fprintf (dis_file, GTXT ("Enter selection: ")); + if (fgets (buf, (int) sizeof (buf), inp_file) == NULL) + { + fprintf (stderr, GTXT ("Error: Invalid number entered:\n")); + return -1; + } + which = (int) getNumber (buf, last); + if (last && *last == '\0') + if (which >= 0 && which <= list->size ()) + return which - 1; + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), buf); + } +} + +static bool +match_basename (char *name, char *full_name, int len = -1) +{ + if (full_name == NULL) + return false; + if (!strchr (name, '/')) + full_name = get_basename (full_name); + if (len == -1) + return streq (name, full_name); + return strncmp (name, full_name, len) == 0; +} + +LoadObject * +DbeSession::map_NametoLoadObject (char *name, Vector<Histable*> *list, int which) +{ + // Search the tree to find the first module whose module name + // matches "name" or whose source file name matches "name" + // Issues: is the name a pathname, or a base name? + // Should we look at suffix to disambiguate? + LoadObject *loitem; + int index; + Vec_loop (LoadObject*, lobjs, index, loitem) + { + // try pathname first + // if failed, try object name next + if (match_basename (name, loitem->get_pathname ()) || + match_basename (name, loitem->get_name ())) + { + if (which == list->size ()) + return loitem; + list->append (loitem); + } + } + return (LoadObject *) NULL; +} + +Module * +DbeSession::map_NametoModule (char *name, Vector<Histable*> *list, int which) +{ + // Search the tree to find the first loadobject whose loadobject name + // matches "name". + + // Issues: is the name a pathname, or a base name? + // Should we look at suffix to disambiguate? + LoadObject *loitem; + Module *mitem; + int index1, index2; + Vec_loop (LoadObject*, lobjs, index1, loitem) + { + Vec_loop (Module*, loitem->seg_modules, index2, mitem) + { + // try source name first + // if failed, try object name next + if (match_basename (name, mitem->file_name) || + match_basename (name, mitem->get_name ())) + { + if (which == list->size ()) + return mitem; + list->append (mitem); + } + } + } + return (Module *) NULL; +} + +Function * +DbeSession::map_NametoFunction (char *name, Vector<Histable*> *list, + const char *sel) +{ + // Search the tree to find the first function whose + // name matches "name". + // Issues: is the name a full name, or a short name? + // Is it a demangled name? If so, what about spaces + // within the name? + // Is there a way to return all names that match? + // How can the user specify a particular function of that name? + LoadObject *loitem; + Function *fitem, *main_func = NULL; + Module *mitem, *main_mod = NULL; + int index1, index2, index3, which = -1; + if (sel) + { + char *last = NULL; + if (*sel == '@') + { // 'sel' is "@seg_num:address" + which = (int) getNumber (sel + 1, last); + if (last == NULL || *last != ':' || (which < 0) || (which >= lobjs->size ())) + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + return NULL; + } + uint64_t address = getNumber (last + 1, last); + if (last == NULL || *last != '\0') + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + return NULL; + } + loitem = lobjs->fetch (which); + Vec_loop (Module*, loitem->seg_modules, index2, mitem) + { + Vec_loop (Function*, mitem->functions, index3, fitem) + { + if (address == fitem->img_offset && match_FName (name, fitem)) + return fitem; + } + } + return NULL; + } + + which = (int) getNumber (sel, last); + if (last == NULL || *last != '\0') + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\n"), sel); + return NULL; + } + which--; + } + + int len_path = 0; + char *with_path = name; + name = StrRchr (name, '`'); + if (name != with_path) + len_path = (int) (name - with_path); + else + with_path = NULL; + + Vec_loop (LoadObject*, lobjs, index1, loitem) + { + Vec_loop (Module*, loitem->seg_modules, index2, mitem) + { + if (with_path) + { // with file name + // try source name first + // if failed, try object name next + if (!match_basename (with_path, mitem->file_name, len_path) && + !match_basename (with_path, mitem->get_name (), len_path)) + continue; + } + Vec_loop (Function*, mitem->functions, index3, fitem) + { + if (match_FName (name, fitem)) + { + if (which == list->size ()) + return fitem; + list->append (fitem); + continue; + } + if (streq (fitem->get_name (), NTXT ("MAIN_")) && mitem->is_fortran ()) + { + main_func = fitem; + main_mod = mitem; + } + } + } + } + + if (main_mod && main_func) + { + main_mod->read_stabs (); + if (streq (main_func->get_match_name (), name) && which <= 1) + return main_func; + } + return (Function *) NULL; +} + +DataObject * +DbeSession::map_NametoDataObject (char *name, Vector<Histable*> *list, + int which) +{ + // Search master list to find dataobjects whose names match "name" + // selecting only the entry corresponding to "which" if it is not -1. + // Issues: is the name fully qualified or only partially? + DataObject *ditem = NULL; + int index; + char *full_name; + Vec_loop (DataObject*, dobjs, index, ditem) + { + if (ditem->scope) continue; // skip non-master dataobjects + + // try fully-qualified dataobject name first + if ((full_name = ditem->get_name ()) != NULL) + { + if (streq (name, full_name)) + { + if (which == list->size ()) + return ditem; + list->append (ditem); + } + } + } + if (list->size () > 0) + return ditem; // return fully-qualified match + + // if fully-qualified name doesn't match anything, try a partial match + Vec_loop (DataObject*, dobjs, index, ditem) + { + if (ditem->scope) continue; // skip non-master dataobjects + + // try fully-qualified dataobject name first + if ((full_name = ditem->get_name ()) != NULL) + { + if (strstr (full_name, name)) + { + if (which == list->size ()) + return ditem; + list->append (ditem); + } + } + } + return (DataObject *) NULL; +} + +bool +DbeSession::match_FName (char *name, Function *func) +{ + size_t len; + char buf[MAXDBUF]; + char *full_name; + if (streq (func->get_name (), name)) // try full name comparison + return true; + if (streq (func->get_mangled_name (), name)) // try mangled name + return true; + if (streq (func->get_match_name (), name)) // try match name + return true; + + Module *md = func->module; // try FORTRAN name + if (md && md->is_fortran ()) + { + char *mangled_name = func->get_mangled_name (); + len = strlen (name); + if (((len + 1) == strlen (mangled_name)) && + (strncmp (name, mangled_name, len) == 0)) + return true; + } + snprintf (buf, sizeof (buf), NTXT ("%s"), func->get_name ()); + full_name = buf; + char *arg = NULL; // find modifier and C++ class name + int i = get_paren (buf); + if (i >= 0) + { + arg = buf + i; + *arg = '\0'; + } + + char *mod = strchr (full_name, ' '); + char *cls = strchr (full_name, ':'); + + if (mod) + { + len = mod - full_name + 1; + if (!strncmp (full_name, name, len)) + name += len; + full_name += len; + if (streq (full_name, name)) // try without modifier + return true; + } + + size_t len_cmp = strlen (name); + if (arg) + { + *arg = '('; + len = arg - full_name; // try without 'args' + if (len_cmp == len && !strncmp (full_name, name, len)) + return true; + if (cls) + { + len = arg - cls - 2; // and without 'class name' + if ((len_cmp == len) && !strncmp (cls + 2, name, len)) + return true; + } + } + + if (cls) + { + len = cls - full_name; // try C++ class name only + if (len_cmp == len && !strncmp (full_name, name, len)) + return true; + if (streq (cls + 2, name)) // try without 'class name' + return true; + } + return false; +} + +bool +DbeSession::add_path (char *path) +{ + return add_path (path, get_search_path ()); +} + +bool +DbeSession::add_classpath (char *path) +{ + return add_path (path, classpath); +} + +Vector<DbeFile*> * +DbeSession::get_classpath () +{ + if (classpath_df == NULL) + classpath_df = new Vector<DbeFile*>; + for (int i = classpath_df->size (), sz = classpath->size (); i < sz; i++) + classpath_df->store (i, getDbeFile (classpath->fetch (i), + DbeFile::F_DIR_OR_JAR)); + return classpath_df; +} + +bool +DbeSession::add_path (char *path, Vector<char*> *pathes) +{ + bool result = false; + Vector <char *> *tokens = split_str (path, ':'); + for (long j = 0, jsz = VecSize (tokens); j < jsz; j++) + { + char *spath = tokens->get (j); + // Don't append path if it's already there + bool got = false; + for (int i = 0, sz = pathes->size (); i < sz; i++) + { + char *nm = pathes->get (i); + if (streq (nm, spath)) + { + got = true; + break; + } + } + if (!got) + { + pathes->append (spath); + result = true; + } + else + free (spath); + } + delete tokens; + return result; +} + +void +DbeSession::set_need_refind () +{ + Vector<DbeFile*> *f_list = dbeFiles->values (); + for (long i = 0, sz = f_list == NULL ? 0 : f_list->size (); i < sz; i++) + { + DbeFile *f = f_list->get (i); + f->set_need_refind (true); + } + delete f_list; + for (long i = 0, sz = sources == NULL ? 0 : sources->size (); i < sz; i++) + { + SourceFile *f = sources->get (i); + if (f && f->dbeFile) + f->dbeFile->set_need_refind (true); + } +} + +void +DbeSession::set_search_path (Vector<char*> *path, bool reset) +{ + if (reset) + search_path->destroy (); + for (int i = 0, sz = path == NULL ? 0 : path->size (); i < sz; i++) + { + char *name = path->fetch (i); + if (add_path (name)) + reset = true; + } + if (reset) + { + set_need_refind (); + + // now reset the string setting for it + StringBuilder sb; + for (int i = 0, sz = search_path == NULL ? 0 : search_path->size (); i < sz; i++) + { + char *name = search_path->fetch (i); + if (sb.length () != 0) + sb.append (':'); + sb.append (name); + } + free (settings->str_search_path); + settings->str_search_path = sb.toString (); + } +} + +void +DbeSession::set_search_path (char *_lpath, bool reset) +{ + Vector<char *> *path = new Vector<char*>; + char *lpath = dbe_strdup (_lpath); + for (char *s = lpath; s;) + { + path->append (s); + s = strchr (s, ':'); + if (s) + { + *s = 0; + s++; + } + } + set_search_path (path, reset); + delete path; + free (lpath); +} + +void +DbeSession::set_pathmaps (Vector<pathmap_t*> *newPathMap) +{ + set_need_refind (); + settings->set_pathmaps (newPathMap); +} + +Vector<pathmap_t*> * +DbeSession::get_pathmaps () +{ + return settings->pathmaps; +} + +void +DbeSession::mobj_define (MemObjType_t *mobj) +{ + settings->mobj_define (mobj, false); + DbeView *dbev; + int index; + Vec_loop (DbeView*, views, index, dbev) + { + dbev->get_settings ()->mobj_define (mobj, false); + } +} + +void +DbeSession::dump_segments (FILE *out) +{ + int index; + LoadObject *loitem; + Vec_loop (LoadObject*, lobjs, index, loitem) + { + fprintf (out, NTXT ("Segment %d -- %s -- %s\n\n"), + index, loitem->get_name (), loitem->get_pathname ()); + loitem->dump_functions (out); + fprintf (out, NTXT ("\n End Segment %d -- %s -- %s\n\n"), + index, loitem->get_name (), loitem->get_pathname ()); + } +} + +void +DbeSession::dump_dataobjects (FILE *out) +{ + DataObject *ditem; + int index; + + fprintf (out, NTXT ("\nMaster list of DataObjects:\n")); + Vec_loop (DataObject*, dobjs, index, ditem) + { + Histable* scope = ditem->get_scope (); + DataObject* parent = ditem->get_parent (); + DataObject* master = ditem->get_master (); + if (parent != NULL) + fprintf (out, "id %6lld: [%4lld] parent = %6lld, offset = %+4lld %s\n", + (ll_t) ditem->id, (ll_t) ditem->get_size (), + (ll_t) parent->id, (ll_t) ditem->get_offset (), + ditem->get_name ()); + else + { + // parent is NULL + fprintf (out, NTXT ("id %6lld: [%4lld] %s "), + (ll_t) ditem->id, (ll_t) ditem->get_size (), + ditem->get_name ()); + if (master != NULL) + fprintf (out, NTXT (" master=%lld "), (ll_t) master->id); + else if (scope != NULL) + fprintf (out, NTXT (" master=?? ")); + else + fprintf (out, NTXT (" MASTER ")); +#if DEBUG + if (scope != NULL) + { + switch (scope->get_type ()) + { + case Histable::LOADOBJECT: + case Histable::FUNCTION: + fprintf (out, NTXT ("%s"), scope->get_name ()); + break; + case Histable::MODULE: + { + char *filename = get_basename (scope->get_name ()); + fprintf (out, NTXT ("%s"), filename); + break; + } + default: + fprintf (out, NTXT (" Unexpected scope %d:%s"), + scope->get_type (), scope->get_name ()); + } + } +#endif + fprintf (out, NTXT ("\n")); + } + } +} + +void +DbeSession::dump_map (FILE *out) +{ + Experiment *exp; + int index; + Vec_loop (Experiment*, exps, index, exp) + { + exp->dump_map (out); + } +} + +void +DbeSession::dump_stacks (FILE *outfile) +{ + Experiment *exp; + int n = nexps (); + FILE *f = (outfile == NULL ? stderr : outfile); + for (int i = 0; i < n; i++) + { + exp = get_exp (i); + fprintf (f, GTXT ("Experiment %d -- %s\n"), i, exp->get_expt_name ()); + exp->dump_stacks (f); + } +} + +void +DbeSession::propNames_name_store (int propId, const char *propName) +{ + PropDescr *prop = new PropDescr (propId, propName); + prop->flags = PRFLAG_NOSHOW; // do not show descriptions + propNames->store (propId, prop); +} + +void +DbeSession::propNames_name_store (int propId, const char* propName, + const char* propUname, VType_type dataType, + int flags) +{ + PropDescr *prop = new PropDescr (propId, propName); + prop->vtype = dataType; + prop->uname = dbe_strdup (propUname); + prop->flags = flags; + propNames->store (propId, prop); +} + +char * +DbeSession::propNames_name_fetch (int i) +{ + PropDescr *prop = propNames->fetch (i); + if (prop) + return prop->name; + return NULL; +} + +int +DbeSession::registerPropertyName (const char *name) +{ + if (name == NULL) + return PROP_NONE; + for (int i = 0; i < propNames->size (); i++) + { + char *pname = propNames_name_fetch (i); + if (pname && strcasecmp (pname, name) == 0) + return i; + } + int propId = propNames->size (); + propNames_name_store (propId, name); + return propId; +} + +int +DbeSession::getPropIdByName (const char *name) +{ + if (name == NULL) + return PROP_NONE; + for (int i = 0; i < propNames->size (); i++) + { + char *pname = propNames_name_fetch (i); + if (pname && strcasecmp (pname, name) == 0) + return i; + } + return PROP_NONE; +} + +char * +DbeSession::getPropName (int propId) +{ + if (!propNames) + return NULL; + if (propId < 0 || propId >= propNames->size ()) + return NULL; + return dbe_strdup (propNames_name_fetch (propId)); +} + +char * +DbeSession::getPropUName (int propId) +{ + if (!propNames) + return NULL; + if (propId < 0 || propId >= propNames->size ()) + return NULL; + PropDescr *prop = propNames->fetch (propId); + if (prop) + return dbe_strdup (prop->uname); + return NULL; +} + +void +DbeSession::append (UserLabel *lbl) +{ + if (lbl->expr) + { + if (userLabels == NULL) + userLabels = new Vector<UserLabel*> (); + userLabels->append (lbl); + } +} + +void +DbeSession::append (SourceFile *sf) +{ + sources->append (sf); + objs->append (sf); +} + +UserLabel * +DbeSession::findUserLabel (char *name) +{ + for (int i = 0, sz = userLabels ? userLabels->size () : 0; i < sz; i++) + { + UserLabel *lbl = userLabels->fetch (i); + if (strcasecmp (lbl->name, name) == 0) + return lbl; + } + return NULL; +} + +Expression * +DbeSession::findObjDefByName (char *name) +{ + Expression *expr = NULL; + + MemObjType_t *mot = MemorySpace::findMemSpaceByName (name); + if (mot != NULL) + { + char *index_expr_str = mot->index_expr; + expr = ql_parse (index_expr_str); + } + + if (expr == NULL) + { + int indxtype = findIndexSpaceByName (name); + expr = getIndexSpaceExpr (indxtype); + } + if (expr == NULL) + { + UserLabel *ulbl = findUserLabel (name); + if (ulbl) + expr = ulbl->expr; + } + return expr; +} + +Expression * +DbeSession::ql_parse (const char *expr_spec) +{ + /* (This slight duplication means we don't need to worry about copy + constructors for the QL::Result, nor about the lifetime of the + expr_spec.) */ + if (expr_spec != NULL) + { + QL::Result result (expr_spec); + QL::Parser qlparser (result); + if (qlparser () != 0) + return NULL; + return result (); + } + else + { + QL::Result result; + QL::Parser qlparser (result); + if (qlparser () != 0) + return NULL; + return result (); + } +} + +Vector<void*> * +DbeSession::getIndxObjDescriptions () +{ + int size = dyn_indxobj_indx; + if (size == 0) + return NULL; + Vector<int> *type = new Vector<int>(dyn_indxobj_indx); + Vector<char*> *desc = new Vector<char*>(dyn_indxobj_indx); + Vector<char*> *i18ndesc = new Vector<char*>(dyn_indxobj_indx); + Vector<char> *mnemonic = new Vector<char>(dyn_indxobj_indx); + Vector<int> *orderList = new Vector<int>(dyn_indxobj_indx); + Vector<char*> *exprList = new Vector<char*>(dyn_indxobj_indx); + Vector<char*> *sdesc = new Vector<char*>(dyn_indxobj_indx); + Vector<char*> *ldesc = new Vector<char*>(dyn_indxobj_indx); + + for (long i = 0, sz = VecSize (dyn_indxobj); i < sz; i++) + { + IndexObjType_t *tot = dyn_indxobj->get (i); + if (tot->memObj == NULL) + { + type->append ((int) tot->type); + desc->append (dbe_strdup (tot->name)); + i18ndesc->append (dbe_strdup (tot->i18n_name)); + sdesc->append (dbe_strdup (tot->short_description)); + ldesc->append (dbe_strdup (tot->long_description)); + mnemonic->append (tot->mnemonic); + orderList->append (settings->indx_tab_order->fetch (i)); + exprList->append (dbe_strdup (tot->index_expr_str)); + } + } + Vector<void*> *res = new Vector<void*>(8); + res->store (0, type); + res->store (1, desc); + res->store (2, mnemonic); + res->store (3, i18ndesc); + res->store (4, orderList); + res->store (5, exprList); + res->store (6, sdesc); + res->store (7, ldesc); + return (res); +} + +// Static function to get a vector of custom index object definitions +Vector<void*> * +DbeSession::getCustomIndxObjects () +{ + Vector<char*> *name = new Vector<char*>; + Vector<char*> *formula = new Vector<char*>; + for (long i = dyn_indxobj_indx_fixed, sz = VecSize (dyn_indxobj); i < sz; i++) + { + IndexObjType_t *tot = dyn_indxobj->get (i); + if (tot->memObj == NULL) + { + name->append (dbe_strdup (tot->name)); + formula->append (dbe_strdup (tot->index_expr_str)); + } + } + Vector<void*> *res = new Vector<void*>(2); + res->store (0, name); + res->store (1, formula); + return (res); +} + +// Static function to define a new index object type +char * +DbeSession::indxobj_define (const char *mname, char *i18nname, const char *index_expr_str, char *short_description, char *long_description) +{ + if (mname == NULL) + return dbe_strdup (GTXT ("No index object type name has been specified.")); + if (isalpha ((int) (mname[0])) == 0) + return dbe_sprintf (GTXT ("Index Object type name %s does not begin with an alphabetic character"), + mname); + const char *p = mname; + while (*p != 0) + { + if ((isalnum ((int) (*p)) == 0) && (*p != '_')) + return dbe_sprintf (GTXT ("Index Object type name %s contains a non-alphanumeric character"), + mname); + p++; + } + + // make sure the name is not in use + if (MemorySpace::findMemSpaceByName (mname) != NULL) + return dbe_sprintf (GTXT ("Memory/Index Object type name %s is already defined"), + mname); + + int idxx = findIndexSpaceByName (mname); + if (idxx >= 0) + { + IndexObjType_t *mt = dyn_indxobj->fetch (idxx); + if (strcmp (mt->index_expr_str, index_expr_str) == 0) + // It's a redefinition, but the new definition is the same + return NULL; + return dbe_sprintf (GTXT ("Memory/Index Object type name %s is already defined"), + mname); + } + if (index_expr_str == NULL) + return dbe_strdup (GTXT ("No index-expr has been specified.")); + if (strlen (index_expr_str) == 0) + return dbe_sprintf (GTXT ("Index Object index expression is invalid: %s"), + index_expr_str); + + // verify that the index expression parses correctly + char *expr_str = dbe_strdup (index_expr_str); + Expression *expr = ql_parse (expr_str); + if (expr == NULL) + return dbe_sprintf (GTXT ("Index Object index expression is invalid: %s"), + expr_str); + + // It's OK, create the new table entry + IndexObjType_t *tot = new IndexObjType_t; + tot->type = dyn_indxobj_indx++; + tot->name = dbe_strdup (mname); + tot->i18n_name = dbe_strdup (i18nname); + tot->short_description = dbe_strdup (short_description); + tot->long_description = dbe_strdup (long_description); + tot->index_expr_str = expr_str; + tot->index_expr = expr; + tot->mnemonic = mname[0]; + + // add it to the list + dyn_indxobj->append (tot); + idxobjs->append (new HashMap<uint64_t, Histable*>); + + // tell the session + settings->indxobj_define (tot->type, false); + + DbeView *dbev; + int index; + Vec_loop (DbeView*, views, index, dbev) + { + dbev->addIndexSpace (tot->type); + } + return NULL; +} + +char * +DbeSession::getIndexSpaceName (int index) +{ + if (index < 0 || index >= dyn_indxobj->size ()) + return NULL; + return dyn_indxobj->fetch (index)->name; +} + +char * +DbeSession::getIndexSpaceDescr (int index) +{ + if (index < 0 || index >= dyn_indxobj->size ()) + return NULL; + return dyn_indxobj->fetch (index)->i18n_name; +} + +Expression * +DbeSession::getIndexSpaceExpr (int index) +{ + if (index < 0 || index >= dyn_indxobj->size ()) + return NULL; + return dyn_indxobj->fetch (index)->index_expr; +} + +char * +DbeSession::getIndexSpaceExprStr (int index) +{ + if (index < 0 || index >= dyn_indxobj->size ()) + return NULL; + return dyn_indxobj->fetch (index)->index_expr_str; +} + +int +DbeSession::findIndexSpaceByName (const char *mname) +{ + int idx; + IndexObjType_t *mt; + Vec_loop (IndexObjType_t*, dyn_indxobj, idx, mt) + { + if (strcasecmp (mt->name, mname) == 0) + return idx; + } + return -1; +} + +void +DbeSession::removeIndexSpaceByName (const char *mname) +{ + IndexObjType_t *indObj = findIndexSpace (mname); + if (indObj) + indObj->name[0] = 0; +} + +IndexObjType_t * +DbeSession::getIndexSpace (int index) +{ + return ((index < 0) || (index >= VecSize (dyn_indxobj))) ? NULL : dyn_indxobj->get (index); +} + +IndexObjType_t * +DbeSession::findIndexSpace (const char *mname) +{ + return getIndexSpace (findIndexSpaceByName (mname)); +} + +void +DbeSession::get_filter_keywords (Vector<void*> *res) +{ + Vector <char*> *kwCategory = (Vector<char*>*) res->fetch (0); + Vector <char*> *kwCategoryI18N = (Vector<char*>*) res->fetch (1); + Vector <char*> *kwDataType = (Vector<char*>*) res->fetch (2); + Vector <char*> *kwKeyword = (Vector<char*>*) res->fetch (3); + Vector <char*> *kwFormula = (Vector<char*>*) res->fetch (4); + Vector <char*> *kwDescription = (Vector<char*>*) res->fetch (5); + Vector <void*> *kwEnumDescs = (Vector<void*>*) res->fetch (6); + + char *vtypeNames[] = VTYPE_TYPE_NAMES; + for (long i = 0, sz = userLabels ? userLabels->size () : 0; i < sz; i++) + { + UserLabel *lbl = userLabels->fetch (i); + kwCategory->append (dbe_strdup (NTXT ("FK_LABEL"))); + kwCategoryI18N->append (dbe_strdup (GTXT ("Labels"))); + kwDataType->append (dbe_strdup (vtypeNames[TYPE_BOOL])); + kwKeyword->append (dbe_strdup (lbl->name)); + kwFormula->append (dbe_strdup (lbl->str_expr)); + kwDescription->append (dbe_strdup (lbl->comment)); + kwEnumDescs->append (NULL); + } + + for (long i = 0, sz = propNames ? propNames->size () : 0; i < sz; i++) + { + PropDescr *prop = propNames->fetch (i); + char *pname = prop ? prop->name : NULL; + if (pname == NULL || *pname == 0 || prop->flags & PRFLAG_NOSHOW) + continue; + int vtypeNum = prop->vtype; + if (vtypeNum < 0 || vtypeNum >= TYPE_LAST) + vtypeNum = TYPE_NONE; + kwCategory->append (dbe_strdup (NTXT ("FK_EVTPROP"))); //Event Property + kwCategoryI18N->append (dbe_strdup (GTXT ("Misc. Definitions"))); + kwDataType->append (dbe_strdup (vtypeNames[vtypeNum])); + kwKeyword->append (dbe_strdup (pname)); + kwFormula->append (NULL); + kwDescription->append (dbe_strdup (prop->uname)); + kwEnumDescs->append (NULL); + } + + for (long i = 0, sz = dyn_indxobj ? dyn_indxobj->size () : 0; i < sz; i++) + { + IndexObjType_t *obj = dyn_indxobj->get (i); + if (obj->memObj) + continue; + kwCategory->append (dbe_strdup (NTXT ("FK_IDXOBJ"))); + kwCategoryI18N->append (dbe_strdup (GTXT ("Index Object Definitions"))); + kwDataType->append (dbe_strdup (vtypeNames[TYPE_INT64])); + kwKeyword->append (dbe_strdup (obj->name)); + kwFormula->append (dbe_strdup (obj->index_expr_str)); + kwDescription->append (dbe_strdup (obj->i18n_name)); + kwEnumDescs->append (NULL); + } +} + +Histable * +DbeSession::findIndexObject (int idxtype, uint64_t idx) +{ + HashMap<uint64_t, Histable*> *iobjs = idxobjs->fetch (idxtype); + return iobjs->get (idx); +} + +Histable * +DbeSession::createIndexObject (int idxtype, int64_t idx) +{ + HashMap<uint64_t, Histable*> *iobjs = idxobjs->fetch (idxtype); + + Histable *idxobj = iobjs->get (idx); + if (idxobj == NULL) + { + idxobj = new IndexObject (idxtype, idx); + if (idx == -1) + idxobj->set_name (dbe_strdup (GTXT ("<Unknown>"))); + iobjs->put (idx, idxobj); + } + + return idxobj; +} + +Histable * +DbeSession::createIndexObject (int idxtype, Histable *hobj) +{ + HashMap<uint64_t, Histable*> *iobjs = idxobjs->fetch (idxtype); + int64_t idx = hobj ? hobj->id : -1; + Histable *idxobj = iobjs->get (idx); + if (idxobj == NULL) + { + idxobj = new IndexObject (idxtype, hobj); + if (idx == -1) + idxobj->set_name (dbe_strdup (GTXT ("<Unknown>"))); + iobjs->put (idx, idxobj); + } + + return idxobj; +} + +Histable * +DbeSession::findObjectById (Histable::Type type, int subtype, uint64_t id) +{ + switch (type) + { + case Histable::FUNCTION: + case Histable::MODULE: + case Histable::LOADOBJECT: + return ( id < (uint64_t) objs->size ()) ? objs->fetch ((int) id) : NULL; + case Histable::INDEXOBJ: + return findIndexObject (subtype, id); + // ignoring the following cases + case Histable::INSTR: + case Histable::LINE: + case Histable::EADDR: + case Histable::MEMOBJ: + case Histable::PAGE: + case Histable::DOBJECT: + case Histable::SOURCEFILE: + case Histable::IOACTFILE: + case Histable::IOACTVFD: + case Histable::IOCALLSTACK: + case Histable::HEAPCALLSTACK: + case Histable::OTHER: + case Histable::EXPERIMENT: + break; + } + return NULL; +} + +// return a vector of Functions that match the regular expression input string +Vector<JThread *> * +DbeSession::match_java_threads (char *ustr, int matchParent, + Vector<uint64_t> * &grids, + Vector<uint64_t> * &expids) +{ + if (ustr == NULL) + return NULL; + + char *str = dbe_sprintf (NTXT ("^%s$"), ustr); + regex_t regex_desc; + int rc = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + if (rc) // syntax error in parsing string + return NULL; + + // allocate the new vector + Vector<JThread *> *ret = new Vector<JThread*>; + grids = new Vector<uint64_t>; + expids = new Vector<uint64_t>; + + int index; + JThread *jthread; + int expid; + Experiment* exp; + Vec_loop (Experiment*, exps, expid, exp) + { + + Vec_loop (JThread*, exp->get_jthreads (), index, jthread) + { + const char * name; + if (matchParent) + name = jthread->parent_name; + else + name = jthread->group_name; + if (name == NULL) + name = ""; + if (!regexec (®ex_desc, name, 0, NULL, 0)) + { + // this one matches + ret->append (jthread); + grids->append (exp->groupId); + expids->append (exp->getUserExpId ()); + } + } + } + + regfree (®ex_desc); + return ret; +} + +// return a vector of Functions that match the regular expression input string +Vector<Function *> * +DbeSession::match_func_names (const char *ustr, Histable::NameFormat nfmt) +{ + if (ustr == NULL) + return NULL; + char *str = dbe_sprintf (NTXT ("^%s$"), ustr); + regex_t regex_desc; + int rc = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + if (rc) // syntax error in parsing string + return NULL; + + // allocate the new vector + Vector<Function *> *ret = new Vector<Function*>; + + int index; + Histable *obj; + Vec_loop (Histable*, objs, index, obj) + { + if (obj->get_type () == Histable::FUNCTION) + { + Function *func = (Function*) obj; + if (!regexec (®ex_desc, func->get_name (nfmt), 0, NULL, 0)) + // this one matches + ret->append (func); + } + } + regfree (®ex_desc); + return ret; +} + +// return a vector of Functions that match the regular expression input string +Vector<FileData *> * +DbeSession::match_file_names (char *ustr, Histable::NameFormat nfmt) +{ + if (ustr == NULL) + return NULL; + char *str = dbe_sprintf (NTXT ("^%s$"), ustr); + regex_t regex_desc; + int rc = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + if (rc) // syntax error in parsing string + return NULL; + + // allocate the new vector + Vector<FileData *> *ret = new Vector<FileData*>; + int numExps = nexps (); + DefaultMap<int64_t, FileData*>* fDataMap; + Vector<FileData *> *fDataObjs; + FileData *fData; + int size; + for (int i = 0; i < numExps; i++) + { + Experiment *exp = get_exp (i); + fDataMap = exp->getFDataMap (); + fDataObjs = fDataMap->values (); + size = fDataObjs->size (); + for (int j = 0; j < size; j++) + { + fData = fDataObjs->fetch (j); + if (fData + && !regexec (®ex_desc, fData->get_raw_name (nfmt), 0, NULL, 0)) + // this one matches + ret->append (fData); + } + } + regfree (®ex_desc); + return ret; +} + +// return a vector of DataObjects that match the regular expression input string +Vector<DataObject *> * +DbeSession::match_dobj_names (char *ustr) +{ + if (ustr == NULL) + return NULL; + char *str = dbe_sprintf (NTXT ("^%s$"), ustr); + regex_t regex_desc; + int rc = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + if (rc) // syntax error in parsing string + return NULL; + + // allocate the new vector + Vector<DataObject *> *ret = new Vector<DataObject*>; + int index; + DataObject *ditem; + Vec_loop (DataObject*, dobjs, index, ditem) + { + // does this one match + if (!regexec (®ex_desc, ditem->get_name (), 0, NULL, 0)) + // this one matches + ret->append (ditem); + } + regfree (®ex_desc); + return ret; +} + +void +DbeSession::dump (char *msg, Vector<BaseMetric*> *mlist) +{ + if (msg) + fprintf (stderr, "%s\n", msg); + int sz = mlist ? mlist->size () : -1; + for (int i = 0; i < sz; i++) + { + BaseMetric *m = mlist->fetch (i); + char *s = m->dump (); + fprintf (stderr, "%2d %s\n", i, s); + free (s); + } + fprintf (stderr, "======END of mlist[%d] =========\n", sz); +} + +void +DbeSession::dump (char *msg, Vector<Metric*> *mlist) +{ + if (msg) + fprintf (stderr, "%s\n", msg); + int sz = mlist ? mlist->size () : -1; + for (int i = 0; i < sz; i++) + { + Metric *m = mlist->fetch (i); + char *s = m->dump (); + fprintf (stderr, "%2d %s\n", i, s); + free (s); + } + fprintf (stderr, "======END of mlist[%d] =========\n", sz); +} diff --git a/gprofng/src/DbeSession.h b/gprofng/src/DbeSession.h new file mode 100644 index 0000000..0a5c01c --- /dev/null +++ b/gprofng/src/DbeSession.h @@ -0,0 +1,481 @@ +/* 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. */ + +/* The DbeSession class is instantiated by a DbeApplication, and contains + * all the data referring to a set of loaded experiments + * + * It manages a set of tables for the Experiments, for the LoadObjects + * referenced in them, and for the other objects representing the + * elements in the program hierarchy that can have metrics associated + * with them. It also has a master list of all the metrics available + * from all the loaded experiments. + * + * It gets an instance of the Settings class, instantiated as a copy + * of the one in the DbeApplication that instantiated the DbeSession. + * + * In addition, it manages a vector of DbeView's (q.v.); each DbeView + * represents a window into the DbeSession, and has its own set of + * Settings, and FilterSets for the Experiments, and is the access point + * for all processed data. + */ + +#ifndef _DBESESSION_H +#define _DBESESSION_H + + +#include <stdio.h> +#include "dbe_structs.h" +#include "vec.h" +#include "Hist_data.h" +#include "Histable.h" +#include "BaseMetric.h" +#include "BaseMetricTreeNode.h" +#include "MemorySpace.h" +#include "hwcentry.h" +#include "dbe_types.h" +#include "Settings.h" +#include "HashMap.h" +#include "Table.h" +#include "Experiment.h" + +class DbeSession; +class Experiment; +class Expression; +class ExpGroup; +class Function; +class JMethod; +class Histable; +class DbeView; +class Module; +class LoadObject; +class DataObject; +class SourceFile; +class Settings; +class StringBuilder; +class UserLabel; +class DbeFile; +class DbeJarFile; +class FileData; +class HeapData; +template <typename ITEM> class DbeSyncMap; +template <class ITEM> class Vector; + +struct DispTab; +struct List; +struct Countable; +class IndexObjType_t; + +typedef struct +{ + char *path; + Experiment *exp; + DbeSession *ds; + bool read_ahead; +} exp_ctx; + +class DbeSession +{ +public: + DbeSession (Settings *_settings, bool _ipc_mode, bool _rdt_mode); + ~DbeSession (); + + void reset (); + void reset_data (); + + void + set_interactive (bool _interactive) + { + interactive = _interactive; + } + + bool + is_interactive () + { + return interactive; + } + + bool is_datamode_available (); + bool is_leaklist_available (); + bool is_heapdata_available (); + bool is_iodata_available (); + bool is_racelist_available (); + bool is_deadlocklist_available (); + bool is_timeline_available (); + bool is_ifreq_available (); + bool is_omp_available (); + bool has_java (); + bool has_ompavail (); + + // XXX get_clock should be removed, to support cpus with different clocks + // XXX means reworking time-convertible HWC code + int get_clock (int id); + + // Access functions for DbeView's + int createView (); + int createView (int index, int cloneindex); + DbeView *getView (int index); + void dropView (int index); + + // Access functions controlling the experiment list + Vector<char*> *get_group_or_expt (char *path); // load an experiment or group + + void open_experiment (Experiment *exp, char *path); + Experiment *get_exp (int exp_ind); + Vector<Vector<char*>*> *getExperimensGroups (); + char *setExperimentsGroups (Vector<Vector<char*>*> *groups); + char *drop_experiment (int exp_ind); + int find_experiment (char *path); + + int + nexps () + { + return exps->size (); + } + int ngoodexps (); + + // Access functions controlling the DataObject list + DataObject *createDataObject (); + DataObject *createDataObject (DataObject *d, DataObject *p = NULL); + DataObject *createMasterDataObject (DataObject *); + Vector<DataObject*> *get_dobj_elements (DataObject *); + + DataObject * + get_Total_DataObject () + { + return d_total; + }; + + DataObject * + get_Unknown_DataObject () + { + return d_unknown; + }; + + DataObject * + get_Scalars_DataObject () + { + return d_scalars; + }; + + DataObject *find_dobj_by_name (char *dobj_name); + DataObject *find_dobj_match (DataObject *dobj); + DataObject *find_dobj_master (DataObject *dobj); + + int + ndobjs () + { + return dobjs->size (); + } + + // check if no -xhwcprof should be ignored + bool + check_ignore_no_xhwcprof () + { + return settings->get_ignore_no_xhwcprof (); + }; + + // check if FS warning should be comment, or real warning + bool + check_ignore_fs_warn () + { + return settings->get_ignore_fs_warn (); + }; + + // Access functions controlling the LoadObject list + DbeSyncMap<LoadObject> *loadObjMap; + void append (LoadObject *lobj); + LoadObject *createLoadObject (const char *nm, int64_t cksum = 0); + LoadObject *createLoadObject (const char *nm, const char *runTimePath, DbeFile *df); + + Vector<LoadObject *> * + get_LoadObjects () + { + return lobjs; + }; + + void dobj_updateHT (DataObject *dobj); + LoadObject *get_Total_LoadObject (); + Vector<LoadObject*> *get_text_segments (); + LoadObject *get_Unknown_LoadObject (); + LoadObject *find_lobj_by_name (const char *lobj_name, int64_t cksum = 0); + + // Access functions controlling the Tab list + Vector<DispTab*> * + get_TabList () + { + return settings->get_TabList (); + }; + + Vector<bool> * + get_MemTabList () + { + return settings->get_MemTabState (); + }; + + void mobj_define (MemObjType_t *); + + // Access functions controlling metrics + BaseMetric *find_base_reg_metric (char *mcmd); + Vector<BaseMetric*> *get_base_reg_metrics (); // excludes comparison (expr) variants + + Vector<BaseMetric*> * + get_all_reg_metrics () + { + return reg_metrics; // includes comparison (expr) variants + }; + + BaseMetricTreeNode *get_reg_metrics_tree (); + BaseMetric *register_metric_expr (BaseMetric::Type type, char *aux, char *expr_spec); + BaseMetric *register_metric (BaseMetric::Type type); + BaseMetric *register_metric (char *name, char *username, char *_def); + BaseMetric *register_metric (Hwcentry *ctr, const char* cmdname, const char* username); + void drop_metric (BaseMetric *); + Module *createModule (LoadObject *lo, const char *nm); + Module *createUnknownModule (LoadObject *lo); + Module *createClassFile (char *className); + DbeFile *getDbeFile (char *filename, int filetype); + SourceFile *get_Unknown_Source (); + SourceFile *createSourceFile (const char *path); + Histable *createHistObject (Histable::Type); + Function *createFunction (); + Function *create_hide_function (LoadObject *lo); + Function *get_Total_Function (); + Function *get_Unknown_Function (); + Function *get_JUnknown_Function (); + Function *get_jvm_Function (); + LoadObject *get_OMP_LoadObject (); + Function *get_OMP_Function (int); + JMethod *createJMethod (); + Histable *createIndexObject (int idxtype, int64_t idx); + Histable *createIndexObject (int idxtype, Histable *hobj); + + enum SpecialFunction + { + TruncatedStackFunc, + FailedUnwindFunc, + LastSpecialFunction + }; + Function *getSpecialFunction (SpecialFunction); + + Histable * + findObjectById (uint64_t _id) + { + long id = (long) _id; + return (id >= 0 && id < objs->size ()) ? objs->fetch (id) : NULL; + } + + Histable *findObjectById (Histable::Type type, int subtype, uint64_t id); + + // Other access functions + bool find_obj (FILE *dis_file, FILE *inp_file, Histable *&obj, char *name, + const char *sel, Histable::Type type, bool xdefault); + int ask_which (FILE *dis_file, FILE *inp_file, Vector<Histable*> *list, char *name); + LoadObject *map_NametoLoadObject (char *name, Vector<Histable*> *, int which); + Module *map_NametoModule (char *name, Vector<Histable*> *, int which); + Function *map_NametoFunction (char *, Vector<Histable*> *, const char *); + DataObject *map_NametoDataObject (char *name, Vector<Histable*> *, int which); + bool match_FName (char *name, Function *func); + + // Functions to convert a string to all matching Functions/DataObjects + Vector<Function *> *match_func_names (const char *ustr, Histable::NameFormat nfmt); + Vector<DataObject *> *match_dobj_names (char *); + + // Functions to convert a string to all matching JThreads + Vector<JThread*> *match_java_threads (char *ustr, int matchParent, + Vector<uint64_t> * &grids, + Vector<uint64_t> * &expids); + // Function to convert a string to all matching File names + Vector<FileData *> *match_file_names (char *ustr, Histable::NameFormat nfmt); + + // Access functions concerning the search path + Vector<char*> * + get_search_path () + { + return search_path; + } + + Vector<DbeFile*>*get_classpath (); + void set_search_path (Vector<char*> *path, bool reset); + void set_search_path (char *lpath, bool reset); + bool add_classpath (char *path); + bool add_path (char *path); + void set_pathmaps (Vector<pathmap_t*> *newPathMap); + Vector<pathmap_t*> *get_pathmaps (); + + // functions to aid debugging + void dump_stacks (FILE *); + void dump_dataobjects (FILE *); + void dump_segments (FILE *); + void dump_map (FILE *); + + // Find dynamic property by name + int registerPropertyName (const char *name); + int getPropIdByName (const char *name); + char* getPropName (int propId); + char* getPropUName (int propId); + + Vector<UserLabel*> *userLabels; // List of er_labels + UserLabel *findUserLabel (char *name); + DbeJarFile *get_JarFile (const char *name); + void append (UserLabel *lbl); + void append (SourceFile *sf); + void append (Experiment *exp); + void append (Hwcentry *exp); + void set_need_refind (); + + // Find user defined object by name + Expression *findObjDefByName (char *); + void get_filter_keywords (Vector<void*> *res); + + // Get the Settings class object + Settings * + get_settings () + { + return settings; + } + + // static members, used to define or fetch the various IndexSpaces + Vector<void*> *getIndxObjDescriptions (void); + Vector<void*> *getCustomIndxObjects (void); + char *indxobj_define (const char *, char *, const char *, char *, char *); + char *getIndexSpaceName (int index); + char *getIndexSpaceDescr (int index); + Expression *getIndexSpaceExpr (int index); + char *getIndexSpaceExprStr (int index); + int findIndexSpaceByName (const char *mname); + void removeIndexSpaceByName (const char *mname); + IndexObjType_t *getIndexSpace (int index); + IndexObjType_t *findIndexSpace (const char *mname); + Expression *ql_parse (const char *expr_spec); + BaseMetric *find_metric (BaseMetric::Type type, const char *cmd, const char *expr_spec = NULL); + static void dump (char *msg, Vector<Metric*> *mlist); + static void dump (char *msg, Vector<BaseMetric*> *mlist); + static Platform_t platform; // Sparc, Intel + Vector<ExpGroup *> *expGroups; + HashMap<char*, LoadObject *> *comp_lobjs; // list of comparable LoadObjects + HashMap<char*, DbeLine *> *comp_dbelines; // list of comparable DbeLines + HashMap<char*, SourceFile*>*comp_sources; // list of comparable SourceFiles + char *localized_SP_UNKNOWN_NAME; + + void + set_lib_visibility_used () + { + lib_visibility_used = true; + } + + bool + is_lib_visibility_used () + { + return lib_visibility_used; + } + + void unlink_tmp_files (); + char *get_tmp_file_name (const char *nm, bool for_java); + + Vector<char *> *tmp_files; + int status_ompavail; + int archive_mode; + bool ipc_mode; + bool rdt_mode; + + // data and methods concerning the machine model + // methods are in source file MachineModel.cc + Vector<char*> *list_mach_models (); // scan . and system lib directory for models + char *load_mach_model (char *); + + char * + get_mach_model () + { + return dbe_strdup (mach_model_loaded); + }; + Vector<SourceFile *> *get_sources (); + +private: + void init (); + void check_tab_avail (); + bool add_path (char *path, Vector<char*> *pathes); + Experiment *createExperiment (); + + // Divide the regular createExperiment into two parts - + // Part1 creates just the Experiment data structure + // Part2 updates related fields and vectors + Experiment *createExperimentPart1 (); + void createExperimentPart2 (Experiment *exp); + + Histable *findIndexObject (int idxtype, uint64_t idx); + void append_mesgs (StringBuilder *sb, char *path, Experiment *exp); + static void insert_metric (BaseMetric *mtr, Vector<BaseMetric*> *mlist); + void update_metric_tree (BaseMetric *m); + + char *find_mach_model (char *); // fine machine model file by name + Vector<Experiment*> *exps; // Master list of experiments + Vector<Histable*> *objs; // Master list of Functions,Modules,Segments + Vector<DataObject*> *dobjs; // Master list of DataObjects + Vector<LoadObject*> *lobjs; // Auxiliary list of LoadObjects + Vector<Hwcentry*> *hwcentries; + Vector<HashMap<uint64_t, Histable*>*> *idxobjs; // Master list of IndexObjects + HashMap<char*, SourceFile*> *sourcesMap; // list of Source which were not archived + Vector<SourceFile*> *sources; // list of SourceFiles + Map<const char*, DbeJarFile*>*dbeJarFiles; + Vector<Countable*> *metrics; + Vector<BaseMetric*> *reg_metrics; // Master list of BaseMetrics + BaseMetricTreeNode* reg_metrics_tree; // Hierarchy of BaseMetrics + Vector<char*> *search_path; + Vector<char*> *classpath; + Vector<DbeFile*> *classpath_df; + Map<const char*, DbeFile*>*dbeFiles; + Vector<DbeView*> *views; // Master list of DbeViews + bool interactive; // interactive mode + bool lib_visibility_used; + LoadObject *lo_total; // Total LoadObject + Function *f_total; // Total function + LoadObject *lo_unknown; // Unknown LoadObject + Function *f_unknown; // Unknown function + SourceFile *sf_unknown; // Unknown source file + Function *f_jvm; // pseudo-function <JVM-System> + Vector<Function*> *f_special; // pseudo-functions + Function *j_unknown; // pseudo-function <no Java callstack> + LoadObject *lo_omp; // OMP LoadObject (libmtsk) + Vector<Function*> *omp_functions; // OMP-overhead, etc. + DataObject *d_unknown; // Unknown dataobject + DataObject *d_scalars; // Scalars dataobject + DataObject *d_total; // Total dataobject + List **dnameHTable; // DataObject name hash table + Settings *settings; // setting/defaults structure + Vector<IndexObjType_t*> *dyn_indxobj; // Index Object definitions + int dyn_indxobj_indx; + int dyn_indxobj_indx_fixed; + + void propNames_name_store (int propId, const char *propName); + void propNames_name_store (int propId, const char *propName, + const char *propUName, VType_type vType, int flags); + char* propNames_name_fetch (int propId); + Vector<PropDescr*> *propNames; + char *defExpName; + int user_exp_id_counter; + char *mach_model_loaded; + char *tmp_dir_name; +}; + +// For now, there's only one, so keep its pointer +extern DbeSession *dbeSession; + +extern Vector<char *> *split_str (char *str, char delimiter); +#endif /* _DBESESSION_H */ diff --git a/gprofng/src/DbeSyncMap.h b/gprofng/src/DbeSyncMap.h new file mode 100644 index 0000000..f13f7b4 --- /dev/null +++ b/gprofng/src/DbeSyncMap.h @@ -0,0 +1,224 @@ +/* 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. */ + +#ifndef _DbeSyncMap_h +#define _DbeSyncMap_h + +#include "DbeLock.h" +#include "DbeLinkList.h" +#include "vec.h" + +typedef unsigned long hash_ty; + +template <class ITEM> class DbeSyncMap : public DbeLock +{ +public: + DbeSyncMap (int _chunkSize = DefaultChunkSize); + virtual ~DbeSyncMap (); + void reset (); + ITEM *get (const char *nm, int64_t chksum); + ITEM *sync_create_item (const char *nm, int64_t chksum); + ITEM *get (const char *nm, const char *path, DbeFile *df); + ITEM *sync_create_item (const char *nm, const char *path, DbeFile *df); + virtual void dump (); + + Vector<ITEM *> * + values () + { + return items; + }; + +private: + hash_ty hash (const char *key); + + DbeLinkList<ITEM *> **chunk; + Vector<ITEM *> *items; + long chunkSize; + + enum + { + DefaultChunkSize = 1024 + }; +}; + +template <class ITEM> +DbeSyncMap<ITEM>::DbeSyncMap (int _chunkSize) +{ + chunkSize = _chunkSize; + chunk = new DbeLinkList<ITEM *> * [chunkSize]; + for (long i = 0; i < chunkSize; i++) + chunk[i] = NULL; + items = new Vector<ITEM *>(512); +} + +template <class ITEM> +DbeSyncMap<ITEM>::~DbeSyncMap () +{ + for (long i = 0; i < chunkSize; i++) + Destroy (chunk[i]); + delete[] chunk; + delete items; +} + +template <class ITEM> +void +DbeSyncMap<ITEM>::reset () +{ + for (long i = 0; i < chunkSize; i++) + { + Destroy (chunk[i]); + chunk[i] = NULL; + } + items->reset (); +} + +template <class ITEM> +ITEM * +DbeSyncMap<ITEM>::get (const char *nm, int64_t chksum) +{ + hash_ty h = hash (nm); + for (DbeLinkList<ITEM *> *dl = chunk[h]; dl; dl = dl->get_next ()) + { + ITEM *item = dl->get_item (); + if (item->compare (nm, chksum)) + return item; + } + return NULL; +} + +template <class ITEM> +hash_ty +DbeSyncMap<ITEM>::hash (const char *key) +{ + return (hash_ty) (crc64 (key, strlen (key)) % chunkSize); +} + +template <class ITEM> +ITEM * +DbeSyncMap<ITEM>::sync_create_item (const char *nm, int64_t chksum) +{ + hash_ty h = hash (nm); + for (DbeLinkList<ITEM *> *dl = chunk[h]; dl; dl = dl->get_next ()) + { + ITEM *item = dl->get_item (); + if (item->compare (nm, chksum)) + return item; + } + aquireLock (); + for (DbeLinkList<ITEM *> *dl = chunk[h]; dl; dl = dl->get_next ()) + { + ITEM *item = dl->get_item (); + if (item->compare (nm, chksum)) + { + releaseLock (); + return item; + } + } + ITEM *item = ITEM::create_item (nm, chksum); + DbeLinkList<ITEM *> *dl = new DbeLinkList<ITEM *>(item); + dl->set_next (chunk[h]); + chunk[h] = dl; + items->append (item); + releaseLock (); + return item; +} + +template <class ITEM> +ITEM * +DbeSyncMap<ITEM>::get (const char *nm, const char *path, DbeFile *df) +{ + int mask = 1 + (path != NULL ? 2 : 0) + (df != NULL ? 4 : 0); + hash_ty h = hash (nm); + for (DbeLinkList<ITEM *> *dl = chunk[h]; dl; dl = dl->get_next ()) + { + ITEM *item = dl->get_item (); + if (mask == item->compare (nm, path, df)) + return item; + } + return NULL; +} + +template <class ITEM> +ITEM * +DbeSyncMap<ITEM>::sync_create_item (const char *nm, const char *path, DbeFile *df) +{ + int mask = CMP_PATH; + if (path != NULL) + mask += CMP_RUNTIMEPATH; + if (df != NULL) + mask += CMP_CHKSUM; + hash_ty h = hash (nm); + for (DbeLinkList<ITEM *> *dl = chunk[h]; dl; dl = dl->get_next ()) + { + ITEM *item = dl->get_item (); + if (mask == item->compare (nm, path, df)) + return item; + } + aquireLock (); + for (DbeLinkList<ITEM *> *dl = chunk[h]; dl; dl = dl->get_next ()) + { + ITEM *item = dl->get_item (); + if (mask == item->compare (nm, path, df)) + { + releaseLock (); + return item; + } + } + ITEM *item = ITEM::create_item (nm, path, df); + DbeLinkList<ITEM *> *dl = new DbeLinkList<ITEM *>(item); + dl->set_next (chunk[h]); + chunk[h] = dl; + items->append (item); + releaseLock (); + return item; +} + +template <class ITEM> +void +DbeSyncMap<ITEM>::dump () +{ + Dprintf (1, NTXT ("\nDbeSyncMap::dump: vals=%ld\n"), (long) VecSize (items)); + int tot = 0; + int max_cnt = 0; + for (long i = 0; i < chunkSize; i++) + { + DbeLinkList<ITEM *> *lp = chunk[i]; + if (lp) + { + int cnt = 0; + for (DbeLinkList<ITEM *> *lp1 = lp; lp1; lp1 = lp1->get_next ()) + cnt++; + tot += cnt; + if (max_cnt < cnt) + max_cnt = cnt; + cnt = 1; + for (DbeLinkList<ITEM *> *lp1 = lp; lp1; lp1 = lp1->get_next ()) + { + ITEM *p = lp1->get_item (); + Dprintf (1, NTXT (" %2d %s\n"), cnt, p->get_name ()); + cnt++; + } + } + } + Dprintf (1, NTXT ("\nDbeSyncMap::dump: vals=%ld max_cnt=%d tot=%d\n"), + (long) VecSize (items), max_cnt, tot); +} + +#endif /* _DbeSyncMap_h */ diff --git a/gprofng/src/DbeThread.cc b/gprofng/src/DbeThread.cc new file mode 100644 index 0000000..7eb3cb7 --- /dev/null +++ b/gprofng/src/DbeThread.cc @@ -0,0 +1,224 @@ +/* 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#include "DbeThread.h" +#include "util.h" +#include "vec.h" + +static void +cleanup_free_mutex (void* arg) { + // pthread_mutex_t *p_mutex = (pthread_mutex_t *) arg; + // if (p_mutex) + // pthread_mutex_unlock (p_mutex); +} + +static void* +thread_pool_loop (void* arg) +{ + DbeThreadPool *thrp = (DbeThreadPool*) arg; + Dprintf (DEBUG_THREADS, "thread_pool_loop:%d starting thread=%llu\n", + __LINE__, (unsigned long long) pthread_self ()); + + /* set my cancel state to 'enabled', and cancel type to 'defered'. */ + pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED, NULL); + + /* set thread cleanup handler */ + pthread_cleanup_push (cleanup_free_mutex, (void*) & (thrp->p_mutex)); + for (;;) + { + DbeQueue *q = thrp->get_queue (); + if (q) + { /* a request is pending */ + Dprintf (DEBUG_THREADS, + "thread_pool_loop:%d thread=%llu queue=%d start\n", + __LINE__, (unsigned long long) pthread_self (), q->id); + q->func (q->arg); + Dprintf (DEBUG_THREADS, + "thread_pool_loop:%d thread=%llu queue=%d done\n", + __LINE__, (unsigned long long) pthread_self (), q->id); + delete q; + continue; + } + if (thrp->no_new_queues) + { + Dprintf (DEBUG_THREADS, "thread_pool_loop:%d exit thread=%llu\n", + __LINE__, (unsigned long long) pthread_self ()); + pthread_exit (NULL); + } + Dprintf (DEBUG_THREADS, + "thread_pool_loop:%d before pthread_cond_wait thread=%llu\n", + __LINE__, (unsigned long long) pthread_self ()); + pthread_mutex_lock (&thrp->p_mutex); + pthread_cond_wait (&thrp->p_cond_var, &thrp->p_mutex); + Dprintf (DEBUG_THREADS, + "thread_pool_loop:%d after pthread_cond_wait thread=%llu\n", + __LINE__, (unsigned long long) pthread_self ()); + pthread_mutex_unlock (&thrp->p_mutex); + } + + // never reached, but we must use it here. See `man pthread_cleanup_push` + pthread_cleanup_pop (0); +} + +DbeThreadPool::DbeThreadPool (int _max_threads) +{ + static const int DBE_NTHREADS_DEFAULT = 4; + char *s = getenv ("GPROFNG_DBE_NTHREADS"); + if (s) + { + max_threads = atoi (s); + if (max_threads < 0) + max_threads = 0; + if (_max_threads > 0 && max_threads < _max_threads) + max_threads = _max_threads; + } + else + { + max_threads = _max_threads; + if (max_threads < 0) + max_threads = DBE_NTHREADS_DEFAULT; + } + Dprintf (DEBUG_THREADS, "DbeThreadPool:%d max_threads %d ---> %d\n", + __LINE__, _max_threads, max_threads); + pthread_mutex_init (&p_mutex, NULL); + pthread_cond_init (&p_cond_var, NULL); + threads = new Vector <pthread_t>(max_threads); + queue = NULL; + last_queue = NULL; + no_new_queues = false; + queues_cnt = 0; + total_queues = 0; +} + +DbeThreadPool::~DbeThreadPool () +{ + delete threads; +} + +DbeQueue * +DbeThreadPool::get_queue () +{ + pthread_mutex_lock (&p_mutex); + DbeQueue *q = queue; + Dprintf (DEBUG_THREADS, + "get_queue:%d thr: %lld id=%d queues_cnt=%d threads_cnt=%d max_threads=%d\n", + __LINE__, (unsigned long long) pthread_self (), + q ? q->id : -1, queues_cnt, (int) threads->size (), max_threads); + if (q) + { + queue = q->next; + queues_cnt--; + } + pthread_mutex_unlock (&p_mutex); + return q; +} + +void +DbeThreadPool::put_queue (DbeQueue *q) +{ + if (max_threads == 0) + { + // nothing runs in parallel + q->id = ++total_queues; + Dprintf (DEBUG_THREADS, NTXT ("put_queue:%d thr=%lld max_threads=%d queue (%d) runs on the worked thread\n"), + __LINE__, (unsigned long long) pthread_self (), max_threads, q->id); + q->func (q->arg); + delete q; + return; + } + + pthread_mutex_lock (&p_mutex); + // nothing runs in parallel + q->id = ++total_queues; + Dprintf (DEBUG_THREADS, "put_queue:%d thr=%lld max_threads=%d queue (%d)\n", + __LINE__, (unsigned long long) pthread_self (), max_threads, q->id); + if (queue) + { + last_queue->next = q; + last_queue = q; + } + else + { + queue = q; + last_queue = q; + } + queues_cnt++; + Dprintf (DEBUG_THREADS, + "put_queue:%d id=%d queues_cnt=%d threads_cnt=%d max_threads=%d\n", + __LINE__, q->id, queues_cnt, (int) threads->size (), max_threads); + if (queues_cnt > threads->size () && threads->size () < max_threads) + { + pthread_t thr; + int r = pthread_create (&thr, NULL, thread_pool_loop, (void *) this); + Dprintf (DEBUG_THREADS, + "put_queue:%d pthread_create returns %d thr=%llu\n", + __LINE__, r, (unsigned long long) thr); + if (r) + fprintf (stderr, GTXT ("pthread_create failed. errnum=%d (%s)\n"), r, + STR (strerror (r))); + else + threads->append (thr); + } + pthread_cond_signal (&p_cond_var); + pthread_mutex_unlock (&p_mutex); +} + +void +DbeThreadPool::wait_queues () +{ + pthread_mutex_lock (&p_mutex); + no_new_queues = true; + pthread_mutex_unlock (&p_mutex); + pthread_cond_broadcast (&p_cond_var); + for (;;) // Run requests on the worked thread too + { + DbeQueue *q = get_queue (); + if (q == NULL) + break; + Dprintf (DEBUG_THREADS, "wait_queues:%d thread=%llu queue=%d start\n", + __LINE__, (unsigned long long) pthread_self (), q->id); + q->func (q->arg); + Dprintf (DEBUG_THREADS, "wait_queues:%d thread=%llu queue=%d done\n", + __LINE__, (unsigned long long) pthread_self (), q->id); + delete q; + } + for (int i = 0, sz = threads->size (); i < sz; i++) + { + void *retval; + pthread_join (threads->get (i), &retval); + } +} + +DbeQueue::DbeQueue (int (*_func) (void *arg), void *_arg) +{ + func = _func; + arg = _arg; + next = NULL; +} + +DbeQueue::~DbeQueue () { } diff --git a/gprofng/src/DbeThread.h b/gprofng/src/DbeThread.h new file mode 100644 index 0000000..8ee148b --- /dev/null +++ b/gprofng/src/DbeThread.h @@ -0,0 +1,61 @@ +/* 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. */ + +#ifndef _DBETHREAD_H +#define _DBETHREAD_H +#include <pthread.h> +#include "DbeLinkList.h" + +template <class ITEM> class Vector; + +class DbeQueue +{ +public: + DbeQueue (int (*_func)(void *arg), void *_arg); + ~DbeQueue (); + + int (*func) (void *arg); + void *arg; + int id; + DbeQueue *next; +}; + +class DbeThreadPool +{ +public: + DbeThreadPool (int _max_threads); + ~DbeThreadPool (); + DbeQueue *get_queue (); + void put_queue (DbeQueue *q); + void wait_queues (); + + pthread_mutex_t p_mutex; + pthread_cond_t p_cond_var; + volatile bool no_new_queues; +private: + Vector<pthread_t> *threads; + int max_threads; + DbeQueue *volatile queue; + DbeQueue *volatile last_queue; + volatile int queues_cnt; + volatile int total_queues; +}; + +#endif diff --git a/gprofng/src/DbeView.cc b/gprofng/src/DbeView.cc new file mode 100644 index 0000000..68613a5 --- /dev/null +++ b/gprofng/src/DbeView.cc @@ -0,0 +1,3126 @@ +/* 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 "util.h" +#include "Application.h" +#include "DbeSession.h" +#include "CallStack.h" +#include "Command.h" +#include "DataObject.h" +#include "Experiment.h" +#include "ExpGroup.h" +#include "FilterExp.h" +#include "FilterSet.h" +#include "Function.h" +#include "DbeView.h" +#include "PathTree.h" +#include "DataSpace.h" +#include "MemorySpace.h" +#include "IOActivity.h" +#include "HeapActivity.h" +#include "Print.h" +#include "MetricList.h" +#include "Module.h" +#include "Filter.h" +#include "LoadObject.h" +#include "dbe_types.h" +#include "StringBuilder.h" + +DbeView::DbeView (Application *_app, Settings *_settings, int _vindex) +{ + init (); + phaseIdx = 0; + settings = new Settings (_settings); + ptree = new PathTree (this); + dspace = new DataSpace (this); + memspaces = new Vector<MemorySpace*>; + iospace = new IOActivity (this); + heapspace = new HeapActivity (this); + filters = new Vector<FilterSet*>; + lo_expands = new Vector<enum LibExpand>; + cur_filter_str = NULL; + prev_filter_str = NULL; + cur_filter_expr = NULL; + filter_active = false; + noParFilter = false; + dataViews = new Vector<Vector<DataView*>*>; + names_src[0] = NULL; + names_src[1] = NULL; + names_src[2] = NULL; + names_dis[0] = NULL; + names_dis[1] = NULL; + names_dis[2] = NULL; + marks = new Vector<int>; + marks2dsrc = new Vector<int_pair_t>; + marks2dsrc_inc = new Vector<int_pair_t>; + marks2ddis = new Vector<int_pair_t>; + marks2ddis_inc = new Vector<int_pair_t>; + app = _app; + + // set the view's index + vindex = _vindex; + + // clear the precomputed data + func_data = NULL; + line_data = NULL; + pc_data = NULL; + src_data = NULL; + dis_data = NULL; + fitem_data = NULL; + callers = NULL; + callees = NULL; + dobj_data = NULL; + dlay_data = NULL; + iofile_data = NULL; + iovfd_data = NULL; + iocs_data = NULL; + heapcs_data = NULL; + + // and clear the selections + sel_obj = NULL; + sel_dobj = NULL; + sel_binctx = NULL; + func_scope = false; + lastSelInstr = NULL; + lastSelFunc = NULL; + + // Initialize index spaces + int sz = settings->get_IndxTabState ()->size (); + indxspaces = new Vector<PathTree*>(sz); + indx_data = new Vector<Hist_data*>(sz); + sel_idxobj = new Vector<Histable*>(sz); + for (int i = 0; i < sz; i++) + { + PathTree *is = new PathTree (this, i); + indxspaces->store (i, is); + indx_data->store (i, NULL); + sel_idxobj->store (i, NULL); + } + reset (); + + lobjectsNoJava = NULL; + + // set lo_expands for already existing LoadObjects + int idx; + LoadObject *lo; + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + Vec_loop (LoadObject*, lobjs, idx, lo) + { + lo_expands->store (lo->seg_idx, LIBEX_SHOW); + set_lo_expand (lo->seg_idx, LIBEX_SHOW); + } + delete lobjs; +} + +DbeView::DbeView (DbeView *dbev, int _vindex) +{ + init (); + phaseIdx = 0; + settings = new Settings (dbev->settings); + ptree = new PathTree (this); + dspace = new DataSpace (this); + iospace = new IOActivity (this); + heapspace = new HeapActivity (this); + memspaces = new Vector<MemorySpace*>; + filters = new Vector<FilterSet*>; + lo_expands = new Vector<enum LibExpand>; + cur_filter_str = NULL; + prev_filter_str = NULL; + cur_filter_expr = NULL; + noParFilter = false; + dataViews = new Vector<Vector<DataView*>*>; + names_src[0] = NULL; + names_src[1] = NULL; + names_src[2] = NULL; + names_dis[0] = NULL; + names_dis[1] = NULL; + names_dis[2] = NULL; + marks = new Vector<int>; + marks2dsrc = new Vector<int_pair_t>; + marks2dsrc_inc = new Vector<int_pair_t>; + marks2ddis = new Vector<int_pair_t>; + marks2ddis_inc = new Vector<int_pair_t>; + app = dbev->app; + + // set the view's index + vindex = _vindex; + + // clear the precomputed data + func_data = NULL; + line_data = NULL; + pc_data = NULL; + src_data = NULL; + dis_data = NULL; + fitem_data = NULL; + callers = NULL; + callees = NULL; + dobj_data = NULL; + dlay_data = NULL; + iofile_data = NULL; + iovfd_data = NULL; + iocs_data = NULL; + heapcs_data = NULL; + + // and clear the selections + sel_obj = NULL; + sel_dobj = NULL; + sel_binctx = NULL; + func_scope = false; + lastSelInstr = NULL; + lastSelFunc = NULL; + + // create the vector of IndexSpaces + int sz = dbev->indxspaces->size (); + indxspaces = new Vector<PathTree*>(sz); + indx_data = new Vector<Hist_data*>(sz); + sel_idxobj = new Vector<Histable*>(sz); + for (int i = 0; i < sz; i++) + { + PathTree *is = new PathTree (this, i); + indxspaces->store (i, is); + indx_data->store (i, NULL); + sel_idxobj->store (i, NULL); + } + reset (); + + // now copy the relevant information from the original view + for (int i = 0; i < dbeSession->nexps (); i++) + add_experiment (i, dbev->get_exp_enable (i)); + update_advanced_filter (); + delete lo_expands; + lo_expands = dbev->lo_expands->copy (); + lobjectsNoJava = NULL; +} + +DbeView::~DbeView () +{ + delete settings; + delete ptree; + delete dspace; + delete iospace; + delete heapspace; + Destroy (memspaces); + Destroy (filters); + delete lo_expands; + free (cur_filter_str); + free (prev_filter_str); + delete cur_filter_expr; + for (int exp_id = 0; exp_id < dataViews->size (); ++exp_id) + { + Vector<DataView*> *expDataViewList = dataViews->fetch (exp_id); + Destroy (expDataViewList); + } + delete dataViews; + delete reg_metrics; + metrics_lists->destroy (); + delete metrics_lists; + metrics_ref_lists->destroy (); + delete metrics_ref_lists; + delete derived_metrics; + delete marks; + delete marks2dsrc; + delete marks2dsrc_inc; + delete marks2ddis; + delete marks2ddis_inc; + + // Index spaces + indxspaces->destroy (); + delete indxspaces; + + indx_data->destroy (); + delete indx_data; + delete sel_idxobj; + delete lobjectsNoJava; +} + +void +DbeView::init () +{ + phaseIdx = 0; + reg_metrics = new Vector<BaseMetric*>; + metrics_lists = new Vector<MetricList*>; + metrics_ref_lists = new Vector<MetricList*>; + for (int i = 0; i <= MET_HEAP; i++) + { + metrics_lists->append (NULL); + metrics_ref_lists->append (NULL); + } + derived_metrics = new DerivedMetrics; + derived_metrics->add_definition (GTXT ("CPI"), GTXT ("Cycles Per Instruction"), GTXT ("cycles/insts")); + derived_metrics->add_definition (GTXT ("IPC"), GTXT ("Instructions Per Cycle"), GTXT ("insts/cycles")); + derived_metrics->add_definition (GTXT ("K_CPI"), GTXT ("Kernel Cycles Per Instruction"), GTXT ("K_cycles/K_insts")); + derived_metrics->add_definition (GTXT ("K_IPC"), GTXT ("Kernel Instructions Per Cycle"), GTXT ("K_insts/K_cycles")); +} + +bool +DbeView::set_libexpand (char *liblist, enum LibExpand flag) +{ + bool changed = settings->set_libexpand (liblist, flag, false); + // Show/hide performance optimization: + // No need to call update_lo_expand for every library because dbev->set_libexpand() + // is called from a loop in Dbe.cc SetLoadObjectState for every load object. + // It is sufficient to call update_lo_expand() just once at the end of the loop. + // At all other places such as er_print.cc which calls specific set_libexpand() + // explicitly call update_lo_expands(); + return changed; +} + +bool +DbeView::set_libdefaults () +{ + bool changed = settings->set_libdefaults (); + if (changed == true) + update_lo_expands (); + return changed; +} + +void +DbeView::update_lo_expands () +{ + int index; + LoadObject *lo; + + // search all load objects + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + Vec_loop (LoadObject*, lobjs, index, lo) + { + // now search the settings list for this one + enum LibExpand flag = settings->get_lo_setting (lo->get_pathname ()); + set_lo_expand (lo->seg_idx, flag); + } + delete lobjs; +} + +enum LibExpand +DbeView::get_lo_expand (int idx) +{ + if (idx < lo_expands->size ()) + return lo_expands->get (idx); + return LIBEX_SHOW; +} + +bool +DbeView::set_lo_expand (int idx, enum LibExpand flag) +{ + // LIBRARY_VISIBILITY + if (flag == LIBEX_HIDE) + { + resetShowAll (); + dbeSession->set_lib_visibility_used (); + } + // if no change + if (idx < lo_expands->size () && flag == get_lo_expand (idx)) + return false; + setShowHideChanged (); // this is necessary if called from er_print + + // change the flag + lo_expands->store (idx, flag); + + // and reset the data + fflush (stderr); + purge_events (); + reset_data (true); + return true; +} + +void +DbeView::reset () +{ + phaseIdx++; + + // reset all the per-experiment arrays + filters->destroy (); + lo_expands->reset (); + free (cur_filter_str); + cur_filter_str = NULL; + free (prev_filter_str); + prev_filter_str = NULL; + delete cur_filter_expr; + cur_filter_expr = NULL; + noParFilter = false; + for (int exp_id = 0; exp_id < dataViews->size (); ++exp_id) + { + Vector<DataView*> *expDataViewList = dataViews->fetch (exp_id); + if (expDataViewList) + expDataViewList->destroy (); + } + dataViews->destroy (); + reset_metrics (); + + // now reset any cached data + reset_data (true); + ompDisMode = false; + showAll = true; + showHideChanged = false; + newViewMode = false; +} + +void +DbeView::reset_data (bool all) +{ + // clear the precomputed data + if (func_data != NULL) + { + delete func_data; + func_data = NULL; + } + if (line_data != NULL) + { + delete line_data; + line_data = NULL; + } + if (pc_data != NULL) + { + delete pc_data; + pc_data = NULL; + } + if (src_data != NULL) + { + delete src_data; + src_data = NULL; + } + if (dis_data != NULL) + { + delete dis_data; + dis_data = NULL; + } + if (fitem_data != NULL) + { + delete fitem_data; + fitem_data = NULL; + } + if (callers != NULL) + { + delete callers; + callers = NULL; + } + if (callees != NULL) + { + delete callees; + callees = NULL; + } + if (dobj_data != NULL) + { + delete dobj_data; + dobj_data = NULL; + } + if (dlay_data != NULL) + { + delete dlay_data; + dlay_data = NULL; + } + if (iofile_data != NULL) + { + delete iofile_data; + iofile_data = NULL; + } + if (iovfd_data != NULL) + { + delete iovfd_data; + iovfd_data = NULL; + } + if (iocs_data != NULL) + { + delete iocs_data; + iocs_data = NULL; + } + if (heapcs_data != NULL) + { + delete heapcs_data; + heapcs_data = NULL; + } + + // and reset the selections + if (all) + { + sel_obj = NULL; + sel_dobj = NULL; + lastSelInstr = NULL; + lastSelFunc = NULL; + // Set selected object <Total> if possible + Function * ft = dbeSession->get_Total_Function (); + set_sel_obj (ft); + } + sel_binctx = NULL; + + dspace->reset (); + iospace->reset (); + heapspace->reset (); + + // loop over MemorySpaces, resetting each one + for (long i = 0, sz = VecSize (memspaces); i < sz; i++) + { + MemorySpace *ms = memspaces->get (i); + ms->reset (); + } + + // loop over IndexSpaces, resetting cached data + indx_data->destroy (); + for (long i = 0, sz = VecSize (indxspaces); i < sz; i++) + { + indx_data->store (i, NULL); + sel_idxobj->store (i, NULL); + } +} + +Vector<BaseMetric*> * +DbeView::get_all_reg_metrics () +{ + Vector<BaseMetric*> *mlist = dbeSession->get_all_reg_metrics (); + return mlist; +} + +BaseMetric * +DbeView::register_metric_expr (BaseMetric::Type type, char *cmd, char *expr_spec) +{ + BaseMetric *bm = dbeSession->register_metric_expr (type, cmd, expr_spec); + return bm; +} + +Metric * +DbeView::get_compare_metric (Metric *mtr, int groupNum) +{ + if (groupNum == 0 || !mtr->comparable ()) + return new Metric (*mtr); + ExpGroup *gr = dbeSession->expGroups->get (groupNum - 1); + char buf[128]; + snprintf (buf, sizeof (buf), NTXT ("EXPGRID==%d"), gr->groupId); + BaseMetric *bm = register_metric_expr (mtr->get_type (), mtr->get_cmd (), buf); + Metric *m = new Metric (bm, mtr->get_subtype ()); + m->set_raw_visbits (mtr->get_visbits ()); + if (m->legend == NULL) + m->legend = dbe_strdup (get_basename (gr->name)); + return m; +} + +MetricList * +DbeView::get_metric_ref (MetricType mtype) +{ + if (metrics_ref_lists->fetch (MET_COMMON) == NULL) + { + Vector<BaseMetric*> *base_metrics = dbeSession->get_base_reg_metrics (); + metrics_ref_lists->store (MET_SRCDIS, new MetricList (base_metrics, MET_SRCDIS)); + metrics_ref_lists->store (MET_COMMON, new MetricList (base_metrics, MET_COMMON)); + metrics_ref_lists->store (MET_NORMAL, new MetricList (base_metrics, MET_NORMAL)); + metrics_ref_lists->store (MET_CALL, new MetricList (base_metrics, MET_CALL)); + metrics_ref_lists->store (MET_CALL_AGR, new MetricList (base_metrics, MET_CALL_AGR)); + metrics_ref_lists->store (MET_DATA, new MetricList (base_metrics, MET_DATA)); + metrics_ref_lists->store (MET_INDX, new MetricList (base_metrics, MET_INDX)); + metrics_ref_lists->store (MET_IO, new MetricList (base_metrics, MET_IO)); + metrics_ref_lists->store (MET_HEAP, new MetricList (base_metrics, MET_HEAP)); + delete base_metrics; + } + return metrics_ref_lists->fetch (mtype); +} + +// logically, the function list must be created first, and it +// will create the other two; +MetricList * +DbeView::get_metric_list (MetricType mtype) +{ + if (metrics_lists->fetch (MET_COMMON) == NULL) + { + Vector<BaseMetric*> *base_metrics = dbeSession->get_base_reg_metrics (); + metrics_lists->store (MET_SRCDIS, new MetricList (base_metrics, MET_SRCDIS)); + metrics_lists->store (MET_COMMON, new MetricList (base_metrics, MET_COMMON)); + metrics_lists->store (MET_NORMAL, new MetricList (base_metrics, MET_NORMAL)); + metrics_lists->store (MET_CALL, new MetricList (base_metrics, MET_CALL)); + metrics_lists->store (MET_CALL_AGR, new MetricList (base_metrics, MET_CALL_AGR)); + metrics_lists->store (MET_DATA, new MetricList (base_metrics, MET_DATA)); + metrics_lists->store (MET_INDX, new MetricList (base_metrics, MET_INDX)); + metrics_lists->store (MET_IO, new MetricList (base_metrics, MET_IO)); + metrics_lists->store (MET_HEAP, new MetricList (base_metrics, MET_HEAP)); + delete base_metrics; + + // set the defaults + if (settings->str_dmetrics == NULL) + settings->str_dmetrics = strdup (Command::DEFAULT_METRICS); + char *status = setMetrics (settings->str_dmetrics, true); + if (status != NULL) + { + fprintf (stderr, "XXX setMetrics(\"%s\") failed: %s\n", settings->str_dmetrics, status); + abort (); + } + + // set the default sort + setSort (settings->str_dsort, MET_NORMAL, true); + } + return metrics_lists->fetch (mtype); +} + +MetricList * +DbeView::get_metric_list (int dsptype, int subtype) +{ + MetricList *mlist; + switch (dsptype) + { + case DSP_DISASM: + case DSP_SOURCE: + case DSP_SOURCE_DISASM: + mlist = get_metric_list (MET_COMMON); + mlist = new MetricList (mlist); + if (subtype != 0) + { + for (long i = 0, sz = mlist->size (); i < sz; i++) + { + Metric *m = mlist->get (i); + if (m->comparable ()) + { + Metric *m1 = get_compare_metric (m, subtype); + mlist->put (i, m1); + delete m; + } + } + } + break; + default: + mlist = get_metric_list (MET_NORMAL); + mlist = new MetricList (mlist); + break; + } + return mlist; +} + +void +DbeView::reset_metrics () +{ + for (int i = 0, sz = metrics_lists->size (); i < sz; i++) + { + delete metrics_lists->fetch (i); + metrics_lists->store (i, NULL); + } + for (int i = 0, sz = metrics_ref_lists->size (); i < sz; i++) + { + delete metrics_ref_lists->fetch (i); + metrics_ref_lists->store (i, NULL); + } +} + +bool +DbeView::comparingExperiments () +{ + if (dbeSession->expGroups->size () <= 1) + return false; + return 0 != (settings->get_compare_mode () & (CMP_DELTA | CMP_ENABLE | CMP_RATIO)); +} + +void +DbeView::set_compare_mode (int mode) +{ + if (mode == get_compare_mode ()) + return; + settings->set_compare_mode (mode); + if (comparingExperiments ()) + { + Vector<BaseMetric*> *bm_list = dbeSession->get_base_reg_metrics (); + for (int i = 0, sz = bm_list->size (); i < sz; i++) + { + BaseMetric *m = bm_list->fetch (i); + if (m->get_expr_spec () || !m->comparable ()) + continue; + for (int i1 = 0, sz1 = dbeSession->expGroups->size (); i1 < sz1; i1++) + { + ExpGroup *gr = dbeSession->expGroups->fetch (i1); + char buf[128]; + snprintf (buf, sizeof (buf), NTXT ("EXPGRID==%d"), gr->groupId); + register_metric_expr (m->get_type (), m->get_cmd (), buf); + } + } + } + MetricList *mlist = get_metric_list (MET_NORMAL); + MetricList *gmlist = get_metric_list (MET_CALL); + MetricList *dmlist = get_metric_list (MET_DATA); + MetricList *imlist = get_metric_list (MET_INDX); + if (comparingExperiments ()) + { + add_compare_metrics (mlist); + add_compare_metrics (gmlist); + add_compare_metrics (dmlist); + add_compare_metrics (imlist); + } + else + { + remove_compare_metrics (mlist); + remove_compare_metrics (gmlist); + remove_compare_metrics (dmlist); + remove_compare_metrics (imlist); + } +} + +void +DbeView::ifreq (FILE *outfile) +{ + if (!dbeSession->is_ifreq_available ()) + { + fprintf (outfile, GTXT ("No instruction frequency data available\n")); + return; + } + for (int index = 0; index < filters->size (); index++) + { + Experiment *exp = dbeSession->get_exp (index); + if (exp->broken || !get_exp_enable (index) || !exp->ifreqavail) + continue; + + // this experiment has the data; print it + fprintf (outfile, + GTXT ("Instruction frequency data from experiment %s\n\n"), + exp->get_expt_name ()); + fprintf (outfile, NTXT ("%s"), pr_mesgs (exp->fetch_ifreq (), "", "")); + } +} + +// When adding multiple sub-experiments of an experiment, it is +// not necessary to do the following every-time. It is sufficient to call reset_metrics() +// and call get_metric_ref() and get_metric_list() in the end after all the sub-experiments +// have been added +void +DbeView::add_experiment_epilogue () +{ + bool flag_LIBEX_HIDE = false; + bool flag_ShowHideChanged = false; + Vector<LoadObject*> *lobjs = dbeSession->get_LoadObjects (); + for (long i = lo_expands->size (), sz = lobjs ? lobjs->size () : 0; i < sz; i++) + { + flag_ShowHideChanged = true; + LoadObject *lo = lobjs->get (i); + enum LibExpand flag = settings->get_lo_setting (lo->get_pathname ()); + if (flag == LIBEX_HIDE) + flag_LIBEX_HIDE = true; + lo_expands->store (lo->seg_idx, flag); + } + if (flag_LIBEX_HIDE) + { + resetShowAll (); + dbeSession->set_lib_visibility_used (); + } + if (flag_ShowHideChanged) + { + setShowHideChanged (); // this is necessary if called from er_print + purge_events (); + reset_data (true); + } + reset_metrics (); + (void) get_metric_ref (MET_NORMAL); + (void) get_metric_ref (MET_CALL); + (void) get_metric_ref (MET_CALL_AGR); + (void) get_metric_ref (MET_DATA); + (void) get_metric_ref (MET_INDX); + (void) get_metric_ref (MET_IO); + (void) get_metric_ref (MET_HEAP); + (void) get_metric_list (MET_NORMAL); + (void) get_metric_list (MET_CALL); + (void) get_metric_list (MET_CALL_AGR); + (void) get_metric_list (MET_DATA); + (void) get_metric_list (MET_INDX); + (void) get_metric_list (MET_IO); + (void) get_metric_list (MET_HEAP); +} + +// When adding multiple sub-experiments of an experiment, avoid invoking the steps in +// add_experiment_epilogue() every time and instead call it separately in the end +// after all sub-experiments have been added +void +DbeView::add_subexperiment (int index, bool enabled) +{ + // phaseIdx doesn't change, PathTree can handle adding + // new experiments without reset + + // Set up the FilterSet for the experiments + Experiment *exp = dbeSession->get_exp (index); + FilterSet *filterset = new FilterSet (this, exp); + filterset->set_enabled (enabled); + filters->store (index, filterset); + + assert (index == dataViews->size ()); + Vector<DataView*> *expDataViewList = new Vector<DataView*>; + for (int data_id = 0; data_id < DATA_LAST; ++data_id) + expDataViewList->append (NULL); //experiment data_id's are not known yet + dataViews->store (index, expDataViewList); +} + +void +DbeView::add_experiment (int index, bool enabled) +{ + // phaseIdx doesn't change, PathTree can handle adding + // new experiments without reset + + // delete any cached data + reset_data (true); + + // Set up the FilterSet for the experiments + Experiment *exp = dbeSession->get_exp (index); + FilterSet *filterset = new FilterSet (this, exp); + filterset->set_enabled (enabled); + filters->store (index, filterset); + + assert (index == dataViews->size ()); + Vector<DataView*> *expDataViewList = new Vector<DataView*>; + for (int data_id = 0; data_id < DATA_LAST; ++data_id) + expDataViewList->append (NULL); //experiment data_id's are not known yet + dataViews->store (index, expDataViewList); + + reset_metrics (); + (void) get_metric_ref (MET_NORMAL); + (void) get_metric_ref (MET_CALL); + (void) get_metric_ref (MET_CALL_AGR); + (void) get_metric_ref (MET_DATA); + (void) get_metric_ref (MET_INDX); + (void) get_metric_ref (MET_IO); + (void) get_metric_ref (MET_HEAP); + (void) get_metric_list (MET_NORMAL); + (void) get_metric_list (MET_CALL); + (void) get_metric_list (MET_CALL_AGR); + (void) get_metric_list (MET_DATA); + (void) get_metric_list (MET_INDX); + (void) get_metric_list (MET_IO); + (void) get_metric_list (MET_HEAP); +} + +void +DbeView::drop_experiment (int index) +{ + phaseIdx++; + filters->remove (index); + + // reset any cached data + reset_data (true); + + Vector<DataView*> *expDataViewList = dataViews->remove (index); + if (expDataViewList) + { + expDataViewList->destroy (); + delete expDataViewList; + } +} + +bool +DbeView::get_exp_enable (int n) +{ + return filters ? filters->fetch (n)->get_enabled () : true; +} + +void +DbeView::set_exp_enable (int n, bool e) +{ + FilterSet *fs = filters->fetch (n); + if (fs->get_enabled () != e) + { + fs->set_enabled (e); + purge_events (n); + phaseIdx++; + } +} + +void +DbeView::reset_metric_list (MetricList *mlist, int cmp_mode) +{ + MetricType mtype = mlist->get_type (); + switch (mtype) + { + case MET_NORMAL: + case MET_COMMON: + delete metrics_lists->fetch (MET_COMMON); + metrics_lists->store (MET_COMMON, new MetricList (mlist)); + remove_compare_metrics (metrics_lists->fetch (MET_COMMON)); + break; + // ignoring the following cases (why?) + case MET_SRCDIS: + case MET_CALL: + case MET_DATA: + case MET_INDX: + case MET_CALL_AGR: + case MET_IO: + case MET_HEAP: + break; + } + + if (cmp_mode != -1) + { + settings->set_compare_mode (cmp_mode); + if (comparingExperiments ()) + add_compare_metrics (mlist); + } + + switch (mtype) + { + case MET_NORMAL: + delete metrics_lists->fetch (mtype); + metrics_lists->store (mtype, mlist); + // fall through to next case + case MET_COMMON: + metrics_lists->fetch (MET_SRCDIS)->set_metrics (mlist); + metrics_lists->fetch (MET_CALL)->set_metrics (mlist); + metrics_lists->fetch (MET_CALL_AGR)->set_metrics (mlist); + remove_compare_metrics (metrics_lists->fetch (MET_CALL_AGR)); + metrics_lists->fetch (MET_DATA)->set_metrics (mlist); + metrics_lists->fetch (MET_INDX)->set_metrics (mlist); + metrics_lists->fetch (MET_IO)->set_metrics (mlist); + metrics_lists->fetch (MET_HEAP)->set_metrics (mlist); + break; + case MET_CALL_AGR: + delete metrics_lists->fetch (MET_CALL_AGR); + metrics_lists->store (MET_CALL_AGR, mlist); + remove_compare_metrics (mlist); + break; + case MET_SRCDIS: + case MET_CALL: + case MET_DATA: + case MET_INDX: + case MET_IO: + case MET_HEAP: + delete metrics_lists->fetch (mtype); + metrics_lists->store (mtype, mlist); + break; + default: + abort (); + } + reset_data (false); +} + +void +DbeView::add_compare_metrics (MetricList *mlist) +{ + if (mlist == NULL || !comparingExperiments ()) + return; + int sort_ref_index = mlist->get_sort_ref_index (); + Vector<Metric*> *items = mlist->get_items (); + Vector<Metric*> *newItems = new Vector<Metric*>(); + int mode = get_compare_mode (); + int cmp_vbits = 0; + if ((mode & CMP_DELTA) != 0) + cmp_vbits = VAL_DELTA; + else if ((mode & CMP_RATIO) != 0) + cmp_vbits = VAL_RATIO; + for (long i = 0, sz = items->size (); i < sz; i++) + { + Metric *mtr = items->get (i); + if (sort_ref_index == i) + mlist->set_sort_ref_index (newItems->size ()); + int vbits = mtr->get_visbits () & ~(VAL_DELTA | VAL_RATIO); + mtr->set_raw_visbits (vbits); + if (!mtr->comparable ()) + { + newItems->append (mtr); + continue; + } + if (mtr->get_expr_spec ()) + { + if (strcmp (mtr->get_expr_spec (), NTXT ("EXPGRID==1")) != 0) + { + if ((cmp_vbits & VAL_RATIO) != 0) + // for ratios, make sure VAL_TIMEVAL is off and VAL_VALUE is on + mtr->set_raw_visbits ((vbits | VAL_VALUE | cmp_vbits) & ~VAL_TIMEVAL); + else + { + int ind = mlist->get_listorder (mtr->get_cmd (), mtr->get_subtype (), NTXT ("EXPGRID==1")); + if (ind >= 0) + // take VAL_VALUE and VAL_TIMEVAL from base experiment + mtr->set_raw_visbits (cmp_vbits + | (vbits & ~(VAL_VALUE | VAL_TIMEVAL)) + | (mlist->get (ind)->get_visbits () + & (VAL_VALUE | VAL_TIMEVAL))); + else + mtr->set_raw_visbits (cmp_vbits | vbits); + } + } + newItems->append (mtr); + continue; + } + for (long i1 = 0, sz1 = dbeSession->expGroups->size (); i1 < sz1; i1++) + { + Metric *m = get_compare_metric (mtr, i1 + 1); + switch (m->get_vtype ()) + { + case VT_LABEL: + case VT_ADDRESS: + case VT_OFFSET: + m->set_raw_visbits (vbits); + break; + default: + if (i1 == 0) + m->set_raw_visbits (vbits); + else if (cmp_vbits == VAL_RATIO + && ((vbits & (VAL_VALUE | VAL_TIMEVAL)) + == (VAL_VALUE | VAL_TIMEVAL))) + // make ratios for VAL_VALUE only + m->set_raw_visbits ((vbits | VAL_VALUE | cmp_vbits) & ~VAL_TIMEVAL); + else + m->set_raw_visbits (vbits | cmp_vbits); + break; + } + newItems->append (m); + } + } + items->reset (); + items->addAll (newItems); + delete newItems; + phaseIdx++; + reset_data (false); +} + +MetricList * +DbeView::get_compare_mlist (MetricList *met_list, int grInd) +{ + MetricList *mlist = new MetricList (met_list->get_type ()); + mlist->set_sort_ref_index (met_list->get_sort_ref_index ()); + mlist->set_sort_rev (met_list->get_sort_rev ()); + + Vector<Metric*> *items_old = met_list->get_items (); + for (int i = 0, sz = items_old->size (); i < sz; i++) + { + Metric *m = get_compare_metric (items_old->get (i), grInd + 1); + mlist->append (m); + } + return mlist; +} + +void +DbeView::remove_compare_metrics (MetricList *mlist) +{ + Vector<Metric*> *items = mlist->get_items (); + Vector<Metric*> *items_old = items->copy (); + items->reset (); + int sort_index = mlist->get_sort_ref_index (); + mlist->set_sort_ref_index (0); + for (int i = 0, sz = items_old->size (); i < sz; i++) + { + Metric *m = items_old->fetch (i); + if (m->get_expr_spec () == NULL) + { + // this is a 'non-compare' metric; add it + items->append (m); + if (sort_index == i) + mlist->set_sort_ref_index (items->size () - 1); + continue; + } + // is the 'non-compare' version of the metric already in the list? + int ind = mlist->get_listorder (m->get_cmd (), m->get_subtype ()); + if (ind == -1) + { + // not in the list; add it + BaseMetric *bm = dbeSession->find_metric (m->get_type (), m->get_cmd (), NULL); + Metric *new_met = new Metric (bm, m->get_subtype ()); + new_met->set_raw_visbits (m->get_visbits () & ~(CMP_DELTA | CMP_RATIO)); + items->append (new_met); + if (sort_index == i) + mlist->set_sort_ref_index (items->size () - 1); + } + delete m; + } + delete items_old; + reset_data (false); +} + +// setMetrics -- set the metric list according to specification +// The previous sort is preserved, if possible +// Otherwise, the default sort setting is used +// Returns NULL if OK, or an error string if not +//YXXX only MET_NORMAL appears to be used... code could be simplified +char * +DbeView::setMetrics (char *mspec, bool fromRcFile) +{ + char *ret; + MetricType mtype = MET_NORMAL; + // note that setting the default is done here, while all else is in MetricList + if (mspec == NULL) abort (); + if (strcasecmp (mspec, Command::DEFAULT_CMD) == 0) + { + mspec = settings->get_default_metrics (); + fromRcFile = true; + } + MetricList *mlist = get_metric_list (mtype); + mlist = new MetricList (mlist); + ret = mlist->set_metrics (mspec, fromRcFile, derived_metrics); + if (ret == NULL) + { + switch (mtype) + { + case MET_NORMAL: + case MET_COMMON: + delete metrics_lists->fetch (MET_COMMON); + metrics_lists->store (MET_COMMON, new MetricList (mlist)); + break; + // ignoring the following cases (why?) + case MET_SRCDIS: + case MET_CALL: + case MET_DATA: + case MET_INDX: + case MET_CALL_AGR: + case MET_IO: + case MET_HEAP: + break; + } + add_compare_metrics (mlist); + + //YXXX looks like cut/paste code here, see reset_metric_list() + switch (mtype) + { + case MET_NORMAL: + delete metrics_lists->fetch (mtype); + metrics_lists->store (mtype, mlist); + //YXXX is lack of break intentional? If so, add comment... + case MET_COMMON: + metrics_lists->fetch (MET_SRCDIS)->set_metrics (mlist); + metrics_lists->fetch (MET_CALL)->set_metrics (mlist); + metrics_lists->fetch (MET_CALL_AGR)->set_metrics (mlist); + remove_compare_metrics (metrics_lists->fetch (MET_CALL_AGR)); + metrics_lists->fetch (MET_DATA)->set_metrics (mlist); + metrics_lists->fetch (MET_INDX)->set_metrics (mlist); + metrics_lists->fetch (MET_IO)->set_metrics (mlist); + metrics_lists->fetch (MET_HEAP)->set_metrics (mlist); + break; + case MET_CALL_AGR: + delete metrics_lists->fetch (MET_CALL_AGR); + metrics_lists->store (MET_CALL_AGR, mlist); + remove_compare_metrics (mlist); + break; + case MET_SRCDIS: + case MET_CALL: + case MET_DATA: + case MET_INDX: + case MET_IO: + case MET_HEAP: + delete metrics_lists->fetch (mtype); + metrics_lists->store (mtype, mlist); + break; + default: + abort (); + } + reset_data (false); + } + else + delete mlist; + return ret; +} + + +// Set Sort by name (er_print) +char * +DbeView::setSort (char * sort_list, MetricType mtype, bool fromRcFile) +{ + MetricList *mlist = NULL; + + // note that setting the default is done here, while all else is in MetricList + if ((sort_list == NULL) || (strcmp (sort_list, Command::DEFAULT_CMD) == 0)) + { + if (settings->str_dsort == NULL) + settings->str_dsort = strdup (Command::DEFAULT_METRICS); + sort_list = settings->get_default_sort (); + } + mlist = get_metric_list (mtype); + + if (mlist == NULL) + abort (); + + // set the new sort + char *ret = mlist->set_sort (sort_list, fromRcFile); + if (ret != NULL) + return ret; + + // now resort all cached data + resortData (mtype); + return NULL; +} + +// Set sort from the visible index (Analyzer) +void +DbeView::setSort (int visindex, MetricType mtype, bool reverse) +{ + MetricList *mlist = get_metric_list (mtype); + Vector<Metric*> *items = mlist->get_items (); + if (visindex >= items->size ()) + return; + mlist->set_sort (visindex, reverse); + resortData (mtype); + if (mtype == MET_NORMAL) + { + int idx_cc = -1; + MetricList *mlist_cc = get_metric_list (MET_CALL); + Vector<Metric*> *items_cc = mlist_cc->get_items (); + for (int i = 0; i < items_cc->size (); i++) + { + char * name_cc = items_cc->fetch (i)->get_username (); + char * name_normal = items->fetch (visindex)->get_username (); + if (0 == strncmp (name_cc, name_normal, strlen (name_cc))) + { + idx_cc = i; + break; + } + } + if (idx_cc != -1) + { + mlist_cc->set_sort (idx_cc, reverse); + resortData (MET_CALL); + // Change a sort metric for MET_CALL_AGR + Metric *m = items_cc->fetch (idx_cc); + MetricList *cList = get_metric_list (MET_CALL_AGR); + Metric *m1 = cList->find_metric (m->get_cmd (), m->get_subtype ()); + if (m1) + cList->set_sort_metric (m1->get_cmd (), m1->get_subtype (), reverse); + } + } + if (mtype == MET_CALL) + { + int idx_norm = -1; + MetricList *mlist_norm = get_metric_list (MET_NORMAL); + Vector<Metric*> *items_norm = mlist_norm->get_items (); + for (int i = 0; i < items_norm->size (); i++) + { + char * name_norm = items_norm->fetch (i)->get_username (); + char * name_cc = items->fetch (visindex)->get_username (); + if (mlist_norm->get_sort_ref_index () == i + && 0 == strncmp (name_norm, name_cc, strlen (name_norm))) + { + idx_norm = i; + break; + } + } + if (idx_norm == -1) + { + for (int i = 0; i < items_norm->size (); i++) + { + char * name_norm = items_norm->fetch (i)->get_username (); + char * name_cc = items->fetch (visindex)->get_username (); + if (0 == strncmp (name_norm, name_cc, strlen (name_norm))) + { + idx_norm = i; + break; + } + } + } + if (idx_norm != -1) + { + mlist_norm->set_sort (idx_norm, reverse); + resortData (MET_NORMAL); + } + // Change a sort metric for MET_CALL_AGR + Metric *m = items->fetch (visindex); + MetricList *cList = get_metric_list (MET_CALL_AGR); + Metric *m1 = cList->find_metric (m->get_cmd (), m->get_subtype ()); + if (m1) + cList->set_sort_metric (m1->get_cmd (), m1->get_subtype (), reverse); + } +} + +void +DbeView::resortData (MetricType mtype) +{ + int idx; + Hist_data *data; + + MetricList *mlist = get_metric_list (mtype); + switch (mtype) + { + case MET_NORMAL: + if (func_data != NULL) + func_data->resort (mlist); + if (line_data != NULL) + line_data->resort (mlist); + if (pc_data != NULL) + pc_data->resort (mlist); + break; + case MET_CALL: + case MET_CALL_AGR: + if (fitem_data != NULL) + fitem_data->resort (mlist); + if (callers != NULL) + callers->resort (mlist); + if (callees != NULL) + callees->resort (mlist); + break; + case MET_DATA: + if (dobj_data != NULL) + dobj_data->resort (mlist); + if (dlay_data != NULL) + { + delete dlay_data; + dlay_data = NULL; + } + break; + case MET_INDX: + Vec_loop (Hist_data*, indx_data, idx, data) + { + if (data) + data->resort (mlist); + } + break; + case MET_IO: + if (iofile_data != NULL) + iofile_data->resort (mlist); + if (iovfd_data != NULL) + iovfd_data->resort (mlist); + if (iocs_data != NULL) + iocs_data->resort (mlist); + break; + case MET_HEAP: + if (heapcs_data != NULL) + heapcs_data->resort (mlist); + break; + case MET_COMMON: + case MET_SRCDIS: + break; + } +} + +// Get the sort metric name +char * +DbeView::getSort (MetricType mtype) +{ + MetricList *mlist = get_metric_list (mtype); + return mlist->get_sort_name (); +} + +// Get the sort command (to use for resetting) +char * +DbeView::getSortCmd (MetricType mtype) +{ + MetricList *mlist = get_metric_list (mtype); + return mlist->get_sort_cmd (); +} + +int +DbeView::get_sel_ind (Histable *selObj, int type, int subtype) +{ + Hist_data *data; + switch (type) + { + case DSP_FUNCTION: + data = func_data; + break; + case DSP_LINE: + data = line_data; + break; + case DSP_PC: + data = pc_data; + break; + case DSP_SOURCE: + case DSP_SOURCE_V2: + data = src_data; + break; + case DSP_DISASM: + case DSP_DISASM_V2: + data = dis_data; + break; + case DSP_DLAYOUT: + data = dlay_data; + break; + case DSP_DATAOBJ: + data = dobj_data; + break; + case DSP_IOACTIVITY: + data = iofile_data; + break; + case DSP_IOVFD: + data = iovfd_data; + break; + case DSP_IOCALLSTACK: + data = iocs_data; + break; + case DSP_HEAPCALLSTACK: + data = heapcs_data; + break; + case DSP_MEMOBJ: + case DSP_INDXOBJ: + data = get_indxobj_data (subtype); + break; + default: + data = NULL; + break; + } + if (data == NULL || data->get_status () != Hist_data::SUCCESS) + return -1; + Vector<Hist_data::HistItem*> *hi_data = data->get_hist_items (); + for (int i = 0, sz = hi_data->size (); i < sz; i++) + { + Hist_data::HistItem *hi = hi_data->fetch (i); + if (hi->obj == selObj) + return i; + } + return -1; +} + +MetricList * +DbeView::get_metric_list (MetricType mtype, bool compare, int gr_num) +{ + MetricList *mlist; + switch (mtype) + { + case MET_COMMON:// comparison mode, src & disasm views + if (gr_num == 0) + {// signifies same src file (or load obj) used by all groups + // show compare metrics in columns (not in separate source panels) + mlist = get_metric_list (MET_NORMAL); + break; + } + // once source panel per group; get metrics for this group + mlist = get_metric_list (mtype); + if (compare) + { + mlist = get_compare_mlist (mlist, gr_num - 1); + int mode = get_compare_mode (); + if ((mode & (CMP_DELTA | CMP_RATIO)) != 0) + { + for (long i = 0, sz = mlist->size (); i < sz; i++) + { + Metric *m = mlist->get (i); + char *expr_spec = m->get_expr_spec (); + if (expr_spec && (strcmp (expr_spec, NTXT ("EXPGRID==1")) != 0)) + { + int vbits = m->get_visbits () & ~(VAL_DELTA | VAL_RATIO); + if ((mode & CMP_RATIO) != 0) + vbits |= VAL_RATIO; + else if ((mode & CMP_DELTA) != 0) + vbits |= VAL_DELTA; + m->set_raw_visbits (vbits); + } + } + } + } + break; + default: + mlist = get_metric_list (mtype); + break; + } + return mlist; +} + +Hist_data * +DbeView::get_data (MetricList *mlist, Histable *selObj, int type, int subtype) +{ + Hist_data *data; + switch (type) + { + case DSP_FUNCTION: + delete func_data; + mlist = new MetricList (mlist); + func_data = get_hist_data (mlist, Histable::FUNCTION, subtype, Hist_data::ALL); + return func_data; + case DSP_LINE: + delete line_data; + mlist = new MetricList (mlist); + line_data = get_hist_data (mlist, Histable::LINE, subtype, Hist_data::ALL); + return line_data; + case DSP_PC: + delete pc_data; + mlist = new MetricList (mlist); + pc_data = get_hist_data (mlist, Histable::INSTR, subtype, Hist_data::ALL); + return pc_data; + case DSP_DATAOBJ: + delete dobj_data; + dobj_data = get_hist_data (mlist, Histable::DOBJECT, subtype, + Hist_data::ALL); + break; + case DSP_MEMOBJ: + return get_hist_data (mlist, Histable::MEMOBJ, subtype, Hist_data::ALL); + case DSP_INDXOBJ: + data = get_hist_data (mlist, Histable::INDEXOBJ, subtype, Hist_data::ALL); + indx_data->store (subtype, data); + return data; + case DSP_DLAYOUT: + delete dlay_data; + marks->reset (); + data = get_hist_data (mlist, Histable::DOBJECT, subtype, + Hist_data::LAYOUT); + // .. provides metric data for layout + dlay_data = get_data_space ()->get_layout_data (data, marks, + get_thresh_dis ()); + return dlay_data; + case DSP_CALLER: + delete callers; + callers = get_hist_data (mlist, Histable::FUNCTION, subtype, + Hist_data::CALLERS, selObj); + return callers; + case DSP_CALLEE: + delete callees; + callees = get_hist_data (mlist, Histable::FUNCTION, subtype, + Hist_data::CALLEES, selObj); + return callees; + case DSP_SELF: + // Center Function item + delete fitem_data; + fitem_data = get_hist_data (mlist, Histable::FUNCTION, subtype, + Hist_data::SELF, selObj); + return fitem_data; + case DSP_SOURCE_V2: + case DSP_DISASM_V2: + case DSP_SOURCE: + case DSP_DISASM: + { + // Source or disassembly + if (selObj == NULL) + { + error_msg = status_str (DBEVIEW_NO_SEL_OBJ); + return NULL; + } + Function *func = (Function *) selObj->convertto (Histable::FUNCTION); + if (func == NULL) + { + error_msg = dbe_strdup (GTXT ("Not a real function; no source or disassembly available.")); + return NULL; + } + if (func->flags & FUNC_FLAG_SIMULATED) + { + error_msg = dbe_strdup (GTXT ("Not a real function; no source or disassembly available.")); + return NULL; + } + if (func->get_name () == NULL) + { + error_msg = dbe_strdup (GTXT ("Source location not recorded in experiment")); + return NULL; + } + Module *module = func->module; + if (module == NULL || module->get_name () == NULL) + { + error_msg = dbe_strdup (GTXT ("Object name not recorded in experiment")); + return NULL; + } + marks->reset (); + SourceFile *srcContext = (SourceFile *) selObj->convertto (Histable::SOURCEFILE); + sel_binctx = func; + + if (func_data == NULL) + func_data = get_hist_data (mlist, Histable::FUNCTION, subtype, Hist_data::ALL); + + // for source and disassembly the name needs to be invisible, + // but that's handled in the module code + if (type == DSP_SOURCE || type == DSP_SOURCE_V2) + { + marks2dsrc->reset (); + marks2dsrc_inc->reset (); + delete src_data; + data = src_data = module->get_data (this, mlist, Histable::LINE, + func_data->get_totals ()->value, srcContext, func, + marks, get_thresh_src (), get_src_compcom (), + get_src_visible (), get_hex_visible (), + false, false, marks2dsrc, marks2dsrc_inc); + } + else + { /* type == DSP_DISASM */ + marks2ddis->reset (); + marks2ddis_inc->reset (); + delete dis_data; + data = dis_data = module->get_data (this, mlist, Histable::INSTR, + func_data->get_totals ()->value, srcContext, func, + marks, get_thresh_dis (), get_dis_compcom (), + get_src_visible (), get_hex_visible (), + get_func_scope (), false, marks2ddis, + marks2ddis_inc); + } + return data; + } + default: + abort (); + } + return NULL; +} + +Histable * +DbeView::get_compare_obj (Histable *obj) +{ + char *nm; + switch (obj->get_type ()) + { + case Histable::LINE: + nm = obj->get_name (); + if (nm == NULL) + break; + if (dbeSession->comp_dbelines == NULL) + dbeSession->comp_dbelines = new HashMap<char*, DbeLine*>; + return dbeSession->comp_dbelines->get (nm, (DbeLine*) obj); + case Histable::SOURCEFILE: + nm = obj->get_name (); + if (nm == NULL) + break; + nm = get_basename (nm); + if (dbeSession->comp_sources == NULL) + dbeSession->comp_sources = new HashMap<char*, SourceFile*>; + return dbeSession->comp_sources->get (nm, (SourceFile*) obj); + default: + return obj->get_compare_obj (); + } + return obj; +} + +// +// get_hist_data() creates a new Hist_data object; +// it's caller's responsibility to delete it. +Hist_data * +DbeView::get_hist_data (MetricList *mlist_orig, Histable::Type type, + int subtype, Hist_data::Mode mode, Histable *obj, + Histable *context, Vector<Histable*> *sel_objs, + PathTree::PtreeComputeOption flag) +{ + Vector<Histable*> *objs = NULL; + if (obj != NULL) + { + objs = new Vector<Histable*>(); + objs->append (obj); + } + Hist_data *res = get_hist_data (mlist_orig, type, subtype, mode, objs, context, sel_objs, flag); + delete objs; + return res; +} + +Hist_data * +DbeView::get_hist_data (MetricList *mlist_orig, Histable::Type type, + int subtype, Hist_data::Mode mode, + Vector<Histable*> *objs, + Histable *context, Vector<Histable*> *sel_objs, + PathTree::PtreeComputeOption flag) +{ + MetricList *mlist = new MetricList (mlist_orig); + /* + * mlist differs from mlist_orig in two ways: + * - extra metrics have been added as needed to compute derived metrics + * - extra metrics have been added as needed to compute time for HWC (time converted) metrics + * (We don't drop those extra metrics but we don't display they to user.) + * - visibility bits have been added for compare mode (e.g., VAL_DELTA or VAL_RATIO) + * (We want to preserve those visbits.) + */ + // loop over mlist to add missing dependencies for derived metrics + for (long i = 0, sz = mlist->get_items ()->size (); i < sz; i++) + { + Metric *m = mlist->get_items ()->fetch (i); + char *expr_spec = m->get_expr_spec (); + if (expr_spec && (strcmp (expr_spec, NTXT ("EXPGRID==1")) != 0)) + { + int ind = mlist->get_listorder (m->get_cmd (), m->get_subtype (), NTXT ("EXPGRID==1")); + if (ind < 0) + { + BaseMetric *bm1 = dbeSession->find_metric (m->get_type (), m->get_cmd (), NTXT ("EXPGRID==1")); + Metric *m1 = new Metric (bm1, m->get_subtype ()); + m1->set_dmetrics_visbits (VAL_VALUE); + mlist->append (m1); + } + } + } + + for (long i = 0, sz = mlist->get_items ()->size (); i < sz; i++) + { + Metric *m = mlist->get_items ()->fetch (i); + if (m->get_type () == BaseMetric::DERIVED) + { + Definition *def = m->get_definition (); + Vector<BaseMetric*> *dependencies = def->get_dependencies (); + long *map = def->get_map (); + for (long i1 = 0, sz1 = dependencies ? dependencies->size () : 0; i1 < sz1; i1++) + { + BaseMetric *bm = dependencies->fetch (i1); + int ind = mlist->get_listorder (bm->get_cmd (), m->get_subtype (), m->get_expr_spec ()); + if (ind < 0) + { + BaseMetric *bm1 = dbeSession->find_metric (bm->get_type (), bm->get_cmd (), m->get_expr_spec ()); + assert (bm1 != NULL); + Metric *m1 = new Metric (bm1, m->get_subtype ()); + m1->set_dmetrics_visbits (VAL_VALUE); + ind = mlist->size (); + mlist->append (m1); + } + map[i1] = ind; + } + } + else if (m->get_type () == BaseMetric::HWCNTR) + { + if (m->is_tvisible () && m->get_dependent_bm ()) + { + int ii = mlist->get_listorder (m->get_dependent_bm ()->get_cmd (), + m->get_subtype (), m->get_expr_spec ()); + if (ii < 0) + { + BaseMetric *bm1 = dbeSession->find_metric (m->get_type (), + m->get_dependent_bm ()->get_cmd (), + m->get_expr_spec ()); + assert (bm1 != NULL); + Metric *m1 = new Metric (bm1, m->get_subtype ()); + m1->set_dmetrics_visbits ((m->get_visbits () + & ~VAL_VALUE) | VAL_TIMEVAL); + mlist->append (m1); + } + } + } + } + + // compute Hist_data + Hist_data *data; + switch (type) + { + case Histable::INSTR: + case Histable::LINE: + data = ptree->compute_metrics (mlist, type, mode, objs, context, sel_objs); + break; + case Histable::FUNCTION: + case Histable::MODULE: + case Histable::LOADOBJECT: + data = ptree->compute_metrics (mlist, type, mode, objs, NULL, + sel_objs, flag); + break; + case Histable::DOBJECT: + data = dspace->compute_metrics (mlist, type, mode, + objs ? objs->fetch (0) : NULL); + break; + case Histable::MEMOBJ: + case Histable::INDEXOBJ: + data = indxspaces->get (subtype)->compute_metrics (mlist, type, mode, + objs, NULL); + break; + case Histable::IOACTFILE: + if (objs == NULL) + { + data = iofile_data = iospace->compute_metrics (mlist, type, mode, + NULL); + break; + } + else + { + data = iospace->compute_metrics (mlist, type, mode, objs->fetch (0)); + break; + } + case Histable::IOACTVFD: + if (objs == NULL) + data = iovfd_data = iospace->compute_metrics (mlist, type, mode, NULL); + else + data = iospace->compute_metrics (mlist, type, mode, objs->fetch (0)); + break; + case Histable::IOCALLSTACK: + if (objs == NULL) + data = iocs_data = iospace->compute_metrics (mlist, type, mode, NULL); + else + data = iospace->compute_metrics (mlist, type, mode, objs->fetch (0)); + break; + case Histable::HEAPCALLSTACK: + if (objs == NULL) + data = heapcs_data = heapspace->compute_metrics (mlist, type, mode, NULL); + else + data = heapspace->compute_metrics (mlist, type, mode, objs->fetch (0)); + break; + default: + data = NULL; + break; + } + for (long i = mlist_orig->get_items ()->size (), + sz = mlist->get_items ()->size (); i < sz; i++) + { + Metric *m = mlist->get_items ()->get (i); + m->set_dmetrics_visbits (VAL_HIDE_ALL | m->get_visbits ()); + } + if (data) + data->nmetrics = mlist_orig->size (); + return data; +} + +char * +DbeView::get_mobj_name (int subtype) +{ + MemorySpace *ms = getMemorySpace (subtype); + if (ms == NULL) + ms = addMemorySpace (subtype); + return ms->getMemObjTypeName (); +} + +MemorySpace * +DbeView::getMemorySpace (int subtype) +{ + for (long i = 0, sz = VecSize (memspaces); i < sz; i++) + { + MemorySpace *ms = memspaces->get (i); + if (subtype == ms->getMemObjType ()) + return ms; + } + return NULL; +} + +MemorySpace * +DbeView::addMemorySpace (int subtype) +{ + MemorySpace *ms = new MemorySpace (this, subtype); + memspaces->append (ms); + return ms; +} + +Hist_data * +DbeView::get_indxobj_data (int subtype) +{ + if (subtype < 0 || subtype >= indx_data->size ()) + return NULL; + return indx_data->fetch (subtype); +} + +void +DbeView::set_indxobj_sel (int subtype, int sel_ind) +{ + Hist_data *data = get_indxobj_data (subtype); + if (data == NULL) + return; + if (sel_ind >= 0 && sel_ind < data->size ()) + { + Histable *obj = data->fetch (sel_ind)->obj; + sel_idxobj->store (subtype, obj); + } +} + +Histable * +DbeView::get_indxobj_sel (int subtype) +{ + return sel_idxobj->fetch (subtype); +} + +void +DbeView::addIndexSpace (int subtype) +{ + PathTree *is = new PathTree (this, subtype); + indxspaces->store (subtype, is); + indx_data->store (subtype, NULL); + sel_idxobj->store (subtype, NULL); + settings->indxobj_define (subtype, false); +} + +Histable * +DbeView::get_sel_obj_io (uint64_t id, Histable::Type type) +{ + if (iospace == NULL) + return NULL; + Histable *obj = NULL; + Hist_data *data = NULL; + switch (type) + { + case Histable::IOACTFILE: + data = iofile_data; + break; + case Histable::IOACTVFD: + data = iovfd_data; + break; + case Histable::IOCALLSTACK: + data = iocs_data; + break; + default: + break; + } + if (data == NULL) + return NULL; + + Vector<Hist_data::HistItem*> *hi_data = data->get_hist_items (); + int size = hi_data->size (); + for (int i = 0; i < size; i++) + { + Hist_data::HistItem *hi = hi_data->fetch (i); + if (hi->obj != NULL && (uint64_t) hi->obj->id == id) + { + obj = hi->obj; + break; + } + } + return obj; +} + +Histable * +DbeView::get_sel_obj_heap (uint64_t id) +{ + if (heapspace == NULL || heapcs_data == NULL) + return NULL; + Histable *obj = NULL; + Hist_data *data = heapcs_data; + Vector<Hist_data::HistItem*> *hi_data = data->get_hist_items (); + int size = hi_data->size (); + for (int i = 0; i < size; i++) + { + Hist_data::HistItem *hi = hi_data->fetch (i); + if ((hi->obj != NULL) && ((uint64_t) hi->obj->id) == id) + { + obj = hi->obj; + break; + } + } + return obj; +} + +CStack_data * +DbeView::get_cstack_data (MetricList *mlist) +{ + return ptree->get_cstack_data (mlist); +} + +Stats_data * +DbeView::get_stats_data (int index) +{ + DataView *packets = get_filtered_events (index, DATA_SAMPLE); + if (packets == NULL) + return NULL; + return new Stats_data (packets); +} + +Ovw_data * +DbeView::get_ovw_data (int index) +{ + DataView *packets = get_filtered_events (index, DATA_SAMPLE); + Experiment* exp = dbeSession->get_exp (index); + hrtime_t starttime = 0; + if (exp != NULL) + starttime = exp->getStartTime (); + if (packets == NULL) + return NULL; + return new Ovw_data (packets, starttime); +} + +char * +DbeView::set_filter (const char *filter_spec) +{ + if (dbe_strcmp (filter_spec, cur_filter_str) == 0) // Nothing was changed + return NULL; + + // if string is NULL, delete the filter + if (filter_spec == NULL) + { + if (cur_filter_str) + { + free (cur_filter_str); + cur_filter_str = NULL; + } + if (cur_filter_expr) + { + delete cur_filter_expr; + cur_filter_expr = NULL; + } + noParFilter = false; + purge_events (); + reset_data (false); + return NULL; + } + + // process the filter + Expression *expr = dbeSession->ql_parse (filter_spec); + if (expr == NULL) + return dbe_sprintf (GTXT ("Invalid filter specification `%s'\n"), filter_spec); + + if (dbe_strcmp (filter_spec, "1") == 0) + noParFilter = false; + else if (sel_obj != NULL) + if (sel_obj->get_type () == Histable::LINE) + if (expr->verifyObjectInExpr (sel_obj)) + noParFilter = true; + + // valid new filter + if (cur_filter_str != NULL) + { + free (prev_filter_str); + prev_filter_str = dbe_strdup (cur_filter_str); + } + free (cur_filter_str); + cur_filter_str = dbe_strdup (filter_spec); + delete cur_filter_expr; + cur_filter_expr = expr; + purge_events (); + reset_data (false); + return NULL; +} + +FilterExp * +DbeView::get_FilterExp (Experiment *exp) +{ + if (cur_filter_expr == NULL) + return NULL; + Expression::Context *ctx = new Expression::Context (this, exp); + return new FilterExp (cur_filter_expr, ctx, noParFilter); +} + +char * +DbeView::get_filter () +{ + return dbe_strdup (cur_filter_str); +} + +FilterSet * +DbeView::get_filter_set (int n) +{ + fflush (stderr); + if (n >= filters->size ()) + return NULL; + return ( filters->fetch (n)); +} + +Vector<FilterNumeric*> * +DbeView::get_all_filters (int nexp) +{ + FilterSet *fs = get_filter_set (nexp); + return fs ? fs->get_all_filters () : NULL; +} + +FilterNumeric * +DbeView::get_FilterNumeric (int nexp, int idx) +{ + FilterSet *fs = get_filter_set (nexp); + return fs ? fs->get_filter (idx) : NULL; +} + +void +DbeView::backtrack_filter() +{ + if (prev_filter_str != NULL) + set_filter(prev_filter_str); + else set_filter("1"); // reset + +} + +void +DbeView::update_advanced_filter () +{ + char *s = get_advanced_filter (); + if (dbe_strcmp (s, cur_filter_str)) + { + phaseIdx++; + char *err_msg = set_filter (s); + if (err_msg) + { +#ifdef DEBUG + fprintf (stderr, NTXT ("ERROR: Advanced Filter: '%s'\n"), err_msg); +#endif + } + } + free (s); +} + +bool +DbeView::set_pattern (int n, Vector<char *> *pattern_str, bool *error) +{ + Vector<FilterNumeric*> *filts = get_all_filters (n); + + bool ret = false; + *error = false; + int imax = pattern_str->size (); + if (imax > filts->size ()) + imax = filts->size (); + for (int i = 0; i < imax; i++) + { + FilterNumeric *f = filts->fetch (i); + char *s = pattern_str->fetch (i); + if (s == NULL) + continue; + if (f->set_pattern (s, error)) + ret = true; + } + + if (ret || cur_filter_expr) + { + update_advanced_filter (); + filter_active = true; + } + return ret; +} + +static void +append_experiments (StringBuilder *sb, int first, int last) +{ + if (first == -1) + return; + if (sb->length () != 0) + sb->append (NTXT (" || ")); + sb->append ('('); + if (first == last) + { + sb->append (NTXT ("EXPID==")); + sb->append (first); + } + else + { + sb->append (NTXT ("EXPID>=")); + sb->append (first); + sb->append (NTXT (" && EXPID<=")); + sb->append (last); + } + sb->append (')'); +} + +char * +DbeView::get_advanced_filter () +{ + StringBuilder sb; + bool wasFalse = false; + int first = -1, last = -1; + for (int n = 0, nexps = dbeSession->nexps (); n < nexps; n++) + { + FilterSet *fs = get_filter_set (n); + char *s = fs->get_advanced_filter (); + if (s) + { + if (streq (s, NTXT ("1"))) + { + last = n + 1; + if (first == -1) + first = last; + continue; + } + append_experiments (&sb, first, last); + first = -1; + if (streq (s, NTXT ("0"))) + { + wasFalse = true; + continue; + } + if (sb.length () != 0) + sb.append (NTXT (" || ")); + sb.append (NTXT ("(EXPID==")); + sb.append (n + 1); + sb.append (NTXT (" && (")); + sb.append (s); + free (s); + sb.append (NTXT ("))")); + } + else + { + last = n + 1; + if (first == -1) + first = last; + } + } + if (first != 1) + { + append_experiments (&sb, first, last); + first = -1; + } + if (sb.length () == 0) + sb.append (wasFalse ? '0' : '1'); + else + append_experiments (&sb, first, last); + return sb.toString (); +} + +bool +DbeView::set_pattern (int m, char *pattern) +{ + bool error = false; + + // Store original setting in case of error + int nexps = dbeSession->nexps (); + int orig_phaseIdx = phaseIdx; + bool *orig_enable = new bool[nexps]; + char **orig_pattern = new char*[nexps]; + for (int i = 0; i < nexps; i++) + { + orig_pattern[i] = get_FilterNumeric (i, m)->get_pattern (); + orig_enable[i] = get_exp_enable (i); + set_exp_enable (i, false); + } + + // Copy the pattern so that we could safely modify it + char *buf = dbe_strdup (pattern); + FilterNumeric *fexp = NULL; + char *pb, *pe; + pb = pe = buf; + for (bool done = false; !done; pe++) + { + if (*pe == ':') + { + // experiment filter; + *pe = '\0'; + fexp = new FilterNumeric (NULL, NULL, NULL); + fexp->set_range (1, nexps, nexps); + fexp->set_pattern (pb, &error); + if (error) + break; + pb = pe + 1; + } + else if (*pe == '+' || *pe == '\0') + { + // entity filter + if (*pe == '\0') + done = true; + else + *pe = '\0'; + for (int i = 0; i < nexps; i++) + { + if (!fexp || fexp->is_selected (i + 1)) + { + FilterNumeric *f = get_FilterNumeric (i, m); + f->set_pattern (pb, &error); + if (error) + break; + set_exp_enable (i, true); + } + } + if (error) + break; + delete fexp; + fexp = NULL; + pb = pe + 1; + } + } + + if (error) + { + for (int i = 0; i < nexps; i++) + { + bool err; + set_exp_enable (i, orig_enable[i]); + FilterNumeric *f = get_FilterNumeric (i, m); + f->set_pattern (orig_pattern[i], &err); + free (orig_pattern[i]); + } + phaseIdx = orig_phaseIdx; + } + else + { + update_advanced_filter (); + filter_active = true; + } + delete[] orig_enable; + delete[] orig_pattern; + delete fexp; + free (buf); + return !error; +} + +void +DbeView::set_view_mode (VMode newmode) +{ + if (newmode != settings->get_view_mode ()) + { + + // For OpenMP, the expert mode path-tree is already present with the user mode + // No need to increase the phaseIdx to trigger recomputation of path-tree + // if we toggle between user and expert modes + if (!(dbeSession->is_omp_available () + && ((newmode == VMODE_EXPERT + && settings->get_view_mode () == VMODE_USER) + || (newmode == VMODE_USER + && settings->get_view_mode () == VMODE_EXPERT)))) + phaseIdx++; // For all other cases + setNewViewMode (); + settings->set_view_mode (newmode); + } +} + +Cmd_status +DbeView::set_view_mode (char *str, bool fromRC) +{ + VMode old = settings->get_view_mode (); + Cmd_status ret = settings->set_view_mode (str, fromRC); + if (old != settings->get_view_mode ()) + phaseIdx++; + return ret; +} + +Cmd_status +DbeView::set_en_desc (char *str, bool fromRC) +{ + // Tell the session + Settings *s = dbeSession->get_settings (); + s->set_en_desc (str, fromRC); + + // and tell our settings + return settings->set_en_desc (str, fromRC); +} + +// Get processor stats messages +char * +DbeView::get_processor_msg (int type) +{ + if (ptree == NULL) // if no PathTree, no messages + return NULL; + + StringBuilder sb; + Emsg *m = (type == PSTAT_MSG) ? ptree->fetch_stats () : ptree->fetch_warnings (); + for (; m != NULL; m = m->next) + { + char* newmsg = m->get_msg (); + sb.append (newmsg); + sb.append ("\n"); + } + + if (type == PSTAT_MSG) + ptree->delete_stats (); + else + ptree->delete_warnings (); + return (sb.length () > 0) ? sb.toString () : NULL; +} + +void +DbeView::dump_nodes (FILE *outfile) +{ + FILE *f = (outfile == NULL ? stderr : outfile); + ptree->print (f); +} + +// Dump the clock profile events +void +DbeView::dump_profile (FILE *out_file) +{ + for (int idx = 0; idx < dbeSession->nexps (); idx++) + { + Experiment *exp = dbeSession->get_exp (idx); + VMode view_mode = get_view_mode (); + char * stateNames [/*LMS_NUM_STATES*/] = LMS_STATE_STRINGS; + + // Process clock profile date + DataView *packets = get_filtered_events (idx, DATA_CLOCK); + if (packets && packets->getSize () != 0) + { + hrtime_t start = exp->getStartTime (); + fprintf (out_file, + GTXT ("\nTotal Clock Profiling Packets: %d Experiment: %s\n"), + (int) packets->getSize (), exp->get_expt_name ()); + for (long i = 0; i < packets->getSize (); i++) + { + hrtime_t expr_ts = (hrtime_t) packets->getLongValue (PROP_TSTAMP, i); + hrtime_t ts = expr_ts - start; + + // get the properties from the packet + uint32_t thrid = (uint32_t) packets->getIntValue (PROP_THRID, i); + uint32_t cpuid = (uint32_t) packets->getIntValue (PROP_CPUID, i); + int mstate = (int) packets->getIntValue (PROP_MSTATE, i); + int nticks = (int) packets->getIntValue (PROP_NTICK, i); + + char *sname; + char buf[1024]; + if (mstate >= 0 && mstate < LMS_NUM_STATES) + sname = stateNames[mstate]; + else + { + snprintf (buf, sizeof (buf), NTXT ("Unexpected mstate = %d"), mstate); + sname = buf; + } + + // get the stack IGNORE HIDE + Vector<Histable*> *stack = getStackPCs (view_mode, packets, i); + int stack_size = stack->size (); + + // print the packet header with the count of stack frames + fprintf (out_file, + GTXT ("#%6ld: %lld, %3lld.%09lld (%4lld.%09lld) t = %d, cpu = %d, frames = %d\n"), + i, expr_ts, ts / NANOSEC, ts % NANOSEC, + expr_ts / NANOSEC, expr_ts % NANOSEC, + thrid, cpuid, stack_size); + fprintf (out_file, + GTXT (" mstate = %d (%s), nticks = %d\n"), + mstate, sname, nticks); + + // dump the callstack + for (int j = stack_size - 1; j >= 0; j--) + { + Histable *frame = stack->fetch (j); + fprintf (out_file, GTXT (" %s [0x%016llx]\n"), frame->get_name (), (long long) frame); + } + fprintf (out_file, "\n"); + } + } + else + fprintf (out_file, + GTXT ("\nNo Clock Profiling Packets in Experiment: %s\n"), + exp->get_expt_name ()); + } +} + +// Dump the sync trace events +void +DbeView::dump_sync (FILE *out_file) +{ + for (int idx = 0; idx < dbeSession->nexps (); idx++) + { + Experiment *exp = dbeSession->get_exp (idx); + VMode view_mode = get_view_mode (); + + // Process heap trace date + DataView *packets = get_filtered_events (idx, DATA_SYNCH); + if (packets && packets->getSize () != 0) + { + hrtime_t start = exp->getStartTime (); + fprintf (out_file, + GTXT ("\nTotal Synctrace Packets: %d Experiment: %s\n"), + (int) packets->getSize (), exp->get_expt_name ()); + + for (long i = 0; i < packets->getSize (); i++) + { + hrtime_t expr_ts = (hrtime_t) packets->getLongValue (PROP_TSTAMP, i); + hrtime_t ts = expr_ts - start; + + // get the properties from the packet + uint32_t thrid = (uint32_t) packets->getIntValue (PROP_THRID, i); + uint32_t cpuid = (uint32_t) packets->getIntValue (PROP_CPUID, i); + uint64_t syncobj = (uint64_t) packets->getLongValue (PROP_SOBJ, i); + hrtime_t syncrtime = (uint64_t) packets->getLongValue (PROP_SRQST, i); + hrtime_t syncdelay = expr_ts - syncrtime; + + // get the stack IGNORE HIDE + Vector<Histable*> *stack = getStackPCs (view_mode, packets, i); + int stack_size = stack->size (); + + // print the packet header with the count of stack frames + fprintf (out_file, + GTXT ("#%6ld: %lld, %3lld.%09lld (%4lld.%09lld) t = %d, cpu = %d, frames = %d\n"), + i, expr_ts, ts / NANOSEC, ts % NANOSEC, + expr_ts / NANOSEC, expr_ts % NANOSEC, thrid, + cpuid, stack_size); + fprintf (stderr, + GTXT (" synchronization object @ 0x%016llx; synchronization delay %3lld.%09lld\n"), + (unsigned long long) syncobj, (long long) (syncdelay / NANOSEC), (long long) (syncdelay % NANOSEC)); + + // dump the callstack + for (int j = stack_size - 1; j >= 0; j--) + { + Histable *frame = stack->fetch (j); + fprintf (out_file, GTXT (" %s [0x%016llx]\n"), frame->get_name (), (long long) frame); + } + fprintf (out_file, "\n"); + } + } + else + fprintf (out_file, GTXT ("\nNo Synctrace Packets in Experiment: %s\n"), + exp->get_expt_name ()); + } +} + +// Dump the IO trace events +void +DbeView::dump_iotrace (FILE *out_file) +{ + for (int idx = 0; idx < dbeSession->nexps (); idx++) + { + Experiment *exp = dbeSession->get_exp (idx); + VMode view_mode = get_view_mode (); + + // Process IO trace date + DataView *packets = get_filtered_events (idx, DATA_IOTRACE); + if (packets && packets->getSize () != 0) + { + hrtime_t start = exp->getStartTime (); + fprintf (out_file, + GTXT ("\nTotal IO trace Packets: %d Experiment: %s\n"), + (int) packets->getSize (), exp->get_expt_name ()); + for (long i = 0; i < packets->getSize (); i++) + { + hrtime_t expr_ts = (hrtime_t) packets->getLongValue (PROP_TSTAMP, i); + hrtime_t ts = expr_ts - start; + + // get the properties from the packet + uint32_t thrid = (uint32_t) packets->getIntValue (PROP_THRID, i); + uint32_t cpuid = (uint32_t) packets->getIntValue (PROP_CPUID, i); + IOTrace_type iotrtype = (IOTrace_type) packets->getIntValue (PROP_IOTYPE, i); + uint32_t iofd = (uint32_t) packets->getIntValue (PROP_IOFD, i); + uint64_t ionbyte = (uint64_t) packets->getIntValue (PROP_IONBYTE, i); + hrtime_t iorqst = (hrtime_t) packets->getLongValue (PROP_IORQST, i); + uint32_t ioofd = (uint32_t) packets->getIntValue (PROP_IOOFD, i); + FileSystem_type iofstype = (FileSystem_type) packets->getIntValue (PROP_CPUID, i); + int64_t iovfd = (int64_t) packets->getIntValue (PROP_IOVFD, i); + + char *fName = NULL; + StringBuilder *sb = (StringBuilder*) packets->getObjValue (PROP_IOFNAME, i); + if (sb != NULL && sb->length () > 0) + fName = sb->toString (); + + // get the stack IGNORE HIDE + Vector<Histable*> *stack = getStackPCs (view_mode, packets, i); + int stack_size = stack->size (); + const char *iotrname; + switch (iotrtype) + { + case READ_TRACE: + iotrname = "ReadTrace"; + break; + case WRITE_TRACE: + iotrname = "WriteTrace"; + break; + case OPEN_TRACE: + iotrname = "OpenTrace"; + break; + case CLOSE_TRACE: + iotrname = "CloseTrace"; + break; + case OTHERIO_TRACE: + iotrname = "OtherIOTrace"; + break; + case READ_TRACE_ERROR: + iotrname = "ReadTraceError"; + break; + case WRITE_TRACE_ERROR: + iotrname = "WriteTraceError"; + break; + case OPEN_TRACE_ERROR: + iotrname = "OpenTraceError"; + break; + case CLOSE_TRACE_ERROR: + iotrname = "CloseTraceError"; + break; + case OTHERIO_TRACE_ERROR: + iotrname = "OtherIOTraceError"; + break; + default: + iotrname = "UnknownIOTraceType"; + break; + } + + // print the packet header with the count of stack frames + fprintf (out_file, + GTXT ("#%6ld: %lld, %3lld.%09lld (%4lld.%09lld) t = %d, cpu = %d, frames = %d\n"), + i, expr_ts, ts / NANOSEC, ts % NANOSEC, + expr_ts / NANOSEC, expr_ts % NANOSEC, + thrid, cpuid, stack_size); + fprintf (out_file, + GTXT (" %s: fd = %d, ofd = %d, vfd = %lld, fstype = %d, rqst = %3lld.%09lld\n"), + iotrname, (int) iofd, (int) ioofd, (long long) iovfd, + (int) iofstype, (long long) (iorqst / NANOSEC), + (long long) (iorqst % NANOSEC)); + fprintf (out_file, GTXT (" filename = `%s', nbytes = %d\n"), + STR (fName), (int) ionbyte); + free (fName); + + // dump the callstack + for (int j = stack_size - 1; j >= 0; j--) + { + Histable *frame = stack->fetch (j); + fprintf (out_file, GTXT (" %s [0x%016llx]\n"), frame->get_name (), (long long) frame); + } + fprintf (out_file, "\n"); + } + } + else + fprintf (out_file, GTXT ("\nNo IO trace Packets in Experiment: %s\n"), + exp->get_expt_name ()); + } +} + +// Dump the HWC Profiling events +void +DbeView::dump_hwc (FILE *out_file) +{ + for (int idx = 0; idx < dbeSession->nexps (); idx++) + { + Experiment *exp = dbeSession->get_exp (idx); + VMode view_mode = get_view_mode (); + + // Dump HWC profiling data + DataView *packets = get_filtered_events (idx, DATA_HWC); + if (packets && packets->getSize () != 0) + { + hrtime_t start = exp->getStartTime (); + fprintf (out_file, + GTXT ("\nTotal HW Counter Profiling Packets: %d Experiment: %s\n"), + (int) packets->getSize (), exp->get_expt_name ()); + for (long i = 0; i < packets->getSize (); i++) + { + const char * hwc_name; + hrtime_t expr_ts = (hrtime_t) packets->getLongValue (PROP_TSTAMP, i); + hrtime_t ts = expr_ts - start; + uint32_t tag = (uint32_t) packets->getIntValue (PROP_HWCTAG, i); + uint32_t thrid = (uint32_t) packets->getIntValue (PROP_THRID, i); + uint32_t cpuid = (uint32_t) packets->getIntValue (PROP_CPUID, i); + + // This will work even with a different counter in every packet. + if (tag < 0 || tag >= MAX_HWCOUNT + || !exp->coll_params.hw_aux_name[tag]) + // if the packet has an invalid tag, use <invalid> as its name + hwc_name = "<invalid>"; + else + hwc_name = exp->coll_params.hw_aux_name[tag]; + int64_t mval = packets->getLongValue (PROP_HWCINT, i); + const char *err = HWCVAL_HAS_ERR (mval) ? " $$" : ""; + + // get the stack IGNORE HIDE + Vector<Histable*> *stack = getStackPCs (view_mode, packets, i); + int stack_size = stack->size (); + + // print the packet header with the count of stack frames + fprintf (out_file, + GTXT ("#%6ld: %lld, %3lld.%09lld (%4lld.%09lld) t = %d, cpu = %d, frames = %d\n count = %10lld (0x%016llx), tag = %d (%s)%s\n"), + (long) i, (long long) expr_ts, + (long long) (ts / NANOSEC), (long long) (ts % NANOSEC), + (long long) (expr_ts / NANOSEC), (long long) (expr_ts % NANOSEC), + (int) thrid, (int) cpuid, (int) stack_size, + (long long) (HWCVAL_CLR_ERR (mval)), (long long) mval, + (int) tag, hwc_name, err); + + // dump extended HWC packets values + uint64_t va = (uint64_t) packets->getLongValue (PROP_VADDR, i); + uint64_t pa = (uint64_t) packets->getLongValue (PROP_PADDR, i); + fprintf (out_file, GTXT (" va = 0x%016llx, pa = 0x%016llx\n"), + (unsigned long long) va, (unsigned long long) pa); + + // dump the callstack + for (int j = stack_size - 1; j >= 0; j--) + { + Histable *frame = stack->fetch (j); + fprintf (out_file, GTXT (" %s [0x%016llx]\n"), frame->get_name (), (long long) frame); + } + fprintf (out_file, "\n"); + } + } + else + fprintf (out_file, + GTXT ("\nNo HWC Profiling Packets in Experiment: %s\n"), + exp->get_expt_name ()); + } +} + +// Dump the Heap events +void +DbeView::dump_heap (FILE *out_file) +{ + char *heapstrings[] = HEAPTYPE_STATE_USTRINGS; + for (int idx = 0; idx < dbeSession->nexps (); idx++) + { + Experiment *exp = dbeSession->get_exp (idx); + VMode view_mode = get_view_mode (); + + // Process heap trace date + DataView *packets = get_filtered_events (idx, DATA_HEAP); + if (packets && packets->getSize () != 0) + { + hrtime_t start = exp->getStartTime (); + fprintf (out_file, GTXT ("\nTotal Heaptrace Packets: %d Experiment: %s\n"), + (int) packets->getSize (), exp->get_expt_name ()); + for (long i = 0; i < packets->getSize (); i++) + { + hrtime_t expr_ts = (hrtime_t) packets->getLongValue (PROP_TSTAMP, i); + hrtime_t ts = expr_ts - start; + + // get the properties from the packet + uint32_t thrid = (uint32_t) packets->getIntValue (PROP_THRID, i); + uint32_t cpuid = (uint32_t) packets->getIntValue (PROP_CPUID, i); + uint32_t heaptype = (uint32_t) packets->getIntValue (PROP_HTYPE, i); + uint64_t heapsize = (uint64_t) packets->getULongValue (PROP_HSIZE, i); + uint64_t heapvaddr = (uint64_t) packets->getULongValue (PROP_HVADDR, i); + uint64_t heapovaddr = (uint64_t) packets->getULongValue (PROP_HOVADDR, i); + if (heaptype == MUNMAP_TRACE) + { + heapsize = (uint64_t) packets->getULongValue (PROP_HOVADDR, i); + heapovaddr = 0; + } + + // get the stack IGNORE HIDE + Vector<Histable*> *stack = getStackPCs (view_mode, packets, i); + int stack_size = stack->size (); + + // print the packet header with the count of stack frames + fprintf (out_file, + GTXT ("#%6ld: %lld, %3lld.%09lld (%4lld.%09lld) t = %d, cpu = %d, frames = %d\n"), + i, expr_ts, ts / NANOSEC, ts % NANOSEC, + expr_ts / NANOSEC, expr_ts % NANOSEC, + thrid, cpuid, stack_size); + char *typestr = heapstrings[heaptype]; + fprintf (out_file, + GTXT (" type = %d (%s), size = %llu (0x%llx), VADDR = 0x%016llx, OVADDR = 0x%016llx\n"), + (int) heaptype, typestr, (long long unsigned int) heapsize, + (long long unsigned int) heapsize, + (long long unsigned int) heapvaddr, + (long long unsigned int) heapovaddr); + + // dump the callstack + for (int j = stack_size - 1; j >= 0; j--) + { + Histable *frame = stack->fetch (j); + fprintf (out_file, GTXT (" %s [0x%016llx]\n"), frame->get_name (), (long long) frame); + } + fprintf (out_file, "\n"); + } + } + else + fprintf (out_file, GTXT ("\nNo Heaptrace Packets in Experiment: %s\n"), + exp->get_expt_name ()); + } +} + +// Dump the Java garbage collector events +void +DbeView::dump_gc_events (FILE *out_file) +{ + for (int idx = 0; idx < dbeSession->nexps (); idx++) + { + Experiment *exp = dbeSession->get_exp (idx); + if (!exp->has_java) + fprintf (out_file, + GTXT ("# No GC events in experiment %d, %s (PID %d, %s)\n"), + idx, exp->get_expt_name (), exp->getPID (), exp->utargname); + else + { + Vector<GCEvent*> *gce = exp->get_gcevents (); + GCEvent *this_event; + int index; + fprintf (out_file, + GTXT ("# %li events in experiment %d: %s (PID %d, %s)\n"), + gce->size (), idx, + exp->get_expt_name (), exp->getPID (), exp->utargname); + fprintf (out_file, + GTXT ("# exp:idx GC_start, GC_end, GC_duration\n")); + Vec_loop (GCEvent*, gce, index, this_event) + { + hrtime_t start = this_event->start - exp->getStartTime (); + hrtime_t end = this_event->end - exp->getStartTime (); + hrtime_t delta = this_event->end - this_event->start; + fprintf (out_file, + "%5d:%d, %3lld.%09lld, %3lld.%09lld, %3lld.%09lld\n", + idx, index, + (long long) (start / NANOSEC), (long long) (start % NANOSEC), + (long long) (end / NANOSEC), (long long) (end % NANOSEC), + (long long) (delta / NANOSEC), (long long) (delta % NANOSEC)); + } + } + } +} + +void +DbeView::purge_events (int n) +{ + phaseIdx++; + int lst; + if (n == -1) + lst = filters->size (); + else + lst = n > filters->size () ? filters->size () : n + 1; + for (int i = n == -1 ? 0 : n; i < lst; i++) + { + Vector<DataView*> *expDataViewList = dataViews->fetch (i); + if (expDataViewList) + { + // clear out all the data_ids, but don't change the vector size + for (int data_id = 0; data_id < expDataViewList->size (); ++data_id) + { + delete expDataViewList->fetch (data_id); + expDataViewList->store (data_id, NULL); + } + } + } + filter_active = false; +} + + +// LIBRARY_VISIBILITY +void +DbeView::resetAndConstructShowHideStacks () +{ + for (int n = 0, nexps = dbeSession->nexps (); n < nexps; n++) + { + Experiment *exp = dbeSession->get_exp (n); + if (exp != NULL) + resetAndConstructShowHideStack (exp); + } +} + +// LIBRARY_VISIBILITY +void +DbeView::resetAndConstructShowHideStack (Experiment *exp) +{ + exp->resetShowHideStack (); + /* Vector<DataDescriptor*> *dDscrs = */ exp->getDataDescriptors (); + + DataDescriptor *dd; + // Construct show hide stack only for objects which have call stacks + // list below similar to path tree. What about HEAP_SZ? (DBFIXME) + dd = exp->get_raw_events (DATA_CLOCK); + if (dd != NULL) + constructShowHideStack (dd, exp); + dd = exp->get_raw_events (DATA_SYNCH); + if (dd != NULL) + constructShowHideStack (dd, exp); + dd = exp->get_raw_events (DATA_IOTRACE); + if (dd != NULL) + constructShowHideStack (dd, exp); + dd = exp->get_raw_events (DATA_HWC); + if (dd != NULL) + constructShowHideStack (dd, exp); + dd = exp->get_raw_events (DATA_HEAP); + if (dd != NULL) + constructShowHideStack (dd, exp); + dd = exp->get_raw_events (DATA_RACE); + if (dd != NULL) + constructShowHideStack (dd, exp); + dd = exp->get_raw_events (DATA_DLCK); + if (dd != NULL) + constructShowHideStack (dd, exp); +} + +// LIBRARY_VISIBILITY +void +DbeView::constructShowHideStack (DataDescriptor *dDscr, Experiment *exp) +{ + if (dDscr == NULL) + return; + int stack_prop = PROP_NONE; + VMode view_mode = get_view_mode (); + if (view_mode == VMODE_MACHINE) + stack_prop = PROP_MSTACK; + else if (view_mode == VMODE_EXPERT) + stack_prop = PROP_XSTACK; + else if (view_mode == VMODE_USER) + stack_prop = PROP_USTACK; + + for (long j = 0, sz = dDscr->getSize (); j < sz; j++) + { + void *stackId = dDscr->getObjValue (stack_prop, j); + Vector<Histable*> *stack = (Vector<Histable*>*)CallStack::getStackPCs (stackId); + int stack_size = stack->size (); + bool hide_on = false; + LoadObject *hide_lo = NULL; + Histable *last_addr = NULL; + Histable *api_addr = NULL; + DbeInstr *h_instr; + + Vector<Histable*> *hidepcs = new Vector<Histable*>; + for (int i = stack_size - 1; i >= 0; i--) + { + bool leaf = (i == 0); + Histable *cur_addr = stack->fetch (i); + Function *func = (Function*) cur_addr->convertto (Histable::FUNCTION); + if (func != NULL) + { + Module *mod = func->module; + LoadObject *lo = mod->loadobject; + int segx = lo->seg_idx; + if ((get_lo_expand (segx) == LIBEX_API) && (i != (stack_size - 1))) + { + leaf = true; + api_addr = cur_addr; + } + else if (get_lo_expand (segx) == LIBEX_HIDE) + { + if (hide_on) + { + if (lo != hide_lo) + { + // Changed hidden loadobject + if (last_addr != NULL) + { + h_instr = hide_lo->get_hide_instr ((DbeInstr*) last_addr); + hidepcs->append (h_instr); + last_addr = cur_addr; + } + hide_lo = lo; + } + } + else + { + // Start hide + hide_on = true; + last_addr = cur_addr; + hide_lo = lo; + } + if (!leaf) + continue; + } + else + { + hide_on = false; + if (last_addr != NULL) + { + h_instr = hide_lo->get_hide_instr ((DbeInstr*) last_addr); + hidepcs->append (h_instr); + last_addr = NULL; + } + } + } + if (last_addr != NULL && leaf) cur_addr = last_addr; + if (hide_on) + { + h_instr = hide_lo->get_hide_instr ((DbeInstr*) cur_addr); + hidepcs->append (h_instr); + if (api_addr != NULL) + hidepcs->append (api_addr); + } + else + hidepcs->append (cur_addr); + if (leaf) + break; + } + for (int i = 0, k = hidepcs->size () - 1; i < k; ++i, --k) + hidepcs->swap (i, k); + + CallStack *cstkSH = exp->callTreeShowHide (); + CallStackNode *hstack = (CallStackNode *) cstkSH->add_stack (hidepcs); + dDscr->setObjValue (PROP_HSTACK, j, hstack); + CallStack::setHideStack (stackId, hstack); + delete hidepcs; + delete stack; + } +} + +DataView * +DbeView::get_filtered_events (int idx, int data_id) +{ + if (idx < 0 || idx >= dataViews->size ()) + return NULL; + Vector<DataView*> *expDataViewList = dataViews->fetch (idx); + if (!expDataViewList) + return NULL; // Weird + + DataView *dview = expDataViewList->fetch (data_id); + Experiment *exp = dbeSession->get_exp (idx); + if (dview) + { + // if show-hide is on force a reconstruction of hide stacks + // LIBRARY_VISIBILITY + if (!showAll && (showHideChanged || newViewMode)) + { + DataDescriptor *dDscr = exp->get_raw_events (data_id); + constructShowHideStack (dDscr, exp); + } + return dview; + } + + int orig_data_id = data_id; + data_id = exp->base_data_id (data_id); + if (orig_data_id != data_id) + // orig_data_id is a derived DataView. Get the master DataView: + dview = expDataViewList->fetch (data_id); + if (dview == NULL) + { + Expression *saved = cur_filter_expr; + if (!adjust_filter (exp)) + return NULL; + + DataDescriptor *dDscr = exp->get_raw_events (data_id); + if (!showAll && (showHideChanged || newViewMode)) + constructShowHideStack (dDscr, exp); + + Emsg *m = exp->fetch_warnings (); + if (m != NULL) + this->warning_msg = m->get_msg (); + + if (dDscr != NULL) + { + FilterExp *filter = get_FilterExp (exp); + dview = dDscr->createView (); + dview->setFilter (filter); + if (dview->getSize () < dDscr->getSize ()) + filter_active = true; + } + expDataViewList->store (data_id, dview); + + if (saved) + { + delete cur_filter_expr; + cur_filter_expr = saved; + } + } + if (orig_data_id != data_id) + { + // create the derived DataView: + dview = exp->create_derived_data_view (orig_data_id, dview); + expDataViewList->store (orig_data_id, dview); + } + return dview; +} + +DataView * +DbeView::get_filtered_events (int idx, int data_id, + const int sortprops[], int sortprop_count) +{ + DataView *packets = get_filtered_events (idx, data_id); + if (packets) + packets->sort (sortprops, sortprop_count); + return packets; +} + +bool +DbeView::adjust_filter (Experiment *exp) +{ + if (cur_filter_expr) + { + Expression::Context ctx (this, exp); + resetFilterHideMode (); + Expression *fltr = cur_filter_expr->pEval (&ctx); + if (fltr->complete ()) + { // Filter is a constant + if (fltr->eval (NULL) == 0) + return false; + delete fltr; + fltr = NULL; + } + cur_filter_expr = fltr; + } + return true; +} + +// Moved from Cacheable.cc: +char * +DbeView::status_str (DbeView_status status) +{ + switch (status) + { + case DBEVIEW_SUCCESS: + return NULL; + case DBEVIEW_NO_DATA: + return dbe_strdup (GTXT ("Data not available for this filter selection")); + case DBEVIEW_IO_ERROR: + return dbe_strdup (GTXT ("Unable to open file")); + case DBEVIEW_BAD_DATA: + return dbe_strdup (GTXT ("Data corrupted")); + case DBEVIEW_BAD_SYMBOL_DATA: + return dbe_strdup (GTXT ("Functions/Modules information corrupted")); + case DBEVIEW_NO_SEL_OBJ: + return dbe_strdup (GTXT ("No selected object, bring up Functions Tab")); + } + return NULL; +} + +Histable * +DbeView::set_sel_obj (Histable *obj) +{ + if (obj) + { + switch (obj->get_type ()) + { + case Histable::INSTR: + lastSelInstr = (DbeInstr *) obj; + lastSelFunc = lastSelInstr->func; + this->sel_binctx = lastSelFunc; + break; + case Histable::FUNCTION: + if (lastSelInstr && lastSelInstr->func != obj) + lastSelInstr = NULL; + lastSelFunc = (Function *) obj; + break; + case Histable::LINE: + { + DbeLine *dbeLine = (DbeLine *) obj; + if (dbeLine->func) + { + // remember previous DbeInstr and DbeFunc + lastSelFunc = dbeLine->func; + if (lastSelInstr && lastSelInstr->func != lastSelFunc) + lastSelInstr = NULL; + this->sel_binctx = lastSelFunc; + } + else + this->sel_binctx = dbeLine->convertto (Histable::FUNCTION); + break; + } + case Histable::MODULE: + case Histable::LOADOBJECT: + case Histable::EADDR: + case Histable::MEMOBJ: + case Histable::INDEXOBJ: + case Histable::PAGE: + case Histable::DOBJECT: + case Histable::SOURCEFILE: + case Histable::IOACTFILE: + case Histable::IOACTVFD: + case Histable::IOCALLSTACK: + case Histable::HEAPCALLSTACK: + case Histable::EXPERIMENT: + case Histable::OTHER: + break; + } + } + sel_obj = obj; + Dprintf (DEBUG_DBE, NTXT ("### set_sel_obj: DbeView.cc:%d obj %s\n"), + __LINE__, obj ? obj->dump () : "NULL"); + Dprintf (DEBUG_DBE, NTXT ("### set_sel_obj: DbeView.cc:%d sel_obj %s\n"), + __LINE__, sel_obj ? sel_obj->dump () : "NULL"); + Dprintf (DEBUG_DBE, NTXT ("### set_sel_obj: DbeView.cc:%d lastSelFunc %s\n"), + __LINE__, lastSelFunc ? lastSelFunc->dump () : "NULL"); + Dprintf (DEBUG_DBE, NTXT ("### set_sel_obj: DbeView.cc:%d lastSelInstr %s\n"), + __LINE__, lastSelInstr ? lastSelInstr->dump () : "NULL"); + return sel_obj; +} + +DbeInstr * +DbeView::convert_line_to_instr (DbeLine *dbeLine) +{ + Dprintf (DEBUG_DBE, "### convert_line_to_instr DbeView::%d dbeLine=%s\n", __LINE__, dbeLine->dump ()); + Function *func = convert_line_to_func (dbeLine); + if (func) + { + Dprintf (DEBUG_DBE, "### convert_line_to_instr DbeView::%d func=%s\n", __LINE__, func->dump ()); + DbeInstr *dbeInstr = func->mapLineToPc (dbeLine); + Dprintf (DEBUG_DBE && dbeInstr, "### convert_line_to_instr DbeView::%d dbeInstr=%s\n", __LINE__, dbeInstr->dump ()); + return dbeInstr; + } + Dprintf (DEBUG_DBE && lastSelInstr, "### convert_line_to_instr DbeView::%d lastSelInstr=%s\n", __LINE__, lastSelInstr->dump ()); + return lastSelInstr; +} + +DbeInstr * +DbeView::convert_func_to_instr (Function *func) +{ + return (lastSelInstr && lastSelInstr->func == func) ? + lastSelInstr : (DbeInstr *) func->convertto (Histable::INSTR); +} + +Function * +DbeView::convert_line_to_func (DbeLine *dbeLine) +{ + Function *func = dbeLine->func; + if (func) + return func; + if (lastSelFunc != NULL) + // Can be mapped to the same function ? + for (DbeLine *dl = dbeLine->dbeline_base; dl; dl = dl->dbeline_func_next) + if (dl->func == lastSelFunc) + return lastSelFunc; + + PathTree *pathTree = NULL; + Function *firstFunc = NULL; + for (DbeLine *dl = dbeLine->dbeline_base; dl; dl = dl->dbeline_func_next) + { + // Find a first function with non-zero metrics + if (dl->func) + { + if (pathTree == NULL) + pathTree = get_path_tree (); + if (pathTree->get_func_nodeidx (dl->func)) + return dl->func; + if (firstFunc == NULL) + firstFunc = dl->func; + } + } + // Take a first function + return firstFunc; +} + +Histable * +DbeView::get_sel_obj (Histable::Type type) +{ + Histable *lastSelObj = sel_obj; + Dprintf (DEBUG_DBE, NTXT ("### get_sel_obj: DbeView.cc:%d type=%d sel_obj %s\n"), + __LINE__, type, lastSelObj ? lastSelObj->dump () : "NULL"); + if (lastSelObj == NULL) + return NULL; + switch (type) + { + case Histable::INSTR: + if (!showAll) + { + // DBFIXME LIBRARY VISIBILITY + // hack to get to the hide mode object for PCs when filtering + // with a PC in timeline + if (lastSelObj->get_type () == Histable::INSTR) + { + Function *func = (Function*) (lastSelObj->convertto (Histable::FUNCTION)); + LoadObject *lo = func->module->loadobject; + if (get_lo_expand (lo->seg_idx) == LIBEX_HIDE) + return lo->get_hide_function (); + } + } + if (lastSelObj->get_type () == Histable::LINE) + return convert_line_to_instr ((DbeLine*) lastSelObj); + else if (lastSelObj->get_type () == Histable::FUNCTION) + return convert_func_to_instr ((Function *) lastSelObj); + return lastSelObj->convertto (type); + case Histable::FUNCTION: + if (lastSelObj->get_type () == Histable::LINE) + { + Function *func = convert_line_to_func ((DbeLine*) lastSelObj); + if (func) + return func; + return NULL; + } + return lastSelObj->convertto (type); + case Histable::LINE: + default: + return lastSelObj->convertto (type); + } +} diff --git a/gprofng/src/DbeView.h b/gprofng/src/DbeView.h new file mode 100644 index 0000000..bb6bb90 --- /dev/null +++ b/gprofng/src/DbeView.h @@ -0,0 +1,842 @@ +/* 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. */ + +/* + * The DbeView class represents a window into the data managed by a DbeSession + * + * A DbeView has a Settings class that determines the user preferences, + * instantiated initially as a copy of the one in the DbeSession + * that created it, or in the DbeView being cloned by the DbeSession + * + * A DbeView has a vector of Experiment pointers, matching the one in the + * DbeSession, and a vector of enable bits governing which of the + * Experiments are currently being used to process the data. + * + * A DbeView has three vectors of Metrics, one for functions, etc., + * a second for callers/callees, and a third for dataspace/memoryspace. + * + * A DbeView has a vector of FilterSet's (q.v.), one for each Experiment, + * used to determine how the data is filtered. + * + * Each DbeView has its own instantiation of the objects representing + * the processed, filtered data. Currently these are a PathTree + * for computing text-based metrics, a DataSpace for computing + * data-based metrics, and a MemorySpace used for computing + * memory-object-based metrics. + */ + +#ifndef _DBEVIEW_H +#define _DBEVIEW_H + +#include <stdio.h> +#include "dbe_structs.h" +#include "vec.h" +#include "enums.h" +#include "util.h" +#include "DerivedMetrics.h" +#include "Histable.h" +#include "Hist_data.h" +#include "Settings.h" +#include "Metric.h" +#include "Table.h" +#include "PathTree.h" + +class Application; +class DataView; +class Experiment; +class Expression; +class FilterSet; +class FilterNumeric; +class FilterExp; +class Function; +class Histable; +class MetricList; +class Module; +class Ovw_data; +class PathTree; +class DataSpace; +class MemorySpace; +class Stats_data; +class LoadObject; +class IOActivity; +class HeapActivity; + +class DbeView +{ +public: + DbeView (Application *app, Settings *_settings, int _vindex); + DbeView (DbeView *dbev, int _vindex); + ~DbeView (); + + // Access functions for settings in the view + Settings * + get_settings () + { + return settings; + }; + + // Get the list of tabs for this view + Vector<DispTab*> * + get_TabList () + { + return settings->get_TabList (); + }; + + // Get the list of memory tabs for this view + Vector<bool> * + get_MemTabState () + { + return settings->get_MemTabState (); + }; + + // Set the list of memory tabs for this view + void + set_MemTabState (Vector<bool>*sel) + { + settings->set_MemTabState (sel); + }; + + // Get the list of index tabs for this view + Vector<bool> * + get_IndxTabState () + { + return settings->get_IndxTabState (); + }; + + // Set the list of memory tabs for this view + void + set_IndxTabState (Vector<bool>*sel) + { + settings->set_IndxTabState (sel); + }; + + // controlling the name format + Cmd_status + set_name_format (char *str) + { + return settings->set_name_format (str); + }; + + void + set_name_format (int fname_format, bool soname) + { + settings->set_name_format (fname_format, soname); + }; + + Histable::NameFormat + get_name_format () + { + return settings->name_format; + } + + // processing modes: view_mode + Cmd_status set_view_mode (char *str, bool fromRC); // from a string + void set_view_mode (VMode mode); // from the analyzer + + VMode + get_view_mode () + { + return settings->get_view_mode (); + }; + + // handling of descendant processes + Cmd_status set_en_desc (char *str, bool rc); // from a string + + bool + check_en_desc (const char * lineage_name = NULL, const char *targname = NULL) + { + return settings->check_en_desc (lineage_name, targname); + }; + + // Controlling the print line-limit + char * + set_limit (char *str, bool rc) // from a string + { + settings->set_limit (str, rc); + return NULL; + }; + + char * + set_limit (int _limit) + { + settings->limit = _limit; + return NULL; + }; + + int + get_limit () + { + return settings->limit; + }; + + // Controlling the print format mode + char * + set_printmode (char *str) + { + return settings->set_printmode (str); + }; + + enum PrintMode + get_printmode () + { + return settings->print_mode; + }; + + char + get_printdelimiter () + { + return settings->print_delim; + }; + + char * + get_printmode_str () + { + return dbe_strdup (settings->str_printmode); + }; + + // processing compiler commentary visibility bits, and other source annotation + // controls + Cmd_status + proc_compcom (const char *cmd, bool isSrc, bool rc) + { + return settings->proc_compcom (cmd, isSrc, rc); + }; + + char * + get_str_scompcom () + { + return settings->str_scompcom; + }; + + char * + get_str_dcompcom () + { + return settings->str_dcompcom; + }; + + void + set_src_compcom (int v) + { + settings->src_compcom = v; + }; + + int + get_src_compcom () + { + return settings->src_compcom; + }; + + void + set_dis_compcom (int v) + { + settings->dis_compcom = v; + }; + + int + get_dis_compcom () + { + return settings->dis_compcom; + }; + + void + set_cmpline_visible (bool vis) + { + settings->set_cmpline_visible (vis); + } + + int + get_cmpline_visible () + { + return settings->cmpline_visible; + } + + void + set_funcline_visible (bool vis) + { + settings->set_funcline_visible (vis); + } + + int + get_funcline_visible () + { + return settings->funcline_visible; + } + + // controls for disassembly presentation + void + set_src_visible (int vis) + { + settings->set_src_visible (vis); + } + + int + get_src_visible () + { + return settings->src_visible; + } + + void + set_srcmetric_visible (bool vis) + { + settings->set_srcmetric_visible (vis); + } + + bool + get_srcmetric_visible () + { + return settings->srcmetric_visible; + } + + void + set_hex_visible (bool vis) + { + settings->set_hex_visible (vis); + } + + bool + get_hex_visible () + { + return settings->hex_visible; + } + + // processing and accessing the threshold settings + Cmd_status + proc_thresh (char *cmd, bool isSrc, bool rc) + { + return settings->proc_thresh (cmd, isSrc, rc); + }; + + void + set_thresh_src (int v) + { + settings->threshold_src = v; + }; + + int + get_thresh_src () + { + return settings->threshold_src; + }; + + void + set_thresh_dis (int v) + { + settings->threshold_dis = v; + }; + + int + get_thresh_dis () + { + return settings->threshold_dis; + }; + + // controls for the Timeline mode, stack presentation + Cmd_status + proc_tlmode (char *cmd, bool rc) + { + return settings->proc_tlmode (cmd, rc); + }; + + void + set_tlmode (int _tlmode) + { + settings->tlmode = _tlmode; + }; + + int + get_tlmode () + { + return settings->tlmode; + }; + + void + set_stack_align (int _stack_align) + { + settings->stack_align = _stack_align; + }; + + int + get_stack_align () + { + return settings->stack_align; + }; + + void + set_stack_depth (int _stack_depth) + { + settings->stack_depth = _stack_depth; + }; + + int + get_stack_depth () + { + return settings->stack_depth; + }; + + // Controls for which data is shown in Timeline + Cmd_status + proc_tldata (char *cmd, bool rc) + { + return settings->proc_tldata (cmd, rc); + }; + + void + set_tldata (const char* tldata_cmd) + { + settings->set_tldata (tldata_cmd); + }; + + char* + get_tldata () + { + return settings->get_tldata (); + }; + + // settings controlling the expand/collapse of functions within each LoadObject + enum LibExpand get_lo_expand (int idx); + + // set_lo_expand -- returns true if any change + bool set_lo_expand (int idx, enum LibExpand how); + + // set_libexpand -- returns true if any change + bool set_libexpand (char *liblist, enum LibExpand flag); + void update_lo_expands (); + bool set_libdefaults (); + void reset (); + void reset_data (bool all); + + char * + get_error_msg () + { + return error_msg; + }; + + void + clear_error_msg () + { + error_msg = NULL; + }; + + char * + get_warning_msg () + { + return warning_msg; + }; + + void + clear_warning_msg () + { + warning_msg = NULL; + }; + char *get_processor_msg (int type); + + // methods controlling the metric list + BaseMetric *register_metric_expr (BaseMetric::Type type, char *aux, char *expr_spec); + Vector<BaseMetric*> *get_all_reg_metrics (); + void reset_metric_list (MetricList *mlist, int cmp_mode); + + // Get the various metric master lists + MetricList *get_metric_ref (MetricType mtype); + + // Get the various current metric lists + MetricList *get_metric_list (int dsptype, int subtype); + MetricList *get_metric_list (MetricType mtype); + MetricList *get_metric_list (MetricType mtype, bool compare, int gr_num); + MetricList *get_compare_mlist (MetricList *met_list, int grInd); + + // Set the metric list, from a string specification + char *setMetrics (char *metricspec, bool fromRcFile); + + // Set the sort metric, from its name + char *setSort (char *sortname, MetricType mtype, bool fromRcFile); + + // Set the sort metric, from its visible index (Analyzer) + void setSort (int visindex, MetricType mtype, bool reverse); + + // Resort any cached data, after changing sort + void resortData (MetricType mtype); + + // Get the sort metric + char *getSort (MetricType mtype); + char *getSortCmd (MetricType mtype); + + // reset the metrics + void reset_metrics (); + bool comparingExperiments (); + + int + get_compare_mode () + { + return settings->compare_mode; + }; + + void + reset_compare_mode (int mode) + { + settings->compare_mode = mode; + }; + + void set_compare_mode (int mode); // modifies the global MET_* arrays + void add_compare_metrics (MetricList *mlist); + void remove_compare_metrics (MetricList *mlist); + Histable *get_compare_obj (Histable *obj); + + // method for printing the instruction-frequency report + void ifreq (FILE *); + + // methods controlling the experiment list + void add_experiment (int index, bool enabled); + void add_subexperiment (int index, bool enabled); + void add_experiment_epilogue (); + void drop_experiment (int index); + bool get_exp_enable (int n); + void set_exp_enable (int n, bool e); + + // method for new-style filtering + char *set_filter (const char *filter_spec); + char *get_filter (void); + char *get_advanced_filter (); + void backtrack_filter (); + void update_advanced_filter (); + FilterExp *get_FilterExp (Experiment *exp); + + Expression * + get_filter_expr () + { + return cur_filter_expr; + }; + + // methods controlling old-style filtering + Vector<FilterNumeric*> *get_all_filters (int nexp); + FilterNumeric *get_FilterNumeric (int nexp, int idx); + bool set_pattern (int n, Vector<char *> *pattern_str, bool *error); + bool set_pattern (int m, char *pattern); + + // Data processing objects + PathTree * + get_path_tree () + { + return ptree; + }; + + DataSpace * + get_data_space () + { + return dspace; + }; + + IOActivity * + get_io_space () + { + return iospace; + }; + + HeapActivity * + get_heap_space () + { + return heapspace; + }; + Hist_data *get_data (MetricList *mlist, Histable *selObj, int type, int subtype); + int get_sel_ind (Histable *selObj, int type, int subtype); + + // Return histogram data for the specified arguments. + Hist_data *get_hist_data (MetricList *mlist, Histable::Type type, + int subtype, // used for memory objects only + Hist_data::Mode mode, + Vector<Histable*> *objs = NULL, + Histable *context = NULL, + Vector<Histable*> *sel_objs = NULL, + PathTree::PtreeComputeOption flag = PathTree::COMPUTEOPT_NONE + ); + Hist_data *get_hist_data (MetricList *mlist, Histable::Type type, + int subtype, // used for memory objects only + Hist_data::Mode mode, Histable *obj, + Histable *context = NULL, + Vector<Histable*> *sel_objs = NULL, + PathTree::PtreeComputeOption flag = PathTree::COMPUTEOPT_NONE + ); + CStack_data *get_cstack_data (MetricList *); + Stats_data *get_stats_data (int index); + Ovw_data *get_ovw_data (int index); + + char *names_src[3]; // names for anno-src + char *names_dis[3]; // names for anno-dis + + // Get filtered packets. Ordering is NOT guaranteed to be + // stable between calls; DataView indexes are not persistent - + // use underlying DataDescriptor ids if you don't consume data right away. + // Parameters: idx==exp_id, data_id==kind==ProfData_type + DataView *get_filtered_events (int idx, int data_id); + DataView *get_filtered_events (int idx, int data_id, + const int sortprops[], int sortprop_count); + + // SORT is not used for PathTree. + // PathTree reads data once and discards. It doesn't + // depend on the indices so sort can be performed w/o recomputing pathtree. + // Timeline is the primary consumer of sort(), however Races also need to sort. + // + // YM: I can't remember the context for the following note, but + // In case I need it when we refactor more TBR stuff, here it is: + // base metrics like USER_CPU are known,(but we should/should not?) + // explicitly set DATA_CLOCK as a property/attribute? + bool adjust_filter (Experiment *exp); + + // Generated report data + Hist_data *func_data; // function list data + Hist_data *line_data; // hot line list data + Hist_data *pc_data; // hot PC list data + Hist_data *src_data; // annotated source data + Hist_data *dis_data; // annotated disasm data + Hist_data *fitem_data; // func item for callers/callees + Hist_data *callers; // callers data + Hist_data *callees; // callees data + Hist_data *dobj_data; // data object list data + Hist_data *dlay_data; // data layout data + Hist_data *iofile_data; // io data aggregated by file name + Hist_data *iovfd_data; // io data aggregated by virtual fd + Hist_data *iocs_data; // io data aggregated by call stack + Hist_data *heapcs_data; // heap data aggregated by call stack + Vector<Hist_data*> *indx_data; // index object data + Vector<int> *lobjectsNoJava; // List of indices into LoadObjects excluding java classes + + // memory object data -- create MemorySpace, if needed + MemorySpace *getMemorySpace (int subtype); + char *get_mobj_name (int subtype); + void addIndexSpace (int type); + Hist_data *get_indxobj_data (int subtype); + void set_indxobj_sel (int subtype, int sel_ind); + Histable *get_indxobj_sel (int subtype); + void set_obj_sel_io (int type, long sel_ind); + Histable *set_sel_obj (Histable *obj); + Histable *get_sel_obj (Histable::Type type); + Histable *get_sel_obj_io (uint64_t id, Histable::Type type); + Histable *get_sel_obj_heap (uint64_t id); + Histable *sel_obj; // current selected obj + Histable *sel_dobj; // current selected data obj + Histable *sel_binctx; // current binary context + Vector<Histable*> *sel_idxobj; // selected index objects + char *error_msg; // error message + char *warning_msg; // warning message + Vector<int> *marks; // flagged as important for anno src/dis + Vector<int_pair_t> *marks2dsrc; + Vector<int_pair_t> *marks2dsrc_inc; + Vector<int_pair_t> *marks2ddis; + Vector<int_pair_t> *marks2ddis_inc; + + void dump_nodes (FILE *); // dump out the pathtree nodes + void dump_profile (FILE *); // dump out the clock profiling events + void dump_sync (FILE *); // dump out the synctrace events + void dump_iotrace (FILE *); // dump out the IO trace events + void dump_hwc (FILE *); // dump out the HWC Profiling events + void dump_heap (FILE *); // dump out the heap trace events + void dump_gc_events (FILE *); // dump out the Java garbage collector events + + int vindex; // index of this view -- set by Analyzer + bool func_scope; + + bool + get_func_scope () + { + return func_scope; + }; + + void + set_func_scope (bool scope_only) + { + func_scope = scope_only; + }; + + // value set T if filtering is active, i.e., some packets were dropped + bool filter_active; + + bool + get_filter_active () + { + return filter_active; + }; + + DerivedMetrics * + get_derived_metrics () + { + return derived_metrics; + } + + // Internal time (time means change) + int + getPhaseIdx () + { + return phaseIdx; + } + + enum DbeView_status + { + DBEVIEW_SUCCESS = 0, + DBEVIEW_NO_DATA, + DBEVIEW_IO_ERROR, + DBEVIEW_BAD_DATA, + DBEVIEW_BAD_SYMBOL_DATA, + DBEVIEW_NO_SEL_OBJ + }; + static char *status_str (DbeView_status status); + + bool + isOmpDisMode () + { + return ompDisMode; + } + + void + setOmpDisMode () + { + ompDisMode = true; + } + + void + resetOmpDisMode () + { + ompDisMode = false; + } + + bool + isShowHideChanged () + { + return showHideChanged; + } + + void + setShowHideChanged () + { + showHideChanged = true; + } + + void + resetShowHideChanged () + { + showHideChanged = false; + } + + bool + isNewViewMode () + { + return newViewMode; + } + + void + setNewViewMode () + { + newViewMode = true; + } + + void + resetNewViewMode () + { + newViewMode = false; + } + + bool + isFilterHideMode () + { + return filterHideMode; + } + + void + setFilterHideMode () + { + filterHideMode = true; + } + + void + resetFilterHideMode () + { + filterHideMode = false; + } + + bool + isShowAll () + { + return showAll; + } + + void + setShowAll () + { + showAll = true; + } + + void + resetShowAll () + { + showAll = false; + } + void resetAndConstructShowHideStacks (); + +private: + void init (); + Metric *get_compare_metric (Metric *mtr, int groupNum); + + // methods controlling old-style filtering + FilterSet *get_filter_set (int n); + + void purge_events (int n = -1); + + char *cur_filter_str; + char *prev_filter_str; + Expression *cur_filter_expr; + bool noParFilter; + + // MemorySpace's -- added when a request is made; for now, never dropped + Vector<MemorySpace*> *memspaces; + MemorySpace *addMemorySpace (int mtype); + + Vector<FilterSet*> *filters; + Vector<enum LibExpand> *lo_expands; + Vector<BaseMetric*> *reg_metrics; // vector of registered metrics + Vector<MetricList*> *metrics_lists; // metrics list of MET_NORMAL, MET_CALL... + // note: includes compare metrics + Vector<MetricList*> *metrics_ref_lists; + DerivedMetrics *derived_metrics; // vector of derived metrics + + DataSpace *dspace; + PathTree *ptree; + Vector<PathTree *> *indxspaces; + IOActivity *iospace; + HeapActivity *heapspace; + int phaseIdx; + bool ompDisMode; + bool filterHideMode; + bool showAll; + bool showHideChanged; + bool newViewMode; + + // Filtered events + Vector<Vector<DataView*>*> *dataViews; //outer idx is exp_id, inner is data_id + Settings *settings; + + Application *app; + Function *convert_line_to_func (DbeLine *dbeLine); + DbeInstr *convert_line_to_instr (DbeLine *dbeLine); + DbeInstr *convert_func_to_instr (Function *func); + DbeInstr *lastSelInstr; + Function *lastSelFunc; + void constructShowHideStack (DataDescriptor* dDscr, Experiment *exp); + void resetAndConstructShowHideStack (Experiment *exp); +}; + +#endif /* _DBEVIEW_H */ diff --git a/gprofng/src/DefaultHandler.h b/gprofng/src/DefaultHandler.h new file mode 100644 index 0000000..4c3d82c --- /dev/null +++ b/gprofng/src/DefaultHandler.h @@ -0,0 +1,114 @@ +/* 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. */ + +/* + * org/xml/sax/helpers/DefaultHandler.java + * Based on JavaTM 2 Platform Standard Ed. 5.0 + */ + +#ifndef _DefaultHandler_h +#define _DefaultHandler_h + +/* + * org/xml/sax/Attributes.java + */ +class Attributes +{ +public: + virtual ~Attributes () { }; + + virtual int getLength () = 0; + virtual const char *getQName (int index) = 0; + virtual const char *getValue (int index) = 0; + virtual int getIndex (const char *qName) = 0; + virtual const char *getValue (const char *qName) = 0; +}; + +/* + * org/xml/sax/SAXException.java + */ +class SAXException +{ +public: + SAXException (); + SAXException (const char *message); + virtual ~SAXException (); + char *getMessage (); + +private: + char *message; +}; + +class SAXParseException : public SAXException +{ +public: + SAXParseException (char *message, int lineNumber, int columnNumber); + + int + getLineNumber () + { + return lineNumber; + } + + int + getColumnNumber () + { + return columnNumber; + } + +private: + int lineNumber; + int columnNumber; +}; + +class DefaultHandler +{ +public: + virtual ~DefaultHandler () { }; + + virtual void startDocument () = 0; + virtual void endDocument () = 0; + virtual void startElement (char *uri, char *localName, char *qName, + Attributes *attributes) = 0; + virtual void endElement (char *uri, char *localName, char *qName) = 0; + virtual void characters (char *ch, int start, int length) = 0; + virtual void ignorableWhitespace (char *ch, int start, int length) = 0; + + virtual void + warning (SAXParseException *e) + { + delete e; + } + + virtual void + error (SAXParseException *e) + { + delete e; + } + + virtual void + fatalError (SAXParseException *e) + { + throw ( e); + } + void dump_startElement (const char *qName, Attributes *attributes); +}; + +#endif /* _DefaultHandler_h */ diff --git a/gprofng/src/DefaultMap.h b/gprofng/src/DefaultMap.h new file mode 100644 index 0000000..4a87fcc --- /dev/null +++ b/gprofng/src/DefaultMap.h @@ -0,0 +1,232 @@ +/* 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. */ + +#ifndef _DBE_DEFAULTMAP_H +#define _DBE_DEFAULTMAP_H + +#include <assert.h> +#include <vec.h> +#include <Map.h> + +template <typename Key_t, typename Value_t> +class DefaultMap : public Map<Key_t, Value_t> +{ +public: + + DefaultMap (); + ~DefaultMap (); + void clear (); + void put (Key_t key, Value_t val); + Value_t get (Key_t key); + Value_t get (Key_t key, typename Map<Key_t, Value_t>::Relation rel); + Value_t remove (Key_t); + Vector<Key_t> *keySet (); + Vector<Value_t> *values (); + +private: + + struct Entry + { + Key_t key; + Value_t val; + }; + + static const int CHUNK_SIZE; + static const int HTABLE_SIZE; + + int entries; + int nchunks; + Entry **chunks; + Vector<Entry*> *index; + Entry **hashTable; +}; + + +template <typename Key_t, typename Value_t> +const int DefaultMap<Key_t, Value_t>::CHUNK_SIZE = 16384; +template <typename Key_t, typename Value_t> +const int DefaultMap<Key_t, Value_t>::HTABLE_SIZE = 1024; + +template <typename Key_t, typename Value_t> +DefaultMap<Key_t, Value_t>::DefaultMap () +{ + entries = 0; + nchunks = 0; + chunks = NULL; + index = new Vector<Entry*>; + hashTable = new Entry*[HTABLE_SIZE]; + for (int i = 0; i < HTABLE_SIZE; i++) + hashTable[i] = NULL; +} + +template <typename Key_t, typename Value_t> +DefaultMap<Key_t, Value_t>::~DefaultMap () +{ + for (int i = 0; i < nchunks; i++) + delete[] chunks[i]; + delete[] chunks; + delete index; + delete[] hashTable; +} + +template <typename Key_t, typename Value_t> +void +DefaultMap<Key_t, Value_t>::clear () +{ + entries = 0; + index->reset (); + for (int i = 0; i < HTABLE_SIZE; i++) + hashTable[i] = NULL; +} + +template <typename Key_t> +inline unsigned +hash (Key_t key) +{ + unsigned h = (unsigned) ((unsigned long) key); + h ^= (h >> 20) ^ (h >> 12); + return (h ^ (h >> 7) ^ (h >> 4)); +} + +template <typename Key_t, typename Value_t> +void +DefaultMap<Key_t, Value_t>::put (Key_t key, Value_t val) +{ + unsigned idx = hash (key) % HTABLE_SIZE; + Entry *entry = hashTable[idx]; + if (entry && entry->key == key) + { + entry->val = val; + return; + } + int lo = 0; + int hi = entries - 1; + while (lo <= hi) + { + int md = (lo + hi) / 2; + entry = index->fetch (md); + int cmp = entry->key < key ? -1 : entry->key > key ? 1 : 0; + if (cmp < 0) + lo = md + 1; + else if (cmp > 0) + hi = md - 1; + else + { + entry->val = val; + return; + } + } + if (entries >= nchunks * CHUNK_SIZE) + { + nchunks++; + // Reallocate Entry chunk array + Entry **new_chunks = new Entry*[nchunks]; + for (int i = 0; i < nchunks - 1; i++) + new_chunks[i] = chunks[i]; + delete[] chunks; + chunks = new_chunks; + + // Allocate new chunk for entries. + chunks[nchunks - 1] = new Entry[CHUNK_SIZE]; + } + entry = &chunks[entries / CHUNK_SIZE][entries % CHUNK_SIZE]; + entry->key = key; + entry->val = val; + index->insert (lo, entry); + hashTable[idx] = entry; + entries++; +} + +template <typename Key_t, typename Value_t> +Value_t +DefaultMap<Key_t, Value_t>::get (Key_t key) +{ + unsigned idx = hash (key) % HTABLE_SIZE; + Entry *entry = hashTable[idx]; + if (entry && entry->key == key) + return entry->val; + + int lo = 0; + int hi = entries - 1; + while (lo <= hi) + { + int md = (lo + hi) / 2; + entry = index->fetch (md); + int cmp = entry->key < key ? -1 : entry->key > key ? 1 : 0; + if (cmp < 0) + lo = md + 1; + else if (cmp > 0) + hi = md - 1; + else + { + hashTable[idx] = entry; + return entry->val; + } + } + return (Value_t) 0; +} + +template <typename Key_t, typename Value_t> +Value_t +DefaultMap<Key_t, Value_t>::get (Key_t key, + typename Map<Key_t, Value_t>::Relation rel) +{ + if (rel != Map<Key_t, Value_t>::REL_EQ) + return (Value_t) 0; + return get (key); +} + +template <typename Key_t, typename Value_t> +Value_t +DefaultMap<Key_t, Value_t>::remove (Key_t) +{ + // Not implemented + if (1) + assert (0); + return (Value_t) 0; +} + +template <typename Key_t, typename Value_t> +Vector<Value_t> * +DefaultMap<Key_t, Value_t>::values () +{ + Vector<Value_t> *vals = new Vector<Value_t>(entries); + for (int i = 0; i < entries; ++i) + { + Entry *entry = index->fetch (i); + vals->append (entry->val); + } + return vals; +} + +template <typename Key_t, typename Value_t> +Vector<Key_t> * +DefaultMap<Key_t, Value_t>::keySet () +{ + Vector<Key_t> *keys = new Vector<Key_t>(entries); + for (int i = 0; i < entries; ++i) + { + Entry *entry = index->fetch (i); + keys->append (entry->key); + } + return keys; +} + +#endif diff --git a/gprofng/src/DefaultMap2D.h b/gprofng/src/DefaultMap2D.h new file mode 100644 index 0000000..8045aad --- /dev/null +++ b/gprofng/src/DefaultMap2D.h @@ -0,0 +1,147 @@ +/* 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. */ + +#ifndef _DBE_DEFAULTMAP2D_H +#define _DBE_DEFAULTMAP2D_H + +#include <assert.h> +#include <vec.h> +#include <DefaultMap.h> +#include <IntervalMap.h> +#include <Map2D.h> + +/* + * Default Map2D implementation. + * + * Default Map2D is a cartesian product of two default Maps. + */ + +template <typename Key1_t, typename Key2_t, typename Value_t> +class DefaultMap2D : public Map2D<Key1_t, Key2_t, Value_t> +{ +public: + DefaultMap2D (); + DefaultMap2D (typename Map2D<Key1_t, Key2_t, Value_t>::MapType _type); + ~DefaultMap2D (); + void put (Key1_t key1, Key2_t key2, Value_t val); + Value_t get (Key1_t key1, Key2_t key2); + Value_t get (Key1_t key1, Key2_t key2, + typename Map2D<Key1_t, Key2_t, Value_t>::Relation rel); + Value_t remove (Key1_t, Key2_t); + +private: + typename Map2D<Key1_t, Key2_t, Value_t>::MapType type; + Map<Key1_t, Map<Key2_t, Value_t>*> *map1; + Vector<Map<Key2_t, Value_t>*> *map2list; +}; + +template <typename Key1_t, typename Key2_t, typename Value_t> +DefaultMap2D<Key1_t, Key2_t, Value_t>::DefaultMap2D () +{ + type = Map2D<Key1_t, Key2_t, Value_t>::Default; + map1 = new DefaultMap<Key1_t, Map<Key2_t, Value_t>*>; + map2list = new Vector<Map<Key2_t, Value_t>*>; +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +DefaultMap2D<Key1_t, Key2_t, Value_t>::DefaultMap2D ( + typename Map2D<Key1_t, Key2_t, Value_t>::MapType _type) +{ + type = _type; + map1 = new DefaultMap<Key1_t, Map<Key2_t, Value_t>*>; + map2list = new Vector<Map<Key2_t, Value_t>*>; +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +DefaultMap2D<Key1_t, Key2_t, Value_t>::~DefaultMap2D () +{ + map2list->destroy (); + delete map2list; + delete map1; +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +void +DefaultMap2D<Key1_t, Key2_t, Value_t>::put (Key1_t key1, Key2_t key2, Value_t val) +{ + Map<Key2_t, Value_t> *map2 = map1->get (key1); + if (map2 == NULL) + { + if (type == Map2D<Key1_t, Key2_t, Value_t>::Interval) + map2 = new IntervalMap<Key2_t, Value_t>; + else + map2 = new DefaultMap<Key2_t, Value_t>; + map2list->append (map2); + map1->put (key1, map2); + } + map2->put (key2, val); +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +Value_t +DefaultMap2D<Key1_t, Key2_t, Value_t>::get (Key1_t key1, Key2_t key2) +{ + Map<Key2_t, Value_t> *map2 = map1->get (key1); + if (map2 == NULL) + return (Value_t) 0; + return map2->get (key2); +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +Value_t +DefaultMap2D<Key1_t, Key2_t, Value_t>::get (Key1_t key1, Key2_t key2, + typename Map2D<Key1_t, Key2_t, Value_t>::Relation rel) +{ + Map<Key2_t, Value_t> *map2 = map1->get (key1); + if (map2 == NULL) + return (Value_t) 0; + typename Map<Key2_t, Value_t>::Relation rel2; + switch (rel) + { + case Map2D<Key1_t, Key2_t, Value_t>::REL_EQLT: + rel2 = map2->REL_LT; + break; + case Map2D<Key1_t, Key2_t, Value_t>::REL_EQLE: + rel2 = map2->REL_LE; + break; + case Map2D<Key1_t, Key2_t, Value_t>::REL_EQGE: + rel2 = map2->REL_GE; + break; + case Map2D<Key1_t, Key2_t, Value_t>::REL_EQGT: + rel2 = map2->REL_GT; + break; + default: + rel2 = map2->REL_EQ; + break; + } + return map2->get (key2, rel2); +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +Value_t +DefaultMap2D<Key1_t, Key2_t, Value_t>::remove (Key1_t, Key2_t) +{ + // Not implemented + if (1) + assert (0); + return (Value_t) 0; +} + +#endif diff --git a/gprofng/src/DerivedMetrics.cc b/gprofng/src/DerivedMetrics.cc new file mode 100644 index 0000000..9f504a5 --- /dev/null +++ b/gprofng/src/DerivedMetrics.cc @@ -0,0 +1,293 @@ +/* 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 <strings.h> +#include "DerivedMetrics.h" +#include "util.h" + +enum opType +{ + opNULL, + opPrimitive, + opDivide +}; + +class definition +{ +public: + definition(); + ~definition(); + char *name; + char *def; + opType op; + definition *arg1; + definition *arg2; + int index; +}; + +definition::definition () +{ + name = def = NULL; + arg1 = arg2 = NULL; +} + +definition::~definition () +{ + free (name); + free (def); +} + +DerivedMetrics::DerivedMetrics () +{ + items = new Vector<definition*>; +} + +DerivedMetrics::~DerivedMetrics () +{ + Destroy (items); +} + +definition * +DerivedMetrics::add_definition (char *_name, char *_username, char *_def) +{ + definition *p; + + // if the name doesn't matter, maybe there is a duplicate we can use + if (_name == NULL) + { + int i; + Vec_loop (definition*, items, i, p) + { + if (strcmp (p->def, _def) == 0) + return p; + } + } + + p = new definition; + p->name = dbe_strdup (_name); + p->def = dbe_strdup (_def); + + // parse the definition + if (strchr (_def, '/') == NULL) + { + // it's a primitive metric + p->op = opPrimitive; + p->arg1 = p->arg2 = NULL; + + } + else + { + // it's some operation on arguments + p->op = opDivide; + char *op_ptr = strchr (p->def, '/'); + *op_ptr = 0; + p->arg1 = add_definition (NULL, NULL, p->def); + *op_ptr = '/'; + p->arg2 = add_definition (NULL, NULL, op_ptr + 1); + } + p->index = items->size (); + items->append (p); + return p; +} + +int * +DerivedMetrics::construct_map (Vector<Metric*> *mitems, BaseMetric::SubType st, char *expr_spec) +{ + if (items == NULL) + return NULL; + int ndm = items->size (); + if (ndm == 0) + return NULL; + int nmetrics = mitems->size (); + + // allocate arrays for the mapping between derived metrics and requested values + int *map = (int *) malloc (ndm * sizeof (int)); + + // map derived metrics to requested metrics // EUGENE explain this more clearly + // 0 means not mapped + // >0 means primitive metric maps to map-1 + // <0 means derived metric maps to 1-map + int ndm_requested = 0; + for (int idm = 0; idm < ndm; idm++) + { + definition *defdm = items->fetch (idm); + map[idm] = 0; + + // figure out what name to use for this derived metric + char *dname; + if (defdm->op == opPrimitive) + dname = defdm->def; + else + { + dname = defdm->name; + if (dname == NULL) break; + } + + // look for this name among metrics + int im; + for (im = 0; im < nmetrics; im++) + { + Metric *m = mitems->fetch (im); + if (strcmp (dname, m->get_cmd ()) == 0 && m->get_subtype () == st) + // apparent match, but let's check comparison mode + if (dbe_strcmp (expr_spec, m->get_expr_spec ()) == 0) + break; + } + + // encode the mapping + if (im >= nmetrics) + map[idm] = 0; // does not map to requested metrics + else if (defdm->op == opPrimitive) + map[idm] = +1 + im; // encode as a positive index + else + { + map[idm] = -1 - im; // encode as a negative index + ndm_requested++; + } + } + if (ndm_requested == 0) + { + free (map); + map = NULL; + } + return map; +} + +void +DerivedMetrics::fill_dependencies (definition *def, int *vec) +{ + switch (def->op) + { + case opPrimitive: + vec[def->index] = 1; + break; + case opDivide: + fill_dependencies (def->arg1, vec); + fill_dependencies (def->arg2, vec); + break; + default: + break; + } +} + +Vector<definition*> * +DerivedMetrics::get_dependencies (definition *def) +{ + int n = items->size (); + + // zero out a vector representing definitions + int *vec = (int *) malloc (n * sizeof (int)); + for (int i = 0; i < n; i++) + vec[i] = 0; + fill_dependencies (def, vec); + + // construct the dependency vector + Vector<definition*> *dependencies = new Vector<definition*>; + for (int i = 0; i < n; i++) + if (vec[i] == 1) + dependencies->append (items->fetch (i)); + free (vec); + return dependencies; +} + +void +DerivedMetrics::dump (FILE *dis_file, int verbosity) +{ + int i; + definition *item; + + // deal with the possibility that names might be NULL + const char *UNNAMED = "(unnamed)"; +#define NAME(x) ( (x) ? (x) : UNNAMED) + + Vec_loop (definition*, items, i, item) + { + // at low verbosity, skip over some items + if (verbosity == 0) + { + if (item->name == NULL) + continue; + if (strcmp (item->name, item->def) && item->op == opPrimitive) + continue; + } + + // dump the definition + switch (item->op) + { + case opPrimitive: + fprintf (dis_file, "%s [%s] is a primitive metric\n", NAME (item->name), + item->def); + break; + case opDivide: + fprintf (dis_file, "%s [%s] = %s [%s] / %s [%s]\n", NAME (item->name), + item->def, NAME (item->arg1->name), item->arg1->def, + NAME (item->arg2->name), item->arg2->def); + break; + default: + fprintf (dis_file, "%s [%s] has an unrecognized op %d\n", + NAME (item->name), item->def, item->op); + break; + } + } +} + +double +DerivedMetrics::eval_one_item (definition *def, int *map, double *values) +{ + switch (def->op) + { + case opNULL: + fprintf (stderr, GTXT ("cannot eval NULL expression\n")); + return 0.; + case opPrimitive: + { + int ival = map[def->index]; + if (ival <= 0) return 0.; + ival--; + return values[ival]; + } + case opDivide: + { + double x1 = eval_one_item (def->arg1, map, values); + double x2 = eval_one_item (def->arg2, map, values); + if (x2 == 0) return 0.; + return (x1 / x2); + } + default: + fprintf (stderr, GTXT ("unknown expression\n")); + return 0.; + } +} + +int +DerivedMetrics::eval (int *map, double *values) +{ + for (int i = 0, n = items->size (); i < n; i++) + { + if (map[i] < 0) + { + int ival = -1 - map[i]; + values[ival] = eval_one_item (items->fetch (i), map, values); + } + } + return 0; +} + diff --git a/gprofng/src/DerivedMetrics.h b/gprofng/src/DerivedMetrics.h new file mode 100644 index 0000000..b457e5e --- /dev/null +++ b/gprofng/src/DerivedMetrics.h @@ -0,0 +1,54 @@ +/* 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. */ + +#ifndef _DERIVEDMETRICS_H +#define _DERIVEDMETRICS_H + +#include <stdio.h> +#include "BaseMetric.h" +#include "Metric.h" + +class definition; + +class DerivedMetrics +{ +public: + DerivedMetrics (); + ~DerivedMetrics (); + definition *add_definition (char *_name, char *_username, char *_def); + int *construct_map (Vector<Metric*> *mitems, BaseMetric::SubType st, + char *expr_spec); + void dump (FILE *dis_file, int verbosity); + double eval_one_item (definition *def, int *map, double *values); + int eval (int *map, double *values); + void fill_dependencies (definition *def, int *vec); + Vector<definition*> *get_dependencies (definition *def); + + Vector<definition*> * + get_items () + { + return items; + } + +private: + Vector<definition*> *items; +}; + +#endif diff --git a/gprofng/src/Disasm.cc b/gprofng/src/Disasm.cc new file mode 100644 index 0000000..0fec9c3 --- /dev/null +++ b/gprofng/src/Disasm.cc @@ -0,0 +1,403 @@ +/* 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 <stdio.h> +#include <string.h> +#include <sys/param.h> + +#include "disassemble.h" +#include "dis-asm.h" +#include "demangle.h" +#include "dbe_types.h" +#include "DbeSession.h" +#include "Elf.h" +#include "Disasm.h" +#include "Stabs.h" +#include "i18n.h" +#include "util.h" +#include "StringBuilder.h" + +struct DisContext +{ + bool is_Intel; + Stabs *stabs; + uint64_t pc; // first_pc <= pc < last_pc + uint64_t first_pc; + uint64_t last_pc; + uint64_t f_offset; // file offset for first_pc + int codeptr[4]; // longest instruction length may not be > 16 + Data_window *elf; +}; + +static const int MAX_DISASM_STR = 2048; +static const int MAX_INSTR_SIZE = 8; + +Disasm::Disasm (char *fname) +{ + dwin = NULL; + dis_str = NULL; + need_swap_endian = false; + my_stabs = Stabs::NewStabs (fname, fname); + if (my_stabs == NULL) + return; + stabs = my_stabs; + platform = stabs->get_platform (); + disasm_open (); +} + +Disasm::Disasm (Platform_t _platform, Stabs *_stabs) +{ + dwin = NULL; + dis_str = NULL; + need_swap_endian = false; + stabs = _stabs; + platform = _platform; + my_stabs = NULL; + disasm_open (); +} + +static int +fprintf_func (void *arg, const char *fmt, ...) +{ + char buf[512]; + va_list vp; + va_start (vp, fmt); + int cnt = vsnprintf (buf, sizeof (buf), fmt, vp); + va_end (vp); + + Disasm *dis = (Disasm *) arg; + dis->dis_str->append (buf); + return cnt; +} + +/* Get LENGTH bytes from info's buffer, at target address memaddr. + Transfer them to myaddr. */ +static int +read_memory_func (bfd_vma memaddr, bfd_byte *myaddr, unsigned int length, + disassemble_info *info) +{ + unsigned int opb = info->octets_per_byte; + size_t end_addr_offset = length / opb; + size_t max_addr_offset = info->buffer_length / opb; + size_t octets = (memaddr - info->buffer_vma) * opb; + if (memaddr < info->buffer_vma + || memaddr - info->buffer_vma > max_addr_offset + || memaddr - info->buffer_vma + end_addr_offset > max_addr_offset + || (info->stop_vma && (memaddr >= info->stop_vma + || memaddr + end_addr_offset > info->stop_vma))) + return -1; + memcpy (myaddr, info->buffer + octets, length); + return 0; +} + +static void +print_address_func (bfd_vma addr, disassemble_info *info) +{ + (*info->fprintf_func) (info->stream, "0x%llx", (unsigned long long) addr); +} + +static asymbol * +symbol_at_address_func (bfd_vma addr ATTRIBUTE_UNUSED, + disassemble_info *info ATTRIBUTE_UNUSED) +{ + return NULL; +} + +static bfd_boolean +symbol_is_valid (asymbol * sym ATTRIBUTE_UNUSED, + disassemble_info *info ATTRIBUTE_UNUSED) +{ + return TRUE; +} + +static void +memory_error_func (int status, bfd_vma addr, disassemble_info *info) +{ + info->fprintf_func (info->stream, "Address 0x%llx is out of bounds.\n", + (unsigned long long) addr); +} + +void +Disasm::disasm_open () +{ + hex_visible = 1; + snprintf (addr_fmt, sizeof (addr_fmt), NTXT ("%s"), NTXT ("%8llx: ")); + if (dis_str == NULL) + dis_str = new StringBuilder; + + switch (platform) + { + case Aarch64: + case Intel: + case Amd64: + need_swap_endian = (DbeSession::platform == Sparc); + break; + case Sparcv8plus: + case Sparcv9: + case Sparc: + default: + need_swap_endian = (DbeSession::platform != Sparc); + break; + } + + memset (&dis_info, 0, sizeof (dis_info)); + dis_info.flavour = bfd_target_unknown_flavour; + dis_info.endian = BFD_ENDIAN_UNKNOWN; + dis_info.endian_code = dis_info.endian; + dis_info.octets_per_byte = 1; + dis_info.disassembler_needs_relocs = FALSE; + dis_info.fprintf_func = fprintf_func; + dis_info.stream = this; + dis_info.disassembler_options = NULL; + dis_info.read_memory_func = read_memory_func; + dis_info.memory_error_func = memory_error_func; + dis_info.print_address_func = print_address_func; + dis_info.symbol_at_address_func = symbol_at_address_func; + dis_info.symbol_is_valid = symbol_is_valid; + dis_info.display_endian = BFD_ENDIAN_UNKNOWN; + dis_info.symtab = NULL; + dis_info.symtab_size = 0; + dis_info.buffer_vma = 0; + switch (platform) + { + case Aarch64: + dis_info.arch = bfd_arch_aarch64; + dis_info.mach = bfd_mach_aarch64; + break; + case Intel: + case Amd64: + dis_info.arch = bfd_arch_i386; + dis_info.mach = bfd_mach_x86_64; + break; + case Sparcv8plus: + case Sparcv9: + case Sparc: + default: + dis_info.arch = bfd_arch_unknown; + dis_info.endian = BFD_ENDIAN_UNKNOWN; + break; + } + dis_info.display_endian = dis_info.endian = BFD_ENDIAN_BIG; + dis_info.display_endian = dis_info.endian = BFD_ENDIAN_LITTLE; + dis_info.display_endian = dis_info.endian = BFD_ENDIAN_UNKNOWN; + disassemble_init_for_target (&dis_info); +} + +Disasm::~Disasm () +{ + delete my_stabs; + delete dwin; + delete dis_str; +} + +void +Disasm::set_img_name (char *img_fname) +{ + if (stabs == NULL && img_fname && dwin == NULL) + { + dwin = new Data_window (img_fname); + if (dwin->not_opened ()) + { + delete dwin; + dwin = NULL; + return; + } + dwin->need_swap_endian = need_swap_endian; + } +} + +void +Disasm::remove_disasm_hndl (void *hndl) +{ + DisContext *ctx = (DisContext *) hndl; + delete ctx; +} + +#if 0 +int +Disasm::get_instr_size (uint64_t vaddr, void *hndl) +{ + DisContext *ctx = (DisContext *) hndl; + if (ctx == NULL || vaddr < ctx->first_pc || vaddr >= ctx->last_pc) + return -1; + ctx->pc = vaddr; + size_t sz = ctx->is_Intel ? sizeof (ctx->codeptr) : 4; + if (sz > ctx->last_pc - vaddr) + sz = (size_t) (ctx->last_pc - vaddr); + if (ctx->elf->get_data (ctx->f_offset + (vaddr - ctx->first_pc), + sz, ctx->codeptr) == NULL) + return -1; + + char buf[MAX_DISASM_STR]; + *buf = 0; + uint64_t inst_vaddr = vaddr; +#if MEZ_NEED_TO_FIX + size_t instrs_cnt = 0; + disasm_err_code_t status = disasm (handle, &inst_vaddr, ctx->last_pc, 1, + ctx, buf, sizeof (buf), &instrs_cnt); + if (instrs_cnt != 1 || status != disasm_err_ok) + return -1; +#endif + return (int) (inst_vaddr - vaddr); +} +#endif + +void +Disasm::set_addr_end (uint64_t end_address) +{ + char buf[32]; + int len = snprintf (buf, sizeof (buf), "%llx", (long long) end_address); + snprintf (addr_fmt, sizeof (addr_fmt), "%%%dllx: ", len < 8 ? 8 : len); +} + +char * +Disasm::get_disasm (uint64_t inst_address, uint64_t end_address, + uint64_t start_address, uint64_t f_offset, int64_t &inst_size) +{ + inst_size = 0; + if (inst_address >= end_address) + return NULL; + Data_window *dw = stabs ? stabs->openElf (false) : dwin; + if (dw == NULL) + return NULL; + + unsigned char buffer[MAX_DISASM_STR]; + dis_info.buffer = buffer; + dis_info.buffer_length = end_address - inst_address; + if (dis_info.buffer_length > sizeof (buffer)) + dis_info.buffer_length = sizeof (buffer); + dw->get_data (f_offset + (inst_address - start_address), + dis_info.buffer_length, dis_info.buffer); + + dis_str->setLength (0); + bfd abfd; + disassembler_ftype disassemble = disassembler (dis_info.arch, dis_info.endian, + dis_info.mach, &abfd); + if (disassemble == NULL) + { + printf ("ERROR: unsupported disassemble\n"); + return NULL; + } + inst_size = disassemble (0, &dis_info); + if (inst_size <= 0) + { + inst_size = 0; + return NULL; + } + StringBuilder sb; + sb.appendf (addr_fmt, inst_address); // Write address + + // Write hex bytes of instruction + if (hex_visible) + { + char bytes[64]; + *bytes = '\0'; + for (int i = 0; i < inst_size; i++) + { + unsigned int hex_value = buffer[i] & 0xff; + snprintf (bytes + 3 * i, sizeof (bytes) - 3 * i, "%02x ", hex_value); + } + const char *fmt = "%s "; + if (platform == Intel) + fmt = "%-21s "; // 21 = 3 * 7 - maximum instruction length on Intel + sb.appendf (fmt, bytes); + } + sb.append (dis_str); +#if MEZ_NEED_TO_FIX + // Write instruction + if (ctx.is_Intel) // longest instruction length for Intel is 7 + sb.appendf (NTXT ("%-7s %s"), parts_array[1], parts_array[2]); + else // longest instruction length for SPARC is 11 + sb.appendf (NTXT ("%-11s %s"), parts_array[1], parts_array[2]); + if (strcmp (parts_array[1], NTXT ("call")) == 0) + { + if (strncmp (parts_array[2], NTXT ("0x"), 2) == 0) + sb.append (GTXT ("\t! (Unable to determine target symbol)")); + } +#endif + return sb.toString (); +} + +#if MEZ_NEED_TO_FIX +void * +Disasm::get_inst_ptr (disasm_handle_t, uint64_t vaddr, void *pass_through) +{ + // Actually it fetches only one instruction at a time for sparc, + // and one byte at a time for intel. + DisContext *ctx = (DisContext*) pass_through; + size_t sz = ctx->is_Intel ? 1 : 4; + if (vaddr + sz > ctx->last_pc) + { + ctx->codeptr[0] = -1; + return ctx->codeptr; + } + if (ctx->elf->get_data (ctx->f_offset + (vaddr - ctx->first_pc), sz, ctx->codeptr) == NULL) + { + ctx->codeptr[0] = -1; + return ctx->codeptr; + } + if (ctx->elf->need_swap_endian && !ctx->is_Intel) + ctx->codeptr[0] = ctx->elf->decode (ctx->codeptr[0]); + return ctx->codeptr; +} + +// get a symbol name for an address +disasm_err_code_t +Disasm::get_sym_name (disasm_handle_t, // an open disassembler handle + uint64_t target_address, // the target virtual address + uint64_t inst_address, // virtual address of instruction + // being disassembled + int use_relocation, // flag to use relocation information + char *buffer, // places the symbol here + size_t buffer_size, // limit on symbol length + int *, // sys/elf_{SPARC.386}.h + uint64_t *offset, // from the symbol to the address + void *pass_through) // disassembler context +{ + char buf[MAXPATHLEN]; + if (!use_relocation) + return disasm_err_symbol; + + DisContext *ctxp = (DisContext*) pass_through; + char *name = NULL; + if (ctxp->stabs) + { + uint64_t addr = ctxp->f_offset + (inst_address - ctxp->first_pc); + name = ctxp->stabs->sym_name (target_address, addr, use_relocation); + } + if (name == NULL) + return disasm_err_symbol; + + char *s = NULL; + if (*name == '_') + s = cplus_demangle (name, DMGL_PARAMS); + if (s) + { + snprintf (buffer, buffer_size, NTXT ("%s"), s); + free (s); + } + else + snprintf (buffer, buffer_size, NTXT ("%s"), name); + + *offset = 0; + return disasm_err_ok; +} +#endif diff --git a/gprofng/src/Disasm.h b/gprofng/src/Disasm.h new file mode 100644 index 0000000..c1530cc --- /dev/null +++ b/gprofng/src/Disasm.h @@ -0,0 +1,66 @@ +/* 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. */ + +#ifndef _DISASM_H +#define _DISASM_H + +#include "disassemble.h" + +class Data_window; +class Stabs; +class StringBuilder; +enum Platform_t; + +class Disasm +{ +public: + Disasm (char *fname); + Disasm (Platform_t _platform, Stabs *_stabs); + ~Disasm (); + void remove_disasm_hndl (void *hndl); + void *get_disasm_hndl (uint64_t vaddr, uint64_t f_offset, size_t size); + int get_instr_size (uint64_t vaddr, void *hndl); + void set_addr_end (uint64_t end_address); + + void + set_hex_visible (int set) + { + hex_visible = set; + } + + char *get_disasm (uint64_t inst_address, uint64_t end_address, + uint64_t start_address, uint64_t f_offset, int64_t &inst_size); + void set_img_name (char *fname); // Only for dynfunc + + StringBuilder *dis_str; + +private: + void disasm_open (); + + disassemble_info dis_info; + Data_window *dwin; + Stabs *stabs, *my_stabs; + Platform_t platform; + char addr_fmt[32]; + int hex_visible; + bool need_swap_endian; +}; + +#endif /* _DISASM_H */ diff --git a/gprofng/src/Dwarf.cc b/gprofng/src/Dwarf.cc new file mode 100644 index 0000000..eb8bd9e --- /dev/null +++ b/gprofng/src/Dwarf.cc @@ -0,0 +1,1041 @@ +/* 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 "util.h" +#include "DbeSession.h" +#include "Elf.h" +#include "Stabs.h" +#include "Dwarf.h" +#include "DataObject.h" +#include "Function.h" +#include "LoadObject.h" +#include "Module.h" +#include "DefaultMap.h" + +static int +datatypeCmp (const void *a, const void *b) +{ + uint32_t o1 = ((datatype_t *) a)->datatype_id; + uint32_t o2 = ((datatype_t *) b)->datatype_id; + return o1 == o2 ? 0 : (o1 < o2 ? -1 : 1); +} + +static int +targetOffsetCmp (const void *a, const void *b) +{ + uint32_t o1 = ((target_info_t *) a)->offset; + uint32_t o2 = ((target_info_t *) b)->offset; + return o1 == o2 ? 0 : (o1 < o2 ? -1 : 1); +} + + +////////////////////////////////////////////////////////// +// class Dwr_type +class Dwr_type +{ +public: + + Dwr_type (int64_t _cu_die_offset, int _tag) + { + cu_die_offset = _cu_die_offset; + tag = _tag; + name = NULL; + dobj_name = NULL; + dtype = NULL; + extent = 0; + parent = 0; + child = 0; + next = 0; + ref_type = 0; + size = 0; + elems = 0; + offset = -1; + bit_size = 0; + }; + + char *name, *dobj_name; + int64_t cu_die_offset, ref_type, extent, parent, child, next; + int64_t size, elems, offset; + int tag, bit_size; + + DataObject *get_dobj (Dwarf_cnt *ctx); + char *get_dobjname (Dwarf_cnt *ctx); + char *dump (); + +private: + datatype_t *dtype; + datatype_t *get_datatype (Dwarf_cnt *ctx); + void get_dobj_for_members (Dwarf_cnt *ctx); + void set_dobjname (char *spec, char *nm); +}; + + +////////////////////////////////////////////////////////// +// class Dwarf_cnt +Dwarf_cnt::Dwarf_cnt () +{ + cu_offset = 0; + parent = 0; + module = NULL; + name = NULL; + func = NULL; + fortranMAIN = NULL; + dwr_types = NULL; + inlinedSubr = NULL; + level = 0; +} + +Dwr_type * +Dwarf_cnt::get_dwr_type (int64_t cu_die_offset) +{ + Dwr_type *t = dwr_types->get (cu_die_offset); + if (t == NULL) + { + Dprintf (DUMP_DWARFLIB, "DWARF_ERROR: %s:%d wrong cu_die_offset=%lld in Dwarf_cnt::get_dwr_type\n", + get_basename (__FILE__), (int) __LINE__, + (long long) cu_die_offset); + t = put_dwr_type (cu_die_offset, 0); // DOBJ_UNSPECIFIED + } + return t; +} + +Dwr_type * +Dwarf_cnt::put_dwr_type (int64_t cu_die_offset, int tag) +{ + Dwr_type *t = new Dwr_type (cu_die_offset, tag); + dwr_types->put (cu_die_offset, t); + return t; +} + +Dwr_type * +Dwarf_cnt::put_dwr_type (Dwr_Tag *dwrTag) +{ + Dwr_type *t = new Dwr_type (dwrTag->die, dwrTag->tag); + dwr_types->put (dwrTag->die, t); + return t; +} + +////////////////////////////////////////////////////////// +// class Dwr_type +char * +Dwr_type::dump () +{ + char *s = dbe_sprintf ("%lld %-15s name='%s' parent=%lld next=%lld child=%lld dtype=%llx", + (long long) cu_die_offset, DwrCU::tag2str (tag), + STR (name), (long long) parent, (long long) next, + (long long) child, (long long) dtype); + return s; +} + +void +Dwr_type::set_dobjname (char *spec, char *nm) +{ + if (spec) + { + if (nm) + dobj_name = dbe_sprintf ("%s%s", spec, nm); + else + dobj_name = dbe_sprintf ("%s<ANON=%lld>", spec, + (long long) cu_die_offset); + } + else + { + if (nm) + dobj_name = dbe_sprintf ("%s", nm); + else + dobj_name = dbe_sprintf ("<ANON=%lld>", (long long) cu_die_offset); + } +} + +char * +Dwr_type::get_dobjname (Dwarf_cnt *ctx) +{ + if (dobj_name) + return dobj_name; + switch (tag) + { + case DW_TAG_base_type: + set_dobjname (NULL, name); + for (int i = 0, len = (int) strlen (dobj_name); i < len; i++) + { + if (dobj_name[i] == ' ') + dobj_name[i] = '_'; + } + break; + case DW_TAG_constant: + case DW_TAG_formal_parameter: + case DW_TAG_variable: + { + Dwr_type *t = ctx->get_dwr_type (ref_type); + set_dobjname (NULL, t->get_dobjname (ctx)); + break; + } + case DW_TAG_unspecified_type: + set_dobjname (NTXT ("unspecified:"), name); + break; + case DW_TAG_enumeration_type: + set_dobjname (NTXT ("enumeration:"), name); + break; + case DW_TAG_typedef: + { + Dwr_type *t = ctx->get_dwr_type (ref_type); + dobj_name = dbe_sprintf ("%s=%s", name, t->get_dobjname (ctx)); + break; + } + case DW_TAG_const_type: + set_dobjname (NTXT ("const+"), name); + break; + case DW_TAG_volatile_type: + set_dobjname (NTXT ("volatile+"), name); + break; + case DW_TAG_pointer_type: + { + Dwr_type *t = ctx->get_dwr_type (ref_type); + set_dobjname (NTXT ("pointer+"), t->get_dobjname (ctx)); + break; + } + case DW_TAG_reference_type: + { + Dwr_type *t = ctx->get_dwr_type (ref_type); + set_dobjname (NTXT ("reference+"), t->get_dobjname (ctx)); + break; + } + case DW_TAG_array_type: + { + Dwr_type *t = ctx->get_dwr_type (ref_type); + if (elems > 0) + dobj_name = dbe_sprintf ("array[%lld]:%s", + (long long) elems, t->get_dobjname (ctx)); + else + dobj_name = dbe_sprintf ("array[]:%s", t->get_dobjname (ctx)); + break; + } + case DW_TAG_structure_type: + set_dobjname (NTXT ("structure:"), name); + break; + case DW_TAG_union_type: + set_dobjname (NTXT ("union:"), name); + break; + case DW_TAG_class_type: + set_dobjname (NTXT ("class:"), name); + break; + case DW_TAG_member: + { + Dwr_type *t = ctx->get_dwr_type (ref_type); + if (bit_size > 0) + dobj_name = dbe_sprintf (NTXT ("%s:%lld"), t->get_dobjname (ctx), + (long long) bit_size); + else + dobj_name = dbe_sprintf (NTXT ("%s"), t->get_dobjname (ctx)); + break; + } + default: + Dprintf (DUMP_DWARFLIB, NTXT ("DWARF_ERROR: %s:%d No case for %s cu_die_offset=%lld\n"), + get_basename (__FILE__), (int) __LINE__, + DwrCU::tag2str (tag), (long long) cu_die_offset); + set_dobjname (NTXT ("Undefined:"), NULL); + break; + } + return dobj_name; +} + +datatype_t* +Dwr_type::get_datatype (Dwarf_cnt *ctx) +{ + if (dtype) + return dtype; + dtype = new datatype_t; + dtype->datatype_id = (unsigned) cu_die_offset; + dtype->memop_refs = 0; + dtype->event_data = 0; + dtype->dobj = NULL; + ctx->module->datatypes->incorporate (dtype, datatypeCmp); + return dtype; +} + +DataObject * +Dwr_type::get_dobj (Dwarf_cnt *ctx) +{ + if (dtype == NULL) + dtype = get_datatype (ctx); + dtype->memop_refs++; + DataObject *dobj = dtype->dobj; + if (dobj) + return dobj; + + if (tag == 0) + dobj = dbeSession->find_dobj_by_name (PTXT (DOBJ_UNSPECIFIED)); + else + { + dobj = dbeSession->createDataObject (); + dobj->size = size; + dobj->offset = offset; + dobj->scope = ctx->func ? (Histable*) ctx->func : (Histable*) ctx->module; + } + dtype->dobj = dobj; + if (parent) + { + Dwr_type *t = ctx->get_dwr_type (parent); + dobj->parent = t->get_dobj (ctx); + } + + if (ref_type) + { + Dwr_type *t = ctx->get_dwr_type (ref_type); + t->get_dobj (ctx); + if (size == 0) + { + size = t->size; + dobj->size = size; + } + } + + switch (tag) + { + case 0: + break; + case DW_TAG_array_type: + case DW_TAG_base_type: + case DW_TAG_unspecified_type: + case DW_TAG_enumeration_type: + case DW_TAG_typedef: + case DW_TAG_const_type: + case DW_TAG_volatile_type: + case DW_TAG_pointer_type: + case DW_TAG_reference_type: + dobj->set_dobjname (get_dobjname (ctx), NULL); + break; + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + dobj->set_dobjname (get_dobjname (ctx), NULL); + dobj->master = dbeSession->find_dobj_by_name (dobj_name); + get_dobj_for_members (ctx); + break; + case DW_TAG_constant: + case DW_TAG_formal_parameter: + case DW_TAG_member: + case DW_TAG_variable: + if (dobj->parent == NULL) + dobj->parent = dbeSession->get_Scalars_DataObject (); + dobj->set_dobjname (get_dobjname (ctx), name); + break; + default: + Dprintf (DUMP_DWARFLIB, NTXT ("DWARF_ERROR: %s:%d No case for %s cu_die_offset=%lld\n"), + get_basename (__FILE__), (int) __LINE__, + DwrCU::tag2str (tag), (long long) cu_die_offset); + break; + } + return dobj; +} + +void +Dwr_type::get_dobj_for_members (Dwarf_cnt *ctx) +{ + for (int64_t i = child; i != 0;) + { + Dwr_type *t = ctx->get_dwr_type (i); + t->get_dobj (ctx); + i = t->next; + } +} + +////////////////////////////////////////////////////////// +// class Dwarf +Dwarf::Dwarf (Stabs *_stabs) +{ + stabs = _stabs; + status = Stabs::DBGD_ERR_NONE; + dwrCUs = 0; + debug_infoSec = NULL; + debug_abbrevSec = NULL; + debug_strSec = NULL; + debug_lineSec = NULL; + debug_rangesSec = NULL; + elf = stabs->openElf (true); + if (elf == NULL) + { + status = Stabs::DBGD_ERR_BAD_ELF_FORMAT; + return; + } + debug_infoSec = dwrGetSec (NTXT (".debug_info")); + if (debug_infoSec) + { + debug_infoSec->reloc = ElfReloc::get_elf_reloc (elf, NTXT (".rela.debug_info"), NULL); + debug_infoSec->reloc = ElfReloc::get_elf_reloc (elf, NTXT (".rel.debug_info"), debug_infoSec->reloc); + if (debug_infoSec->reloc) + debug_infoSec->reloc->dump (); + } + debug_abbrevSec = dwrGetSec (NTXT (".debug_abbrev")); + debug_strSec = dwrGetSec (NTXT (".debug_str")); + debug_lineSec = dwrGetSec (NTXT (".debug_line")); + debug_rangesSec = dwrGetSec (NTXT (".debug_ranges")); + + if ((debug_infoSec == NULL) || (debug_abbrevSec == NULL) || (debug_lineSec == NULL)) + { + status = Stabs::DBGD_ERR_NO_DWARF; + return; + } +} + +Dwarf::~Dwarf () +{ + delete debug_infoSec; + delete debug_abbrevSec; + delete debug_strSec; + delete debug_lineSec; + delete debug_rangesSec; + Destroy (dwrCUs); +} + +DwrSec * +Dwarf::dwrGetSec (const char *sec_name) +{ + int secN = elf->elf_get_sec_num (sec_name); + if (secN > 0) + { + Elf_Data *elfData = elf->elf_getdata (secN); + if (elfData) + return new DwrSec ((unsigned char *) elfData->d_buf, elfData->d_size, + elf->need_swap_endian, + elf->elf_getclass () == ELFCLASS32); + } + return NULL; +} + +uint64_t +DwrCU::get_low_pc () +{ + uint64_t pc = Dwarf_addr (DW_AT_low_pc); + if (pc) + return pc; + return pc; +} + +char * +DwrCU::get_linkage_name () +{ + char *nm = Dwarf_string (DW_AT_linkage_name); + if (nm != NULL) + return nm; + nm = Dwarf_string (DW_AT_SUN_link_name); + if (nm != NULL) + return nm; + return Dwarf_string (DW_AT_MIPS_linkage_name); +} + +void +DwrCU::parseChild (Dwarf_cnt *ctx) +{ + if (!dwrTag.hasChild) + return; + uint64_t old_size = debug_infoSec->size; + uint64_t next_die_offset = 0; + Dwarf_Die next_die; + if (read_ref_attr (DW_AT_sibling, &next_die) == DW_DLV_OK) + { + next_die_offset = next_die + cu_offset; + if (next_die_offset <= debug_infoSec->offset) + { + Dprintf (DEBUG_ERR_MSG, NTXT ("DwrCU::parseChild: next_die(0x%llx) <= debug_infoSec->offset(%llx)\n"), + (long long) next_die, (long long) debug_infoSec->offset); + next_die_offset = 0; + } + else if (debug_infoSec->size > next_die_offset) + debug_infoSec->size = next_die_offset; + } + dwrTag.level++; + ctx->level++; + for (;;) + { + if (set_die (0) != DW_DLV_OK) + break; + Function *func; + char *old_name; + int hasChild = dwrTag.hasChild; + switch (dwrTag.tag) + { + case DW_TAG_imported_declaration: + if (Stabs::is_fortran (ctx->module->lang_code)) + { + char *link_name = Dwarf_string (DW_AT_name); + ctx->fortranMAIN = NULL; + parseChild (ctx); + hasChild = 0; + if (ctx->fortranMAIN) + { + ctx->fortranMAIN->set_match_name (link_name); + ctx->fortranMAIN = NULL; + } + } + break; + case DW_TAG_subprogram: + if (dwrTag.get_attr (DW_AT_abstract_origin)) + break; + if (dwrTag.get_attr (DW_AT_declaration)) + { + // Only declaration + if (Stabs::is_fortran (ctx->module->lang_code)) + { + char *link_name = Dwarf_string (DW_AT_name); + if (link_name && streq (link_name, NTXT ("MAIN"))) + ctx->fortranMAIN = Stabs::find_func (NTXT ("MAIN"), ctx->module->functions, true, true); + } + if (get_linkage_name () == NULL) + break; + } + func = append_Function (ctx); + if (func) + { + if (Stabs::is_fortran (ctx->module->lang_code) && + streq (func->get_match_name (), NTXT ("MAIN"))) + ctx->fortranMAIN = func; + old_name = ctx->name; + Function *old_func = ctx->func; + ctx->name = func->get_match_name (); + ctx->func = func; + parseChild (ctx); + hasChild = 0; + ctx->name = old_name; + ctx->func = old_func; + } + break; + case DW_TAG_module: + old_name = ctx->name; + ctx->name = Dwarf_string (DW_AT_SUN_link_name); + parseChild (ctx); + hasChild = 0; + ctx->name = old_name; + break; + case DW_TAG_class_type: + old_name = ctx->name; + ctx->name = Dwarf_string (DW_AT_name); + parseChild (ctx); + hasChild = 0; + ctx->name = old_name; + break; + case DW_TAG_structure_type: + old_name = ctx->name; + ctx->name = NULL; + parseChild (ctx); + hasChild = 0; + ctx->name = old_name; + break; + case DW_TAG_namespace: + old_name = ctx->name; + ctx->name = Dwarf_string (DW_AT_name); + parseChild (ctx); + hasChild = 0; + ctx->name = old_name; + break; + case DW_TAG_lexical_block: + old_name = ctx->name; + ctx->name = NULL; + parseChild (ctx); + hasChild = 0; + ctx->name = old_name; + break; + case DW_TAG_SUN_memop_info: + isMemop = true; + break; + case DW_TAG_inlined_subroutine: + if (ctx->module) + { + parse_inlined_subroutine (ctx); + hasChild = 0; + } + break; + default: // No more special cases + break; + } + if (hasChild) + parseChild (ctx); + } + ctx->level--; + dwrTag.level--; + if (next_die_offset != 0) + debug_infoSec->offset = next_die_offset; + debug_infoSec->size = old_size; +} + +bool +Dwarf::archive_Dwarf (LoadObject *lo) +{ + if (debug_infoSec == NULL) + return false; + if (dwrCUs) + return true; + dwrCUs = new Vector<DwrCU *>; + + debug_infoSec->offset = 0; + while (debug_infoSec->offset < debug_infoSec->sizeSec) + { + DwrCU *dwrCU = new DwrCU (this); + dwrCUs->append (dwrCU); + debug_infoSec->size = debug_infoSec->sizeSec; + debug_infoSec->offset = dwrCU->next_cu_offset; + + if (dwrCU->set_die (dwrCU->cu_header_offset) != DW_DLV_OK) + { + Dprintf (1, "DwrCU::archive_Dwarf: CU=%lld (offset=0x%llx); set_die(0x%llx) failed\n", + (long long) dwrCUs->size (), (long long) dwrCU->cu_offset, + (long long) dwrCU->cu_header_offset); + continue; + } + + Module *mod = dwrCU->parse_cu_header (lo); + if (mod) + { + mod->hdrOffset = dwrCUs->size (); + DwrLineRegs *lineReg = dwrCU->get_dwrLineReg (); + dwrCU->srcFiles = new Vector<SourceFile *> (VecSize (lineReg->file_names)); + for (long i = 0, sz = VecSize (lineReg->file_names); i < sz; i++) + { + char *fname = lineReg->getPath (i + 1); + SourceFile *sf = mod->findSource (fname, true); + dwrCU->srcFiles->append (sf); + } + + Dwarf_cnt ctx; + ctx.module = mod; + dwrCU->parseChild (&ctx); + if (dwrCU->dwrInlinedSubrs && DUMP_DWARFLIB) + { + char msg[128]; + char *lo_name = mod->loadobject ? mod->loadobject->get_name () + : NULL; + snprintf (msg, sizeof (msg), NTXT ("\ndwrCUs[%lld]: %s:%s\n"), + (long long) dwrCUs->size (), + STR (lo_name), STR (mod->get_name ())); + dwrCU->dwrInlinedSubrs->dump (msg); + } + } + } + return true; +} + +void +Dwarf::srcline_Dwarf (Module *module) +{ + if (module == NULL || module->hdrOffset == 0) + return; + DwrCU *dwrCU = dwrCUs->get (module->hdrOffset - 1); + dwrCU->map_dwarf_lines (module); +} + + +// parse hwcprof info for given module in loadobject + +void +Dwarf::read_hwcprof_info (Module *module) +{ + if (module->datatypes || (module->hdrOffset == 0)) + return; + DwrCU *dwrCU = dwrCUs->get (module->hdrOffset - 1); + if (!dwrCU->isMemop) + return; + module->datatypes = new Vector<datatype_t*>; + if (dwrCU->set_die (dwrCU->cu_header_offset) != DW_DLV_OK) + { + Dprintf (1, "Dwarf::read_hwcprof_info: CU=%lld (offset=0x%llx); set_die(0x%llx) failed\n", + (long long) module->hdrOffset, (long long) dwrCU->cu_offset, + (long long) dwrCU->cu_header_offset); + return; + } + Dwarf_cnt ctx; + ctx.module = module; + ctx.cu_offset = dwrCU->cu_offset; // CU header offset; + ctx.dwr_types = new DefaultMap<int64_t, Dwr_type*>; + ctx.put_dwr_type (0, 0); // for DOBJ_UNSPECIFIED + dwrCU->read_hwcprof_info (&ctx); + + Vector<inst_info_t*> *infoList = module->infoList; + Dprintf (DUMP_DWARFLIB, + "\n\n ### Dwarf::read_hwcprof_info: module: '%s' infoList->size()=%lld\n", + STR (module->get_name ()), + (long long) (infoList ? infoList->size () : -1)); + for (int i = 0, sz = infoList ? infoList->size () : -1; i < sz; i++) + { + inst_info_t *ip = infoList->fetch (i); + memop_info_t *mp = ip->memop; + Dwr_type *t = ctx.get_dwr_type (mp->datatype_id); + t->get_dobj (&ctx); + } + +#ifdef DEBUG + Dprintf (DUMP_DWARFLIB, + "\n\n ### Dwarf::read_hwcprof_info: '%s' infoList->size()=%lld\n", + STR (module->get_name ()), + (long long) (infoList ? infoList->size () : 1)); + for (int i = 0, sz = infoList ? infoList->size () : -1; i < sz; i++) + { + inst_info_t *ip = infoList->fetch (i); + memop_info_t *mp = ip->memop; + Dprintf (DUMP_DWARFLIB, + " %d id=%lld offset=%lld signature=%lld datatype_id=%lld \n", + i, (long long) mp->id, (long long) mp->offset, + (long long) mp->signature, (long long) mp->datatype_id); + } + + Vector<int64_t> *keys = ctx.dwr_types->keySet (); + Dprintf (DUMP_DWARFLIB, + "\n\n ### Dwarf::read_hwcprof_info: '%s' keys->size()=%lld\n", + STR (module->get_name ()), (long long) (keys ? keys->size () : -1)); + for (int i = 0, sz = keys->size (); i < sz; i++) + { + int64_t ind = keys->fetch (i); + Dwr_type *t = ctx.get_dwr_type (ind); + Dprintf (DUMP_DWARFLIB, NTXT (" %d %lld %s\n"), i, + (long long) ind, t->dump ()); + } +#endif +} + +void +DwrCU::read_hwcprof_info (Dwarf_cnt *ctx) +{ + if (!dwrTag.hasChild) + return; + uint64_t old_size = debug_infoSec->size; + uint64_t next_die_offset = 0; + Dwarf_Die next_die; + if (read_ref_attr (DW_AT_sibling, &next_die) == DW_DLV_OK) + { + next_die_offset = next_die + cu_offset; + if (next_die_offset <= debug_infoSec->offset) + next_die_offset = 0; + else if (debug_infoSec->size > next_die_offset) + debug_infoSec->size = next_die_offset; + } + dwrTag.level++; + ctx->level++; + for (;;) + { + if (set_die (0) != DW_DLV_OK) + break; + Dprintf (DUMP_DWARFLIB, NTXT ("%s:%d <%lld:%lld> cu_die=%lld %-15s\n"), + get_basename (__FILE__), (int) __LINE__, (long long) ctx->level, + (long long) dwrTag.die, (long long) dwrTag.offset, + DwrCU::tag2str (dwrTag.tag)); + switch (dwrTag.tag) + { + case DW_TAG_SUN_memop_info: + { + if (ctx->func == NULL) + break; + Dwarf_Unsigned mid = Dwarf_data (DW_AT_SUN_profile_id); + Dwarf_Unsigned off = Dwarf_data (DW_AT_SUN_func_offset); + Dwarf_Unsigned sig = Dwarf_data (DW_AT_SUN_memop_signature); + Dwarf_Off ref = Dwarf_ref (DW_AT_SUN_memop_type_ref); + + // define memop entry + memop_info_t *memop = new memop_info_t; + memop->id = (unsigned) mid; + memop->signature = (unsigned) sig; + memop->datatype_id = ref ? (unsigned) ref : 0; + memop->offset = (unsigned) (ctx->func->img_offset + off); + + // define instop entry + inst_info_t *instop = new inst_info_t; + instop->type = CPF_INSTR_TYPE_PREFETCH; // XXXX UNKNOWN + instop->offset = memop->offset; + instop->memop = memop; + if (ctx->module->infoList == NULL) + ctx->module->infoList = new Vector<inst_info_t*>; + ctx->module->infoList->append (instop); + break; + } + case DW_TAG_SUN_codeflags: + { + if (ctx->func == NULL) + break; + Dwarf_Unsigned kind = Dwarf_data (DW_AT_SUN_cf_kind); + if (kind == DW_ATCF_SUN_branch_target) + { + DwrSec *secp = Dwarf_block (DW_AT_SUN_func_offsets); + if (secp) + { + int foffset = 0; + for (int i = 0; secp->offset < secp->size; i++) + { + int val = (int) secp->GetSLEB128 (); + if (i == 0 || val != 0) + { + foffset += val; + target_info_t *t = new target_info_t; + t->offset = (unsigned) (ctx->func->img_offset + foffset); + ctx->module->bTargets.incorporate (t, targetOffsetCmp); + } + } + delete(secp); + } + } + break; + } + case DW_TAG_subprogram: + { + Function *old_func = ctx->func; + if (dwrTag.get_attr (DW_AT_abstract_origin) + || dwrTag.get_attr (DW_AT_declaration)) + ctx->func = NULL; + else + ctx->func = append_Function (ctx); + read_hwcprof_info (ctx); + ctx->func = old_func; + break; + } + case DW_TAG_base_type: + { + Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); + t->name = Dwarf_string (DW_AT_name); + t->size = Dwarf_data (DW_AT_byte_size); + break; + } + case DW_TAG_unspecified_type: + ctx->put_dwr_type (dwrTag.die, dwrTag.tag); + break; + case DW_TAG_enumeration_type: + { + Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); + t->name = Dwarf_string (DW_AT_name); + t->size = Dwarf_data (DW_AT_byte_size); + break; + } + case DW_TAG_constant: + case DW_TAG_formal_parameter: + case DW_TAG_variable: + case DW_TAG_typedef: + case DW_TAG_const_type: + case DW_TAG_volatile_type: + { + Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); + t->name = Dwarf_string (DW_AT_name); + t->ref_type = Dwarf_ref (DW_AT_type); + break; + } + case DW_TAG_pointer_type: + case DW_TAG_reference_type: + { + Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); + t->name = Dwarf_string (DW_AT_name); + t->ref_type = Dwarf_ref (DW_AT_type); + t->size = (dwarf->stabs->get_class () == W64) ? 8 : 4; + break; + } + case DW_TAG_array_type: + { + Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); + t->name = Dwarf_string (DW_AT_name); + t->ref_type = Dwarf_ref (DW_AT_type); + t->size = Dwarf_data (DW_AT_byte_size); + ctx->size = -1; + read_hwcprof_info (ctx); + t->elems = ctx->size; + break; + } + case DW_TAG_subrange_type: + { + int64_t ref_type = Dwarf_ref (DW_AT_type); + int64_t hi = Dwarf_data (DW_AT_upper_bound); + int64_t lo = Dwarf_data (DW_AT_lower_bound); + int64_t ss = Dwarf_data (DW_AT_stride_size); + ctx->size = 1 + hi - lo; + if (ss != 0) + ctx->size /= ss; + Dprintf (DUMP_DWARFLIB, + "Got subrange [%lld:%lld:%lld] indexed <%lld>: size=%lld\n", + (long long) lo, (long long) hi, (long long) ss, + (long long) ref_type, (long long) ctx->size); + break; + } + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + { + Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); + t->name = Dwarf_string (DW_AT_name); + t->size = Dwarf_data (DW_AT_byte_size); + t->extent = Dwarf_ref (DW_AT_sibling); + int64_t old_parent = ctx->parent; + ctx->parent = t->cu_die_offset; + + char *old_name = ctx->name; + ctx->name = (dwrTag.tag == DW_TAG_class_type) ? Dwarf_string (DW_AT_name) : NULL; + read_hwcprof_info (ctx); + ctx->name = old_name; + ctx->parent = old_parent; + // Reverse children + for (int64_t i = t->child, last = 0; i != 0;) + { + Dwr_type *t1 = ctx->get_dwr_type (i); + t->child = i; + i = t1->next; + t1->next = last; + last = t->child; + } + break; + } + case DW_TAG_member: + { + if (ctx->parent == 0) + { + Dprintf (DUMP_DWARFLIB, NTXT ("DWARF_ERROR: %s:%d %s cu_die_offset=%lld\n"), + get_basename (__FILE__), (int) __LINE__, + DwrCU::tag2str (dwrTag.tag), (long long) dwrTag.die); + break; + } + Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); + t->name = Dwarf_string (DW_AT_name); + t->ref_type = Dwarf_ref (DW_AT_type); + t->offset = Dwarf_location (DW_AT_data_member_location); + Dwr_type *parent = ctx->get_dwr_type (ctx->parent); + t->parent = ctx->parent; + t->next = parent->child; // a reverse order of members + parent->child = t->cu_die_offset; + t->bit_size = (uint32_t) Dwarf_data (DW_AT_bit_size); + break; + } + case DW_TAG_module: + { + char *old_name = ctx->name; + ctx->name = Dwarf_string (DW_AT_SUN_link_name); + read_hwcprof_info (ctx); + ctx->name = old_name; + break; + } + case DW_TAG_namespace: + { + char *old_name = ctx->name; + ctx->name = Dwarf_string (DW_AT_name); + read_hwcprof_info (ctx); + ctx->name = old_name; + break; + } + case DW_TAG_lexical_block: + { + char *old_name = ctx->name; + ctx->name = NULL; + read_hwcprof_info (ctx); + ctx->name = old_name; + break; + } + default: // No more special cases + read_hwcprof_info (ctx); + break; + } + } + ctx->level--; + dwrTag.level--; + if (next_die_offset != 0) + debug_infoSec->offset = next_die_offset; + debug_infoSec->size = old_size; +} + +// Append function to module +Function * +DwrCU::append_Function (Dwarf_cnt *ctx) +{ + char *outerName = ctx->name, *name, tmpname[2048]; + Function *func; + char *fname = Dwarf_string (DW_AT_name); + if (fname && outerName && !strchr (fname, '.')) + { + size_t outerlen = strlen (outerName); + if (outerlen > 0 && outerName[outerlen - 1] == '_') + { + outerlen--; + snprintf (tmpname, sizeof (tmpname), NTXT ("%s"), outerName); + snprintf (tmpname + outerlen, sizeof (tmpname) - outerlen, NTXT (".%s_"), fname); + } + else + snprintf (tmpname, sizeof (tmpname), NTXT ("%s.%s"), outerName, fname); + name = tmpname; + Dprintf (DUMP_DWARFLIB, NTXT ("Generated innerfunc name %s\n"), name); + } + else + name = fname; + + char *link_name = get_linkage_name (); + if (link_name == NULL) + link_name = name; + + uint64_t pc = get_low_pc (); + func = dwarf->stabs->append_Function (module, link_name, pc); + if (func != NULL) + { + int lineno = (int) Dwarf_data (DW_AT_decl_line); + func->set_match_name (name); + if (lineno > 0) + { + func->setLineFirst (lineno); + if (dwrLineReg == NULL) + dwrLineReg = new DwrLineRegs (new DwrSec (dwarf->debug_lineSec, + stmt_list_offset), comp_dir); + int fileno = ((int) Dwarf_data (DW_AT_decl_file)) - 1; + SourceFile *sf = ((fileno >= 0) && (fileno < VecSize (srcFiles))) ? srcFiles->get (fileno) + : module->getMainSrc (); + func->setDefSrc (sf); + func->pushSrcFile (func->def_source, 0); + func->popSrcFile (); + } + } + return func; +} + +// Get language code +Sp_lang_code +DwrCU::Dwarf_lang () +{ + char *str = Dwarf_string (DW_AT_producer); + if (str && strncmp (str, NTXT ("GNU"), 3) == 0) + isGNU = true; + int64_t lang = Dwarf_data (DW_AT_language); + switch (lang) + { + case DW_LANG_C89: + case DW_LANG_C: + return Sp_lang_c; // Sp_lang_ansic? + case DW_LANG_C99: + return Sp_lang_c99; + case DW_LANG_C_plus_plus: + return isGNU ? Sp_lang_gcc : Sp_lang_cplusplus; + case DW_LANG_Fortran90: + return Sp_lang_fortran90; + case DW_LANG_Fortran77: + return Sp_lang_fortran; + case DW_LANG_Java: + return Sp_lang_java; + case DW_LANG_Mips_Assembler: + case DW_LANG_SUN_Assembler: + return Sp_lang_asm; + case DW_LANG_Pascal83: + return Sp_lang_pascal; + default: + case DW_LANG_Ada83: + case DW_LANG_Cobol74: + case DW_LANG_Cobol85: + case DW_LANG_Modula2: + case DW_LANG_Ada95: + case DW_LANG_Fortran95: + case DW_LANG_lo_user: + return Sp_lang_unknown; + } +} diff --git a/gprofng/src/Dwarf.h b/gprofng/src/Dwarf.h new file mode 100644 index 0000000..12ffbc0 --- /dev/null +++ b/gprofng/src/Dwarf.h @@ -0,0 +1,87 @@ +/* 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. */ + +#ifndef _Dwarf_h_ +#define _Dwarf_h_ 1 + +#include "dwarf2.h" + +#include "Stabs.h" +#include "dbe_structs.h" +#include "DwarfLib.h" + +enum +{ + /* ICC extensions */ + DW_AT_icc_flags = 0x3b01, + DW_TAG_icc_compile_unit = 0x7000, + + /* Sun extensions */ + DW_ATCF_SUN_branch_target = 0x46, + DW_AT_SUN_command_line = 0x2205, + DW_AT_SUN_func_offsets = 0x2211, + DW_AT_SUN_cf_kind = 0x2212, + DW_AT_SUN_func_offset = 0x2216, + DW_AT_SUN_memop_type_ref = 0x2217, + DW_AT_SUN_profile_id = 0x2218, + DW_AT_SUN_memop_signature = 0x2219, + DW_AT_SUN_obj_dir = 0x2220, + DW_AT_SUN_obj_file = 0x2221, + DW_AT_SUN_original_name = 0x2222, + DW_AT_SUN_link_name = 0x2226, + + DW_TAG_SUN_codeflags = 0x4206, + DW_TAG_SUN_memop_info = 0x4207, + DW_TAG_SUN_dtor_info = 0x420a, + DW_TAG_SUN_dtor = 0x420b, + + DW_LANG_SUN_Assembler = 0x9001 +}; + + +class LoadObject; +class Module; +class DwrCU; +class DwrSec; + +class Dwarf +{ +public: + Dwarf (Stabs *_stabs); + ~Dwarf (); + bool archive_Dwarf (LoadObject *lo); + void srcline_Dwarf (Module *module); + void read_hwcprof_info (Module *module); + + Stabs::Stab_status status; + Vector<DwrCU *> *dwrCUs; + DwrSec *debug_infoSec; + DwrSec *debug_abbrevSec; + DwrSec *debug_strSec; + DwrSec *debug_lineSec; + DwrSec *debug_rangesSec; + Elf *elf; + Stabs *stabs; + +private: + DwrSec *dwrGetSec (const char *sec_name); +}; + +#endif /* _Dwarf_h_ */ diff --git a/gprofng/src/DwarfLib.cc b/gprofng/src/DwarfLib.cc new file mode 100644 index 0000000..c8af0ab --- /dev/null +++ b/gprofng/src/DwarfLib.cc @@ -0,0 +1,2203 @@ +/* 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 "util.h" +#include "Dwarf.h" +#include "DwarfLib.h" +#include "Elf.h" +#include "Function.h" +#include "Module.h" +#include "StringBuilder.h" +#include "DbeArray.h" +#include "DbeSession.h" + +#define CASE_S(x) case x: s = (char *) #x; break + +static char * +gelf_st_type2str (int type) +{ + static char buf[128]; + char *s; + switch (type) + { + CASE_S (STT_NOTYPE); + CASE_S (STT_OBJECT); + CASE_S (STT_FUNC); + CASE_S (STT_SECTION); + CASE_S (STT_FILE); + CASE_S (STT_COMMON); + CASE_S (STT_TLS); + // CASE_S(STT_NUM); + CASE_S (STT_LOPROC); + CASE_S (STT_HIPROC); + default: s = NTXT ("???"); + break; + } + snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), s, type); + buf[sizeof (buf) - 1] = 0; + return buf; +} + +static char * +special_opcode2str (int opcode) +{ + static char buf[128]; + snprintf (buf, sizeof (buf), NTXT ("SpecialOpcode: %3d"), opcode); + buf[sizeof (buf) - 1] = 0; + return buf; +} + +static char * +extended_opcode2str (int opcode) +{ + static char buf[128]; + char *s; + switch (opcode) + { + CASE_S (DW_LNE_end_sequence); + CASE_S (DW_LNE_set_address); + CASE_S (DW_LNE_define_file); + default: + snprintf (buf, sizeof (buf), NTXT ("??? (%d)"), opcode); + buf[sizeof (buf) - 1] = 0; + s = buf; + break; + } + return s; +} + +static char * +standard_opcode2str (int opcode) +{ + static char buf[128]; + char *s; + switch (opcode) + { + CASE_S (DW_LNS_copy); + CASE_S (DW_LNS_advance_pc); + CASE_S (DW_LNS_advance_line); + CASE_S (DW_LNS_set_file); + CASE_S (DW_LNS_set_column); + CASE_S (DW_LNS_negate_stmt); + CASE_S (DW_LNS_set_basic_block); + CASE_S (DW_LNS_const_add_pc); + CASE_S (DW_LNS_fixed_advance_pc); + default: + snprintf (buf, sizeof (buf), NTXT ("??? (%d)"), opcode); + buf[sizeof (buf) - 1] = 0; + s = buf; + break; + } + return s; +} + +template<> void Vector<DwrInlinedSubr *> +::dump (const char *msg) +{ + Dprintf (1, NTXT ("%s Vector<DwrInlinedSubr *> [%lld]\n"), + msg ? msg : NTXT (""), (long long) size ()); + for (long i = 0, sz = size (); i < sz; i++) + { + DwrInlinedSubr *p = get (i); + Dprintf (1, NTXT ("%ld: "), (long) i); + p->dump (); + } +} + +template<> void Vector<DwrLine *> +::dump (const char *msg) +{ + Dprintf (1, "%s Vector<DwrLine *> [%lld]:\n address [file line column]\n", + msg ? msg : NTXT (""), (long long) size ()); + for (long i = 0, sz = size (); i < sz; i++) + { + DwrLine *lnp = get (i); + Dprintf (1, NTXT (" %2lld 0x%08llx [ %2lld, %lld, %lld ] \n"), + (long long) i, (long long) lnp->address, (long long) lnp->file, + (long long) lnp->line, (long long) lnp->column); + } + Dprintf (1, NTXT ("\n\n")); +} + +////////////////////////////////////////////////////////// +// class ElfReloc + +ElfReloc::ElfReloc (Elf *_elf) +{ + elf = _elf; + reloc = NULL; + cur_reloc_ind = 0; +} + +ElfReloc::~ElfReloc () +{ + if (reloc) + { + reloc->destroy (); + delete reloc; + } +} + +void +ElfReloc::dump_rela_debug_sec (int sec) +{ + if (!DUMP_RELA_SEC) + return; + Elf_Internal_Shdr *shdr = elf->get_shdr (sec); + if (shdr == NULL) + return; + + Elf_Data *data = elf->elf_getdata (sec); + if (data == NULL) + return; + + uint64_t ScnSize = data->d_size; + uint64_t EntSize = shdr->sh_entsize; + if (ScnSize == 0 || EntSize == 0) + return; + + Elf_Internal_Shdr *shdr_sym = elf->get_shdr (shdr->sh_link); + if (shdr_sym == NULL) + return; + Elf_Data *data_sym = elf->elf_getdata (shdr->sh_link); + Elf_Data *data_str = elf->elf_getdata (shdr_sym->sh_link); + char *Strtab = data_str ? (char*) data_str->d_buf : NULL; + Elf_Internal_Rela rela; + int n, cnt = (int) (ScnSize / EntSize); + + char *sec_name = elf->get_sec_name (sec); + if (sec_name == NULL) // It can not be, but let's check + return; + Dprintf (DUMP_RELA_SEC, + "======= DwarfLib::dump_rela_debug_sec Section:%2d '%s'\n", + sec, sec_name); + Dprintf (DUMP_RELA_SEC, + " N |addend| offset | r_info | stt_type |\n"); + for (n = 0; n < cnt; n++) + { + if (strncmp (sec_name, NTXT (".rela."), 6) == 0) + elf->elf_getrela (data, n, &rela); + else + { + elf->elf_getrel (data, n, &rela); + rela.r_addend = 0; + } + int ndx = (int) GELF_R_SYM (rela.r_info); + Elf_Internal_Shdr *secHdr; + Elf_Internal_Sym sym; + elf->elf_getsym (data_sym, ndx, &sym); + Dprintf (DUMP_RELA_SEC, NTXT ("%3d:%5d |%11lld |0x%016llx | %-15s|"), + n, (int) rela.r_addend, + (long long) rela.r_offset, (long long) rela.r_info, + gelf_st_type2str ((int) GELF_ST_TYPE (sym.st_info))); + switch (GELF_ST_TYPE (sym.st_info)) + { + case STT_FUNC: + case STT_OBJECT: + case STT_NOTYPE: + secHdr = elf->get_shdr (sym.st_shndx); + if (secHdr) + Dprintf (DUMP_RELA_SEC, NTXT (" img_offset=0x%llx"), + (long long) (sym.st_value + secHdr->sh_offset)); + if (Strtab && sym.st_name) + Dprintf (DUMP_RELA_SEC, NTXT (" %s"), Strtab + sym.st_name); + break; + case STT_SECTION: + secHdr = elf->get_shdr (sym.st_shndx); + if (secHdr) + { + Dprintf (DUMP_RELA_SEC, NTXT (" value=0x%016llx (%lld)"), + (long long) (secHdr->sh_offset + rela.r_addend), + (long long) (secHdr->sh_offset + rela.r_addend)); + } + break; + default: + break; + } + Dprintf (DUMP_RELA_SEC, NTXT ("\n")); + } + Dprintf (DUMP_RELA_SEC, NTXT ("\n")); +} + +void +ElfReloc::dump () +{ + if (!DUMP_ELF_RELOC || (reloc == NULL) || (reloc->size () == 0)) + return; + Dprintf (DUMP_ELF_RELOC, NTXT ("======= ElfReloc::dump\n")); + Dprintf (DUMP_ELF_RELOC, NTXT (" N | offset | value | STT_TYPE\n")); + for (int i = 0; i < reloc->size (); i++) + { + Sreloc *srlc = reloc->fetch (i); + Dprintf (DUMP_ELF_RELOC, NTXT ("%3d:%11lld |%11lld | %s\n"), + i, (long long) srlc->offset, (long long) srlc->value, + gelf_st_type2str (srlc->stt_type)); + } + Dprintf (DUMP_ELF_RELOC, NTXT ("\n")); +} + +static int +DwrRelocOffsetCmp (const void *a, const void *b) +{ + ElfReloc::Sreloc *item1 = *((ElfReloc::Sreloc **) a); + ElfReloc::Sreloc *item2 = *((ElfReloc::Sreloc **) b); + return item1->offset < item2->offset ? -1 : + item1->offset == item2->offset ? 0 : 1; +} + +ElfReloc * +ElfReloc::get_elf_reloc (Elf *elfp, char *sec_name, ElfReloc *rlc) +{ + int et = elfp->elf_getehdr ()->e_type; + if (et == ET_EXEC || et == ET_DYN) + return rlc; + int sec = elfp->elf_get_sec_num (sec_name); + if (sec == 0) + return rlc; + Elf_Internal_Shdr *shdr = elfp->get_shdr (sec); + if (shdr == NULL || shdr->sh_entsize == 0) + return rlc; + + Elf_Data *data = elfp->elf_getdata (sec); + if (data == NULL || data->d_size == 0) + return rlc; + + int cnt = (int) (data->d_size / shdr->sh_entsize); + Elf_Internal_Shdr *shdr_sym = elfp->get_shdr (shdr->sh_link); + if (shdr_sym == NULL) + return rlc; + Elf_Data *data_sym = elfp->elf_getdata (shdr->sh_link); + Vector<Sreloc *> *vp = NULL; + + for (int n = 0; n < cnt; n++) + { + Elf_Internal_Shdr *secHdr; + Sreloc *srlc; + Elf_Internal_Rela rela; + if (strncmp (sec_name, NTXT (".rela."), 6) == 0) + elfp->elf_getrela (data, n, &rela); + else + { + elfp->elf_getrel (data, n, &rela); + rela.r_addend = 0; + } + int ndx = (int) GELF_R_SYM (rela.r_info); + Elf_Internal_Sym sym; + elfp->elf_getsym (data_sym, ndx, &sym); + + srlc = new Sreloc; + srlc->offset = rela.r_offset; + srlc->value = 0; + srlc->stt_type = (int) GELF_ST_TYPE (sym.st_info); + switch (GELF_ST_TYPE (sym.st_info)) + { + case STT_FUNC: + secHdr = elfp->get_shdr (sym.st_shndx); + if (secHdr) + srlc->value = secHdr->sh_offset + sym.st_value; + break; + case STT_OBJECT: + case STT_NOTYPE: + secHdr = elfp->get_shdr (shdr->sh_info); + if (secHdr) + { + srlc->offset = rela.r_info; + srlc->value = secHdr->sh_offset + rela.r_addend; + } + break; + case STT_SECTION: + secHdr = elfp->get_shdr (sym.st_shndx); + if (secHdr) + srlc->value = rela.r_addend; + break; + default: + srlc->value = 0; + break; + } + if (rlc == NULL) + { + rlc = new ElfReloc (elfp); + vp = rlc->reloc; + } + if (vp == NULL) + { + vp = new Vector<Sreloc*>; + rlc->reloc = vp; + } + vp->append (srlc); + } + if (vp) + vp->sort (DwrRelocOffsetCmp); + if (rlc) + { + rlc->dump_rela_debug_sec (sec); + rlc->dump (); + } + return rlc; +} + +long long +ElfReloc::get_reloc_addr (long long offset) +{ + Sreloc *srlc; + int i = cur_reloc_ind - 1; + if (i >= 0 && i < reloc->size ()) + { + srlc = reloc->fetch (i); + if (srlc->offset > offset) // need to reset + cur_reloc_ind = 0; + } + for (; cur_reloc_ind < reloc->size (); cur_reloc_ind++) + { + srlc = reloc->fetch (cur_reloc_ind); + if (srlc->offset == offset) + return srlc->value; + if (srlc->offset > offset) + return 0; + } + return 0; +} + +DwrLocation * +DwrCU::dwr_get_location (DwrSec *secp, DwrLocation *lp) +{ + lp->offset = secp->offset; + lp->lc_number = 0; + lp->lc_number2 = 0; + lp->op = secp->Get_8 (); + switch (lp->op) + { + // registers + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + break; + case DW_OP_regx: + lp->lc_number = secp->GetULEB128 (); + break; + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + lp->lc_number = secp->GetSLEB128 (); + break; + case DW_OP_fbreg: + lp->lc_number = secp->GetSLEB128 (); + break; + case DW_OP_bregx: + lp->lc_number = secp->GetULEB128 (); + lp->lc_number2 = secp->GetSLEB128 (); + break; + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + lp->lc_number = lp->op - DW_OP_lit0; + break; + case DW_OP_addr: + lp->lc_number = secp->GetADDR (); + break; + case DW_OP_const1u: + lp->lc_number = secp->Get_8 (); + break; + case DW_OP_const1s: + { + signed char x; + x = secp->Get_8 (); + lp->lc_number = x; + } + break; + case DW_OP_const2u: + lp->lc_number = secp->Get_16 (); + break; + case DW_OP_const2s: + { + signed short x; + x = secp->Get_16 (); + lp->lc_number = x; + } + break; + case DW_OP_const4u: + lp->lc_number = secp->Get_32 (); + break; + case DW_OP_const4s: + { + signed int x; + x = secp->Get_32 (); + lp->lc_number = x; + } + break; + case DW_OP_const8u: + lp->lc_number = secp->Get_64 (); + break; + case DW_OP_const8s: + { + signed long long x; + x = secp->Get_64 (); + lp->lc_number = x; + } + break; + case DW_OP_plus_uconst: + case DW_OP_constu: + lp->lc_number = secp->GetULEB128 (); + break; + case DW_OP_consts: + lp->lc_number = secp->GetSLEB128 (); + break; + + // Stack operations + case DW_OP_pick: + case DW_OP_deref_size: + case DW_OP_xderef_size: + lp->lc_number = secp->Get_8 (); + break; + case DW_OP_dup: + case DW_OP_drop: + case DW_OP_over: + case DW_OP_swap: + case DW_OP_rot: + case DW_OP_deref: + case DW_OP_xderef: + // Arithmetic and Logical Operations + case DW_OP_abs: + case DW_OP_and: + case DW_OP_div: + case DW_OP_minus: + case DW_OP_mod: + case DW_OP_mul: + case DW_OP_neg: + case DW_OP_not: + case DW_OP_or: + case DW_OP_plus: + case DW_OP_shl: + case DW_OP_shr: + case DW_OP_shra: + case DW_OP_xor: + case DW_OP_le: + case DW_OP_ge: + case DW_OP_eq: + case DW_OP_lt: + case DW_OP_gt: + case DW_OP_ne: + case DW_OP_nop: + break; + case DW_OP_skip: + case DW_OP_bra: + lp->lc_number = secp->Get_16 (); + break; + case DW_OP_piece: + lp->lc_number = secp->GetULEB128 (); + break; + case DW_OP_push_object_address: /* DWARF3 */ + break; + case DW_OP_call2: /* DWARF3 */ + lp->lc_number = secp->Get_16 (); + break; + case DW_OP_call4: /* DWARF3 */ + lp->lc_number = secp->Get_32 (); + break; + case DW_OP_call_ref: /* DWARF3 */ + lp->lc_number = secp->GetADDR (); + break; + default: + return (NULL); + } + return lp; +} + +char * +DwrCU::tag2str (int tag) +{ + static char buf[128]; + char *s; + + switch (tag) + { + CASE_S (DW_TAG_array_type); + CASE_S (DW_TAG_class_type); + CASE_S (DW_TAG_entry_point); + CASE_S (DW_TAG_enumeration_type); + CASE_S (DW_TAG_formal_parameter); + CASE_S (DW_TAG_imported_declaration); + CASE_S (DW_TAG_label); + CASE_S (DW_TAG_lexical_block); + CASE_S (DW_TAG_member); + CASE_S (DW_TAG_pointer_type); + CASE_S (DW_TAG_reference_type); + CASE_S (DW_TAG_compile_unit); + CASE_S (DW_TAG_string_type); + CASE_S (DW_TAG_structure_type); + CASE_S (DW_TAG_subroutine_type); + CASE_S (DW_TAG_typedef); + CASE_S (DW_TAG_union_type); + CASE_S (DW_TAG_unspecified_parameters); + CASE_S (DW_TAG_variant); + CASE_S (DW_TAG_common_block); + CASE_S (DW_TAG_common_inclusion); + CASE_S (DW_TAG_inheritance); + CASE_S (DW_TAG_inlined_subroutine); + CASE_S (DW_TAG_module); + CASE_S (DW_TAG_ptr_to_member_type); + CASE_S (DW_TAG_set_type); + CASE_S (DW_TAG_subrange_type); + CASE_S (DW_TAG_with_stmt); + CASE_S (DW_TAG_access_declaration); + CASE_S (DW_TAG_base_type); + CASE_S (DW_TAG_catch_block); + CASE_S (DW_TAG_const_type); + CASE_S (DW_TAG_constant); + CASE_S (DW_TAG_enumerator); + CASE_S (DW_TAG_file_type); + CASE_S (DW_TAG_friend); + CASE_S (DW_TAG_namelist); + CASE_S (DW_TAG_namelist_item); + CASE_S (DW_TAG_packed_type); + CASE_S (DW_TAG_subprogram); + CASE_S (DW_TAG_template_type_param); + CASE_S (DW_TAG_template_value_param); + CASE_S (DW_TAG_thrown_type); + CASE_S (DW_TAG_try_block); + CASE_S (DW_TAG_variant_part); + CASE_S (DW_TAG_variable); + CASE_S (DW_TAG_volatile_type); + CASE_S (DW_TAG_dwarf_procedure); + CASE_S (DW_TAG_restrict_type); + CASE_S (DW_TAG_interface_type); + CASE_S (DW_TAG_namespace); + CASE_S (DW_TAG_imported_module); + CASE_S (DW_TAG_unspecified_type); + CASE_S (DW_TAG_partial_unit); + CASE_S (DW_TAG_imported_unit); + CASE_S (DW_TAG_lo_user); + CASE_S (DW_TAG_MIPS_loop); + CASE_S (DW_TAG_format_label); + CASE_S (DW_TAG_function_template); + CASE_S (DW_TAG_class_template); + CASE_S (DW_TAG_GNU_BINCL); + CASE_S (DW_TAG_GNU_EINCL); + CASE_S (DW_TAG_GNU_call_site); + CASE_S (DW_TAG_GNU_call_site_parameter); + CASE_S (DW_TAG_SUN_codeflags); + CASE_S (DW_TAG_SUN_memop_info); + CASE_S (DW_TAG_hi_user); + CASE_S (DW_TAG_icc_compile_unit); + default: s = NTXT ("???"); + break; + } + snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), s, tag); + buf[sizeof (buf) - 1] = 0; + return buf; +} + +char * +DwrCU::at2str (int tag) +{ + static char buf[128]; + char *s; + switch (tag) + { + CASE_S (DW_AT_sibling); + CASE_S (DW_AT_location); + CASE_S (DW_AT_name); + CASE_S (DW_AT_ordering); + CASE_S (DW_AT_subscr_data); + CASE_S (DW_AT_byte_size); + CASE_S (DW_AT_bit_offset); + CASE_S (DW_AT_bit_size); + CASE_S (DW_AT_element_list); + CASE_S (DW_AT_stmt_list); + CASE_S (DW_AT_low_pc); + CASE_S (DW_AT_high_pc); + CASE_S (DW_AT_language); + CASE_S (DW_AT_member); + CASE_S (DW_AT_discr); + CASE_S (DW_AT_discr_value); + CASE_S (DW_AT_visibility); + CASE_S (DW_AT_import); + CASE_S (DW_AT_string_length); + CASE_S (DW_AT_common_reference); + CASE_S (DW_AT_comp_dir); + CASE_S (DW_AT_const_value); + CASE_S (DW_AT_containing_type); + CASE_S (DW_AT_default_value); + CASE_S (DW_AT_inline); + CASE_S (DW_AT_is_optional); + CASE_S (DW_AT_lower_bound); + CASE_S (DW_AT_producer); + CASE_S (DW_AT_prototyped); + CASE_S (DW_AT_return_addr); + CASE_S (DW_AT_start_scope); + CASE_S (DW_AT_stride_size); + CASE_S (DW_AT_upper_bound); + CASE_S (DW_AT_abstract_origin); + CASE_S (DW_AT_accessibility); + CASE_S (DW_AT_address_class); + CASE_S (DW_AT_artificial); + CASE_S (DW_AT_base_types); + CASE_S (DW_AT_calling_convention); + CASE_S (DW_AT_count); + CASE_S (DW_AT_data_member_location); + CASE_S (DW_AT_decl_column); + CASE_S (DW_AT_decl_file); + CASE_S (DW_AT_decl_line); + CASE_S (DW_AT_declaration); + CASE_S (DW_AT_discr_list); + CASE_S (DW_AT_encoding); + CASE_S (DW_AT_external); + CASE_S (DW_AT_frame_base); + CASE_S (DW_AT_friend); + CASE_S (DW_AT_identifier_case); + CASE_S (DW_AT_macro_info); + CASE_S (DW_AT_namelist_item); + CASE_S (DW_AT_priority); + CASE_S (DW_AT_segment); + CASE_S (DW_AT_specification); + CASE_S (DW_AT_static_link); + CASE_S (DW_AT_type); + CASE_S (DW_AT_use_location); + CASE_S (DW_AT_variable_parameter); + CASE_S (DW_AT_virtuality); + CASE_S (DW_AT_vtable_elem_location); + CASE_S (DW_AT_allocated); + CASE_S (DW_AT_associated); + CASE_S (DW_AT_data_location); + CASE_S (DW_AT_byte_stride); + CASE_S (DW_AT_entry_pc); + CASE_S (DW_AT_use_UTF8); + CASE_S (DW_AT_extension); + CASE_S (DW_AT_ranges); + CASE_S (DW_AT_trampoline); + CASE_S (DW_AT_call_column); + CASE_S (DW_AT_call_file); + CASE_S (DW_AT_call_line); + CASE_S (DW_AT_description); + CASE_S (DW_AT_binary_scale); + CASE_S (DW_AT_decimal_scale); + CASE_S (DW_AT_small); + CASE_S (DW_AT_decimal_sign); + CASE_S (DW_AT_digit_count); + CASE_S (DW_AT_picture_string); + CASE_S (DW_AT_mutable); + CASE_S (DW_AT_threads_scaled); + CASE_S (DW_AT_explicit); + CASE_S (DW_AT_object_pointer); + CASE_S (DW_AT_endianity); + CASE_S (DW_AT_elemental); + CASE_S (DW_AT_pure); + CASE_S (DW_AT_recursive); + CASE_S (DW_AT_signature); + CASE_S (DW_AT_main_subprogram); + CASE_S (DW_AT_data_bit_offset); + CASE_S (DW_AT_const_expr); + CASE_S (DW_AT_enum_class); + CASE_S (DW_AT_linkage_name); + CASE_S (DW_AT_lo_user); + CASE_S (DW_AT_MIPS_fde); + CASE_S (DW_AT_MIPS_loop_begin); + CASE_S (DW_AT_MIPS_tail_loop_begin); + CASE_S (DW_AT_MIPS_epilog_begin); + CASE_S (DW_AT_MIPS_loop_unroll_factor); + CASE_S (DW_AT_MIPS_software_pipeline_depth); + CASE_S (DW_AT_MIPS_linkage_name); + CASE_S (DW_AT_MIPS_stride); + CASE_S (DW_AT_MIPS_abstract_name); + CASE_S (DW_AT_MIPS_clone_origin); + CASE_S (DW_AT_MIPS_has_inlines); + CASE_S (DW_AT_sf_names); + CASE_S (DW_AT_src_info); + CASE_S (DW_AT_mac_info); + CASE_S (DW_AT_src_coords); + CASE_S (DW_AT_body_begin); + CASE_S (DW_AT_body_end); + CASE_S (DW_AT_GNU_vector); + CASE_S (DW_AT_GNU_guarded_by); + CASE_S (DW_AT_GNU_pt_guarded_by); + CASE_S (DW_AT_GNU_guarded); + CASE_S (DW_AT_GNU_pt_guarded); + CASE_S (DW_AT_GNU_locks_excluded); + CASE_S (DW_AT_GNU_exclusive_locks_required); + CASE_S (DW_AT_GNU_shared_locks_required); + CASE_S (DW_AT_GNU_odr_signature); + CASE_S (DW_AT_GNU_template_name); + CASE_S (DW_AT_GNU_call_site_value); + CASE_S (DW_AT_GNU_call_site_data_value); + CASE_S (DW_AT_GNU_call_site_target); + CASE_S (DW_AT_GNU_call_site_target_clobbered); + CASE_S (DW_AT_GNU_tail_call); + CASE_S (DW_AT_GNU_all_tail_call_sites); + CASE_S (DW_AT_GNU_all_call_sites); + CASE_S (DW_AT_GNU_all_source_call_sites); + CASE_S (DW_AT_SUN_command_line); + CASE_S (DW_AT_SUN_func_offsets); + CASE_S (DW_AT_SUN_cf_kind); + CASE_S (DW_AT_SUN_func_offset); + CASE_S (DW_AT_SUN_memop_type_ref); + CASE_S (DW_AT_SUN_profile_id); + CASE_S (DW_AT_SUN_memop_signature); + CASE_S (DW_AT_SUN_obj_dir); + CASE_S (DW_AT_SUN_obj_file); + CASE_S (DW_AT_SUN_original_name); + CASE_S (DW_AT_SUN_link_name); + CASE_S (DW_AT_hi_user); + CASE_S (DW_AT_icc_flags); + default: s = NTXT ("???"); + break; + } + snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), s, tag); + buf[sizeof (buf) - 1] = 0; + return buf; +} + +char * +DwrCU::form2str (int tag) +{ + static char buf[128]; + char *s; + switch (tag) + { + CASE_S (DW_FORM_addr); + CASE_S (DW_FORM_block2); + CASE_S (DW_FORM_block4); + CASE_S (DW_FORM_data2); + CASE_S (DW_FORM_data4); + CASE_S (DW_FORM_data8); + CASE_S (DW_FORM_string); + CASE_S (DW_FORM_block); + CASE_S (DW_FORM_block1); + CASE_S (DW_FORM_data1); + CASE_S (DW_FORM_flag); + CASE_S (DW_FORM_sdata); + CASE_S (DW_FORM_strp); + CASE_S (DW_FORM_udata); + CASE_S (DW_FORM_ref_addr); + CASE_S (DW_FORM_ref1); + CASE_S (DW_FORM_ref2); + CASE_S (DW_FORM_ref4); + CASE_S (DW_FORM_ref8); + CASE_S (DW_FORM_ref_udata); + CASE_S (DW_FORM_indirect); + CASE_S (DW_FORM_sec_offset); + CASE_S (DW_FORM_exprloc); + CASE_S (DW_FORM_flag_present); + CASE_S (DW_FORM_ref_sig8); + default: s = NTXT ("???"); + break; + } + snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), s, tag); + buf[sizeof (buf) - 1] = 0; + return buf; +} + +void +Dwr_Tag::dump () +{ + Dprintf (DUMP_DWARFLIB, + "\n<%2d>:<0x%08llx> %-30s <abbrev %lld> offset=0x%llx %s\n", + (int) level, (long long) die, DwrCU::tag2str (tag), (long long) num, + (long long) offset, + hasChild ? NTXT ("DW_children_yes") : NTXT ("DW_children_no")); + for (int i1 = firstAttribute; i1 < lastAttribute; i1++) + { + Dwr_Attr *atrp = abbrevAtForm->get (i1); + Dprintf (DUMP_DWARFLIB, " %-30s ", DwrCU::at2str (atrp->at_name)); + switch (atrp->at_form) + { + case DW_FORM_strp: + case DW_FORM_string: + Dprintf (DUMP_DWARFLIB, " \"%s\" len=%ld", + atrp->u.str ? atrp->u.str : NTXT ("<NULL>"), + (long) atrp->len); + break; + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + Dprintf (DUMP_DWARFLIB, " len=%3ld %p", (long) atrp->len, + atrp->u.str); + break; + case DW_FORM_addr: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + case DW_FORM_data1: + case DW_FORM_flag: + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_ref_addr: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + case DW_FORM_indirect: + case DW_FORM_sec_offset: + case DW_FORM_exprloc: + case DW_FORM_ref_sig8: + case DW_FORM_flag_present: + Dprintf (DUMP_DWARFLIB, " 0x%llx (%lld)", (long long) atrp->u.val, + (long long) atrp->u.val); + break; + default: + DEBUG_CODE + { + Dprintf (1, "Attribute form 0x%llx (%lld) is not implemented\n", + (long long) atrp->at_form, (long long) atrp->at_form); + assert (false); + } + } + Dprintf (DUMP_DWARFLIB, NTXT ("\n")); + } +} + + +////////////////////////////////////////////////////////// +// class DwrSec + +DwrSec::DwrSec (unsigned char *_data, uint64_t _size, bool _need_swap_endian, bool _addr32) +{ + isCopy = false; + data = _data; + sizeSec = _size; + size = (data ? _size : 0); + offset = 0; + fmt64 = false; + reloc = NULL; + need_swap_endian = _need_swap_endian; + addr32 = _addr32; +} + +DwrSec::DwrSec (DwrSec *secp, uint64_t _offset) +{ + isCopy = true; + data = secp->data; + sizeSec = secp->sizeSec; + size = secp->size; + offset = _offset; + fmt64 = secp->fmt64; + reloc = secp->reloc; + need_swap_endian = secp->need_swap_endian; + addr32 = secp->addr32; +} + +DwrSec::~DwrSec () +{ + if (!isCopy) + delete reloc; +} + +bool +DwrSec::bounds_violation (uint64_t sz) +{ + if (offset + sz > size) + { + Dprintf (DEBUG_ERR_MSG, "DwrSec::bounds_violation: offset=%lld + sz=%lld > size=%lld\n", + (long long) offset, (long long) sz, (long long) size); + return true; + } + return false; +} + +uint64_t +DwrSec::ReadLength () +{ + fmt64 = false; + uint64_t val = Get_32 (); + if (((uint32_t) val) == 0xffffffff) + { + fmt64 = true; + val = Get_64 (); + } + size = (val + offset < sizeSec) ? val + offset : sizeSec; + return size; +} + +unsigned char +DwrSec::Get_8 () +{ + unsigned char n = 0; + if (bounds_violation (sizeof (char))) + return n; + n = data[offset]; + offset += sizeof (char); + return n; +} + +unsigned short +DwrSec::Get_16 () +{ + unsigned short n = 0; + if (bounds_violation (sizeof (short))) + return n; + memcpy ((char *) &n, data + offset, sizeof (short)); + offset += sizeof (short); + if (need_swap_endian) + SWAP_ENDIAN (n); + return n; +} + +uint32_t +DwrSec::Get_32 () +{ + uint32_t n = 0; + if (bounds_violation (sizeof (uint32_t))) + return n; + memcpy ((char *) &n, data + offset, sizeof (uint32_t)); + offset += sizeof (uint32_t); + if (need_swap_endian) + SWAP_ENDIAN (n); + return n; +} + +uint64_t +DwrSec::Get_64 () +{ + uint64_t n = 0; + if (bounds_violation (sizeof (uint64_t))) + return n; + memcpy ((char *) &n, data + offset, sizeof (uint64_t)); + offset += sizeof (uint64_t); + if (need_swap_endian) + SWAP_ENDIAN (n); + return n; +} + +char * +DwrSec::GetData (uint64_t len) +{ + char *s = ((char *) data) + offset; + if (bounds_violation (len)) + s = NULL; + offset += len; + return s; +} + +char * +DwrSec::GetString (uint64_t *lenp) +{ + if (offset < size) + { + uint64_t len = 0; + for (char *s = ((char *) data) + offset; offset + len < size; len++) + { + if (s[len] == 0) + { // '\0' is inside section + offset += len + 1; + if (len == 0) + return NULL; + if (lenp) + *lenp = len + 1; + return s; + } + } + offset += len; + return NULL; // The section is not '\0' terminated + } + return NULL; +} + +uint64_t +DwrSec::GetLong () +{ + if (fmt64) + return Get_64 (); + return Get_32 (); +} + +uint64_t +DwrSec::GetADDR_32 () +{ + uint64_t res = reloc ? reloc->get_reloc_addr (offset) : 0; + res += Get_32 (); + return res; +} + +uint64_t +DwrSec::GetADDR_64 () +{ + uint64_t res = reloc ? reloc->get_reloc_addr (offset) : 0; + res += Get_64 (); + return res; +} + +uint64_t +DwrSec::GetADDR () +{ + if (addr32) + return GetADDR_32 (); + return GetADDR_64 (); +} + +uint64_t +DwrSec::GetRef () +{ + if (fmt64) + return GetADDR_64 (); + return GetADDR_32 (); +} + +ULEB128 +DwrSec::GetULEB128 () +{ + ULEB128 res = 0; + for (int shift = 0;; shift += 7) + { + ULEB128 val = Get_8 (); + res |= (val & 0x7f) << shift; + if ((val & 0x80) == 0) + break; + } + return res; +} + +SLEB128 +DwrSec::GetSLEB128 () +{ + ULEB128 res = 0, val = 0; + size_t shift; + for (shift = 0;;) + { + val = Get_8 (); + res |= (val & 0x7f) << shift; + shift += 7; + if ((val & 0x80) == 0) + break; + } + if ((val & 0x40) && (shift < 8 * sizeof (res))) + res |= -(((ULEB128) 1) << shift); + return (SLEB128) res; +} + +static void +fillBuf (unsigned char *s, int len, int col, unsigned char *buf) +{ + const char *nameX = "0123456789abcdef"; + int i, n, posCh = 2 * col + col / 4 + 5; + + if (len >= col) + len = col; + for (i = n = 0; i < len; i++, n += 2) + { + if ((i % 4) == 0 && i > 0) + { + buf[n] = ' '; + n++; + } + buf[n] = nameX[s[i] >> 4]; + buf[n + 1] = nameX[s[i] & 0xf]; + buf[posCh + i] = isprint (s[i]) ? s[i] : ' '; + } + buf[posCh + i] = 0; + for (i = n; i < posCh; i++) + buf[i] = ' '; +} + +static void +dumpArr (unsigned char *s, int len, int col, int num) +{ + unsigned char buf[128]; + if (col <= 0) + return; + for (int i = 0; i < len; i += col, num += col) + { + fillBuf (s + i, len - i, col, buf); + Dprintf (DUMP_DWARFLIB, "%5d: %s\n", num, buf); + } +} + +void +DwrSec::dump (char *msg) +{ + if (sizeSec > 0) + { + Dprintf (DUMP_DWARFLIB, NTXT ("======= DwrSec::dump\n")); + if (msg) + Dprintf (DUMP_DWARFLIB, NTXT ("%s:\n"), msg); + dumpArr (data, (int) sizeSec, 32, 0); + Dprintf (DUMP_DWARFLIB, NTXT ("\n")); + } +} + +////////////////////////////////////////////////////////// +// class DwrFileNames + +DwrFileName::DwrFileName (char *_fname) +{ + path = NULL; + fname = _fname; + dir_index = 0; + timestamp = 0; + file_size = 0; + isUsed = false; +} + +DwrFileName::~DwrFileName () +{ + if (path != fname) + free (path); +} + + +////////////////////////////////////////////////////////// +// class DwrLine +DwrLine::DwrLine () +{ + address = 0; + file = 0; + line = 0; + column = 0; +} + +DwrLine::~DwrLine () { } + + +////////////////////////////////////////////////////////// +// class DwrLineRegs +static int +LineRegsCmp (const void *a, const void *b) +{ + DwrLine *item1 = *((DwrLine **) a); + DwrLine *item2 = *((DwrLine **) b); + return item1->address == item2->address ? 0 : + item1->address > item2->address ? 1 : -1; +} + +DwrLineRegs::DwrLineRegs (DwrSec *secp, char *dirName) +{ + // `dwarfdump -vv -l` shows a line section (.debug_line) + debug_lineSec = secp; + uint64_t stmt_offset = debug_lineSec->offset; + uint64_t next_cu_offset = debug_lineSec->ReadLength (); + uint64_t header_offset = debug_lineSec->offset; + debug_lineSec->size = next_cu_offset; + version = debug_lineSec->Get_16 (); + header_length = debug_lineSec->GetLong (); + opcode_start = debug_lineSec->offset + header_length; + minimum_instruction_length = debug_lineSec->Get_8 (); + op_index_register = 0; + if (version == 4) + maximum_operations_per_instruction = debug_lineSec->Get_8 (); + else + maximum_operations_per_instruction = 1; + default_is_stmt = debug_lineSec->Get_8 (); + is_stmt = (default_is_stmt != 0); + line_base = debug_lineSec->Get_8 (); + line_range = debug_lineSec->Get_8 (); + opcode_base = debug_lineSec->Get_8 (); + standard_opcode_length = (Dwarf_Small*) debug_lineSec->GetData (opcode_base - 1); + + if (DUMP_DWR_LINE_REGS) + { + Dprintf (DUMP_DWR_LINE_REGS, + "\n.debug_line version=%d stmt_offset=0x%llx" + "header_offset=0x%llx size=%lld dirname='%s'\n" + " header_length=0x%llx opcode_start=0x%llx" + "minimum_instruction_length=%d default_is_stmt=%d\n" + " line_base=%d line_range=%d opcode_base=%d\n", + (int) version, (long long) stmt_offset, + (long long) header_offset, + (long long) (next_cu_offset - header_offset), STR (dirName), + (long long) header_length, (long long) opcode_start, + (int) minimum_instruction_length, (int) default_is_stmt, + (int) line_base, (int) line_range, (int) opcode_base); + if (standard_opcode_length == NULL) + Dprintf (DUMP_DWR_LINE_REGS, "ERROR: standard_opcode_length is NULL\n"); + for (int i = 0, sz = standard_opcode_length ? opcode_base - 1 : 0; + i < sz; i++) + Dprintf (DUMP_DWR_LINE_REGS, " opcode[%2d] length %2d\n", i, + (int) standard_opcode_length[i]); + } + + include_directories = new Vector<char *>; + include_directories->append (dirName); + while (true) + { + char *s = debug_lineSec->GetString (NULL); + if (s == NULL) + break; + include_directories->append (s); + } + + file_names = new Vector<DwrFileName *>; + while (true) + { + char *s = debug_lineSec->GetString (NULL); + if (s == NULL) + break; + DwrFileName *fnp = new DwrFileName (s); + fnp->path = NULL; + fnp->fname = s; + fnp->dir_index = debug_lineSec->GetULEB128_32 (); + fnp->timestamp = debug_lineSec->GetULEB128 (); + fnp->file_size = debug_lineSec->GetULEB128 (); + file_names->append (fnp); + } + lines = NULL; + dump (); +} + +DwrLineRegs::~DwrLineRegs () +{ + Destroy (file_names); + Destroy (lines); + delete debug_lineSec; + delete include_directories; +} + +void +DwrLineRegs::dump () +{ + if (!DUMP_DWR_LINE_REGS) + return; + Dprintf (DUMP_DWR_LINE_REGS, NTXT ("\ninclude_directories size=%lld\n"), (long long) VecSize (include_directories)); + for (long i = 0, sz = VecSize (include_directories); i < sz; i++) + { + char *s = include_directories->get (i); + Dprintf (DUMP_DWR_LINE_REGS, NTXT (" %2lld %s\n"), (long long) i, STR (s)); + } + + Dprintf (DUMP_DWR_LINE_REGS, NTXT ("\nfile_names size=%lld\n"), (long long) VecSize (file_names)); + for (long i = 0, sz = VecSize (file_names); i < sz; i++) + { + DwrFileName *fnp = file_names->get (i); + Dprintf (DUMP_DWR_LINE_REGS, NTXT (" %2lld %-40s dir_index=%4lld timestamp=%8lld file_size=%lld\n"), + (long long) i, STR (fnp->fname), + (long long) fnp->dir_index, (long long) fnp->timestamp, (long long) fnp->file_size); + } + if (lines) + lines->dump (fname); + Dprintf (DUMP_DWR_LINE_REGS, NTXT ("\n\n")); +} + +void +DwrLineRegs::DoExtendedOpcode () +{ + uint64_t size = debug_lineSec->GetULEB128 (); + if (size == 0) + { + Dprintf (DUMP_DWR_LINE_REGS, NTXT ("%-20s"), NTXT ("ExtendedOpCode: size=0")); + return; + } + Dwarf_Small opcode = debug_lineSec->Get_8 (); + Dprintf (DUMP_DWR_LINE_REGS, NTXT ("%-20s"), extended_opcode2str (opcode)); + switch (opcode) + { + case DW_LNE_end_sequence: + end_sequence = true; + reset (); + break; + case DW_LNE_set_address: + address = debug_lineSec->GetADDR (); + break; + case DW_LNE_define_file: + // TODO, add file to file list + fname = debug_lineSec->GetString (NULL); + dir_index = debug_lineSec->GetULEB128 (); + timestamp = debug_lineSec->GetULEB128 (); + file_size = debug_lineSec->GetULEB128 (); + break; + default: + debug_lineSec->GetData (size - 1); // skip unknown opcode + break; + } +} + +void +DwrLineRegs::DoStandardOpcode (int opcode) +{ + switch (opcode) + { + case DW_LNS_copy: + basic_block = false; + EmitLine (); + break; + case DW_LNS_advance_pc: + address += debug_lineSec->GetULEB128 () * minimum_instruction_length; + break; + case DW_LNS_advance_line: + line += (int) debug_lineSec->GetSLEB128 (); + break; + case DW_LNS_set_file: + file = debug_lineSec->GetULEB128_32 (); + break; + case DW_LNS_set_column: + column = debug_lineSec->GetULEB128_32 (); + break; + case DW_LNS_negate_stmt: + is_stmt = -is_stmt; + break; + case DW_LNS_set_basic_block: + basic_block = true; + break; + case DW_LNS_const_add_pc: + address += ((255 - opcode_base) / line_range) * minimum_instruction_length; + break; + case DW_LNS_fixed_advance_pc: + address += debug_lineSec->Get_16 (); + break; + default: // skip unknown opcode/operands + debug_lineSec->GetData (standard_opcode_length ? + standard_opcode_length[opcode] : 1); + break; + } +} + +void +DwrLineRegs::DoSpecialOpcode (int opcode) +{ + int max_op_per_instr = maximum_operations_per_instruction == 0 ? 1 + : maximum_operations_per_instruction; + int operation_advance = (opcode / line_range); + address += minimum_instruction_length * ((op_index_register + operation_advance) / max_op_per_instr); + op_index_register = (op_index_register + operation_advance) % max_op_per_instr; + line += line_base + (opcode % line_range); + basic_block = false; + EmitLine (); +} + +void +DwrLineRegs::reset () +{ + dir_index = 0; + timestamp = 0; + file_size = 0; + address = 0; + file = 1; + line = 1; + column = 0; + is_stmt = (default_is_stmt != 0); + basic_block = false; + end_sequence = false; +} + +void +DwrLineRegs::EmitLine () +{ + DwrLine *lnp = new DwrLine; + + lnp->file = file; + lnp->line = line; + lnp->column = column; + lnp->address = address; + lines->append (lnp); + if ((file > 0) && (file < VecSize (file_names))) + { + DwrFileName *fnp = file_names->get (file); + fnp->isUsed = true; + } +} + +Vector<DwrLine *> * +DwrLineRegs::get_lines () +{ + if (lines == NULL) + { + lines = new Vector<DwrLine *>; + debug_lineSec->offset = opcode_start; + reset (); + Dprintf (DUMP_DWR_LINE_REGS, "\n offset code address (file, line, column) stmt blck end_seq \n"); + while (debug_lineSec->offset < debug_lineSec->size) + { + Dprintf (DUMP_DWR_LINE_REGS, NTXT ("0x%08llx "), + (long long) debug_lineSec->offset); + Dwarf_Small opcode = debug_lineSec->Get_8 (); + if (opcode == 0) + DoExtendedOpcode (); + else if (opcode < opcode_base) + { + DoStandardOpcode (opcode); + Dprintf (DUMP_DWR_LINE_REGS, NTXT ("%-20s"), standard_opcode2str (opcode)); + } + else + { + DoSpecialOpcode (opcode - opcode_base); + Dprintf (DUMP_DWR_LINE_REGS, NTXT ("%-20s"), + special_opcode2str (opcode - opcode_base)); + } + Dprintf (DUMP_DWR_LINE_REGS, + " 0x%08llx (%lld, %lld, %lld) %c %c %c\n", + (long long) address, (long long) file, (long long) line, + (long long) column, is_stmt ? 'T' : 'F', + basic_block ? 'T' : 'F', end_sequence ? 'T' : 'F'); + } + lines->sort (LineRegsCmp); + if (DUMP_DWR_LINE_REGS) + lines->dump (fname); + } + return lines; +} + +char * +DwrLineRegs::getPath (int fn) +{ + fn--; + if ((fn >= VecSize (file_names)) || (fn < 0)) + { + Dprintf (DEBUG_ERR_MSG, NTXT ("DwrLineRegs::getPath: fn=0x%lld file_names->size()=%lld\n"), + (long long) fn, (long long) VecSize (file_names)); + return NULL; + } + DwrFileName *fnp = file_names->fetch (fn); + if (fnp->path) + return fnp->path; + + char *dir = fnp->dir_index < include_directories->size () ? + include_directories->fetch (fnp->dir_index) : NULL; + if ((fnp->fname[0] == '/') || (dir == NULL) || (*dir == 0)) + { + fnp->path = fnp->fname; + return fnp->path; + } + + StringBuilder sb; + if (*dir != '/') + { // not absolute + char *s = include_directories->fetch (0); + sb.append (s); + sb.append ('/'); + } + sb.append (dir); + sb.append ('/'); + sb.append (fnp->fname); + fnp->path = canonical_path (sb.toString ()); + return fnp->path; +} + +DwrCU::DwrCU (Dwarf *_dwarf) +{ + dwarf = _dwarf; + cu_offset = dwarf->debug_infoSec->offset; + debug_infoSec = new DwrSec (dwarf->debug_infoSec, cu_offset); + next_cu_offset = debug_infoSec->ReadLength (); + if (next_cu_offset > debug_infoSec->sizeSec) + { + Dprintf (DEBUG_ERR_MSG, + "DwrCU::DwrCU: next_cu_offset(0x%llx) > debug_infoSec->sizeSec(%llx)\n", + (long long) next_cu_offset, (long long) debug_infoSec->sizeSec); + next_cu_offset = debug_infoSec->sizeSec; + } + debug_infoSec->size = next_cu_offset; + version = debug_infoSec->Get_16 (); + debug_abbrev_offset = debug_infoSec->GetLong (); + address_size = debug_infoSec->Get_8 (); + cu_header_offset = debug_infoSec->offset; + comp_dir = NULL; + module = NULL; + abbrevTable = NULL; + dwrInlinedSubrs = NULL; + srcFiles = NULL; + stmt_list_offset = 0; + dwrLineReg = NULL; + isMemop = false; + isGNU = false; + dwrTag.level = 0; + + build_abbrevTable (dwarf->debug_abbrevSec, debug_abbrev_offset); +#ifdef DEBUG + if (DUMP_DWARFLIB) + { + Dprintf (DUMP_DWARFLIB, + "CU_HEADER: header_offset = 0x%08llx %lld" + "next_header_offset=0x%08llx %lld\n" + " abbrev_offset = 0x%08llx %lld\n" + " unit_length = %lld\n" + " version = %d\n" + " address_size = %d\n" + " fmt64 = %s\n" + "debug_info: need_swap_endian=%s fmt64=%s addr32=%s\n", + (long long) cu_offset, (long long) cu_offset, + (long long) next_cu_offset, (long long) next_cu_offset, + (long long) debug_abbrev_offset, (long long) debug_abbrev_offset, + (long long) (next_cu_offset - cu_offset), + (int) version, (int) address_size, + debug_infoSec->fmt64 ? "true" : "false", + debug_infoSec->need_swap_endian ? "true" : "false", + debug_infoSec->fmt64 ? "true" : "false", + debug_infoSec->addr32 ? "true" : "false"); + Dprintf (DUMP_DWARFLIB, "\n.debug_abbrev cnt=%d offset=0x%08llx %lld\n", + (int) VecSize (abbrevTable), (long long) debug_abbrev_offset, + (long long) debug_abbrev_offset); + for (int i = 1, sz = VecSize (abbrevTable); i < sz; i++) + { + DwrAbbrevTable *abbTbl = abbrevTable->get (i); + Dprintf (DUMP_DWARFLIB, NTXT ("%5d: %-30s %-20s offset=0x%08llx\n"), + (int) i, DwrCU::tag2str (abbTbl->tag), + abbTbl->hasChild ? "DW_children_yes" : "DW_children_no", + (long long) abbTbl->offset); + for (int i1 = abbTbl->firstAtForm; i1 < abbTbl->lastAtForm; i1++) + { + Dwr_Attr *atf = abbrevAtForm->get (i1); + Dprintf (DUMP_DWARFLIB, " %-30s %s\n", + DwrCU::at2str (atf->at_name), + DwrCU::form2str (atf->at_form)); + } + } + } +#endif +} + +DwrCU::~DwrCU () +{ + delete debug_infoSec; + delete abbrevTable; + delete abbrevAtForm; + Destroy (dwrInlinedSubrs); + delete srcFiles; + delete dwrLineReg; + free (comp_dir); +} + +void +DwrCU::build_abbrevTable (DwrSec *_debug_abbrevSec, uint64_t _offset) +{ + if (abbrevTable) + return; + DwrSec *debug_abbrevSec = new DwrSec (_debug_abbrevSec, _offset); + abbrevTable = new DbeArray <DwrAbbrevTable>(128); + abbrevAtForm = new DbeArray <Dwr_Attr>(512); + abbrevTable->allocate (1); // skip first + abbrevAtForm->allocate (1); // skip first + for (int i = 1; debug_abbrevSec->offset < debug_abbrevSec->size; i++) + { + DwrAbbrevTable abbTbl; + abbTbl.offset = debug_abbrevSec->offset; + abbTbl.code = debug_abbrevSec->GetULEB128_32 (); + if (abbTbl.code == 0) + break; + else if (i != abbTbl.code) + { + dwarf->elf->append_msg (CMSG_ERROR, GTXT ("%s: the abbreviations table is corrupted (%lld <--> %lld)\n"), + get_basename (dwarf->elf->get_location ()), + (long long) i, (long long) abbTbl.code); + break; + } + abbTbl.tag = debug_abbrevSec->GetULEB128_32 (); + abbTbl.hasChild = (DW_children_yes == debug_abbrevSec->Get_8 ()); + abbTbl.firstAtForm = abbrevAtForm->size (); + while (debug_abbrevSec->offset < debug_abbrevSec->size) + { + Dwr_Attr atf; + atf.at_name = debug_abbrevSec->GetULEB128_32 (); + atf.at_form = debug_abbrevSec->GetULEB128_32 (); + if (atf.at_name == 0 && atf.at_form == 0) + break; + abbrevAtForm->append (atf); + } + abbTbl.lastAtForm = abbrevAtForm->size (); + abbrevTable->append (abbTbl); + } + delete debug_abbrevSec; +} + +int +DwrCU::set_die (Dwarf_Die die) +{ + if (die > 0) + debug_infoSec->offset = die; + if (debug_infoSec->offset < cu_header_offset + || debug_infoSec->offset >= debug_infoSec->size) + return DW_DLV_ERROR; + dwrTag.offset = debug_infoSec->offset; + dwrTag.die = debug_infoSec->offset - cu_offset; + dwrTag.num = debug_infoSec->GetULEB128_32 (); + if (dwrTag.num == 0) + return DW_DLV_NO_ENTRY; + dwrTag.abbrevAtForm = abbrevAtForm; + DwrAbbrevTable *abbTbl = abbrevTable->get (dwrTag.num); + if (abbTbl == NULL) + { // corrupt dwarf + dwarf->elf->append_msg (CMSG_ERROR, GTXT ("%s: the abbreviation code (%lld) does not match for the Dwarf entry (0x%llx)\n"), + get_basename (dwarf->elf->get_location ()), + (long long) dwrTag.num, (long long) dwrTag.offset); + return DW_DLV_ERROR; + } + dwrTag.tag = abbTbl->tag; + dwrTag.hasChild = abbTbl->hasChild; + dwrTag.firstAttribute = abbTbl->firstAtForm; + dwrTag.lastAttribute = abbTbl->lastAtForm; + for (int k = abbTbl->firstAtForm; k < abbTbl->lastAtForm; k++) + { + Dwr_Attr *atf = abbrevAtForm->get (k); + int at_form = atf->at_form; + if (at_form == DW_FORM_indirect) + at_form = debug_infoSec->GetULEB128_32 (); + switch (at_form) + { + case DW_FORM_addr: + atf->u.offset = (address_size == 4) ? debug_infoSec->GetADDR_32 () + : debug_infoSec->GetADDR_64 (); + break; + case DW_FORM_flag: + atf->u.offset = debug_infoSec->Get_8 (); + break; + case DW_FORM_block: + atf->len = debug_infoSec->GetULEB128 (); + atf->u.str = debug_infoSec->GetData (atf->len); + break; + case DW_FORM_block1: + atf->len = debug_infoSec->Get_8 (); + atf->u.str = debug_infoSec->GetData (atf->len); + break; + case DW_FORM_block2: + atf->len = debug_infoSec->Get_16 (); + atf->u.str = debug_infoSec->GetData (atf->len); + break; + case DW_FORM_block4: + atf->len = debug_infoSec->Get_32 (); + atf->u.str = debug_infoSec->GetData (atf->len); + break; + case DW_FORM_ref1: + atf->u.offset = debug_infoSec->Get_8 (); + break; + case DW_FORM_ref2: + atf->u.offset = debug_infoSec->Get_16 (); + break; + case DW_FORM_ref4: + atf->u.offset = debug_infoSec->Get_32 (); + break; + case DW_FORM_ref8: + atf->u.offset = debug_infoSec->Get_64 (); + break; + case DW_FORM_ref_udata: + atf->u.offset = debug_infoSec->GetULEB128 (); + break; + case DW_FORM_data1: + atf->u.offset = debug_infoSec->Get_8 (); + break; + case DW_FORM_data2: + atf->u.offset = debug_infoSec->Get_16 (); + break; + case DW_FORM_data4: + atf->u.offset = debug_infoSec->Get_32 (); + break; + case DW_FORM_data8: + atf->u.offset = debug_infoSec->Get_64 (); + break; + case DW_FORM_string: + atf->u.str = debug_infoSec->GetString (&atf->len); + break; + case DW_FORM_strp: + atf->u.offset = debug_infoSec->GetRef (); + if (dwarf->debug_strSec == NULL) + { + atf->u.str = NULL; + atf->len = 0; + } + else + { + dwarf->debug_strSec->offset = atf->u.offset; + atf->u.str = dwarf->debug_strSec->GetString (&atf->len); + } + break; + case DW_FORM_sdata: + atf->u.val = debug_infoSec->GetSLEB128 (); + break; + case DW_FORM_udata: + atf->u.offset = debug_infoSec->GetULEB128 (); + break; + case DW_FORM_ref_addr: + atf->u.offset = debug_infoSec->GetADDR (); + break; + case DW_FORM_sec_offset: + atf->u.offset = debug_infoSec->GetRef (); + break; + case DW_FORM_exprloc: + atf->u.offset = debug_infoSec->GetULEB128 (); + debug_infoSec->offset += atf->u.offset; + break; + case DW_FORM_flag_present: + atf->u.val = 1; + break; + case DW_FORM_ref_sig8: + atf->u.offset = debug_infoSec->GetADDR_64 (); + break; + default: + DEBUG_CODE + { + Dprintf (1, "Attribute form 0x%llx (%lld) is not implemented\n", + (long long) atf->at_form, (long long) atf->at_form); + assert (0); + } + atf->u.str = NULL; + atf->len = 0; + break; + } + } + dwrTag.dump (); + return DW_DLV_OK; +} + +static char * +composePath (char *dname, char *fname) +{ + char *s; + if (*fname == '/' || dname == NULL) + s = dbe_sprintf (NTXT ("%s"), fname); + else + s = dbe_sprintf (NTXT ("%s/%s"), dname, fname); + return canonical_path (s); +} + +Module * +DwrCU::parse_cu_header (LoadObject *lo) +{ + // Is tag always DW_TAG_compile_unit? + if (dwrTag.tag != DW_TAG_compile_unit) + { + Dprintf (DEBUG_ERR_MSG, + "parse_cu_header: die=0x%llx tag=%lld is not DW_TAG_compile_unit\n", + (long long) cu_offset, (long long) dwrTag.tag); + return NULL; + } + + char *name = Dwarf_string (DW_AT_name); + if (name == NULL) + name = NTXT ("UnnamedUnit"); + stmt_list_offset = Dwarf_data (DW_AT_stmt_list); + comp_dir = dbe_strdup (Dwarf_string (DW_AT_comp_dir)); + char *dir_name = comp_dir ? StrChr (comp_dir, ':') : NULL; + char *orig_name = Dwarf_string (DW_AT_SUN_original_name); + char *path = composePath (dir_name, orig_name ? orig_name : name); + + module = dwarf->stabs->append_Module (lo, path); + free (path); + if (module == NULL) + return NULL; + module->hasDwarf = true; + if (orig_name) + module->linkerStabName = composePath (dir_name, name); + module->lang_code = Dwarf_lang (); + module->comp_flags = dbe_strdup (Dwarf_string (DW_AT_SUN_command_line)); + if (module->comp_flags == NULL) + module->comp_flags = dbe_strdup (Dwarf_string (DW_AT_icc_flags)); + module->comp_dir = dbe_strdup (dir_name); + + char *obj_file = Dwarf_string (DW_AT_SUN_obj_file); + char *obj_dir = Dwarf_string (DW_AT_SUN_obj_dir); + if (obj_dir && obj_file) + { + // object information may not be available + dir_name = StrChr (obj_dir, ':'); + path = composePath (dir_name, obj_file); + if (module->dot_o_file == NULL) + module->dot_o_file = module->createLoadObject (path); + } + else + path = dbe_strdup (dwarf->stabs->path); + module->set_name (path); + return module; +} + +Dwr_Attr * +Dwr_Tag::get_attr (Dwarf_Half attr) +{ + for (long i = firstAttribute; i < lastAttribute; i++) + { + Dwr_Attr *atf = abbrevAtForm->get (i); + if (atf->at_name == attr) + return atf; + } + return NULL; +} + +char * +DwrCU::Dwarf_string (Dwarf_Half attr) +{ + Dwr_Attr *dwrAttr = dwrTag.get_attr (attr); + return dwrAttr ? dwrAttr->u.str : NULL; +} + +uint64_t +DwrCU::get_high_pc (uint64_t low_pc) +{ + Dwr_Attr *dwrAttr = dwrTag.get_attr (DW_AT_high_pc); + if (dwrAttr) + switch (dwrAttr->at_form) + { + case DW_FORM_addr: + return dwrAttr->u.offset; + default: + return dwrAttr->u.offset + low_pc; + } + return 0; +} + +Dwarf_Addr +DwrCU::Dwarf_addr (Dwarf_Half attr) +{ + Dwr_Attr *dwrAttr = dwrTag.get_attr (attr); + if (dwrAttr) + switch (dwrAttr->at_form) + { + case DW_FORM_addr: + return dwrAttr->u.offset; + } + return 0; +} + +DwrSec* +DwrCU::Dwarf_block (Dwarf_Half attr) +{ + Dwr_Attr *dwrAttr = dwrTag.get_attr (attr); + if (dwrAttr && dwrAttr->u.block) + switch (dwrAttr->at_form) + { + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + return new DwrSec (dwrAttr->u.block, dwrAttr->len, + dwarf->elf->need_swap_endian, + dwarf->elf->elf_getclass () == ELFCLASS32); + } + return NULL; +} + +int +DwrCU::read_data_attr (Dwarf_Half attr, int64_t *retVal) +{ + Dwr_Attr *dwrAttr = dwrTag.get_attr (attr); + if (dwrAttr) + switch (dwrAttr->at_form) + { + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + case DW_FORM_udata: + case DW_FORM_sec_offset: + *retVal = dwrAttr->u.val; + return DW_DLV_OK; + + } + return DW_DLV_ERROR; +} + +int +DwrCU::read_ref_attr (Dwarf_Half attr, int64_t *retVal) +{ + Dwr_Attr *dwrAttr = dwrTag.get_attr (attr); + if (dwrAttr) + switch (dwrAttr->at_form) + { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + case DW_FORM_sec_offset: + case DW_FORM_exprloc: + case DW_FORM_ref_sig8: + *retVal = dwrAttr->u.val; + return DW_DLV_OK; + } + return DW_DLV_ERROR; +} + +int64_t +DwrCU::Dwarf_data (Dwarf_Half attr) +{ + int64_t retVal; + if (read_data_attr (attr, &retVal) == DW_DLV_OK) + return retVal; + return 0; +} + +int64_t +DwrCU::Dwarf_ref (Dwarf_Half attr) +{ + int64_t retVal; + if (read_ref_attr (attr, &retVal) == DW_DLV_OK) + return retVal; + return 0; +} + +Dwarf_Addr +DwrCU::Dwarf_location (Dwarf_Attribute attr) +{ + DwrSec *secp = Dwarf_block (attr); + if (secp) + { + DwrLocation loc; + DwrLocation *lp = dwr_get_location (secp, &loc); + delete secp; + if (lp) + return lp->lc_number; + } + return 0; +} + +void +DwrCU::map_dwarf_lines (Module *mod) +{ + DwrLineRegs *lineReg = get_dwrLineReg (); + long inlinedSubrCnt = VecSize (dwrInlinedSubrs); + if (isGNU && (inlinedSubrCnt > 0)) + { + Function *func = NULL; + mod->inlinedSubr = (InlinedSubr *) malloc (inlinedSubrCnt + * sizeof (InlinedSubr)); + for (long i = 0; i < inlinedSubrCnt; i++) + { + DwrInlinedSubr *inlinedSubr = dwrInlinedSubrs->get (i); + uint64_t low_pc; + Function *f = dwarf->stabs->map_PC_to_func (inlinedSubr->low_pc, + low_pc, mod->functions); + if (f == NULL) + continue; + if (func != f) + { + func = f; + func->inlinedSubrCnt = 0; + func->inlinedSubr = mod->inlinedSubr + i; + } + InlinedSubr *p = func->inlinedSubr + func->inlinedSubrCnt; + func->inlinedSubrCnt++; + int fileno = inlinedSubr->file - 1; + SourceFile *sf = ((fileno >= 0) && (fileno < VecSize (srcFiles))) ? + srcFiles->get (fileno) : dbeSession->get_Unknown_Source (); + p->dbeLine = sf->find_dbeline (inlinedSubr->line); + p->high_pc = inlinedSubr->high_pc - low_pc; + p->low_pc = inlinedSubr->low_pc - low_pc; + p->level = inlinedSubr->level; + p->func = NULL; + p->fname = NULL; + if (set_die (inlinedSubr->abstract_origin) == DW_DLV_OK) + p->fname = dbe_strdup (Dwarf_string (DW_AT_name)); + if (p->fname) + p->func = Stabs::find_func (p->fname, mod->functions, + Stabs::is_fortran (mod->lang_code)); + } + } + Vector<DwrLine *> *lines = lineReg->get_lines (); + + Include *includes = new Include; + includes->new_src_file (mod->getMainSrc (), 0, NULL); + char *path = NULL; + SourceFile *cur_src = NULL; + Function *cur_func = NULL; + for (long i = 0, sz = VecSize (lines); i < sz; i++) + { + DwrLine *dwrLine = lines->get (i); + char *filename = dwrLineReg->getPath (dwrLine->file); + if (filename == NULL) + continue; + uint64_t pc = dwrLine->address; + int lineno = dwrLine->line; + if (path != filename) + { + path = filename; + char *name = StrChr (path, ':'); + SourceFile *src = mod->setIncludeFile (name); + if (cur_src != src) + { + includes->new_src_file (src, lineno, cur_func); + cur_src = src; + } + } + uint64_t low_pc; + Function *func = dwarf->stabs->map_PC_to_func (pc, low_pc, mod->functions); + if (func && (func->module == mod)) + { + if (func != cur_func) + { + if (cur_func) + while (cur_func->popSrcFile () != NULL) + ; + cur_func = func; + includes->push_src_files (cur_func); + } + cur_func->add_PC_info (pc - low_pc, lineno); + } + } + if (cur_func) + while (cur_func->popSrcFile ()) + ; + delete includes; +} + +DwrLineRegs * +DwrCU::get_dwrLineReg () +{ + if (dwrLineReg == NULL) + dwrLineReg = new DwrLineRegs (new DwrSec (dwarf->debug_lineSec, + stmt_list_offset), comp_dir); + return dwrLineReg; +} + +void +DwrCU::parse_inlined_subroutine (Dwarf_cnt *ctx) +{ + int64_t abstract_origin = Dwarf_ref (DW_AT_abstract_origin); + int fileno = (int) Dwarf_data (DW_AT_call_file); + int lineno = (int) Dwarf_data (DW_AT_call_line); + int level = ctx->inlinedSubr ? (ctx->inlinedSubr->level + 1) : 0; + DwrInlinedSubr *inlinedSubr_old = ctx->inlinedSubr; + + if (dwrInlinedSubrs == NULL) + dwrInlinedSubrs = new Vector<DwrInlinedSubr*>; + Dwr_Attr *dwrAttr = dwrTag.get_attr (DW_AT_ranges); + if (dwrAttr) + { + uint64_t ranges = Dwarf_ref (DW_AT_ranges); + if (dwarf->debug_rangesSec && (ranges < dwarf->debug_rangesSec->size)) + { + dwarf->debug_rangesSec->offset = ranges; + for (;;) + { + uint64_t low_pc = dwarf->debug_rangesSec->GetADDR (); + uint64_t high_pc = dwarf->debug_rangesSec->GetADDR (); + if ((low_pc > 0) && (low_pc <= high_pc)) + { + DwrInlinedSubr *p = new DwrInlinedSubr (abstract_origin, + low_pc, high_pc, fileno, lineno, level); + dwrInlinedSubrs->append (p); + ctx->inlinedSubr = p; + } + else + break; + } + } + } + else + { + uint64_t low_pc = Dwarf_addr (DW_AT_low_pc); + uint64_t high_pc = get_high_pc (low_pc); + if ((low_pc > 0) && (low_pc <= high_pc)) + { + DwrInlinedSubr *p = new DwrInlinedSubr (abstract_origin, low_pc, + high_pc, fileno, lineno, level); + dwrInlinedSubrs->append (p); + ctx->inlinedSubr = p; + } + } + parseChild (ctx); + ctx->inlinedSubr = inlinedSubr_old; +} + + +////////////////////////////////////////////////////////// +// class DwrInlinedSubr +DwrInlinedSubr::DwrInlinedSubr (int64_t _abstract_origin, uint64_t _low_pc, + uint64_t _high_pc, int _file, int _line, int _level) +{ + abstract_origin = _abstract_origin; + low_pc = _low_pc; + high_pc = _high_pc; + file = _file; + line = _line; + level = _level; +} + +void +DwrInlinedSubr::dump () +{ + Dprintf (DUMP_DWARFLIB, + " level=%d 0x%08llx [0x%08llx - 0x%08llx] file=%d line=%d\n", + (int) level, (long long) abstract_origin, (long long) low_pc, + (long long) high_pc, (int) file, (int) line); +} diff --git a/gprofng/src/DwarfLib.h b/gprofng/src/DwarfLib.h new file mode 100644 index 0000000..95eff57 --- /dev/null +++ b/gprofng/src/DwarfLib.h @@ -0,0 +1,313 @@ +/* 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. */ + +#ifndef _DWARFLIB_H_ +#define _DWARFLIB_H_ + +#include "dwarf2.h" + +class ElfReloc; +class Dwr_type; +class SourceFile; + +template <class ITEM> class Vector; +template <class ITEM> class DbeArray; +template <typename Key_t, typename Value_t> class DefaultMap; + +typedef uint64_t ULEB128; +typedef int64_t SLEB128; +typedef unsigned short Dwarf_Half; +typedef unsigned char Dwarf_Small; +typedef uint64_t Dwarf_Off; +typedef uint64_t Dwarf_Addr; +typedef uint64_t Dwarf_Unsigned; +typedef int64_t Dwarf_Die; +typedef int32_t Dwarf_Debug; +typedef int32_t Dwarf_Attribute; + + +class DwrSec +{ +public: + DwrSec (unsigned char *_data, uint64_t _size, bool _need_swap_endian, bool _addr32); + DwrSec (DwrSec *secp, uint64_t _offset); + ~DwrSec (); + unsigned char Get_8 (); + unsigned short Get_16 (); + uint32_t Get_32 (); + uint64_t Get_64 (); + uint64_t GetRef (); + uint64_t GetADDR (); + uint64_t GetADDR_32 (); + uint64_t GetADDR_64 (); + uint64_t GetLong (); + uint64_t ReadLength (); + SLEB128 GetSLEB128 (); + ULEB128 GetULEB128 (); + char *GetString (uint64_t *lenp); + char *GetData (uint64_t len); + void dump (char *msg); + + inline uint32_t + GetULEB128_32 () + { + return (uint32_t) GetULEB128 (); + } + + bool + inRange (uint64_t left, uint64_t right) + { + return (offset >= left) && (offset < right); + }; + + ElfReloc *reloc; + uint64_t sizeSec; + uint64_t size; + uint64_t offset; + bool fmt64; + bool addr32; + bool need_swap_endian; + +private: + bool isCopy; + unsigned char *data; + bool bounds_violation (uint64_t sz); +}; + +class DwrFileName +{ +public: + DwrFileName (char *_fname); + ~DwrFileName (); + uint64_t timestamp; + uint64_t file_size; + int dir_index; + char *fname; + char *path; + bool isUsed; +}; + +class DwrLine +{ +public: + DwrLine (); + ~DwrLine (); + uint64_t address; + uint32_t file; + uint32_t line; + uint32_t column; +}; + +class DwrInlinedSubr +{ +public: + DwrInlinedSubr (int64_t _abstract_origin, uint64_t _low_pc, uint64_t _high_pc, + int _file, int _line, int _level); + void dump (); + int64_t abstract_origin; + uint64_t low_pc; + uint64_t high_pc; + int file; + int line; + int level; +}; + +class DwrLineRegs +{ +public: + DwrLineRegs (DwrSec *_secp, char *dirName); + ~DwrLineRegs (); + char *getPath (int fn); + Vector<DwrLine *> *get_lines (); + void dump (); + + Vector<DwrFileName *> *file_names; + +private: + void DoExtendedOpcode (); + void DoStandardOpcode (int opcode); + void DoSpecialOpcode (int opcode); + void EmitLine (); + void reset (); + + char *fname; + uint64_t dir_index; + uint64_t timestamp; + uint64_t file_size; + uint64_t address; + uint32_t file; + uint32_t line; + uint32_t column; + Dwarf_Half version; + uint64_t op_index_register; + Dwarf_Small maximum_operations_per_instruction; + Dwarf_Small minimum_instruction_length; + Dwarf_Small default_is_stmt; + Dwarf_Small line_range; + Dwarf_Small opcode_base; + signed char line_base; + bool is_stmt; + bool basic_block; + bool end_sequence; + Vector<DwrLine *> *lines; + Vector<char *> *include_directories; + Dwarf_Small *standard_opcode_length; + DwrSec *debug_lineSec; + uint64_t header_length; + uint64_t opcode_start; +}; + +typedef struct Dwr_Attr +{ + union + { + char *str; + unsigned char *block; + uint64_t offset; + int64_t val; + } u; + uint64_t len; // length of u.str + int at_form; + int at_name; +} Dwr_Attr; + +typedef struct Dwr_Tag +{ +public: + Dwr_Attr *get_attr (Dwarf_Half attr); + void dump (); + + DbeArray<Dwr_Attr> *abbrevAtForm; + int64_t die; + int64_t offset; + int firstAttribute; + int lastAttribute; + int tag; + int hasChild; + int num; + int level; +} Dwr_Tag; + +enum +{ + DW_DLV_OK, + DW_DLV_NO_ENTRY, + DW_DLV_ERROR, + DW_DLV_BAD_ELF, + DW_DLV_NO_DWARF, + DW_DLV_WRONG_ARG +}; + +typedef struct DwrLocation +{ + uint64_t offset; + uint64_t lc_number; + uint64_t lc_number2; + uint32_t op; +} DwrLocation; + +typedef struct DwrAbbrevTable +{ + int64_t offset; + int firstAtForm; + int lastAtForm; + int code; + int tag; + bool hasChild; +} DwrAbbrevTable; + +class Dwarf_cnt +{ +public: + Dwarf_cnt (); + int64_t cu_offset; + int64_t parent; + int64_t size; + Module *module; + char *name; + Function *func; + Function *fortranMAIN; + datatype_t *dtype; + DwrInlinedSubr *inlinedSubr; + DefaultMap <int64_t, Dwr_type*> *dwr_types; + int level; + + Dwr_type *get_dwr_type (int64_t cu_die_offset); + Dwr_type *put_dwr_type (int64_t cu_die_offset, int tag); + Dwr_type *put_dwr_type (Dwr_Tag *dwrTag); +}; + +class DwrCU +{ +public: + DwrCU (Dwarf *_dwarf); + ~DwrCU (); + Module *parse_cu_header (LoadObject *lo); + void parseChild (Dwarf_cnt *ctx); + void read_hwcprof_info (Dwarf_cnt *ctx); + void map_dwarf_lines (Module *mod); + int set_die (Dwarf_Die die); + DwrLineRegs *get_dwrLineReg (); + + static char *at2str (int tag); + static char *form2str (int tag); + static char *tag2str (int tag); + + uint64_t cu_header_offset; + uint64_t cu_offset; + uint64_t next_cu_offset; + Vector<DwrInlinedSubr*> *dwrInlinedSubrs; + Vector<SourceFile *> *srcFiles; + bool isMemop; + bool isGNU; + +private: + void build_abbrevTable (DwrSec *debug_abbrevSec, uint64_t stmt_list_offset); + Function *append_Function (Dwarf_cnt *ctx); + void parse_inlined_subroutine (Dwarf_cnt *ctx); + uint64_t get_low_pc (); + uint64_t get_high_pc (uint64_t low_pc); + DwrLocation *dwr_get_location (DwrSec *secp, DwrLocation *lp); + int read_data_attr (Dwarf_Half attr, int64_t *retVal); + int read_ref_attr (Dwarf_Half attr, int64_t *retVal); + char *get_linkage_name (); + char *Dwarf_string (Dwarf_Half attr); + int64_t Dwarf_data (Dwarf_Half attr); + int64_t Dwarf_ref (Dwarf_Half attr); + DwrSec *Dwarf_block (Dwarf_Half attr); + Dwarf_Addr Dwarf_addr (Dwarf_Half attr); + Dwarf_Addr Dwarf_location (Dwarf_Attribute attr); + Sp_lang_code Dwarf_lang (); + + Dwarf *dwarf; + DwrSec *debug_infoSec; + uint64_t debug_abbrev_offset; + uint64_t stmt_list_offset; // offset in .debug_line section (DW_AT_stmt_list) + char *comp_dir; // compilation directory (DW_AT_comp_dir) + Module *module; + Dwarf_Half version; + Dwarf_Small address_size; + Dwr_Tag dwrTag; + DwrLineRegs *dwrLineReg; + DbeArray<DwrAbbrevTable> *abbrevTable; + DbeArray<Dwr_Attr> *abbrevAtForm; +}; + +#endif /* _DWARFLIB_H_ */ diff --git a/gprofng/src/Elf.cc b/gprofng/src/Elf.cc new file mode 100644 index 0000000..4117cf2 --- /dev/null +++ b/gprofng/src/Elf.cc @@ -0,0 +1,1138 @@ +/* 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 <unistd.h> + +#include "util.h" +#include "bfd.h" +#include "elf-bfd.h" +#include "Elf.h" +#include "Map.h" +#include "StringBuilder.h" +#include "DbeFile.h" + +typedef uint32_t Elf32_Word; +typedef uint32_t Elf64_Word; +typedef uint32_t Elf32_Addr; +typedef uint64_t Elf64_Addr; +typedef uint64_t Elf64_Xword; +typedef int32_t Elf32_Sword; +typedef int64_t Elf64_Sxword; +typedef uint16_t Elf32_Half; +typedef uint16_t Elf64_Half; + +// Ancillary entry +typedef struct +{ + Elf32_Word a_tag; /* how to interpret value */ + union + { + Elf32_Word a_val; + Elf32_Addr a_ptr; + } a_un; +} Elf32_Ancillary; + +struct S_Elf64_Ancillary +{ + Elf64_Xword a_tag; /* how to interpret value */ + union + { + Elf64_Xword a_val; + Elf64_Addr a_ptr; + } a_un; +}; + +/* Dynamic section entry. */ +typedef struct +{ + Elf32_Sword d_tag; /* Dynamic entry type */ + + union + { + Elf32_Word d_val; /* Integer value */ + Elf32_Addr d_ptr; /* Address value */ + } d_un; +} Elf32_Dyn; + +struct S_Elf64_Dyn +{ + Elf64_Sxword d_tag; /* Dynamic entry type */ + + union + { + Elf64_Xword d_val; /* Integer value */ + Elf64_Addr d_ptr; /* Address value */ + } d_un; +}; + + +// Symbol table +typedef struct +{ + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; /* bind, type: ELF_32_ST_... */ + unsigned char st_other; + Elf32_Half st_shndx; /* SHN_... */ +} Elf32_Sym; + +typedef struct +{ + Elf64_Word st_name; + unsigned char st_info; /* bind, type: ELF_64_ST_... */ + unsigned char st_other; + Elf64_Half st_shndx; /* SHN_... */ + Elf64_Addr st_value; + Elf64_Xword st_size; +} Elf64_Sym; + + +// Relocation +typedef struct +{ + Elf32_Addr r_offset; + Elf32_Word r_info; /* sym, type: ELF32_R_... */ +} Elf32_Rel; + +typedef struct +{ + Elf32_Addr r_offset; + Elf32_Word r_info; /* sym, type: ELF32_R_... */ + Elf32_Sword r_addend; +} Elf32_Rela; + +typedef struct +{ + Elf64_Addr r_offset; + Elf64_Xword r_info; /* sym, type: ELF64_R_... */ +} Elf64_Rel; + +typedef struct +{ + Elf64_Addr r_offset; + Elf64_Xword r_info; /* sym, type: ELF64_R_... */ + Elf64_Sxword r_addend; +} Elf64_Rela; + +int Elf::bfd_status = -1; + +void +Elf::elf_init () +{ + if (bfd_status == -1) + bfd_status = bfd_init (); +} + +Elf::Elf (char *filename) : DbeMessages (), Data_window (filename) +{ + ehdrp = NULL; + data = NULL; + ancillary_files = NULL; + elfSymbols = NULL; + gnu_debug_file = NULL; + dbeFile = NULL; + abfd = NULL; + if (bfd_status != BFD_INIT_MAGIC) + { + status = ELF_ERR_CANT_OPEN_FILE; + return; + } + abfd = bfd_openr (filename, NULL); + if (abfd == NULL) + { + status = ELF_ERR_CANT_OPEN_FILE; + return; + } + if (!bfd_check_format (abfd, bfd_object)) + { + bfd_close (abfd); + abfd = NULL; + status = ELF_ERR_CANT_OPEN_FILE; + return; + } + ehdrp = elf_getehdr (); + if (ehdrp == NULL) + { + bfd_close (abfd); + abfd = NULL; + status = ELF_ERR_BAD_ELF_FORMAT; + return; + } + elf_class = ehdrp->e_ident[EI_CLASS]; + elf_datatype = ehdrp->e_ident[EI_DATA]; + + if (not_opened ()) + { + status = ELF_ERR_CANT_OPEN_FILE; + return; + } + status = ELF_ERR_NONE; + +#if ARCH(SPARC) + need_swap_endian = is_Intel (); +#else + need_swap_endian = !is_Intel (); +#endif + + analyzerInfo = 0; + SUNW_ldynsym = 0; + gnuLink = 0; + stab = 0; + stabStr = 0; + stabIndex = 0; + stabIndexStr = 0; + stabExcl = 0; + stabExclStr = 0; + symtab = 0; + dynsym = 0; + info = 0; + plt = 0; + dwarf = false; + + for (unsigned int sec = 1; sec < elf_getehdr ()->e_shnum; sec++) + { + char *name = get_sec_name (sec); + if (name == NULL) + continue; + if (streq (name, NTXT (".stab"))) + stab = sec; + else if (streq (name, NTXT (".stabstr"))) + stabStr = sec; + else if (streq (name, NTXT (".stab.index"))) + stabIndex = sec; + else if (streq (name, NTXT (".stab.indexstr"))) + stabIndexStr = sec; + else if (streq (name, NTXT (".stab.excl"))) + stabExcl = sec; + else if (streq (name, NTXT (".stab.exclstr"))) + stabExclStr = sec; + else if (streq (name, NTXT (".gnu_debuglink"))) + gnuLink = sec; + else if (streq (name, NTXT (".__analyzer_info"))) + analyzerInfo = sec; + else if (streq (name, NTXT (".info"))) + info = true; + else if (streq (name, NTXT (".plt"))) + plt = sec; + else if (streq (name, NTXT (".SUNW_ldynsym"))) + SUNW_ldynsym = sec; + else if (streq (name, NTXT (".dynsym"))) + dynsym = sec; + else if (streq (name, NTXT (".symtab"))) + symtab = sec; + else if (strncmp (name, NTXT (".debug"), 6) == 0) + dwarf = true; + } + if (fd != -1) + { + close (fd); + fd = -1; + } +} + +Elf::~Elf () +{ + if (data) + { + for (int i = 0; i < (int) ehdrp->e_shnum; i++) + { + Elf_Data *p = data[i]; + if (p && !mmap_on_file && (p->d_flags & SHF_SUNW_ABSENT) == 0) + free (p->d_buf); + delete p; + } + free (data); + } + if (ancillary_files) + { + ancillary_files->destroy (); + delete ancillary_files; + } + delete elfSymbols; + delete gnu_debug_file; + delete dbeFile; + if (abfd) + bfd_close (abfd); +} + +Elf_Internal_Ehdr * +Elf::elf_getehdr () +{ + if (ehdrp == NULL && abfd) + ehdrp = elf_elfheader (abfd); + return ehdrp; +} + +Elf_Internal_Phdr * +Elf::get_phdr (unsigned int ndx) +{ + if (ehdrp == NULL || ndx >= ehdrp->e_phnum) + return NULL; + return &(elf_tdata (abfd)->phdr[ndx]); +} + +Elf_Internal_Shdr * +Elf::get_shdr (unsigned int ndx) +{ + if (ehdrp == NULL || ndx >= ehdrp->e_shnum) + return NULL; + return elf_elfsections (abfd)[ndx]; +} + +Elf64_Dyn * +Elf::elf_getdyn (Elf_Internal_Phdr *phdr, unsigned int ndx, Elf64_Dyn *pdyn) +{ + if (elf_getclass () == ELFCLASS32) + { + if (ndx * sizeof (Elf32_Dyn) >= phdr->p_filesz) + return NULL; + Elf32_Dyn *hdr = (Elf32_Dyn*) bind (phdr->p_offset + ndx * sizeof (Elf32_Dyn), + sizeof (Elf32_Dyn)); + if (hdr == NULL) + return NULL; + pdyn->d_tag = decode (hdr->d_tag); + pdyn->d_un.d_val = decode (hdr->d_un.d_val); + } + else + { + if (ndx * sizeof (Elf64_Dyn) >= phdr->p_filesz) + return NULL; + Elf64_Dyn *hdr = (Elf64_Dyn*) bind (phdr->p_offset + ndx * sizeof (Elf64_Dyn), + sizeof (Elf64_Dyn)); + if (hdr == NULL) + return NULL; + pdyn->d_tag = decode (hdr->d_tag); + pdyn->d_un.d_val = decode (hdr->d_un.d_val); + } + return pdyn; +} + +unsigned +Elf::elf_version (unsigned ver) +{ + // We compile locally, no need to check the version + return ver; +} + +Elf * +Elf::elf_begin (char *fname, Elf_status *stp) +{ + if (fname == NULL) + { + if (stp) + *stp = ELF_ERR_CANT_OPEN_FILE; + return NULL; + } + Elf *elf = new Elf (fname); + if (stp) + *stp = elf->status; + if (elf->status != ELF_ERR_NONE) + { + delete elf; + return NULL; + } +#if DEBUG + if (DUMP_ELF_SEC) + { + char *str = elf->dump (); + fprintf (stderr, NTXT ("%s\n\n"), str); + free (str); + } +#endif /* DEBUG */ + return elf; +} + +unsigned int +Elf::elf_get_sec_num (const char *name) +{ + if (name == NULL || ehdrp == NULL) + return 0; + for (unsigned int sec = 1; sec < ehdrp->e_shnum; sec++) + { + Elf_Internal_Shdr *shdr = get_shdr (sec); + if (shdr == NULL) + continue; + char *sname = elf_strptr (ehdrp->e_shstrndx, shdr->sh_name); + if (sname != NULL && strcmp (name, sname) == 0) + return sec; + } + return 0; +} + +char * +Elf::get_sec_name (unsigned int sec) +{ + Elf_Internal_Shdr *shdr = get_shdr (sec); + if (ehdrp == NULL || shdr == NULL) + return NULL; + return elf_strptr (ehdrp->e_shstrndx, shdr->sh_name); +} + +Elf_Data * +Elf::elf_getdata (unsigned int sec) +{ + if (data == NULL) + { + data = (Elf_Data **) malloc (ehdrp->e_shnum * sizeof (Elf_Data *)); + for (int i = 0; i < (int) ehdrp->e_shnum; i++) + data[i] = NULL; + } + Elf_Data *edta = data[sec]; + if (edta == NULL) + { + Elf_Internal_Shdr *shdr = get_shdr (sec); + if (shdr == NULL) + return NULL; + edta = new Elf_Data; + data[sec] = edta; + if ((shdr->sh_flags & SHF_SUNW_ABSENT) != 0) + { + char *sname = get_sec_name (sec); + for (int i = 0, sz = VecSize(ancillary_files); i < sz; i++) + { + Elf *ancElf = ancillary_files->fetch (i); + int secNum = sec; + if (dbe_strcmp (sname, ancElf->get_sec_name (sec)) != 0) + { + append_msg (CMSG_WARN, + "Warning: the section #%d (%s) is mismatch in ancillary file '%s')\n", + sec, STR (sname), STR (ancElf->fname)); + secNum = ancElf->elf_get_sec_num (sname); + } + if (secNum > 0) + { + Elf_Data *ed = ancElf->elf_getdata (secNum); + if (ed && ed->d_buf) + { + *edta = *ed; + edta->d_flags |= SHF_SUNW_ABSENT; + return edta; + } + } + } + } + edta->d_buf = get_data (shdr->sh_offset, (size_t) shdr->sh_size, NULL); + edta->d_flags = shdr->sh_flags; + edta->d_size = ((edta->d_buf == NULL) || (shdr->sh_type == SHT_NOBITS)) ? 0 : shdr->sh_size; + edta->d_off = shdr->sh_offset; + edta->d_align = shdr->sh_addralign; + } + return edta; +} + +int64_t +Elf::elf_checksum () +{ + if (ehdrp == NULL) + return 0; + int64_t chk = 0; + for (unsigned int ndx = 0; ndx < ehdrp->e_phnum; ndx++) + { + Elf_Internal_Phdr *phdr = get_phdr (ndx); + if (phdr == NULL) + continue; + if (phdr->p_type == PT_DYNAMIC) + { + Elf64_Dyn edyn; + for (unsigned int i = 0; elf_getdyn (phdr, i, &edyn) != NULL; i++) + { + if (!edyn.d_tag) + break; + if (edyn.d_tag == DT_CHECKSUM) + { + chk = edyn.d_un.d_val; + break; + } + } + } + } + return normalize_checksum (chk); +} + +uint64_t +Elf::get_baseAddr () +{ + uint64_t addr = 0; + for (unsigned int pnum = 0; pnum < elf_getehdr ()->e_phnum; pnum++) + { + Elf_Internal_Phdr *phdr = get_phdr (pnum); + if (phdr->p_type == PT_LOAD && phdr->p_flags == (PF_R | PF_X)) + { + if (addr == 0) + addr = phdr->p_vaddr; + else + { + addr = 0; + break; + } + } + } + return addr; +} + +char * +Elf::elf_strptr (unsigned int sec, uint64_t off) +{ + Elf_Data *edta = elf_getdata (sec); + if (edta && edta->d_buf && edta->d_size > off) + return ((char *) edta->d_buf) + off; + return NULL; +} + +Elf_Internal_Sym * +Elf::elf_getsym (Elf_Data *edta, unsigned int ndx, Elf_Internal_Sym *dst) +{ + if (dst == NULL || edta == NULL) + return NULL; + if (elf_getclass () == ELFCLASS32) + { + if (edta->d_size <= ndx * sizeof (Elf32_Sym)) + return NULL; + Elf32_Sym *hdr = (Elf32_Sym*) bind (edta->d_off + ndx * sizeof (Elf32_Sym), sizeof (Elf32_Sym)); + if (hdr == NULL) + return NULL; + dst->st_name = decode (hdr->st_name); + dst->st_value = decode (hdr->st_value); + dst->st_size = decode (hdr->st_size); + dst->st_info = ELF64_ST_INFO (ELF32_ST_BIND (decode (hdr->st_info)), + ELF32_ST_TYPE (decode (hdr->st_info))); + dst->st_other = decode (hdr->st_other); + dst->st_shndx = decode (hdr->st_shndx); + } + else + { + if (edta->d_size <= ndx * sizeof (Elf64_Sym)) + return NULL; + Elf64_Sym *hdr = (Elf64_Sym*) bind (edta->d_off + ndx * sizeof (Elf64_Sym), + sizeof (Elf64_Sym)); + if (hdr == NULL) + return NULL; + dst->st_name = decode (hdr->st_name); + dst->st_value = decode (hdr->st_value); + dst->st_size = decode (hdr->st_size); + dst->st_info = decode (hdr->st_info); + dst->st_other = decode (hdr->st_other); + dst->st_shndx = decode (hdr->st_shndx); + } + return dst; +} + +Elf_Internal_Rela * +Elf::elf_getrel (Elf_Data *edta, unsigned int ndx, Elf_Internal_Rela *dst) +{ + if (dst == NULL || edta == NULL || edta->d_buf == NULL) + return NULL; + if (elf_getclass () == ELFCLASS32) + { + Elf32_Rel *rel = ((Elf32_Rel *) edta->d_buf) + ndx; + dst->r_offset = decode (rel->r_offset); + dst->r_info = ELF64_R_INFO (ELF32_R_SYM (decode (rel->r_info)), + ELF32_R_TYPE (decode (rel->r_info))); + } + else + { + Elf64_Rel *rel = ((Elf64_Rel *) edta->d_buf) + ndx; + dst->r_offset = decode (rel->r_offset); + dst->r_info = decode (rel->r_info); + } + return dst; +} + +Elf_Internal_Rela * +Elf::elf_getrela (Elf_Data *edta, unsigned int ndx, Elf_Internal_Rela *dst) +{ + if (dst == NULL || edta == NULL || edta->d_buf == NULL) + return NULL; + if (elf_getclass () == ELFCLASS32) + { + Elf32_Rela *rela = ((Elf32_Rela *) edta->d_buf) + ndx; + dst->r_offset = decode (rela->r_offset); + dst->r_addend = decode (rela->r_addend); + dst->r_info = ELF64_R_INFO (ELF32_R_SYM (decode (rela->r_info)), + ELF32_R_TYPE (decode (rela->r_info))); + } + else + { + Elf64_Rela *rela = ((Elf64_Rela *) edta->d_buf) + ndx; + dst->r_offset = decode (rela->r_offset); + dst->r_addend = decode (rela->r_addend); + dst->r_info = decode (rela->r_info); + } + return dst; +} + +Elf64_Ancillary * +Elf::elf_getancillary (Elf_Data *edta, unsigned int ndx, Elf64_Ancillary *dst) +{ + if (dst == NULL || edta == NULL || edta->d_buf == NULL) + return NULL; + if (elf_getclass () == ELFCLASS32) + { + Elf32_Ancillary *p = ((Elf32_Ancillary *) edta->d_buf) + ndx; + dst->a_tag = decode (p->a_tag); + dst->a_un.a_val = decode (p->a_un.a_val); + } + else + { + Elf64_Ancillary *p = ((Elf64_Ancillary *) edta->d_buf) + ndx; + dst->a_tag = decode (p->a_tag); + dst->a_un.a_val = decode (p->a_un.a_val); + } + return dst; +} + +Elf * +Elf::get_related_file (const char *lo_name, const char *nm) +{ + DbeFile *df; + if (*nm == '/') + { + df = new DbeFile (nm); + df->filetype |= (DbeFile::F_FILE | DbeFile::F_DEBUG_FILE); + } + else + { + char *bname = get_basename (lo_name); + char *fnm = dbe_sprintf ("%.*s/%s", (int) (bname - lo_name), lo_name, nm); + df = new DbeFile (fnm); + df->filetype |= (DbeFile::F_FILE | DbeFile::F_DEBUG_FILE); + free (fnm); + } + Dprintf (DEBUG_STABS, "get_related_file: %s -> '%s'\n", nm, df->get_name ()); + Elf_status st = ELF_ERR_CANT_OPEN_FILE; + Elf *elf = elf_begin (df->get_location (), &st); + if (elf) + { + elf->dbeFile = df; + return elf; + } + switch (st) + { + case ELF_ERR_CANT_OPEN_FILE: + append_msg (CMSG_ERROR, GTXT ("Cannot open file `%s'"), df->get_name ()); + break; + case ELF_ERR_BAD_ELF_FORMAT: + default: + append_msg (CMSG_ERROR, GTXT ("Cannot read ELF header of `%s'"), + df->get_name ()); + break; + } + delete df; + return NULL; +} + +Elf * +Elf::find_ancillary_files (char *lo_name) +{ + // read the .gnu_debuglink and .SUNW_ancillary seections + if (gnu_debug_file) + return gnu_debug_file; + unsigned int sec = elf_get_sec_num (NTXT (".gnu_debuglink")); + if (sec > 0) + { + Elf_Data *dp = elf_getdata (sec); + if (dp) + { + gnu_debug_file = get_related_file (lo_name, (char *) (dp->d_buf)); + if (gnu_debug_file) + return gnu_debug_file; + } + } + + sec = elf_get_sec_num (NTXT (".SUNW_ancillary")); + if (sec > 0) + { + Elf_Internal_Shdr *shdr = get_shdr (sec); + uint64_t check_sum = 0; + char *ancName = NULL; + if (shdr) + { + Elf_Data *dp = elf_getdata (sec); + for (int i = 0, sz = (int) (shdr->sh_size / shdr->sh_entsize); + i < sz; i++) + { + Elf64_Ancillary anc; + if (elf_getancillary (dp, i, &anc) == NULL + || anc.a_tag == ANC_SUNW_NULL) + break; + if (anc.a_tag == ANC_SUNW_MEMBER) + ancName = elf_strptr (shdr->sh_link, anc.a_un.a_ptr); + else if (anc.a_tag == ANC_SUNW_CHECKSUM) + { + if (i == 0) + { + check_sum = anc.a_un.a_val; + continue; + } + if (check_sum == anc.a_un.a_val) + ancName = NULL; + if (ancName) + { + Elf *ancElf = get_related_file (lo_name, ancName); + if (ancElf == NULL) + continue; + int ancSec = ancElf->elf_get_sec_num (".SUNW_ancillary"); + if (ancSec > 0) + { + Elf_Internal_Shdr *ancHdr = ancElf->get_shdr (ancSec); + if (ancHdr) + { + Elf_Data *anc_dp = ancElf->elf_getdata (ancSec); + Elf64_Ancillary anc1; + if (ancElf->elf_getancillary (anc_dp, 0, &anc1) + && (anc1.a_tag == ANC_SUNW_CHECKSUM) && + anc1.a_un.a_val == anc.a_un.a_val) + { + if (ancillary_files == NULL) + ancillary_files = new Vector<Elf*>(2); + ancillary_files->append (ancElf); + } + else + append_msg (CMSG_WARN, GTXT ("Load Object: '%s' (checksum Ox%lld). The .anc file '%s' has checksum Ox%llx"), + STR (fname), (long long) check_sum, + STR (ancElf->dbeFile->get_location ()), + (long long) anc1.a_un.a_val); + } + } + ancName = NULL; + } + } + } + } + } + return NULL; +} + +char* +Elf::get_location () +{ + return dbeFile ? dbeFile->get_location () : fname; +} + +#define RET_S(x) if (t == x) return (char *) #x + +static char * +get_elf_class_name (int t) +{ + RET_S (ELFCLASSNONE); + RET_S (ELFCLASS32); + RET_S (ELFCLASS64); + return NTXT ("ELFCLASS_UNKNOWN"); +} + +static char * +get_elf_data_name (int t) +{ + RET_S (ELFDATANONE); + RET_S (ELFDATA2LSB); + RET_S (ELFDATA2MSB); + return NTXT ("ELFDATA_UNKNOWN"); +} + +static char * +get_elf_osabi_name (int t) +{ + RET_S (ELFOSABI_NONE); + RET_S (ELFOSABI_HPUX); + RET_S (ELFOSABI_NETBSD); + RET_S (ELFOSABI_LINUX); + RET_S (ELFOSABI_SOLARIS); + RET_S (ELFOSABI_AIX); + RET_S (ELFOSABI_IRIX); + RET_S (ELFOSABI_FREEBSD); + RET_S (ELFOSABI_TRU64); + RET_S (ELFOSABI_MODESTO); + RET_S (ELFOSABI_OPENBSD); + return NTXT ("ELFOSABI_UNKNOWN"); +} + +static char * +get_elf_etype_name (int t) +{ + RET_S (ET_NONE); + RET_S (ET_REL); + RET_S (ET_EXEC); + RET_S (ET_DYN); + RET_S (ET_CORE); + RET_S (ET_LOPROC); + RET_S (ET_HIPROC); + return NTXT ("ETYPE_UNKNOWN"); +} + +static char * +get_elf_ptype_name (int t) +{ + RET_S (PT_NULL); + RET_S (PT_LOAD); + RET_S (PT_DYNAMIC); + RET_S (PT_INTERP); + RET_S (PT_NOTE); + RET_S (PT_SHLIB); + RET_S (PT_PHDR); + RET_S (PT_TLS); + RET_S (PT_LOOS); + RET_S (PT_GNU_EH_FRAME); + RET_S (PT_GNU_EH_FRAME); + RET_S (PT_HIOS); + RET_S (PT_LOPROC); + RET_S (PT_HIPROC); + return NTXT ("PTYPE_UNKNOWN"); +} + +static char * +get_elf_shtype_name (unsigned int t) +{ + RET_S (SHT_NULL); + RET_S (SHT_PROGBITS); + RET_S (SHT_SYMTAB); + RET_S (SHT_STRTAB); + RET_S (SHT_RELA); + RET_S (SHT_HASH); + RET_S (SHT_DYNAMIC); + RET_S (SHT_NOTE); + RET_S (SHT_NOBITS); + RET_S (SHT_REL); + RET_S (SHT_SHLIB); + RET_S (SHT_DYNSYM); + RET_S (SHT_INIT_ARRAY); + RET_S (SHT_FINI_ARRAY); + RET_S (SHT_PREINIT_ARRAY); + RET_S (SHT_GROUP); + RET_S (SHT_SYMTAB_SHNDX); + RET_S (SHT_LOOS); + RET_S (SHT_SUNW_verdef); + RET_S (SHT_SUNW_verneed); + RET_S (SHT_HIOS); + RET_S (SHT_LOPROC); + RET_S (SHT_HIPROC); + RET_S (SHT_LOUSER); + RET_S (SHT_HIUSER); + return NTXT ("SHTYPE_UNKNOWN"); +} + +static char * +get_elf_machine_name (int t) +{ + RET_S (EM_NONE); + RET_S (EM_M32); + RET_S (EM_SPARC); + RET_S (EM_386); + RET_S (EM_68K); + RET_S (EM_88K); + RET_S (EM_860); + RET_S (EM_MIPS); + RET_S (EM_S370); + RET_S (EM_MIPS_RS3_LE); + RET_S (EM_SPARC32PLUS); + RET_S (EM_960); + RET_S (EM_PPC); + RET_S (EM_PPC64); + RET_S (EM_V800); + RET_S (EM_FR20); + RET_S (EM_RH32); + RET_S (EM_RCE); + RET_S (EM_ARM); + RET_S (EM_ALPHA); + RET_S (EM_SH); + RET_S (EM_SPARCV9); + RET_S (EM_TRICORE); + RET_S (EM_ARC); + RET_S (EM_H8_300); + RET_S (EM_H8_300H); + RET_S (EM_H8S); + RET_S (EM_H8_500); + RET_S (EM_IA_64); + RET_S (EM_MIPS_X); + RET_S (EM_COLDFIRE); + RET_S (EM_68HC12); + RET_S (EM_MMA); + RET_S (EM_PCP); + RET_S (EM_NCPU); + RET_S (EM_NDR1); + RET_S (EM_STARCORE); + RET_S (EM_ME16); + RET_S (EM_ST100); + RET_S (EM_TINYJ); + RET_S (EM_X86_64); + RET_S (EM_PDSP); + RET_S (EM_FX66); + RET_S (EM_ST9PLUS); + RET_S (EM_ST7); + RET_S (EM_68HC16); + RET_S (EM_68HC11); + RET_S (EM_68HC08); + RET_S (EM_68HC05); + RET_S (EM_SVX); + RET_S (EM_ST19); + RET_S (EM_VAX); + RET_S (EM_CRIS); + RET_S (EM_JAVELIN); + RET_S (EM_FIREPATH); + RET_S (EM_ZSP); + RET_S (EM_MMIX); + RET_S (EM_HUANY); + RET_S (EM_PRISM); + RET_S (EM_AVR); + RET_S (EM_FR30); + RET_S (EM_D10V); + RET_S (EM_D30V); + RET_S (EM_V850); + RET_S (EM_M32R); + RET_S (EM_MN10300); + RET_S (EM_MN10200); + RET_S (EM_PJ); + RET_S (EM_OPENRISC); + RET_S (EM_XTENSA); + return NTXT ("ELFMACHINE_UNKNOWN"); +} + +static char * +get_elf_version_name (int t) +{ + RET_S (EV_NONE); + RET_S (EV_CURRENT); + return NTXT ("VERSION_UNKNOWN"); +} + +static char * +get_elf_ancillary_tag (int t) +{ + RET_S (ANC_SUNW_NULL); + RET_S (ANC_SUNW_CHECKSUM); + RET_S (ANC_SUNW_MEMBER); + RET_S (ANC_SUNW_NUM); + return NTXT ("ANCILLARY_TAG_UNKNOWN"); +} + +#define ADD_S(x) if ((f & (x)) == (x)) { sb->append(' '); sb->append(#x); f &= ~(x); } + +static void +dump_sh_flags (StringBuilder *sb, long long flags) +{ + long long f = flags; + if (f != 0) + { + sb->append (NTXT (" [")); + ADD_S (SHF_WRITE) + ADD_S (SHF_ALLOC) + ADD_S (SHF_EXECINSTR) + ADD_S (SHF_MERGE) + ADD_S (SHF_STRINGS) + ADD_S (SHF_INFO_LINK) + ADD_S (SHF_LINK_ORDER) + ADD_S (SHF_OS_NONCONFORMING) + ADD_S (SHF_GROUP) + ADD_S (SHF_TLS) + ADD_S (SHF_SUNW_ABSENT) + ADD_S (SHF_EXCLUDE) + if (f != 0 && f != flags) + sb->appendf (NTXT (" 0x%llx"), (long long) f); + sb->append (NTXT (" ]")); + } + sb->append (NTXT ("\n")); +} + +static void +dump_p_flags (StringBuilder *sb, long long flags) +{ + long long f = flags; + if (f != 0) + { + sb->append (NTXT (" [")); + ADD_S (PF_X) + ADD_S (PF_W) + ADD_S (PF_R) + ADD_S (PF_MASKPROC) + if (f != 0 && f != flags) + sb->appendf (NTXT (" 0x%llx"), (long long) f); + sb->append (NTXT (" ]")); + } + sb->append (NTXT ("\n")); +} + +char * +Elf::dump () +{ + StringBuilder sb; + sb.sprintf (NTXT ("ELF Header: %s\n"), fname ? fname : GTXT ("(unknown)")); + if (ehdrp == NULL) + { + sb.appendf (GTXT ("\n\n Cannot read Elf header\n")); + return sb.toString (); + } + sb.appendf (NTXT (" %-15s "), NTXT ("e_ident")); + for (int i = 0; i < EI_NIDENT; i++) + sb.appendf (NTXT ("%x"), ehdrp->e_ident[i]); + sb.append (NTXT ("\n")); + char *fmt0 = NTXT (" %-15s %10lld ( %s )\n"); + char *fmt1 = NTXT (" %-15s 0x%08llx ( %lld )\n"); + char *fmt2 = NTXT (" %-15s 0x%08llx"); + sb.appendf (fmt0, NTXT ("EI_CLASS"), (long long) ehdrp->e_ident[EI_CLASS], + get_elf_class_name (ehdrp->e_ident[EI_CLASS])); + sb.appendf (fmt0, NTXT ("EI_DATA"), (long long) ehdrp->e_ident[EI_DATA], + get_elf_data_name (ehdrp->e_ident[EI_DATA])); + sb.appendf (fmt0, NTXT ("EI_OSABI"), (long long) ehdrp->e_ident[EI_OSABI], + get_elf_osabi_name (ehdrp->e_ident[EI_OSABI])); + sb.appendf (fmt0, NTXT ("e_type"), (long long) ehdrp->e_type, + get_elf_etype_name (ehdrp->e_type)); + sb.appendf (fmt0, NTXT ("e_machine"), (long long) ehdrp->e_machine, + get_elf_machine_name (ehdrp->e_machine)); + sb.appendf (fmt0, NTXT ("e_version"), (long long) ehdrp->e_version, + get_elf_version_name (ehdrp->e_version)); + sb.appendf (fmt1, NTXT ("e_entry"), (long long) ehdrp->e_entry, + (long long) ehdrp->e_entry); + sb.appendf (fmt1, NTXT ("e_phoff"), (long long) ehdrp->e_phoff, + (long long) ehdrp->e_phoff); + sb.appendf (fmt1, NTXT ("e_shoff"), (long long) ehdrp->e_shoff, + (long long) ehdrp->e_shoff); + sb.appendf (fmt1, NTXT ("e_flags"), (long long) ehdrp->e_flags, + (long long) ehdrp->e_flags); + sb.appendf (fmt1, NTXT ("e_ehsize"), (long long) ehdrp->e_ehsize, + (long long) ehdrp->e_ehsize); + sb.appendf (fmt1, NTXT ("e_phentsize"), (long long) ehdrp->e_phentsize, + (long long) ehdrp->e_phentsize); + sb.appendf (fmt1, NTXT ("e_phnum"), (long long) ehdrp->e_phnum, + (long long) ehdrp->e_phnum); + sb.appendf (fmt1, NTXT ("e_shentsize"), (long long) ehdrp->e_shentsize, + (long long) ehdrp->e_shentsize); + sb.appendf (fmt1, NTXT ("e_shnum"), (long long) ehdrp->e_shnum, + (long long) ehdrp->e_shnum); + sb.appendf (fmt1, NTXT ("e_shstrndx"), (long long) ehdrp->e_shstrndx, + (long long) ehdrp->e_shstrndx); + + for (unsigned int i = 0; i < ehdrp->e_phnum; i++) + { + sb.appendf (NTXT ("\nProgram Header[%d]:\n"), i); + Elf_Internal_Phdr *phdr = get_phdr (i); + if (phdr == NULL) + { + sb.appendf (NTXT (" ERROR: get_phdr(%d) failed\n"), i); + continue; + } + sb.appendf (fmt0, "p_type", (long long) phdr->p_type, + get_elf_ptype_name (phdr->p_type)); + sb.appendf (fmt2, "p_flags", (long long) phdr->p_flags); + dump_p_flags (&sb, phdr->p_flags); + sb.appendf (fmt1, "p_offset", (long long) phdr->p_offset, + (long long) phdr->p_offset); + sb.appendf (fmt1, "p_vaddr", (long long) phdr->p_vaddr, + (long long) phdr->p_vaddr); + sb.appendf (fmt1, "p_paddr", (long long) phdr->p_paddr, + (long long) phdr->p_paddr); + sb.appendf (fmt1, "p_filesz", (long long) phdr->p_filesz, + (long long) phdr->p_filesz); + sb.appendf (fmt1, "p_memsz", (long long) phdr->p_memsz, + (long long) phdr->p_memsz); + sb.appendf (fmt1, "p_align", (long long) phdr->p_align, + (long long) phdr->p_align); + } + + for (unsigned int i = 1; i < ehdrp->e_shnum; i++) + { + sb.appendf (NTXT ("\nSection Header[%d]:\n"), i); + Elf_Internal_Shdr *shdr = get_shdr (i); + if (shdr == NULL) + { + sb.appendf (NTXT (" ERROR: get_shdr(%d) failed\n"), i); + continue; + } + char *s = get_sec_name (i); + sb.appendf (fmt0, "sh_name", (long long) shdr->sh_name, + s ? s : NTXT ("NULL")); + sb.appendf (fmt0, "sh_type", (long long) shdr->sh_type, + get_elf_shtype_name (shdr->sh_type)); + sb.appendf (fmt2, "sh_flags", (long long) shdr->sh_flags); + dump_sh_flags (&sb, shdr->sh_flags); + sb.appendf (fmt1, "sh_addr", (long long) shdr->sh_addr, + (long long) shdr->sh_addr); + sb.appendf (fmt1, "sh_offset", (long long) shdr->sh_offset, + (long long) shdr->sh_offset); + sb.appendf (fmt1, "sh_size", (long long) shdr->sh_size, + (long long) shdr->sh_size); + sb.appendf (fmt1, "sh_link", (long long) shdr->sh_link, + (long long) shdr->sh_link); + sb.appendf (fmt1, "sh_info", (long long) shdr->sh_info, + (long long) shdr->sh_info); + sb.appendf (fmt1, "sh_addralign", (long long) shdr->sh_addralign, + (long long) shdr->sh_addralign); + sb.appendf (fmt1, "sh_entsize", (long long) shdr->sh_entsize, + (long long) shdr->sh_entsize); + } + + for (unsigned int i = 1; i < ehdrp->e_shnum; i++) + { + Elf_Internal_Shdr *shdr = get_shdr (i); + if (shdr == NULL) + continue; + char *secName = get_sec_name (i); + if (secName == NULL) + continue; + if (strcmp (NTXT (".SUNW_ancillary"), secName) == 0) + { + sb.appendf (NTXT ("\nSection[%d]: %s\n"), i, secName); + Elf_Data *dp = elf_getdata (i); + for (int j = 0, cnt = (int) (shdr->sh_size / shdr->sh_entsize); + j < cnt; j++) + { + Elf64_Ancillary anc; + if (elf_getancillary (dp, j, &anc) == NULL) + break; + sb.appendf (NTXT ("%10d %-20s 0x%08llx %6lld"), j, + get_elf_ancillary_tag ((int) anc.a_tag), + (long long) anc.a_un.a_ptr, (long long) anc.a_un.a_ptr); + if (anc.a_tag == ANC_SUNW_MEMBER) + sb.appendf (NTXT (" %s\n"), STR (elf_strptr (shdr->sh_link, anc.a_un.a_ptr))); + else + sb.append (NTXT ("\n")); + } + } + } + return sb.toString (); +} + +void +Elf::dump_elf_sec () +{ + if (!DUMP_ELF_SEC) + return; + if (ehdrp == NULL) + return; + Dprintf (DUMP_ELF_SEC, "======= DwarfLib::dump_elf_sec\n" + " N |type|flags| sh_addr | sh_offset | sh_size | sh_link |" + " sh_info | sh_addralign | sh_entsize | sh_name | name\n"); + for (unsigned int sec = 1; sec < ehdrp->e_shnum; sec++) + { + Elf_Internal_Shdr *shdr = get_shdr (sec); + if (shdr == NULL) + continue; + char *name = elf_strptr (ehdrp->e_shstrndx, shdr->sh_name); + Dprintf (DUMP_ELF_SEC, "%3d:%3d |%4d |%9lld | %9lld |%8lld |%8lld |" + "%8lld |%14d |%11lld | %6lld %s\n", + sec, (int) shdr->sh_type, (int) shdr->sh_flags, + (long long) shdr->sh_addr, (long long) shdr->sh_offset, + (long long) shdr->sh_size, (long long) shdr->sh_link, + (long long) shdr->sh_info, + (int) shdr->sh_addralign, (long long) shdr->sh_entsize, + (long long) shdr->sh_name, name ? name : NTXT ("NULL")); + } + Dprintf (DUMP_ELF_SEC, NTXT ("\n")); +} diff --git a/gprofng/src/Elf.h b/gprofng/src/Elf.h new file mode 100644 index 0000000..3648b88 --- /dev/null +++ b/gprofng/src/Elf.h @@ -0,0 +1,170 @@ +/* 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. */ + +#ifndef _Elf_h_ +#define _Elf_h_ + +#include <string.h> +#include "ansidecl.h" +#include "bfd.h" +#include "elf/common.h" +#include "elf/internal.h" + +#include "Data_window.h" +#include "Emsg.h" + +class Symbol; +class DbeFile; +template <class ITEM> class Vector; +template <typename Key_t, typename Value_t> class Map; + +#define GELF_R_SYM(info) ((info)>>32) +#define GELF_ST_TYPE(info) ((info) & 0xf) +#define GELF_ST_BIND(info) ((info) >> 4) +#define GELF_R_TYPE(info) ((((uint64_t)(info))<<56)>>56) + +#define SHF_SUNW_ABSENT 0x00200000 /* section data not present */ + +// Ancillary values. +#define ANC_SUNW_NULL 0 +#define ANC_SUNW_CHECKSUM 1 /* elf checksum */ +#define ANC_SUNW_MEMBER 2 /* name of ancillary group object */ +#define ANC_SUNW_NUM 3 + + +typedef struct S_Elf64_Dyn Elf64_Dyn; +typedef struct S_Elf64_Ancillary Elf64_Ancillary; + +typedef struct +{ + void *d_buf; + uint64_t d_flags; + uint64_t d_size; + uint64_t d_off; // offset into section + uint64_t d_align; // alignment in section +} Elf_Data; + +class Elf : public DbeMessages, public Data_window +{ +public: + enum Elf_status + { + ELF_ERR_NONE, + ELF_ERR_CANT_OPEN_FILE, + ELF_ERR_CANT_MMAP, + ELF_ERR_BIG_FILE, + ELF_ERR_BAD_ELF_FORMAT, + ELF_ERR_READ_FILE + }; + + Elf (char *_fname); + ~Elf (); + + static void elf_init (); + static unsigned elf_version (unsigned ver); + static Elf *elf_begin (char *_fname, Elf_status *stp = NULL); + + unsigned int elf_get_sec_num (const char *sec_name); + char *get_sec_name (unsigned int sec); + Elf_Internal_Ehdr *elf_getehdr (); + Elf_Internal_Phdr *get_phdr (unsigned int ndx); + Elf_Internal_Shdr *get_shdr (unsigned int ndx); + Elf64_Dyn *elf_getdyn (Elf_Internal_Phdr *phdr, unsigned int ndx, Elf64_Dyn *pdyn); + Elf_Data *elf_getdata (unsigned int sec); + int64_t elf_checksum (); + uint64_t get_baseAddr(); + char *elf_strptr (unsigned int sec, uint64_t off); + Elf_Internal_Sym *elf_getsym (Elf_Data *edta, unsigned int ndx, Elf_Internal_Sym *dst); + Elf_Internal_Rela *elf_getrel (Elf_Data *edta, unsigned int ndx, Elf_Internal_Rela *dst); + Elf_Internal_Rela *elf_getrela (Elf_Data *edta, unsigned int ndx, Elf_Internal_Rela *dst); + Elf64_Ancillary *elf_getancillary (Elf_Data *edta, unsigned int ndx, Elf64_Ancillary *dst); + Elf *find_ancillary_files (char *lo_name); // read the .gnu_debuglink and .SUNW_ancillary seections + char *get_location (); + char *dump (); + void dump_elf_sec (); + + static inline int64_t + normalize_checksum (int64_t chk) + { + return (chk == 0xffffffff || chk == -1) ? 0 : chk; + }; + + inline bool + is_Intel () + { + return elf_datatype == ELFDATA2LSB; + }; + + inline int + elf_getclass () + { + return elf_class; + }; + + inline int + elf_getdatatype () + { + return elf_datatype; + }; + + Elf_status status; + Vector<Elf*> *ancillary_files; + Elf *gnu_debug_file; + DbeFile *dbeFile; + Map<const char*, Symbol*> *elfSymbols; + unsigned int gnuLink, analyzerInfo, SUNW_ldynsym, stab, stabStr, symtab, dynsym; + unsigned int stabIndex, stabIndexStr, stabExcl, stabExclStr, info, plt; + bool dwarf; + +protected: + Elf *get_related_file (const char *lo_name, const char *nm); + int elf_class; + int elf_datatype; + Elf_Internal_Ehdr *ehdrp; + Elf_Data **data; + bfd *abfd; + static int bfd_status; +}; + + +class ElfReloc +{ +public: + struct Sreloc + { + long long offset; + long long value; + int stt_type; + }; + + static ElfReloc *get_elf_reloc (Elf *elf, char *sec_name, ElfReloc *rlc); + ElfReloc (Elf *_elf); + ~ElfReloc (); + long long get_reloc_addr (long long offset); + void dump (); + void dump_rela_debug_sec (int sec); + +private: + Elf *elf; + Vector<Sreloc *> *reloc; + int cur_reloc_ind; +}; + +#endif diff --git a/gprofng/src/Emsg.cc b/gprofng/src/Emsg.cc new file mode 100644 index 0000000..11bad97 --- /dev/null +++ b/gprofng/src/Emsg.cc @@ -0,0 +1,614 @@ +/* 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 <stdarg.h> + +#include "util.h" +#include "Emsg.h" +#include "StringBuilder.h" + +// The Emsg, experiment message, has as objects I18N'd messages +// in a structure suitable for attaching to and fetching +// from a queue of such messages. It is intended to +// be used for collector errors, collector warnings, parser +// errors, and er_archive errors that are encountered when +// reading an experiment + +// ----------------------- Message -------------------------- + +Emsg::Emsg (Cmsg_warn w, const char *i18n_text) +{ + warn = w; + flavor = 0; + par = NULL; + text = strdup (i18n_text); + next = NULL; +} + +Emsg::Emsg (Cmsg_warn w, StringBuilder& sb) +{ + warn = w; + flavor = 0; + par = NULL; + text = sb.toString (); + next = NULL; +} + +Emsg::Emsg (Cmsg_warn w, int f, const char *param) +{ + char *type; + warn = w; + flavor = f; + if (param != NULL) + par = dbe_strdup (param); + else + par = dbe_strdup (""); + next = NULL; + + // determine type + switch (warn) + { + case CMSG_WARN: + type = GTXT ("*** Collector Warning"); + break; + case CMSG_ERROR: + type = GTXT ("*** Collector Error"); + break; + case CMSG_FATAL: + type = GTXT ("*** Collector Fatal Error"); + break; + case CMSG_COMMENT: + type = GTXT ("Comment"); + break; + case CMSG_PARSER: + type = GTXT ("*** Log Error"); + break; + case CMSG_ARCHIVE: + type = GTXT ("*** Archive Error"); + break; + default: + type = GTXT ("*** Internal Error"); + break; + }; + + // now convert the message to its I18N'd string + switch (flavor) + { + case COL_ERROR_NONE: + text = dbe_sprintf (GTXT ("%s: No error"), type); + break; + case COL_ERROR_ARGS2BIG: + text = dbe_sprintf (GTXT ("%s: Data argument too long"), type); + break; + case COL_ERROR_BADDIR: + text = dbe_sprintf (GTXT ("%s: Bad experiment directory name"), type); + break; + case COL_ERROR_ARGS: + text = dbe_sprintf (GTXT ("%s: Data argument format error `%s'"), type, par); + break; + case COL_ERROR_PROFARGS: + text = dbe_sprintf (GTXT ("%s: [UNUSED] Bad clock-profiling argument"), type); + break; + case COL_ERROR_SYNCARGS: + text = dbe_sprintf (GTXT ("%s: [UNUSED] Bad synchronization tracing argument"), type); + break; + case COL_ERROR_HWCARGS: + text = dbe_sprintf (GTXT ("%s: Bad hardware counter profiling argument"), type); + break; + case COL_ERROR_DIRPERM: + text = dbe_sprintf (GTXT ("%s: Experiment directory is not writeable; check umask and permissions"), type); + break; + case COL_ERROR_NOMSACCT: + text = dbe_sprintf (GTXT ("%s: Turning on microstate accounting failed"), type); + break; + case COL_ERROR_PROFINIT: + text = dbe_sprintf (GTXT ("%s: Initializing clock-profiling failed"), type); + break; + case COL_ERROR_SYNCINIT: + text = dbe_sprintf (GTXT ("%s: Initializing synchronization tracing failed"), type); + break; + case COL_ERROR_HWCINIT: + text = dbe_sprintf (GTXT ("%s: Initializing hardware counter profiling failed -- %s"), type, par); + break; + case COL_ERROR_HWCFAIL: + text = dbe_sprintf (GTXT ("%s: HW counter data collection failed; likely cause is that another process preempted the counters"), type); + break; + case COL_ERROR_EXPOPEN: + text = dbe_sprintf (GTXT ("%s: Experiment initialization failed, %s"), type, par); + break; + case COL_ERROR_SIZELIM: + text = dbe_sprintf (GTXT ("%s: Experiment size limit exceeded, writing %s"), type, par); + break; + case COL_ERROR_SYSINFO: + text = dbe_sprintf (GTXT ("%s: system name can not be determined"), type); + break; + case COL_ERROR_OVWOPEN: + text = dbe_sprintf (GTXT ("%s: Can't open overview %s"), type, par); + break; + case COL_ERROR_OVWWRITE: + text = dbe_sprintf (GTXT ("%s: Can't write overview %s"), type, par); + break; + case COL_ERROR_OVWREAD: + text = dbe_sprintf (GTXT ("%s: Can't read overview data for %s"), type, par); + break; + case COL_ERROR_NOZMEM: + text = dbe_sprintf (GTXT ("%s: Open of /dev/zero failed: %s"), type, par); + break; + case COL_ERROR_NOZMEMMAP: + text = dbe_sprintf (GTXT ("%s: Mmap of /dev/zero failed: %s"), type, par); + break; + case COL_ERROR_NOHNDL: + text = dbe_sprintf (GTXT ("%s: Out of data handles for %s"), type, par); + break; + case COL_ERROR_FILEOPN: + text = dbe_sprintf (GTXT ("%s: Open failed %s"), type, par); + break; + case COL_ERROR_FILETRNC: + text = dbe_sprintf (GTXT ("%s: Truncate failed for file %s"), type, par); + break; + case COL_ERROR_FILEMAP: + text = dbe_sprintf (GTXT ("%s: Mmap failed %s"), type, par); + break; + case COL_ERROR_HEAPINIT: + text = dbe_sprintf (GTXT ("%s: Initializing heap tracing failed"), type); + break; + case COL_ERROR_DISPINIT: + text = dbe_sprintf (GTXT ("%s: Initializing SIGPROF dispatcher failed"), type); + break; + case COL_ERROR_ITMRINIT: + text = dbe_sprintf (GTXT ("%s: Initializing interval timer failed; %s"), type, par); + break; + case COL_ERROR_SMPLINIT: + text = dbe_sprintf (GTXT ("%s: Initializing periodic sampling failed"), type); + break; + case COL_ERROR_MPIINIT: + text = dbe_sprintf (GTXT ("%s: Initializing MPI tracing failed"), type); + break; + case COL_ERROR_JAVAINIT: + text = dbe_sprintf (GTXT ("%s: Initializing Java profiling failed"), type); + break; + case COL_ERROR_LINEINIT: + text = dbe_sprintf (GTXT ("%s: Initializing descendant process lineage failed"), type); + break; + case COL_ERROR_NOSPACE: + text = dbe_sprintf (GTXT ("%s: Out of disk space writing `%s'"), type, par); + break; + case COL_ERROR_ITMRRST: + text = dbe_sprintf (GTXT ("%s: Resetting interval timer failed: %s"), type, par); + break; + case COL_ERROR_MKDIR: + text = dbe_sprintf (GTXT ("%s: Unable to create directory `%s'"), type, par); + break; + case COL_ERROR_JVM2NEW: + text = dbe_sprintf (GTXT ("%s: JVM version with JVMTI requires more recent release of the performance tools; please upgrade"), type); + break; + case COL_ERROR_JVMNOTSUPP: + text = dbe_sprintf (GTXT ("%s: JVM version does not support JVMTI; no java profiling is available"), type); + break; + case COL_ERROR_JVMNOJSTACK: + text = dbe_sprintf (GTXT ("%s: JVM version does not support java callstacks; java mode data will not be recorded"), type); + break; + case COL_ERROR_DYNOPEN: + text = dbe_sprintf (GTXT ("%s: Can't open dyntext file `%s'"), type, par); + break; + case COL_ERROR_DYNWRITE: + text = dbe_sprintf (GTXT ("%s: Can't write dyntext file `%s'"), type, par); + break; + case COL_ERROR_MAPOPEN: + text = dbe_sprintf (GTXT ("%s: Can't open map file `%s'"), type, par); + break; + case COL_ERROR_MAPREAD: + text = dbe_sprintf (GTXT ("%s: Can't read map file `%s'"), type, par); + break; + case COL_ERROR_MAPWRITE: + text = dbe_sprintf (GTXT ("%s: Can't write map file"), type); + break; + case COL_ERROR_RESOLVE: + text = dbe_sprintf (GTXT ("%s: Can't resolve map file `%s'"), type, par); + break; + case COL_ERROR_OMPINIT: + text = dbe_sprintf (GTXT ("%s: Initializing OpenMP tracing failed"), type); + break; + case COL_ERROR_DURATION_INIT: + text = dbe_sprintf (GTXT ("%s: Initializing experiment-duration setting to `%s' failed"), type, par); + break; + case COL_ERROR_RDTINIT: + text = dbe_sprintf (GTXT ("%s: Initializing RDT failed"), type); + break; + case COL_ERROR_GENERAL: + if (strlen (par)) + text = dbe_sprintf (GTXT ("%s: %s"), type, par); + else + text = dbe_sprintf (GTXT ("%s: General error"), type); + break; + case COL_ERROR_EXEC_FAIL: + text = dbe_sprintf (GTXT ("%s: Exec of process failed"), type); + break; + case COL_ERROR_THR_MAX: + text = dbe_sprintf (GTXT ("%s: Thread count exceeds maximum (%s); set SP_COLLECTOR_NUMTHREADS for higher value"), type, par); + break; + case COL_ERROR_IOINIT: + text = dbe_sprintf (GTXT ("%s: Initializing IO tracing failed"), type); + break; + case COL_ERROR_NODATA: + text = dbe_sprintf (GTXT ("%s: No data was recorded in the experiment"), type); + break; + case COL_ERROR_DTRACE_FATAL: + text = dbe_sprintf (GTXT ("%s: Fatal error reported from DTrace -- %s"), type, par); + break; + case COL_ERROR_MAPSEEK: + text = dbe_sprintf (GTXT ("%s: Seek error on map file `%s'"), type, par); + break; + case COL_ERROR_UNEXP_FOUNDER: + text = dbe_sprintf (GTXT ("%s: Unexpected value for founder `%s'"), type, par); + break; + case COL_ERROR_LOG_OPEN: + text = dbe_sprintf (GTXT ("%s: Failure to open log file"), type); + break; + case COL_ERROR_TSD_INIT: + text = dbe_sprintf (GTXT ("%s: TSD could not be initialized"), type); + break; + case COL_ERROR_UTIL_INIT: + text = dbe_sprintf (GTXT ("%s: libcol_util.c initialization failed"), type); + break; + case COL_ERROR_MAPCACHE: + text = dbe_sprintf (GTXT ("%s: Unable to cache mappings; internal error (`%s')"), type, par); + break; + case COL_WARN_NONE: + text = dbe_sprintf (GTXT ("%s: No warning"), type); + break; + case COL_WARN_FSTYPE: + text = dbe_sprintf (GTXT ("%s: Experiment was written to a filesystem of type `%s'; data may be distorted"), type, par); + break; + case COL_WARN_PROFRND: + text = dbe_sprintf (GTXT ("%s: Profiling interval was changed from requested %s (microsecs.) used"), type, par); + break; + case COL_WARN_SIZELIM: + text = dbe_sprintf (GTXT ("%s: Experiment size limit exceeded"), type); + break; + case COL_WARN_SIGPROF: + text = dbe_sprintf (GTXT ("%s: SIGPROF handler was changed (%s) during the run; profile data may be unreliable"), type, par); + break; + case COL_WARN_SMPLADJ: + text = dbe_sprintf (GTXT ("%s: Periodic sampling rate adjusted %s microseconds"), type, par); + break; + case COL_WARN_ITMROVR: + text = dbe_sprintf (GTXT ("%s: Application's attempt to set interval timer period to %s was ignored; its behavior may be changed"), type, par); + break; + case COL_WARN_ITMRREP: + text = dbe_sprintf (GTXT ("%s: Collection interval timer period was changed (%s); profile data may be unreliable"), type, par); + break; + case COL_WARN_SIGEMT: + text = dbe_sprintf (GTXT ("%s: SIGEMT handler was changed during the run; profile data may be unreliable"), type); + break; + case COL_WARN_CPCBLK: + text = dbe_sprintf (GTXT ("%s: libcpc access blocked for hardware counter profiling"), type); + break; + case COL_WARN_VFORK: + text = dbe_sprintf (GTXT ("%s: vfork(2) replaced by %s; execution may be affected"), type, par); + break; + case COL_WARN_EXECENV: + text = dbe_sprintf (GTXT ("%s: exec environment augmented with %s missing collection variables"), type, par); + break; + case COL_WARN_SAMPSIGUSED: + text = dbe_sprintf (GTXT ("%s: target installed handler for sample signal %s; samples may be lost"), type, par); + break; + case COL_WARN_PAUSESIGUSED: + text = dbe_sprintf (GTXT ("%s: target installed handler for pause/resume signal %s; data may be lost or unexpected"), + type, par); + break; + case COL_WARN_CPCNOTRESERVED: + text = dbe_sprintf (GTXT ("%s: unable to reserve HW counters; data may be distorted by other users of the counters"), type); + break; + case COL_WARN_LIBTHREAD_T1: /* par contains the aslwpid... do we want to report it? */ + text = dbe_sprintf (GTXT ("%s: application ran with a libthread version that may distort data; see collect(1) man page"), type); + break; + case COL_WARN_SIGMASK: + text = dbe_sprintf (GTXT ("%s: Blocking %s ignored while in use for collection"), type, par); + break; + case COL_WARN_NOFOLLOW: + text = dbe_sprintf (GTXT ("%s: Following disabled for uncollectable target (%s)"), type, par); + break; + case COL_WARN_RISKYFOLLOW: + text = dbe_sprintf (GTXT ("%s: Following unqualified target may be unreliable (%s)"), type, par); + break; + case COL_WARN_IDCHNG: + text = dbe_sprintf (GTXT ("%s: Imminent process ID change (%s) may result in an inconsistent experiment"), type, par); + break; + case COL_WARN_OLDJAVA: + text = dbe_sprintf (GTXT ("%s: Java profiling requires JVM version 1.4.2_02 or later"), type); + break; + case COL_WARN_ITMRPOVR: + text = dbe_sprintf (GTXT ("%s: Collector reset application's profile timer %s; application behavior may be changed"), type, par); + break; + case COL_WARN_NO_JAVA_HEAP: + text = dbe_sprintf (GTXT ("%s: Java heap profiling is not supported by JVMTI; disabled "), type); + break; + case COL_WARN_RDT_PAUSE_NOMEM: + text = dbe_sprintf (GTXT ("%s: Data race detection paused at %s because of running out of internal memory"), type, par); + break; + case COL_WARN_RDT_RESUME: + text = dbe_sprintf (GTXT ("%s: Data race detection resumed"), type); + break; + case COL_WARN_RDT_THROVER: + text = dbe_sprintf (GTXT ("%s: Too many concurrent/created threads; accesses with thread IDs above limit are not checked"), type); + break; + case COL_WARN_THR_PAUSE_RESUME: + text = dbe_sprintf (GTXT ("%s: The collector_thread_pause/collector_thread_resume APIs are deprecated, and will be removed in a future release"), type); + break; + case COL_WARN_NOPROF_DATA: + text = dbe_sprintf (GTXT ("%s: No profile data recorded in experiment"), type); + break; + case COL_WARN_LONG_FSTAT: + text = dbe_sprintf (GTXT ("%s: Long fstat call -- %s"), type, par); + break; + case COL_WARN_LONG_READ: + text = dbe_sprintf (GTXT ("%s: Long read call -- %s"), type, par); + break; + case COL_WARN_LINUX_X86_APICID: + text = dbe_sprintf (GTXT ("%s: Linux libc sched_getcpu() not found; using x86 %s IDs rather than CPU IDs"), type, par); + break; + + case COL_COMMENT_NONE: + text = dbe_sprintf (GTXT ("%s"), par); + break; + case COL_COMMENT_CWD: + text = dbe_sprintf (GTXT ("Initial execution directory `%s'"), par); + break; + case COL_COMMENT_ARGV: + text = dbe_sprintf (GTXT ("Argument list `%s'"), par); + break; + case COL_COMMENT_MAYASSNAP: + text = dbe_sprintf (GTXT ("Mayas snap file `%s'"), par); + break; + + case COL_COMMENT_LINEFORK: + text = dbe_sprintf (GTXT ("Target fork: %s"), par); + break; + case COL_COMMENT_LINEEXEC: + text = dbe_sprintf (GTXT ("Target exec: %s"), par); + break; + case COL_COMMENT_LINECOMBO: + text = dbe_sprintf (GTXT ("Target fork/exec: %s"), par); + break; + case COL_COMMENT_FOXSNAP: + text = dbe_sprintf (GTXT ("Fox snap file `%s'"), par); + break; + case COL_COMMENT_ROCKSNAP: + text = dbe_sprintf (GTXT ("Rock simulator snap file `%s'"), par); + break; + case COL_COMMENT_BITINSTRDATA: + text = dbe_sprintf (GTXT ("Bit instrument data file `%s'"), par); + break; + case COL_COMMENT_BITSNAP: + text = dbe_sprintf (GTXT ("Bit snap file `%s'"), par); + break; + case COL_COMMENT_SIMDSPSNAP: + text = dbe_sprintf (GTXT ("Simulator dataspace profiling snap file `%s'"), par); + break; + case COL_COMMENT_HWCADJ: + text = dbe_sprintf (GTXT ("%s: HWC overflow interval adjusted: %s"), type, par); + break; + case COL_WARN_APP_NOT_READY: + text = dbe_sprintf (GTXT ("*** Collect: %s"), par); + break; + case COL_WARN_RDT_DL_TERMINATE: + text = dbe_sprintf (GTXT ("%s: Actual deadlock detected; process terminated"), type); + break; + case COL_WARN_RDT_DL_TERMINATE_CORE: + text = dbe_sprintf (GTXT ("%s: Actual deadlock detected; process terminated and core dumped"), type); + break; + case COL_WARN_RDT_DL_CONTINUE: + text = dbe_sprintf (GTXT ("%s: Actual deadlock detected; process allowed to continue"), type); + break; + default: + text = dbe_sprintf (GTXT ("%s: Number %d (\"%s\")"), type, flavor, par); + break; + }; +} + +Emsg::~Emsg () +{ + free (par); + free (text); +} + +// ----------------------- Message Queue -------------------- +Emsgqueue::Emsgqueue (char *_qname) +{ + first = NULL; + last = NULL; + qname = strdup (_qname); +} + +Emsgqueue::~Emsgqueue () +{ + free (qname); + clear (); +} + +Emsg * +Emsgqueue::find_msg (Cmsg_warn w, char *msg) +{ + for (Emsg *m = first; m; m = m->next) + if (m->get_warn () == w && strcmp (m->get_msg (), msg) == 0) + return m; + return NULL; +} + +Emsg * +Emsgqueue::append (Cmsg_warn w, char *msg) +{ + Emsg *m = find_msg (w, msg); + if (m) + return m; + m = new Emsg (w, msg); + append (m); + return m; +} + +// Append a single message to a queue +void +Emsgqueue::append (Emsg* m) +{ + m->next = NULL; + if (last == NULL) + { + first = m; + last = m; + } + else + { + last->next = m; + last = m; + } +} + +// Append a queue of messages to a queue +void +Emsgqueue::appendqueue (Emsgqueue* mq) +{ + Emsg *m = mq->first; + if (m == NULL) + return; + if (last == NULL) + first = m; + else + last->next = m; + // now find the new last + while (m->next != NULL) + m = m->next; + last = m; +} + +Emsg * +Emsgqueue::fetch (void) +{ + return first; +} + +// Empty the queue, deleting all messages +void +Emsgqueue::clear (void) +{ + Emsg *pp; + Emsg *p = first; + while (p != NULL) + { + pp = p; + p = p->next; + delete pp; + } + first = NULL; + last = NULL; +} + +// Mark the queue empty, without deleting the messages -- +// used when the messages have been requeued somewhere else +void +Emsgqueue::mark_clear (void) +{ + first = NULL; + last = NULL; +} + +DbeMessages::DbeMessages () +{ + msgs = NULL; +} + +DbeMessages::~DbeMessages () +{ + if (msgs) + { + msgs->destroy (); + delete msgs; + } +} + +Emsg * +DbeMessages::get_error () +{ + for (int i = msgs ? msgs->size () - 1 : -1; i >= 0; i--) + { + Emsg *msg = msgs->get (i); + if (msg->get_warn () == CMSG_ERROR) + return msg; + } + return NULL; +} + +void +DbeMessages::remove_msg (Emsg *msg) +{ + for (int i = 0, sz = msgs ? msgs->size () : 0; i < sz; i++) + if (msg == msgs->get (i)) + { + msgs->remove (i); + delete msg; + return; + } +} + +Emsg * +DbeMessages::append_msg (Cmsg_warn w, const char *fmt, ...) +{ + char buffer[256]; + size_t buf_size; + Emsg *msg; + va_list vp; + + va_start (vp, fmt); + buf_size = vsnprintf (buffer, sizeof (buffer), fmt, vp) + 1; + va_end (vp); + if (buf_size < sizeof (buffer)) + msg = new Emsg (w, buffer); + else + { + va_start (vp, fmt); + char *buf = (char *) malloc (buf_size); + vsnprintf (buf, buf_size, fmt, vp); + va_end (vp); + msg = new Emsg (w, buf); + free (buf); + } + + if (msgs == NULL) + msgs = new Vector<Emsg*>(); + msgs->append (msg); + Dprintf (DEBUG_ERR_MSG, NTXT ("Warning: %s\n"), msg->get_msg ()); + return msg; +} + +void +DbeMessages::append_msgs (Vector<Emsg*> *lst) +{ + if (lst && (lst->size () != 0)) + { + if (msgs == NULL) + msgs = new Vector<Emsg*>(); + for (int i = 0, sz = lst->size (); i < sz; i++) + { + Emsg *m = lst->fetch (i); + msgs->append (new Emsg (m->get_warn (), m->get_msg ())); + } + } +} diff --git a/gprofng/src/Emsg.h b/gprofng/src/Emsg.h new file mode 100644 index 0000000..f1d47c5 --- /dev/null +++ b/gprofng/src/Emsg.h @@ -0,0 +1,112 @@ +/* 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. */ + +#ifndef _EMSG_H +#define _EMSG_H + +#include "Emsgnum.h" +#include "vec.h" + +// +// The Emsg, experiment message, has as objects I18N'd messages +// in a structure suitable for attaching to and fetching +// from a queue of such messages. It is intended to +// be used for collector errors, collector warnings, parser +// errors, and er_archive errors that are encountered when +// reading an experiment + +class Emsg; +class Emsgqueue; +class StringBuilder; + +typedef enum +{ + CMSG_WARN = 0, + CMSG_ERROR, + CMSG_FATAL, + CMSG_COMMENT, + CMSG_PARSER, + CMSG_ARCHIVE +} Cmsg_warn; + +class Emsg +{ +public: + friend class Emsgqueue; + + Emsg (Cmsg_warn w, const char *i18n_text); + Emsg (Cmsg_warn w, StringBuilder& sb); + Emsg (Cmsg_warn w, int f, const char *param); + ~Emsg (); + + char * + get_msg () + { + return text; + }; + + Cmsg_warn + get_warn () + { + return warn; + }; + + Emsg *next; // next message in a queue + +protected: + Cmsg_warn warn; // error/warning/... + int flavor; // the message flavor + char *par; // the input parameter string + char *text; // The I18N text of the message +}; + +class Emsgqueue +{ +public: + Emsgqueue (char *); + ~Emsgqueue (); + + void append (Emsg*); + Emsg *append (Cmsg_warn w, char *msg); + Emsg *find_msg (Cmsg_warn w, char *msg); + void appendqueue (Emsgqueue*); + Emsg *fetch (void); + void clear (void); // empty the queue + void mark_clear (void); // mark the queue empty, without touching messages + +protected: + Emsg *first; + Emsg *last; + char *qname; +}; + +class DbeMessages +{ +public: + DbeMessages (); + ~DbeMessages (); + Vector<Emsg*> *msgs; + void remove_msg (Emsg *msg); + Emsg *get_error (); + Emsg *append_msg (Cmsg_warn w, const char *fmt, ...); + void append_msgs (Vector<Emsg*> *lst); +}; + +#endif /* _EMSG_H */ diff --git a/gprofng/src/Emsgnum.h b/gprofng/src/Emsgnum.h new file mode 100644 index 0000000..cef8332 --- /dev/null +++ b/gprofng/src/Emsgnum.h @@ -0,0 +1,135 @@ +/* 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. */ + +#ifndef _EMSGNUM_H +#define _EMSGNUM_H + +// Define numerical codes for all messages and warnings + +#define COL_ERROR_NONE 0 /* OK */ +#define COL_ERROR_ARGS2BIG 1 /* data descriptor too long */ +#define COL_ERROR_BADDIR 2 /* experiment directory error */ +#define COL_ERROR_ARGS 3 /* data descriptor format error */ +#define COL_ERROR_PROFARGS 4 /* clock profile parameter error */ +#define COL_ERROR_SYNCARGS 5 /* synctrace parameter error */ +#define COL_ERROR_HWCARGS 6 /* HWC profile parameter error */ +#define COL_ERROR_DIRPERM 7 /* experiment directory not writable */ +#define COL_ERROR_NOMSACCT 8 /* failed to turn on microstate accounting */ +#define COL_ERROR_PROFINIT 9 /* failed to initialize profiling */ +#define COL_ERROR_SYNCINIT 10 /* failed to initialize synchronization tracing */ +#define COL_ERROR_HWCINIT 11 /* failed to initialize HWC profiling */ +#define COL_ERROR_HWCFAIL 12 /* HWC profiling failed during run */ +#define COL_ERROR_EXPOPEN 13 /* Experiment initialization failed */ +#define COL_ERROR_SIZELIM 14 /* Experiment exceeded size limit */ +#define COL_ERROR_SYSINFO 15 /* uname call failed */ +#define COL_ERROR_OVWOPEN 16 /* Opening the overview file failed */ +#define COL_ERROR_OVWWRITE 17 /* Writing the overview file failed */ +#define COL_ERROR_OVWREAD 18 /* Reading the overview data failed */ +#define COL_ERROR_NOZMEM 19 /* Unable to open /dev/zero */ +#define COL_ERROR_NOZMEMMAP 20 /* Unable to map /dev/zero */ +#define COL_ERROR_NOHNDL 21 /* No more handles available for data */ +#define COL_ERROR_FILEOPN 22 /* Unable to open file */ +#define COL_ERROR_FILETRNC 23 /* Unable to truncate file */ +#define COL_ERROR_FILEMAP 24 /* Unable to mmap file */ +#define COL_ERROR_HEAPINIT 25 /* Unable to install heap tracing */ +#define COL_ERROR_DISPINIT 26 /* Failed to install dispatcher */ +#define COL_ERROR_ITMRINIT 27 /* Failed to install interval timer */ +#define COL_ERROR_SMPLINIT 28 /* Failed to initialize periodic sampling */ +#define COL_ERROR_MPIINIT 29 /* Failed to initialize MPI tracing */ +#define COL_ERROR_JAVAINIT 30 /* Failed to initialize Java profiling */ +#define COL_ERROR_LINEINIT 31 /* Failed to initialize lineage tracing */ +#define COL_ERROR_NOSPACE 32 /* Ran out of disk space writing file */ +#define COL_ERROR_ITMRRST 33 /* Failed to reset interval timer */ +#define COL_ERROR_MKDIR 34 /* Failed to create (sub)directory */ +#define COL_ERROR_JVM2NEW 35 /* JVM is too new for us to cope (JVMTI interface) */ +#define COL_ERROR_JVMNOTSUPP 36 /* JVM does not support profiling (no JVMTI interface) */ +#define COL_ERROR_JVMNOJSTACK 37 /* JVM does not support java stack unwind */ +#define COL_ERROR_DYNOPEN 38 /* Unable to open dyntext file */ +#define COL_ERROR_DYNWRITE 39 /* Unable to write dyntext file */ +#define COL_ERROR_MAPOPEN 40 /* Unable to open map file */ +#define COL_ERROR_MAPREAD 41 /* Unable to read map file */ +#define COL_ERROR_MAPWRITE 42 /* Unable to write map file */ +#define COL_ERROR_RESOLVE 43 /* Unable to resolve map file */ +#define COL_ERROR_OMPINIT 44 /* Failure to initialize OpenMP tracing */ +#define COL_ERROR_DURATION_INIT 45 /* Failure to initialize -t (duration) processing */ +#define COL_ERROR_RDTINIT 46 /* Unable to install RDT */ +#define COL_ERROR_GENERAL 47 /* General error */ +#define COL_ERROR_EXEC_FAIL 48 /* Can't exec the process */ +#define COL_ERROR_THR_MAX 49 /* More threads than are supported */ +#define COL_ERROR_IOINIT 50 /* failed to initialize IO tracing */ +#define COL_ERROR_NODATA 51 /* No data recorded in experiment */ +#define COL_ERROR_DTRACE_FATAL 52 /* Fatal error from er_kernel DTrace code */ +#define COL_ERROR_MAPSEEK 53 /* Error on seek of map file */ +#define COL_ERROR_UNEXP_FOUNDER 54 /* Unexpected value for SP_COLLECTOR_FOUNDER */ +#define COL_ERROR_LOG_OPEN 55 /* Failure to open log.xml file */ +#define COL_ERROR_TSD_INIT 56 /* TSD could not be initialized */ +#define COL_ERROR_UTIL_INIT 57 /* libcol_util.c could not be initialized */ +#define COL_ERROR_MAPCACHE 58 /* Unable to cache mappings */ + +#define COL_WARN_NONE 200 /* just a note, not a real warning */ +#define COL_WARN_FSTYPE 201 /* Writing to a potentially-distorting file system */ +#define COL_WARN_PROFRND 202 /* Profile interval rounded */ +#define COL_WARN_SIZELIM 203 /* Size limit specified */ +#define COL_WARN_SIGPROF 204 /* SIGPROF handler replaced */ +#define COL_WARN_SMPLADJ 205 /* Periodic sampling rate adjusted */ +#define COL_WARN_ITMROVR 206 /* Application interval timer resetting prevented */ +#define COL_WARN_ITMRREP 207 /* Collection interval timer found to have been overridden */ +#define COL_WARN_SIGEMT 208 /* SIGEMT handler replaced */ +#define COL_WARN_CPCBLK 209 /* libcpc access blocked */ +#define COL_WARN_VFORK 210 /* vfork(2) switched to fork1(2) */ +#define COL_WARN_EXECENV 211 /* incomplete exec environment */ +#define COL_WARN_SAMPSIGUSED 212 /* target installed handler for sample signal */ +#define COL_WARN_PAUSESIGUSED 213 /* target installed handler for pause signal */ +#define COL_WARN_CPCNOTRESERVED 214 /* unable to reserve HW counters for kernel profiling */ +#define COL_WARN_LIBTHREAD_T1 215 /* collection with classic libthread */ +#define COL_WARN_SIGMASK 216 /* profiling signal masking overridden */ +#define COL_WARN_NOFOLLOW 217 /* descendant following disabled */ +#define COL_WARN_RISKYFOLLOW 218 /* descendant following unqualified */ +#define COL_WARN_IDCHNG 219 /* process ID change requested */ +#define COL_WARN_OLDJAVA 220 /* Java profiling requires JVM version 1.4.2_02 or later */ +#define COL_WARN_ITMRPOVR 221 /* Overriding app-set interval timer */ +#define COL_WARN_NO_JAVA_HEAP 222 /* Java heap tracing not supported (JVM 1.5) */ +#define COL_WARN_RDT_PAUSE_NOMEM 223 /* RDT paused because of running out of memory */ +#define COL_WARN_RDT_RESUME 224 /* RDT resumed */ +#define COL_WARN_RDT_THROVER 225 /* RDT: too many threads */ +#define COL_WARN_THR_PAUSE_RESUME 226 /* use of thread pause/resume API is deprecateds */ +#define COL_WARN_APP_NOT_READY 227 /* Application is not instrumented for RDT */ +#define COL_WARN_RDT_DL_TERMINATE 228 /* RDT: terminate execution on actual deadlock */ +#define COL_WARN_RDT_DL_TERMINATE_CORE 229 /* RDT: dump core and terminate execution on actual deadlock */ +#define COL_WARN_RDT_DL_CONTINUE 230 /* RDT: continue execution on actual deadlock */ +#define COL_WARN_NOPROF_DATA 231 /* No profile data recorded in experiment */ +#define COL_WARN_LONG_FSTAT 232 /* fstat call on /proc/self/map took > 200 ms. */ +#define COL_WARN_LONG_READ 233 /* read call on /proc/self/map took > 200 ms. */ +#define COL_WARN_LINUX_X86_APICID 234 /* using x86 APIC IDs rather than Linux sched_getcpu() */ + +#define COL_COMMENT_NONE 400 /* no comment */ +#define COL_COMMENT_CWD 401 /* initial execution directory */ +#define COL_COMMENT_ARGV 402 /* arguments */ +#define COL_COMMENT_MAYASSNAP 403 /* Mayas snap file name */ +#define COL_COMMENT_LINEFORK 404 /* process fork'd */ +#define COL_COMMENT_LINEEXEC 405 /* process exec'd */ +#define COL_COMMENT_LINECOMBO 406 /* process combo fork/exec */ +#define COL_COMMENT_FOXSNAP 407 /* Fox snap file name */ +#define COL_COMMENT_ROCKSNAP 408 /* Rock simulator snap file name */ +#define COL_COMMENT_BITINSTRDATA 409 /* Bit instrdata file name */ +#define COL_COMMENT_BITSNAP 410 /* Bit snap file name */ +#define COL_COMMENT_SIMDSPSNAP 411 /* Simulator dataspace profiling snap file name */ +#define COL_COMMENT_HWCADJ 412 /* HWC overflow interval adjusted */ +#endif /* _EMSGNUM_H */ diff --git a/gprofng/src/ExpGroup.cc b/gprofng/src/ExpGroup.cc new file mode 100644 index 0000000..0ad269a --- /dev/null +++ b/gprofng/src/ExpGroup.cc @@ -0,0 +1,163 @@ +/* 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 "util.h" +#include "ExpGroup.h" +#include "Experiment.h" +#include "LoadObject.h" +#include "DbeSession.h" + +////////////////////////////////////////////////////////// +// class ExpGroup + +int ExpGroup::phaseCompareIdx = 0; + +ExpGroup::ExpGroup (char *nm) +{ + name = dbe_strdup (nm); + canonical_path (name); + exps = new Vector<Experiment*>; + founder = NULL; + groupId = 0; + phaseCompareIdx++; + loadObjs = NULL; + loadObjsMap = NULL; +} + +ExpGroup::~ExpGroup () +{ + phaseCompareIdx++; + free (name); + delete exps; + delete loadObjs; + delete loadObjsMap; +} + +void +ExpGroup::append (Experiment *exp) +{ + for (int i = 0, sz = exps->size (); i < sz; i++) + { + Experiment *e = exps->fetch (i); + if (exp == e) + return; + } + exps->append (exp); + if (exps->size () == 1) + founder = exp; +} + +void +ExpGroup::drop_experiment (Experiment *exp) +{ + for (int i = 0, sz = exps->size (); i < sz; i++) + { + Experiment *e = exps->fetch (i); + if (exp == e) + { + exps->remove (i); + break; + } + } + if (founder == exp) + founder = NULL; +} + +Vector<Experiment*> * +ExpGroup::get_founders () +{ + Vector<Experiment*> *expList = NULL; + for (int i = 0, sz = exps ? exps->size () : 0; i < sz; i++) + { + Experiment *exp = exps->fetch (i); + if (exp->founder_exp == NULL) + { + if (expList == NULL) + expList = new Vector<Experiment*>; + expList->append (exp); + } + } + return expList; +} + +void +ExpGroup::create_list_of_loadObjects () +{ + if (loadObjs == NULL) + { + loadObjs = new Vector<LoadObject*>(); + loadObjsMap = new DefaultMap<LoadObject*, int>(); + for (int i = 0, sz = exps ? exps->size () : 0; i < sz; i++) + { + Experiment *exp = exps->fetch (i); + for (int i1 = 0, sz1 = VecSize(exp->loadObjs); i1 < sz1; i1++) + { + LoadObject *lo = exp->loadObjs->fetch (i1); + if (!loadObjsMap->get (lo)) + { + loadObjs->append (lo); + loadObjsMap->put (lo, loadObjs->size ()); + } + } + } + } +} + +LoadObject * +ExpGroup::get_comparable_loadObject (LoadObject *lo) +{ + create_list_of_loadObjects (); + if (loadObjsMap->get (lo)) + return lo; + if ((lo->flags & SEG_FLAG_EXE) != 0) + if (dbeSession->expGroups->size () == dbeSession->nexps ()) + for (int i = 0, sz = loadObjs ? loadObjs->size () : 0; i < sz; i++) + { + LoadObject *lobj = loadObjs->fetch (i); + if ((lobj->flags & SEG_FLAG_EXE) != 0) + return lobj; + } + + long first_ind = -1; + char *bname = get_basename (lo->get_pathname ()); + for (long i = 0, sz = loadObjs ? loadObjs->size () : 0; i < sz; i++) + { + LoadObject *lobj = loadObjs->get (i); + if (lobj->comparable_objs == NULL + && strcmp (bname, get_basename (lobj->get_pathname ())) == 0) + { + if (lo->platform == lobj->platform) + { + if ((lo->flags & SEG_FLAG_DYNAMIC) != 0) + { + if (dbe_strcmp (lo->firstExp->uarglist, + lobj->firstExp->uarglist) == 0) + return lobj; + } + else + return lobj; + } + if (first_ind == -1) + first_ind = i; + } + } + return first_ind == -1 ? NULL : loadObjs->get (first_ind); +} diff --git a/gprofng/src/ExpGroup.h b/gprofng/src/ExpGroup.h new file mode 100644 index 0000000..b3c9422 --- /dev/null +++ b/gprofng/src/ExpGroup.h @@ -0,0 +1,50 @@ +/* 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. */ + +#ifndef _EXPGROUP_H +#define _EXPGROUP_H + +#include "vec.h" +#include "Map.h" + +class Experiment; +class LoadObject; + +class ExpGroup +{ +public: + ExpGroup (char *nm); + ~ExpGroup (); + void append (Experiment *exp); + void drop_experiment (Experiment *exp); + Vector<Experiment*> *get_founders (); + void create_list_of_loadObjects (); + LoadObject *get_comparable_loadObject (LoadObject *lo); + + Vector<Experiment*> *exps; + Vector<LoadObject*> *loadObjs; + Map <LoadObject*, int> *loadObjsMap; + Experiment *founder; + char *name; + int groupId; + static int phaseCompareIdx; +}; + +#endif /* _EXPGROUP_H */ diff --git a/gprofng/src/Exp_Layout.cc b/gprofng/src/Exp_Layout.cc new file mode 100644 index 0000000..dfe1432 --- /dev/null +++ b/gprofng/src/Exp_Layout.cc @@ -0,0 +1,422 @@ +/* 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 "CallStack.h" +#include "DbeSession.h" +#include "Exp_Layout.h" +#include "Experiment.h" +#include "Function.h" +#include "Table.h" +#include "dbe_types.h" +#include "util.h" + +/* + * PrUsage is a class which wraps access to the values of prusage + * system structure. It was expanded to 64 bit entities in 2.7 + * (experiment version 6 & 7). + */ +PrUsage::PrUsage () +{ + pr_tstamp = pr_create = pr_term = pr_rtime = (hrtime_t) 0; + pr_utime = pr_stime = pr_ttime = pr_tftime = pr_dftime = (hrtime_t) 0; + pr_kftime = pr_ltime = pr_slptime = pr_wtime = pr_stoptime = (hrtime_t) 0; + + pr_minf = pr_majf = pr_nswap = pr_inblk = pr_oublk = 0; + pr_msnd = pr_mrcv = pr_sigs = pr_vctx = pr_ictx = pr_sysc = pr_ioch = 0; +} + +/* + * Resource usage. /proc/<pid>/usage /proc/<pid>/lwp/<lwpid>/lwpusage + */ +struct timestruc_32 +{ /* v8 timestruc_t */ + uint32_t tv_sec; /* seconds */ + uint32_t tv_nsec; /* and nanoseconds */ +}; + +typedef struct ana_prusage +{ + id_t pr_lwpid; /* lwp id. 0: process or defunct */ + int pr_count; /* number of contributing lwps */ + timestruc_32 pr_tstamp; /* current time stamp */ + timestruc_32 pr_create; /* process/lwp creation time stamp */ + timestruc_32 pr_term; /* process/lwp termination time stamp */ + timestruc_32 pr_rtime; /* total lwp real (elapsed) time */ + timestruc_32 pr_utime; /* user level cpu time */ + timestruc_32 pr_stime; /* system call cpu time */ + timestruc_32 pr_ttime; /* other system trap cpu time */ + timestruc_32 pr_tftime; /* text page fault sleep time */ + timestruc_32 pr_dftime; /* data page fault sleep time */ + timestruc_32 pr_kftime; /* kernel page fault sleep time */ + timestruc_32 pr_ltime; /* user lock wait sleep time */ + timestruc_32 pr_slptime; /* all other sleep time */ + timestruc_32 pr_wtime; /* wait-cpu (latency) time */ + timestruc_32 pr_stoptime; /* stopped time */ + timestruc_32 filltime[6]; /* filler for future expansion */ + uint32_t pr_minf; /* minor page faults */ + uint32_t pr_majf; /* major page faults */ + uint32_t pr_nswap; /* swaps */ + uint32_t pr_inblk; /* input blocks */ + uint32_t pr_oublk; /* output blocks */ + uint32_t pr_msnd; /* messages sent */ + uint32_t pr_mrcv; /* messages received */ + uint32_t pr_sigs; /* signals received */ + uint32_t pr_vctx; /* voluntary context switches */ + uint32_t pr_ictx; /* involuntary context switches */ + uint32_t pr_sysc; /* system calls */ + uint32_t pr_ioch; /* chars read and written */ + uint32_t filler[10]; /* filler for future expansion */ +} raw_prusage_32; + +uint64_t +PrUsage::bind32Size () +{ + uint64_t bindSize = sizeof (raw_prusage_32); + return bindSize; +} + +#define timestruc2hr(x) ((hrtime_t)(x).tv_sec*NANOSEC + (hrtime_t)(x).tv_nsec) + +PrUsage * +PrUsage::bind32 (void *p, bool need_swap_endian) +{ + if (p == NULL) + return NULL; + raw_prusage_32 pu, *tmp = (raw_prusage_32*) p; + if (need_swap_endian) + { + pu = *tmp; + tmp = &pu; + SWAP_ENDIAN (pu.pr_tstamp.tv_sec); + SWAP_ENDIAN (pu.pr_tstamp.tv_nsec); + SWAP_ENDIAN (pu.pr_create.tv_sec); + SWAP_ENDIAN (pu.pr_create.tv_nsec); + SWAP_ENDIAN (pu.pr_term.tv_sec); + SWAP_ENDIAN (pu.pr_term.tv_nsec); + SWAP_ENDIAN (pu.pr_rtime.tv_sec); + SWAP_ENDIAN (pu.pr_rtime.tv_nsec); + SWAP_ENDIAN (pu.pr_utime.tv_sec); + SWAP_ENDIAN (pu.pr_utime.tv_nsec); + SWAP_ENDIAN (pu.pr_stime.tv_sec); + SWAP_ENDIAN (pu.pr_stime.tv_nsec); + SWAP_ENDIAN (pu.pr_ttime.tv_sec); + SWAP_ENDIAN (pu.pr_ttime.tv_nsec); + SWAP_ENDIAN (pu.pr_tftime.tv_sec); + SWAP_ENDIAN (pu.pr_tftime.tv_nsec); + SWAP_ENDIAN (pu.pr_dftime.tv_sec); + SWAP_ENDIAN (pu.pr_dftime.tv_nsec); + SWAP_ENDIAN (pu.pr_kftime.tv_sec); + SWAP_ENDIAN (pu.pr_kftime.tv_nsec); + SWAP_ENDIAN (pu.pr_ltime.tv_sec); + SWAP_ENDIAN (pu.pr_ltime.tv_nsec); + SWAP_ENDIAN (pu.pr_slptime.tv_sec); + SWAP_ENDIAN (pu.pr_slptime.tv_nsec); + SWAP_ENDIAN (pu.pr_wtime.tv_sec); + SWAP_ENDIAN (pu.pr_wtime.tv_nsec); + SWAP_ENDIAN (pu.pr_stoptime.tv_sec); + SWAP_ENDIAN (pu.pr_stoptime.tv_nsec); + SWAP_ENDIAN (pu.pr_minf); + SWAP_ENDIAN (pu.pr_majf); + SWAP_ENDIAN (pu.pr_nswap); + SWAP_ENDIAN (pu.pr_inblk); + SWAP_ENDIAN (pu.pr_oublk); + SWAP_ENDIAN (pu.pr_msnd); + SWAP_ENDIAN (pu.pr_mrcv); + SWAP_ENDIAN (pu.pr_sigs); + SWAP_ENDIAN (pu.pr_vctx); + SWAP_ENDIAN (pu.pr_ictx); + SWAP_ENDIAN (pu.pr_sysc); + SWAP_ENDIAN (pu.pr_ioch); + } + pr_tstamp = timestruc2hr (tmp->pr_tstamp); + pr_create = timestruc2hr (tmp->pr_create); + pr_term = timestruc2hr (tmp->pr_term); + pr_rtime = timestruc2hr (tmp->pr_rtime); + pr_utime = timestruc2hr (tmp->pr_utime); + pr_stime = timestruc2hr (tmp->pr_stime); + pr_ttime = timestruc2hr (tmp->pr_ttime); + pr_tftime = timestruc2hr (tmp->pr_tftime); + pr_dftime = timestruc2hr (tmp->pr_dftime); + pr_kftime = timestruc2hr (tmp->pr_kftime); + pr_ltime = timestruc2hr (tmp->pr_ltime); + pr_slptime = timestruc2hr (tmp->pr_slptime); + pr_wtime = timestruc2hr (tmp->pr_wtime); + pr_stoptime = timestruc2hr (tmp->pr_stoptime); + pr_minf = tmp->pr_minf; + pr_majf = tmp->pr_majf; + pr_nswap = tmp->pr_nswap; + pr_inblk = tmp->pr_inblk; + pr_oublk = tmp->pr_oublk; + pr_msnd = tmp->pr_msnd; + pr_mrcv = tmp->pr_mrcv; + pr_sigs = tmp->pr_sigs; + pr_vctx = tmp->pr_vctx; + pr_ictx = tmp->pr_ictx; + pr_sysc = tmp->pr_sysc; + pr_ioch = tmp->pr_ioch; + return this; +} + +struct timestruc_64 +{ /* 64-bit timestruc_t */ + uint64_t tv_sec; /* seconds */ + uint64_t tv_nsec; /* and nanoseconds */ +}; + +typedef struct +{ + id_t pr_lwpid; /* lwp id. 0: process or defunct */ + int pr_count; /* number of contributing lwps */ + timestruc_64 pr_tstamp; /* current time stamp */ + timestruc_64 pr_create; /* process/lwp creation time stamp */ + timestruc_64 pr_term; /* process/lwp termination time stamp */ + timestruc_64 pr_rtime; /* total lwp real (elapsed) time */ + timestruc_64 pr_utime; /* user level cpu time */ + timestruc_64 pr_stime; /* system call cpu time */ + timestruc_64 pr_ttime; /* other system trap cpu time */ + timestruc_64 pr_tftime; /* text page fault sleep time */ + timestruc_64 pr_dftime; /* data page fault sleep time */ + timestruc_64 pr_kftime; /* kernel page fault sleep time */ + timestruc_64 pr_ltime; /* user lock wait sleep time */ + timestruc_64 pr_slptime; /* all other sleep time */ + timestruc_64 pr_wtime; /* wait-cpu (latency) time */ + timestruc_64 pr_stoptime; /* stopped time */ + timestruc_64 filltime[6]; /* filler for future expansion */ + uint64_t pr_minf; /* minor page faults */ + uint64_t pr_majf; /* major page faults */ + uint64_t pr_nswap; /* swaps */ + uint64_t pr_inblk; /* input blocks */ + uint64_t pr_oublk; /* output blocks */ + uint64_t pr_msnd; /* messages sent */ + uint64_t pr_mrcv; /* messages received */ + uint64_t pr_sigs; /* signals received */ + uint64_t pr_vctx; /* voluntary context switches */ + uint64_t pr_ictx; /* involuntary context switches */ + uint64_t pr_sysc; /* system calls */ + uint64_t pr_ioch; /* chars read and written */ + uint64_t filler[10]; /* filler for future expansion */ +} raw_prusage_64; + +uint64_t +PrUsage::bind64Size () +{ + uint64_t bindSize = sizeof (raw_prusage_64); + return bindSize; +} + +PrUsage * +PrUsage::bind64 (void *p, bool need_swap_endian) +{ + if (p == NULL) + { + return NULL; + } + raw_prusage_64 pu, *tmp = (raw_prusage_64*) p; + if (need_swap_endian) + { + pu = *tmp; + tmp = &pu; + SWAP_ENDIAN (pu.pr_tstamp.tv_sec); + SWAP_ENDIAN (pu.pr_tstamp.tv_nsec); + SWAP_ENDIAN (pu.pr_create.tv_sec); + SWAP_ENDIAN (pu.pr_create.tv_nsec); + SWAP_ENDIAN (pu.pr_term.tv_sec); + SWAP_ENDIAN (pu.pr_term.tv_nsec); + SWAP_ENDIAN (pu.pr_rtime.tv_sec); + SWAP_ENDIAN (pu.pr_rtime.tv_nsec); + SWAP_ENDIAN (pu.pr_utime.tv_sec); + SWAP_ENDIAN (pu.pr_utime.tv_nsec); + SWAP_ENDIAN (pu.pr_stime.tv_sec); + SWAP_ENDIAN (pu.pr_stime.tv_nsec); + SWAP_ENDIAN (pu.pr_ttime.tv_sec); + SWAP_ENDIAN (pu.pr_ttime.tv_nsec); + SWAP_ENDIAN (pu.pr_tftime.tv_sec); + SWAP_ENDIAN (pu.pr_tftime.tv_nsec); + SWAP_ENDIAN (pu.pr_dftime.tv_sec); + SWAP_ENDIAN (pu.pr_dftime.tv_nsec); + SWAP_ENDIAN (pu.pr_kftime.tv_sec); + SWAP_ENDIAN (pu.pr_kftime.tv_nsec); + SWAP_ENDIAN (pu.pr_ltime.tv_sec); + SWAP_ENDIAN (pu.pr_ltime.tv_nsec); + SWAP_ENDIAN (pu.pr_slptime.tv_sec); + SWAP_ENDIAN (pu.pr_slptime.tv_nsec); + SWAP_ENDIAN (pu.pr_wtime.tv_sec); + SWAP_ENDIAN (pu.pr_wtime.tv_nsec); + SWAP_ENDIAN (pu.pr_stoptime.tv_sec); + SWAP_ENDIAN (pu.pr_stoptime.tv_nsec); + SWAP_ENDIAN (pu.pr_minf); + SWAP_ENDIAN (pu.pr_majf); + SWAP_ENDIAN (pu.pr_nswap); + SWAP_ENDIAN (pu.pr_inblk); + SWAP_ENDIAN (pu.pr_oublk); + SWAP_ENDIAN (pu.pr_msnd); + SWAP_ENDIAN (pu.pr_mrcv); + SWAP_ENDIAN (pu.pr_sigs); + SWAP_ENDIAN (pu.pr_vctx); + SWAP_ENDIAN (pu.pr_ictx); + SWAP_ENDIAN (pu.pr_sysc); + SWAP_ENDIAN (pu.pr_ioch); + } + + pr_tstamp = timestruc2hr (tmp->pr_tstamp); + pr_create = timestruc2hr (tmp->pr_create); + pr_term = timestruc2hr (tmp->pr_term); + pr_rtime = timestruc2hr (tmp->pr_rtime); + pr_utime = timestruc2hr (tmp->pr_utime); + pr_stime = timestruc2hr (tmp->pr_stime); + pr_ttime = timestruc2hr (tmp->pr_ttime); + pr_tftime = timestruc2hr (tmp->pr_tftime); + pr_dftime = timestruc2hr (tmp->pr_dftime); + pr_kftime = timestruc2hr (tmp->pr_kftime); + pr_ltime = timestruc2hr (tmp->pr_ltime); + pr_slptime = timestruc2hr (tmp->pr_slptime); + pr_wtime = timestruc2hr (tmp->pr_wtime); + pr_stoptime = timestruc2hr (tmp->pr_stoptime); + pr_minf = tmp->pr_minf; + pr_majf = tmp->pr_majf; + pr_nswap = tmp->pr_nswap; + pr_inblk = tmp->pr_inblk; + pr_oublk = tmp->pr_oublk; + pr_msnd = tmp->pr_msnd; + pr_mrcv = tmp->pr_mrcv; + pr_sigs = tmp->pr_sigs; + pr_vctx = tmp->pr_vctx; + pr_ictx = tmp->pr_ictx; + pr_sysc = tmp->pr_sysc; + pr_ioch = tmp->pr_ioch; + return this; +} + +Vector<long long> * +PrUsage::getMstateValues () +{ + const PrUsage *prusage = this; + Vector<long long> *states = new Vector<long long>; + states->store (0, prusage->pr_utime); + states->store (1, prusage->pr_stime); + states->store (2, prusage->pr_ttime); + states->store (3, prusage->pr_tftime); + states->store (4, prusage->pr_dftime); + states->store (5, prusage->pr_kftime); + states->store (6, prusage->pr_ltime); + states->store (7, prusage->pr_slptime); + states->store (8, prusage->pr_wtime); + states->store (9, prusage->pr_stoptime); + assert (LMS_NUM_SOLARIS_MSTATES == states->size ()); + return states; +} + +void* CommonPacket::jvm_overhead = NULL; + +CommonPacket::CommonPacket () +{ + for (int i = 0; i < NTAGS; i++) + tags[i] = 0; + tstamp = 0; + jthread_TBR = NULL; + frinfo = 0; + leafpc = 0; + nat_stack = NULL; + user_stack = NULL; +} + +int +CommonPacket::cmp (const void *a, const void *b) +{ + if ((*(CommonPacket **) a)->tstamp > (*(CommonPacket **) b)->tstamp) + return 1; + else if ((*(CommonPacket **) a)->tstamp < (*(CommonPacket **) b)->tstamp) + return -1; + else + return 0; +} + +void * +CommonPacket::getStack (VMode view_mode) +{ + if (view_mode == VMODE_MACHINE) + return nat_stack; + else if (view_mode == VMODE_USER) + { + if (jthread_TBR == JTHREAD_NONE || (jthread_TBR && jthread_TBR->is_system ())) + return jvm_overhead; + } + else if (view_mode == VMODE_EXPERT) + { + Histable *hist = CallStack::getStackPC (user_stack, 0); + if (hist->get_type () == Histable::INSTR) + { + DbeInstr *instr = (DbeInstr*) hist; + if (instr->func == dbeSession->get_JUnknown_Function ()) + return nat_stack; + } + else if (hist->get_type () == Histable::LINE) + { + DbeLine *line = (DbeLine *) hist; + if (line->func == dbeSession->get_JUnknown_Function ()) + return nat_stack; + } + } + return user_stack; +} + +Histable * +CommonPacket::getStackPC (int n, VMode view_mode) +{ + return CallStack::getStackPC (getStack (view_mode), n); +} + +Vector<Histable*> * +CommonPacket::getStackPCs (VMode view_mode) +{ + return CallStack::getStackPCs (getStack (view_mode)); +} + +void * +getStack (VMode view_mode, DataView *dview, long idx) +{ + void *stack = NULL; + if (view_mode == VMODE_MACHINE) + stack = dview->getObjValue (PROP_MSTACK, idx); + else if (view_mode == VMODE_USER) + stack = dview->getObjValue (PROP_USTACK, idx); + else if (view_mode == VMODE_EXPERT) + stack = dview->getObjValue (PROP_XSTACK, idx); + return stack; +} + +int +stackSize (VMode view_mode, DataView *dview, long idx) +{ + return CallStack::stackSize (getStack (view_mode, dview, idx)); +} + +Histable * +getStackPC (int n, VMode view_mode, DataView *dview, long idx) +{ + return CallStack::getStackPC (getStack (view_mode, dview, idx), n); +} + +Vector<Histable*> * +getStackPCs (VMode view_mode, DataView *dview, long idx) +{ + return CallStack::getStackPCs (getStack (view_mode, dview, idx)); +} diff --git a/gprofng/src/Exp_Layout.h b/gprofng/src/Exp_Layout.h new file mode 100644 index 0000000..c5fbe4c --- /dev/null +++ b/gprofng/src/Exp_Layout.h @@ -0,0 +1,158 @@ +/* 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. */ + +#ifndef _EXP_LAYOUT_H +#define _EXP_LAYOUT_H + +#include <sys/time.h> +#include <sys/types.h> + +#include "dbe_types.h" +#include "gp-experiment.h" +#include "data_pckts.h" +#include "ABS.h" +#include "Data_window.h" +#include "Histable.h" +#include "vec.h" + +class PrUsage +{ +public: + PrUsage (); + PrUsage *bind32 (void *p, bool need_swap_endian); + PrUsage *bind64 (void *p, bool need_swap_endian); + static uint64_t bind32Size (); + static uint64_t bind64Size (); + Vector<long long> * getMstateValues (); + + hrtime_t pr_tstamp; + hrtime_t pr_create; + hrtime_t pr_term; + hrtime_t pr_rtime; + + // the following correspond to PROP_MSTATE LMS_* offsets; see newMstateVec() + hrtime_t pr_utime; + hrtime_t pr_stime; + hrtime_t pr_ttime; + hrtime_t pr_tftime; + hrtime_t pr_dftime; + hrtime_t pr_kftime; + hrtime_t pr_ltime; + hrtime_t pr_slptime; + hrtime_t pr_wtime; + hrtime_t pr_stoptime; + + uint64_t pr_minf; + uint64_t pr_majf; + uint64_t pr_nswap; + uint64_t pr_inblk; + uint64_t pr_oublk; + uint64_t pr_msnd; + uint64_t pr_mrcv; + uint64_t pr_sigs; + uint64_t pr_vctx; + uint64_t pr_ictx; + uint64_t pr_sysc; + uint64_t pr_ioch; +}; + +class DataView; +extern void *getStack (VMode, DataView*, long); +extern int stackSize (VMode, DataView*, long); +extern Histable *getStackPC (int, VMode, DataView*, long); +extern Vector<Histable*> *getStackPCs (VMode, DataView*, long); + +class CommonPacket // use only for RacePacket, please +{ +public: + CommonPacket (); + void *getStack (VMode); + Histable *getStackPC (int, VMode); + Vector<Histable*>*getStackPCs (VMode); + static int cmp (const void *a, const void *b); + + enum Tag_type { LWP, THR, CPU }; + static const int NTAGS = 3; + uint32_t tags[NTAGS]; // lwp_id, thr_id, cpu_id + hrtime_t tstamp; + struct JThread *jthread_TBR; // pointer to JThread or NULL + uint64_t frinfo; // frame info + Vaddr leafpc; // raw leaf PC if availabe + void *nat_stack; // native stack + void *user_stack; // user stack (Java, OMP, etc.) + static void *jvm_overhead; +}; + +class FramePacket +{ +public: + int + stackSize (bool java = false) + { + return java ? jstack->size () / 2 : stack->size (); + } + + Vaddr + getFromStack (int n) + { + return stack->fetch (n); + } + + Vaddr + getMthdFromStack (int n) + { + return jstack->fetch (2 * n + 1); + } + + int + getBciFromStack (int n) + { + return (int) jstack->fetch (2 * n); + } + + bool + isLeafMark (int n) + { + return stack->fetch (n) == (Vaddr) SP_LEAF_CHECK_MARKER; + } + + bool + isTruncatedStack (bool java = false) + { + return java ? jtruncated : truncated == (Vaddr) SP_TRUNC_STACK_MARKER; + } + + bool + isFailedUnwindStack () + { + return truncated == (Vaddr) SP_FAILED_UNWIND_MARKER; + } + uint32_t omp_state; // OpenMP thread state + uint32_t mpi_state; // MPI state + uint64_t omp_cprid; // OpenMP parallel region id (omptrace) + Vector<Vaddr> *stack; + Vaddr truncated; + Vector<Vaddr> *jstack; + bool jtruncated; + Vector<Vaddr> *ompstack; + Vaddr omptruncated; +}; + +#endif /* _EXP_LAYOUT_H */ diff --git a/gprofng/src/Experiment.cc b/gprofng/src/Experiment.cc new file mode 100644 index 0000000..a23c10c --- /dev/null +++ b/gprofng/src/Experiment.cc @@ -0,0 +1,6961 @@ +/* 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 <errno.h> +#include <utime.h> +#include <alloca.h> +#include <dirent.h> +#include <ctype.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/param.h> +#include <set> + +#include "util.h" +#include "CacheMap.h" +#include "DbeFile.h" +#include "DbeCacheMap.h" +#include "DefaultHandler.h" +#include "DefaultMap2D.h" +#include "Emsg.h" +#include "Elf.h" +#include "SAXParser.h" +#include "SAXParserFactory.h" +#include "StringBuilder.h" +#include "DbeSession.h" +#include "DbeThread.h" +#include "Application.h" +#include "CallStack.h" +#include "Experiment.h" +#include "Exp_Layout.h" +#include "DataStream.h" +#include "Expression.h" +#include "Function.h" +#include "HeapMap.h" +#include "LoadObject.h" +#include "Module.h" +#include "Ovw_data.h" +#include "PRBTree.h" +#include "Sample.h" +#include "SegMem.h" +#include "StringMap.h" +#include "UserLabel.h" +#include "Table.h" +#include "dbe_types.h" +#include "FileData.h" +#include "cc_libcollector.h" +#include "ExpGroup.h" + +int nPush; +int nPop; +int pushCnt; +int popCnt; +int pushCnt3; +int popCnt3; + +struct Experiment::UIDnode +{ + uint64_t uid; + uint64_t val; + UIDnode *next; +}; + +struct Experiment::RawFramePacket +{ + uint64_t uid; + UIDnode *uidn; + UIDnode *uidj; + UIDnode *omp_uid; + uint32_t omp_state; +}; + +static hrtime_t +parseTStamp (const char *s) +{ + hrtime_t ts = (hrtime_t) 0; + ts = (hrtime_t) atoi (s) * NANOSEC; + s = strchr (s, '.'); + if (s != NULL) + ts += (hrtime_t) atoi (s + 1); + return ts; +} + +class Experiment::ExperimentFile +{ +public: + + enum + { + EF_NOT_OPENED, + EF_OPENED, + EF_CLOSED, + EF_FAILURE + }; + + ExperimentFile (Experiment *_exp, const char *_fname); + ~ExperimentFile (); + + bool open (bool new_open = false); + + char * + get_name () + { + return fname; + } + + inline int + get_status () + { + return ef_status; + } + + char *fgets (); + void close (); + + FILE *fh; + +private: + Experiment *exp; + char *fname; + off64_t offset; + int bufsz, ef_status; + char *buffer; +}; + +class Experiment::ExperimentHandler : public DefaultHandler +{ +public: + + ExperimentHandler (Experiment *_exp); + ~ExperimentHandler (); + + void + startDocument () { } + void endDocument (); + void startElement (char *uri, char *localName, char *qName, Attributes *attrs); + void endElement (char *uri, char *localName, char *qName); + void characters (char *ch, int start, int length); + + void + ignorableWhitespace (char*, int, int) { } + void + error (SAXParseException *e); + +private: + + enum Element + { + EL_NONE, + EL_EXPERIMENT, + EL_COLLECTOR, + EL_SETTING, + EL_PROCESS, + EL_SYSTEM, + EL_EVENT, + EL_PROFILE, + EL_DATAPTR, + EL_PROFDATA, + EL_PROFPCKT, + EL_FIELD, + EL_CPU, + EL_STATE, + EL_FREQUENCY, + EL_POWERM, + EL_DTRACEFATAL + }; + + static int toInt (Attributes *attrs, const char *atr); + static char*toStr (Attributes *attrs, const char *atr); + void pushElem (Element); + void popElem (); + + Experiment *exp; + Element curElem; + Vector<Element> *stack; + Module *dynfuncModule; + DataDescriptor *dDscr; + PacketDescriptor *pDscr; + PropDescr *propDscr; + char *text; + Cmsg_warn mkind; + int mnum; + int mec; +}; + + +// HTableSize is the size of smemHTable and instHTable +// omazur: both HTableSize and the hash function haven't been tuned; +static const int HTableSize = 8192; + +//-------------------------------------------------- Experiment file handler + +Experiment::ExperimentFile::ExperimentFile (Experiment *_exp, const char *_fname) +{ + exp = _exp; + fh = NULL; + bufsz = 0; + buffer = NULL; + ef_status = EF_NOT_OPENED; + offset = 0; + fname = dbe_sprintf (NTXT ("%s/%s"), exp->expt_name, _fname); +} + +Experiment::ExperimentFile::~ExperimentFile () +{ + close (); + free (buffer); + free (fname); +} + +bool +Experiment::ExperimentFile::open (bool new_open) +{ + if (fh == NULL) + { + fh = fopen64 (fname, NTXT ("r")); + if (fh == NULL) + { + ef_status = EF_FAILURE; + return false; + } + ef_status = EF_OPENED; + if (new_open) + offset = 0; + if (offset != 0) + fseeko64 (fh, offset, SEEK_SET); + } + return true; +} + +char * +Experiment::ExperimentFile::fgets () +{ + if (bufsz == 0) + { + bufsz = 1024; + buffer = (char *) malloc (bufsz); + if (buffer == NULL) + return NULL; + buffer[bufsz - 1] = (char) 1; // sentinel + } + char *res = ::fgets (buffer, bufsz, fh); + if (res == NULL) + return NULL; + while (buffer[bufsz - 1] == (char) 0) + { + int newsz = bufsz + 1024; + char *newbuf = (char *) malloc (newsz); + if (newbuf == NULL) + return NULL; + memcpy (newbuf, buffer, bufsz); + free (buffer); + buffer = newbuf; + buffer[newsz - 1] = (char) 1; // sentinel + // we don't care about fgets result here + ::fgets (buffer + bufsz - 1, newsz - bufsz + 1, fh); + bufsz = newsz; + } + return buffer; +} + +void +Experiment::ExperimentFile::close () +{ + if (fh) + { + offset = ftello64 (fh); + fclose (fh); + ef_status = EF_CLOSED; + fh = NULL; + } +} + + +//-------------------------------------------------- Experiment XML parser +int +Experiment::ExperimentHandler::toInt (Attributes *attrs, const char *atr) +{ + const char *str = attrs->getValue (atr); + return str ? atoi (str) : 0; +} + +char * +Experiment::ExperimentHandler::toStr (Attributes *attrs, const char *atr) +{ + const char *str = attrs->getValue (atr); + return dbe_strdup (str ? str : NTXT ("")); +} + +Experiment::ExperimentHandler::ExperimentHandler (Experiment *_exp) +{ + exp = _exp; + stack = new Vector<Element>; + pushElem (EL_NONE); + dynfuncModule = NULL; + dDscr = NULL; + pDscr = NULL; + propDscr = NULL; + text = NULL; + mkind = (Cmsg_warn) - 1; // CMSG_NONE + mnum = -1; + mec = -1; +} + +Experiment::ExperimentHandler::~ExperimentHandler () +{ + delete stack; + free (text); +} + +void +Experiment::ExperimentHandler::endDocument () +{ + { // SP_TAG_STATE should be used to describe states, but it isn't + // let's do it here: + DataDescriptor *dd = exp->getDataDescriptor (DATA_HEAP); + if (dd != NULL) + { + PropDescr *prop = dd->getProp (PROP_HTYPE); + if (prop != NULL) + { + char * stateNames [HEAPTYPE_LAST] = HEAPTYPE_STATE_STRINGS; + char * stateUNames[HEAPTYPE_LAST] = HEAPTYPE_STATE_USTRINGS; + for (int ii = 0; ii < HEAPTYPE_LAST; ii++) + prop->addState (ii, stateNames[ii], stateUNames[ii]); + } + } + dd = exp->getDataDescriptor (DATA_IOTRACE); + if (dd != NULL) + { + PropDescr *prop = dd->getProp (PROP_IOTYPE); + if (prop != NULL) + { + char * stateNames [IOTRACETYPE_LAST] = IOTRACETYPE_STATE_STRINGS; + char * stateUNames[IOTRACETYPE_LAST] = IOTRACETYPE_STATE_USTRINGS; + for (int ii = 0; ii < IOTRACETYPE_LAST; ii++) + prop->addState (ii, stateNames[ii], stateUNames[ii]); + } + } + } +} + +void +Experiment::ExperimentHandler::pushElem (Element elem) +{ + curElem = elem; + stack->append (curElem); +} + +void +Experiment::ExperimentHandler::popElem () +{ + stack->remove (stack->size () - 1); + curElem = stack->fetch (stack->size () - 1); +} + +void +Experiment::ExperimentHandler::startElement (char*, char*, char *qName, Attributes *attrs) +{ + DEBUG_CODE if (DEBUG_SAXPARSER) dump_startElement (qName, attrs); + if (strcmp (qName, SP_TAG_EXPERIMENT) == 0) + { + pushElem (EL_EXPERIMENT); + const char *str = attrs->getValue (NTXT ("version")); + if (str != NULL) + { + int major = atoi (str); + str = strchr (str, '.'); + int minor = str ? atoi (str + 1) : 0; + exp->exp_maj_version = major; + exp->exp_min_version = minor; + if (major != SUNPERF_VERNUM || minor != SUNPERF_VERNUM_MINOR) + { + // not the current version, see if we support some earlier versions + if (major < 12) + { + StringBuilder sb; + sb.sprintf (GTXT ("*** Error: experiment %s version %d.%d is not supported;\nuse the version of the tools that recorded the experiment to read it"), + exp->get_expt_name (), major, minor); + // exp->errorq->append( new Emsg(CMSG_FATAL, sb) ); + exp->status = FAILURE; + exp->obsolete = 1; + throw new SAXException (sb.toString ()); + } + } + } + } + else if (strcmp (qName, SP_TAG_COLLECTOR) == 0) + pushElem (EL_COLLECTOR); + else if (strcmp (qName, SP_TAG_SETTING) == 0) + { + int found = 0; + pushElem (EL_SETTING); + const char *str = attrs->getValue (SP_JCMD_LIMIT); + if (str != NULL) + { + found = 1; + exp->coll_params.limit = atoi (str); + } + str = attrs->getValue (SP_JCMD_BLKSZ); + if (str != NULL) + { + found = 1; + exp->blksz = strtol (str, NULL, 0); + } + str = attrs->getValue (SP_JCMD_STACKBASE); + if (str) + { + found = 1; + exp->stack_base = strtoull (str, NULL, 0); + } + str = attrs->getValue (SP_JCMD_HWC_DEFAULT); + if (str != NULL) + { + found = 1; + exp->hwc_default = true; + } + str = attrs->getValue (SP_JCMD_NOIDLE); + if (str != NULL) + { + found = 1; + exp->commentq->append (new Emsg (CMSG_COMMENT, + GTXT ("*** Note: experiment does not have events from idle CPUs"))); + } + str = attrs->getValue (SP_JCMD_FAKETIME); + if (str != NULL) + { + found = 1; + exp->timelineavail = false; + exp->commentq->append (new Emsg (CMSG_COMMENT, + GTXT ("*** Note: experiment does not have timestamps; timeline unavailable"))); + } + str = attrs->getValue (SP_JCMD_DELAYSTART); + if (str != NULL) + { + found = 1; + exp->coll_params.start_delay = strdup (str); + } + str = attrs->getValue (SP_JCMD_TERMINATE); + if (str != NULL) + { + found = 1; + exp->coll_params.terminate = strdup (str); + } + str = attrs->getValue (SP_JCMD_PAUSE_SIG); + if (str != NULL) + { + found = 1; + exp->coll_params.pause_sig = strdup (str); + } + str = attrs->getValue (SP_JCMD_SAMPLE_PERIOD); + if (str != NULL) + { + found = 1; + exp->coll_params.sample_periodic = 1; + exp->coll_params.sample_timer = atoi (str); + } + str = attrs->getValue (SP_JCMD_SAMPLE_SIG); + if (str != NULL) + { + found = 1; + exp->coll_params.sample_sig = str; + } + str = attrs->getValue (SP_JCMD_SRCHPATH); + if (str != NULL) + { + found = 1; + StringBuilder sb; + sb.sprintf (GTXT ("Search path: %s"), str); + exp->runlogq->append (new Emsg (CMSG_COMMENT, sb)); + dbeSession->add_classpath ((char*) str); + } + str = attrs->getValue (SP_JCMD_LINETRACE); + if (str != NULL) + { + found = 1; + exp->coll_params.linetrace = strdup (str); + } + + str = attrs->getValue (SP_JCMD_COLLENV); + if (str != NULL) + { + found = 1; + StringBuilder sb; + sb.sprintf (GTXT (" Data collection environment variable: %s"), str); + exp->runlogq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (found == 0) + { + int nattr = attrs->getLength (); + if (nattr != 0) + { + fprintf (stderr, "XXX Unexpected setting found; %d attributes:\n", + nattr); + for (int k = 0; k < nattr; k++) + { + const char *qn = attrs->getQName (k); + const char *vl = attrs->getValue (k); + fprintf (stderr, "XXX %s = %s\n", qn, vl); + } + } + } + // END OF CODE FOR "setting" + } + else if (strcmp (qName, SP_TAG_SYSTEM) == 0) + { + pushElem (EL_SYSTEM); + const char *str = attrs->getValue (NTXT ("hostname")); + if (str != NULL) + exp->hostname = strdup (str); + str = attrs->getValue (NTXT ("os")); + if (str != NULL) + { + exp->os_version = strdup (str); + /* For Linux experiments expect sparse thread ID's */ + if (strncmp (str, NTXT ("SunOS"), 5) != 0) + exp->sparse_threads = true; + } + str = attrs->getValue (NTXT ("arch")); + if (str != NULL) + { + if (strcmp (str, "i86pc") == 0 || strcmp (str, "i686") == 0 + || strcmp (str, "x86_64") == 0) + exp->platform = Intel; + else if (strcmp (str, "aarch64") == 0) + exp->platform = Aarch64; + else + exp->platform = Sparc; + exp->need_swap_endian = (DbeSession::platform == Sparc) ? + (exp->platform != Sparc) : (exp->platform == Sparc); + exp->architecture = strdup (str); + } + str = attrs->getValue (NTXT ("pagesz")); + if (str != NULL) + exp->page_size = atoi (str); + str = attrs->getValue (NTXT ("npages")); + if (str != NULL) + exp->npages = atoi (str); + } + else if (strcmp (qName, SP_TAG_POWERM) == 0) + pushElem (EL_POWERM); + else if (strcmp (qName, SP_TAG_FREQUENCY) == 0) + { + pushElem (EL_FREQUENCY); + const char *str = attrs->getValue (NTXT ("clk")); + if (str != NULL) + exp->set_clock (atoi (str)); + // check for frequency_scaling or turbo_mode recorded from libcollector under dbx + str = attrs->getValue (NTXT ("frequency_scaling")); + const char *str2 = attrs->getValue (NTXT ("turbo_mode")); + if (str != NULL || str2 != NULL) + exp->varclock = 1; + } + else if (strcmp (qName, SP_TAG_CPU) == 0) + { + pushElem (EL_CPU); + exp->ncpus++; + const char *str = attrs->getValue (NTXT ("clk")); + if (str != NULL) + { + int clk = atoi (str); + if (exp->maxclock == 0) + { + exp->minclock = clk; + exp->maxclock = clk; + } + else + { + if (clk < exp->minclock) + exp->minclock = clk; + if (clk > exp->maxclock) + exp->maxclock = clk; + } + exp->clock = clk; + } + // check for frequency_scaling or turbo_mode + str = attrs->getValue (NTXT ("frequency_scaling")); + const char *str2 = attrs->getValue (NTXT ("turbo_mode")); + if (str != NULL || str2 != NULL) + exp->varclock = 1; + } + else if (strcmp (qName, SP_TAG_PROCESS) == 0) + { + pushElem (EL_PROCESS); + const char *str = attrs->getValue (NTXT ("wsize")); + if (str != NULL) + { + int wsz = atoi (str); + if (wsz == 32) + exp->wsize = W32; + else if (wsz == 64) + exp->wsize = W64; + } + str = attrs->getValue (NTXT ("pid")); + if (str != NULL) + exp->pid = atoi (str); + str = attrs->getValue (NTXT ("ppid")); + if (str != NULL) + exp->ppid = atoi (str); + str = attrs->getValue (NTXT ("pgrp")); + if (str != NULL) + exp->pgrp = atoi (str); + str = attrs->getValue (NTXT ("sid")); + if (str != NULL) + exp->sid = atoi (str); + str = attrs->getValue (NTXT ("cwd")); + if (str != NULL) + exp->ucwd = strdup (str); + str = attrs->getValue (NTXT ("pagesz")); + if (str != NULL) + exp->page_size = atoi (str); + } + else if (strcmp (qName, SP_TAG_EVENT) == 0) + { // Start code for event + pushElem (EL_EVENT); + hrtime_t ts = (hrtime_t) 0; + const char *str = attrs->getValue (NTXT ("tstamp")); + if (str != NULL) + ts = parseTStamp (str); + str = attrs->getValue (NTXT ("kind")); + if (str != NULL) + { + if (strcmp (str, SP_JCMD_RUN) == 0) + { + exp->broken = 0; + exp->exp_start_time = ts; + str = attrs->getValue (NTXT ("time")); + if (str != NULL) + exp->start_sec = atol (str); + str = attrs->getValue (NTXT ("pid")); + if (str != NULL) + exp->pid = atoi (str); + str = attrs->getValue (NTXT ("ppid")); + if (str != NULL) + exp->ppid = atoi (str); + str = attrs->getValue (NTXT ("pgrp")); + if (str != NULL) + exp->pgrp = atoi (str); + str = attrs->getValue (NTXT ("sid")); + if (str != NULL) + exp->sid = atoi (str); + exp->status = Experiment::INCOMPLETE; + } + else if (strcmp (str, SP_JCMD_ARCHIVE) == 0) + { + StringBuilder sb; + sb.sprintf (GTXT ("er_archive run: XXXXXXX")); + exp->pprocq->append (new Emsg (CMSG_WARN, sb)); + } + else if (strcmp (str, SP_JCMD_SAMPLE) == 0) + { + exp->update_last_event (exp->exp_start_time + ts); // ts is 0-based + str = attrs->getValue (NTXT ("id")); + int id = str ? atoi (str) : -1; + char *label = dbe_strdup (attrs->getValue (NTXT ("label"))); + exp->process_sample_cmd (NULL, ts, id, label); + } + else if (strcmp (str, SP_JCMD_EXIT) == 0) + { + // don't treat EXIT as an event w.r.t. last_event and non_paused_time + exp->status = Experiment::SUCCESS; + } + else if (strcmp (str, SP_JCMD_CERROR) == 0) + { + mkind = CMSG_ERROR; + str = attrs->getValue (NTXT ("id")); + if (str != NULL) + { + mnum = atoi (str); + } + str = attrs->getValue (NTXT ("ec")); + if (str != NULL) + { + mec = atoi (str); + } + } + else if (strcmp (str, SP_JCMD_CWARN) == 0) + { + mkind = CMSG_WARN; + str = attrs->getValue (NTXT ("id")); + if (str != NULL) + mnum = atoi (str); + } + else if (strcmp (str, SP_JCMD_COMMENT) == 0) + { + mkind = CMSG_COMMENT; + str = attrs->getValue (NTXT ("id")); + if (str != NULL) + mnum = atoi (str); + str = attrs->getValue (NTXT ("text")); + if (str != NULL) + { + StringBuilder sb; + sb.sprintf (GTXT ("*** Note: %s"), str); + exp->commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + } + else if (strcmp (str, SP_JCMD_DESC_START) == 0) + { + char *variant = toStr (attrs, NTXT ("variant")); + char *lineage = toStr (attrs, NTXT ("lineage")); + int follow = toInt (attrs, NTXT ("follow")); + char *msg = toStr (attrs, NTXT ("msg")); + exp->process_desc_start_cmd (NULL, ts, variant, lineage, follow, msg); + } + else if (strcmp (str, SP_JCMD_DESC_STARTED) == 0) + { + char *variant = toStr (attrs, NTXT ("variant")); + char *lineage = toStr (attrs, NTXT ("lineage")); + int follow = toInt (attrs, NTXT ("follow")); + char *msg = toStr (attrs, NTXT ("msg")); + exp->process_desc_started_cmd (NULL, ts, variant, lineage, follow, msg); + } + else if (strcmp (str, SP_JCMD_EXEC_START) == 0) + { + // if successful, acts like experiment termination - no "exit" entry will follow + exp->update_last_event (exp->exp_start_time + ts); // ts is 0-based + char *variant = toStr (attrs, NTXT ("variant")); + char *lineage = toStr (attrs, NTXT ("lineage")); + int follow = toInt (attrs, NTXT ("follow")); + char *msg = toStr (attrs, NTXT ("msg")); + exp->process_desc_start_cmd (NULL, ts, variant, lineage, follow, msg); + exp->exec_started = true; + } + else if (strcmp (str, SP_JCMD_EXEC_ERROR) == 0) + { + exp->update_last_event (exp->exp_start_time + ts); // ts is 0-based + char *variant = toStr (attrs, NTXT ("variant")); + char *lineage = toStr (attrs, NTXT ("lineage")); + int follow = toInt (attrs, NTXT ("follow")); + char *msg = toStr (attrs, NTXT ("msg")); + exp->process_desc_started_cmd (NULL, ts, variant, lineage, follow, msg); + exp->exec_started = false; + } + else if (strcmp (str, SP_JCMD_JTHRSTART) == 0) + { + char *name = dbe_strdup (attrs->getValue (NTXT ("name"))); + char *grpname = dbe_strdup (attrs->getValue (NTXT ("grpname"))); + char *prntname = dbe_strdup (attrs->getValue (NTXT ("prntname"))); + str = attrs->getValue (NTXT ("tid")); + uint64_t tid = str ? strtoull (str, NULL, 0) : 0; + str = attrs->getValue (NTXT ("jthr")); + Vaddr jthr = str ? strtoull (str, NULL, 0) : 0; + str = attrs->getValue (NTXT ("jenv")); + Vaddr jenv = str ? strtoull (str, NULL, 0) : 0; + exp->process_jthr_start_cmd (NULL, name, grpname, prntname, tid, jthr, jenv, ts); + } + else if (strcmp (str, SP_JCMD_JTHREND) == 0) + { + str = attrs->getValue (NTXT ("tid")); + uint64_t tid = str ? strtoull (str, NULL, 0) : 0; + str = attrs->getValue (NTXT ("jthr")); + Vaddr jthr = str ? strtoull (str, NULL, 0) : 0; + str = attrs->getValue (NTXT ("jenv")); + Vaddr jenv = str ? strtoull (str, NULL, 0) : 0; + exp->process_jthr_end_cmd (NULL, tid, jthr, jenv, ts); + } + else if (strcmp (str, SP_JCMD_GCEND) == 0) + { + if (exp->getDataDescriptor (DATA_GCEVENT) == NULL) + exp->newDataDescriptor (DATA_GCEVENT); + exp->process_gc_end_cmd (ts); + } + else if (strcmp (str, SP_JCMD_GCSTART) == 0) + { + if (exp->getDataDescriptor (DATA_GCEVENT) == NULL) + exp->newDataDescriptor (DATA_GCEVENT); + exp->process_gc_start_cmd (ts); + } + else if (strcmp (str, SP_JCMD_PAUSE) == 0) + { + if (exp->resume_ts != MAX_TIME) + { + // data collection was active + hrtime_t delta = ts - exp->resume_ts; + exp->non_paused_time += delta; + exp->resume_ts = MAX_TIME; // collection is paused + } + StringBuilder sb; + str = attrs->getValue (NTXT ("name")); + if (str == NULL) + sb.sprintf (GTXT ("Pause: %ld.%09ld"), (long) (ts / NANOSEC), + (long) (ts % NANOSEC)); + else + sb.sprintf (GTXT ("Pause (%s): %ld.%09ld"), str, + (long) (ts / NANOSEC), (long) (ts % NANOSEC)); + exp->runlogq->append (new Emsg (CMSG_COMMENT, sb)); + } + else if (strcmp (str, SP_JCMD_RESUME) == 0) + { + if (exp->resume_ts == MAX_TIME) + // data collection was paused + exp->resume_ts = ts; // remember start time + StringBuilder sb; + sb.sprintf (GTXT ("Resume: %ld.%09ld"), (long) (ts / NANOSEC), (long) (ts % NANOSEC)); + exp->runlogq->append (new Emsg (CMSG_COMMENT, sb)); + if (exp->exp_start_time == ZERO_TIME) + exp->exp_start_time = ts; + } + else if (strcmp (str, SP_JCMD_THREAD_PAUSE) == 0) + { + str = attrs->getValue (NTXT ("tid")); + uint64_t tid = str ? strtoull (str, NULL, 0) : 0; + StringBuilder sb; + sb.sprintf (GTXT ("Thread %llu pause: %ld.%09ld"), (unsigned long long) tid, + (long) (ts / NANOSEC), (long) (ts % NANOSEC)); + exp->runlogq->append (new Emsg (CMSG_COMMENT, sb)); + } + else if (strcmp (str, SP_JCMD_THREAD_RESUME) == 0) + { + str = attrs->getValue (NTXT ("tid")); + uint64_t tid = str ? strtoull (str, NULL, 0) : 0; + StringBuilder sb; + sb.sprintf (GTXT ("Thread %llu resume: %ld.%09ld"), (unsigned long long) tid, + (long) (ts / NANOSEC), (long) (ts % NANOSEC)); + exp->runlogq->append (new Emsg (CMSG_COMMENT, sb)); + } + else if (strcmp (str, NTXT ("map")) == 0) + { + ts += exp->exp_start_time; + str = attrs->getValue (NTXT ("vaddr")); + Vaddr vaddr = str ? strtoull (str, NULL, 0) : 0; + str = attrs->getValue (NTXT ("size")); + int msize = str ? atoi (str) : 0; + str = attrs->getValue (NTXT ("foffset")); + int64_t offset = str ? strtoll (str, NULL, 0) : 0; + str = attrs->getValue (NTXT ("modes")); + int64_t modes = str ? strtoll (str, NULL, 0) : 0; + str = attrs->getValue (NTXT ("chksum")); + int64_t chksum = 0; + if (str) + chksum = Elf::normalize_checksum (strtoll (str, NULL, 0)); + char *name = (char *) attrs->getValue (NTXT ("name")); + str = attrs->getValue (NTXT ("object")); + if (strcmp (str, NTXT ("segment")) == 0) + { + if (strcmp (name, NTXT ("LinuxKernel")) == 0) + exp->process_Linux_kernel_cmd (ts); + else + exp->process_seg_map_cmd (NULL, ts, vaddr, msize, 0, + offset, modes, chksum, name); + } + else if (strcmp (str, NTXT ("function")) == 0) + { + exp->process_fn_load_cmd (dynfuncModule, name, vaddr, msize, ts); + dynfuncModule = NULL; + } + else if (strcmp (str, NTXT ("dynfunc")) == 0) + { + if (dynfuncModule == NULL) + { + dynfuncModule = dbeSession->createModule (exp->get_dynfunc_lo (DYNFUNC_SEGMENT), name); + dynfuncModule->flags |= MOD_FLAG_UNKNOWN; + dynfuncModule->set_file_name (dbe_strdup (dynfuncModule->getMainSrc ()->get_name ())); + } + (void) exp->create_dynfunc (dynfuncModule, + (char*) attrs->getValue (NTXT ("funcname")), vaddr, msize); + } + else if (strcmp (str, NTXT ("jcm")) == 0) + { + str = attrs->getValue (NTXT ("methodId")); + Vaddr mid = str ? strtoull (str, NULL, 0) : 0; + exp->process_jcm_load_cmd (NULL, mid, vaddr, msize, ts); + } + } + else if (strcmp (str, NTXT ("unmap")) == 0) + { + ts += exp->exp_start_time; + str = attrs->getValue (NTXT ("vaddr")); + Vaddr vaddr = str ? strtoull (str, NULL, 0) : 0; + exp->process_seg_unmap_cmd (NULL, ts, vaddr); + } + } + // end of code for event + } + else if (strcmp (qName, SP_TAG_PROFILE) == 0) + { + pushElem (EL_PROFILE); + const char *str = attrs->getValue (NTXT ("name")); + if (str == NULL) + return; + if (strcmp (str, NTXT ("profile")) == 0) + { + exp->coll_params.profile_mode = 1; + str = attrs->getValue (NTXT ("numstates")); + if (str != NULL) + exp->coll_params.lms_magic_id = atoi (str); + str = attrs->getValue (NTXT ("ptimer")); + if (str != NULL) + exp->coll_params.ptimer_usec = atoi (str); // microseconds + + PropDescr *mstate_prop = NULL; + char * stateNames [/*LMS_NUM_STATES*/] = LMS_STATE_STRINGS; + char * stateUNames[/*LMS_NUM_STATES*/] = LMS_STATE_USTRINGS; + { + dDscr = exp->newDataDescriptor (DATA_CLOCK); + PropDescr *prop = new PropDescr (PROP_MSTATE, NTXT ("MSTATE")); + prop->uname = dbe_strdup (GTXT ("Thread state")); + prop->vtype = TYPE_UINT32; + // (states added below) + dDscr->addProperty (prop); + mstate_prop = prop; + + prop = new PropDescr (PROP_NTICK, NTXT ("NTICK")); + prop->uname = dbe_strdup (GTXT ("Number of Profiling Ticks")); + prop->vtype = TYPE_UINT32; + dDscr->addProperty (prop); + } + + switch (exp->coll_params.lms_magic_id) + { + case LMS_MAGIC_ID_SOLARIS: + exp->register_metric (Metric::CP_TOTAL); + exp->register_metric (Metric::CP_TOTAL_CPU); + exp->register_metric (Metric::CP_LMS_USER); + exp->register_metric (Metric::CP_LMS_SYSTEM); + exp->register_metric (Metric::CP_LMS_TRAP); + exp->register_metric (Metric::CP_LMS_DFAULT); + exp->register_metric (Metric::CP_LMS_TFAULT); + exp->register_metric (Metric::CP_LMS_KFAULT); + exp->register_metric (Metric::CP_LMS_STOPPED); + exp->register_metric (Metric::CP_LMS_WAIT_CPU); + exp->register_metric (Metric::CP_LMS_SLEEP); + exp->register_metric (Metric::CP_LMS_USER_LOCK); + for (int ii = 0; ii < LMS_NUM_SOLARIS_MSTATES; ii++) + mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]); + break; + case LMS_MAGIC_ID_ERKERNEL_KERNEL: + exp->register_metric (Metric::CP_KERNEL_CPU); + { + int ii = LMS_KERNEL_CPU; + mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]); + } + break; + case LMS_MAGIC_ID_ERKERNEL_USER: + exp->register_metric (Metric::CP_TOTAL_CPU); + exp->register_metric (Metric::CP_LMS_USER); + exp->register_metric (Metric::CP_LMS_SYSTEM); + { + int ii = LMS_KERNEL_CPU; + mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]); + ii = LMS_USER; + mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]); + ii = LMS_SYSTEM; + mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]); + } + break; + case LMS_MAGIC_ID_LINUX: + exp->register_metric (Metric::CP_TOTAL_CPU); + { + int ii = LMS_LINUX_CPU; + mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]); + } + break; + default: + // odd + break; + } + } + else if (strcmp (str, NTXT ("heaptrace")) == 0) + { + exp->coll_params.heap_mode = 1; + exp->leaklistavail = true; + exp->heapdataavail = true; + exp->register_metric (Metric::HEAP_ALLOC_BYTES); + exp->register_metric (Metric::HEAP_ALLOC_CNT); + exp->register_metric (Metric::HEAP_LEAK_BYTES); + exp->register_metric (Metric::HEAP_LEAK_CNT); + dDscr = exp->newDataDescriptor (DATA_HEAP); + } + else if (strcmp (str, NTXT ("iotrace")) == 0) + { + exp->coll_params.io_mode = 1; + exp->iodataavail = true; + exp->register_metric (Metric::IO_READ_TIME); + exp->register_metric (Metric::IO_READ_BYTES); + exp->register_metric (Metric::IO_READ_CNT); + exp->register_metric (Metric::IO_WRITE_TIME); + exp->register_metric (Metric::IO_WRITE_BYTES); + exp->register_metric (Metric::IO_WRITE_CNT); + exp->register_metric (Metric::IO_OTHER_TIME); + exp->register_metric (Metric::IO_OTHER_CNT); + exp->register_metric (Metric::IO_ERROR_TIME); + exp->register_metric (Metric::IO_ERROR_CNT); + dDscr = exp->newDataDescriptor (DATA_IOTRACE); + } + else if (strcmp (str, NTXT ("synctrace")) == 0) + { + exp->coll_params.sync_mode = 1; + str = attrs->getValue (NTXT ("threshold")); + if (str != NULL) + exp->coll_params.sync_threshold = atoi (str); + str = attrs->getValue (NTXT ("scope")); + if (str != NULL) + exp->coll_params.sync_scope = atoi (str); + else // Should only happen with old experiments; use the old default + exp->coll_params.sync_scope = SYNCSCOPE_NATIVE | SYNCSCOPE_JAVA; + exp->register_metric (Metric::SYNC_WAIT_TIME); + exp->register_metric (Metric::SYNC_WAIT_COUNT); + dDscr = exp->newDataDescriptor (DATA_SYNCH); + } + else if (strcmp (str, NTXT ("omptrace")) == 0) + { + exp->coll_params.omp_mode = 1; + dDscr = exp->newDataDescriptor (DATA_OMP, DDFLAG_NOSHOW); + } + else if (strcmp (str, NTXT ("hwcounter")) == 0) + { + str = attrs->getValue (NTXT ("cpuver")); + int cpuver = str ? atoi (str) : 0; + char *counter = dbe_strdup (attrs->getValue (NTXT ("hwcname"))); + char *int_name = dbe_strdup (attrs->getValue (NTXT ("int_name"))); // may not be present + str = attrs->getValue (NTXT ("interval")); + int interval = str ? atoi (str) : 0; + str = attrs->getValue (NTXT ("tag")); + int tag = str ? atoi (str) : 0; + str = attrs->getValue (NTXT ("memop")); + int i_tpc = str ? atoi (str) : 0; + char *modstr = dbe_strdup (attrs->getValue (NTXT ("modstr"))); + exp->process_hwcounter_cmd (NULL, cpuver, counter, int_name, interval, tag, i_tpc, modstr); + dDscr = exp->newDataDescriptor (DATA_HWC); + } + else if (strcmp (str, NTXT ("hwsimctr")) == 0) + { + int cpuver = toInt (attrs, NTXT ("cpuver")); + char *hwcname = dbe_strdup (attrs->getValue (NTXT ("hwcname"))); + char *int_name = dbe_strdup (attrs->getValue (NTXT ("int_name"))); + char *metric = dbe_strdup (attrs->getValue (NTXT ("metric"))); + int reg = toInt (attrs, NTXT ("reg_num")); + int interval = toInt (attrs, NTXT ("interval")); + int timecvt = toInt (attrs, NTXT ("timecvt")); + int i_tpc = toInt (attrs, NTXT ("memop")); + int tag = toInt (attrs, NTXT ("tag")); + exp->process_hwsimctr_cmd (NULL, cpuver, hwcname, int_name, metric, reg, + interval, timecvt, i_tpc, tag); + dDscr = exp->newDataDescriptor (DATA_HWC); + } + else if (strcmp (str, NTXT ("dversion")) == 0) + exp->dversion = dbe_strdup (attrs->getValue (NTXT ("version"))); + else if (strcmp (str, NTXT ("jprofile")) == 0) + { + exp->has_java = true; + str = attrs->getValue (NTXT ("jversion")); + if (str != NULL) + exp->jversion = strdup (str); + } + else if (strcmp (str, NTXT ("datarace")) == 0) + { + exp->coll_params.race_mode = 1; + exp->racelistavail = true; + str = attrs->getValue (NTXT ("scheme")); + exp->coll_params.race_stack = str ? atoi (str) : 0; + exp->register_metric (Metric::RACCESS); + dDscr = exp->newDataDescriptor (DATA_RACE); + } + else if (strcmp (str, NTXT ("deadlock")) == 0) + { + exp->coll_params.deadlock_mode = 1; + exp->deadlocklistavail = true; + exp->register_metric (Metric::DEADLOCKS); + dDscr = exp->newDataDescriptor (DATA_DLCK); + } + } + /* XXX -- obsolete tag, but is still written to experiments */ + else if (strcmp (qName, SP_TAG_DATAPTR) == 0) + { + pushElem (EL_DATAPTR); + return; + } + else if (strcmp (qName, SP_TAG_PROFDATA) == 0) + { + pushElem (EL_PROFDATA); + // SS12 HWC experiments are not well structured + const char *fname = attrs->getValue (NTXT ("fname")); + if (fname && strcmp (fname, SP_HWCNTR_FILE) == 0) + dDscr = exp->newDataDescriptor (DATA_HWC); + } + else if (strcmp (qName, SP_TAG_PROFPCKT) == 0) + { + pushElem (EL_PROFPCKT); + const char *str = attrs->getValue (NTXT ("kind")); // see Pckt_type + int kind = str ? atoi (str) : -1; + if (kind < 0) + return; + if (exp->coll_params.omp_mode == 1) + { + if (kind == OMP_PCKT) + dDscr = exp->newDataDescriptor (DATA_OMP, DDFLAG_NOSHOW); + else if (kind == OMP2_PCKT) + dDscr = exp->newDataDescriptor (DATA_OMP2, DDFLAG_NOSHOW); + else if (kind == OMP3_PCKT) + dDscr = exp->newDataDescriptor (DATA_OMP3, DDFLAG_NOSHOW); + else if (kind == OMP4_PCKT) + dDscr = exp->newDataDescriptor (DATA_OMP4, DDFLAG_NOSHOW); + else if (kind == OMP5_PCKT) + dDscr = exp->newDataDescriptor (DATA_OMP5, DDFLAG_NOSHOW); + } + pDscr = exp->newPacketDescriptor (kind, dDscr); + return; + } + else if (strcmp (qName, SP_TAG_FIELD) == 0) + { + pushElem (EL_FIELD); + if (pDscr != NULL) + { + const char *name = attrs->getValue (NTXT ("name")); + if (name == NULL) + return; + int propID = dbeSession->registerPropertyName (name); + propDscr = new PropDescr (propID, name); + FieldDescr *fldDscr = new FieldDescr (propID, name); + + const char *str = attrs->getValue (NTXT ("type")); + if (str) + { + if (strcmp (str, NTXT ("INT32")) == 0) + fldDscr->vtype = TYPE_INT32; + else if (strcmp (str, NTXT ("UINT32")) == 0) + fldDscr->vtype = TYPE_UINT32; + else if (strcmp (str, NTXT ("INT64")) == 0) + fldDscr->vtype = TYPE_INT64; + else if (strcmp (str, NTXT ("UINT64")) == 0) + fldDscr->vtype = TYPE_UINT64; + else if (strcmp (str, NTXT ("STRING")) == 0) + fldDscr->vtype = TYPE_STRING; + else if (strcmp (str, NTXT ("DOUBLE")) == 0) + fldDscr->vtype = TYPE_DOUBLE; + else if (strcmp (str, NTXT ("DATE")) == 0) + { + fldDscr->vtype = TYPE_DATE; + const char *fmt = attrs->getValue (NTXT ("format")); + fldDscr->format = strdup (fmt ? fmt : ""); + } + } + propDscr->vtype = fldDscr->vtype; + + // TYPE_DATE is converted to TYPE_UINT64 in propDscr + if (fldDscr->vtype == TYPE_DATE) + propDscr->vtype = TYPE_UINT64; + + // Fix some types until they are fixed in libcollector + if (propID == PROP_VIRTPC || propID == PROP_PHYSPC) + { + if (fldDscr->vtype == TYPE_INT32) + propDscr->vtype = TYPE_UINT32; + else if (fldDscr->vtype == TYPE_INT64) + propDscr->vtype = TYPE_UINT64; + } + + // The following props get mapped to 32-bit values in readPacket + if (propID == PROP_CPUID || propID == PROP_THRID + || propID == PROP_LWPID) + propDscr->vtype = TYPE_UINT32; // override experiment property + + str = attrs->getValue (NTXT ("uname")); + if (str) + propDscr->uname = strdup (PTXT ((char*) str)); + str = attrs->getValue (NTXT ("noshow")); + if (str && atoi (str) != 0) + propDscr->flags |= PRFLAG_NOSHOW; + + if (dDscr == NULL) + { + StringBuilder sb; + sb.sprintf (GTXT ("*** Error: data parsing failed. Log file is corrupted.")); + exp->warnq->append (new Emsg (CMSG_ERROR, sb)); + throw new SAXException (sb.toString ()); + } + + dDscr->addProperty (propDscr); + str = attrs->getValue (NTXT ("offset")); + if (str) + fldDscr->offset = atoi (str); + pDscr->addField (fldDscr); + } + } + else if (strcmp (qName, SP_TAG_STATE) == 0) + { + pushElem (EL_STATE); + if (propDscr != NULL) + { + const char *str = attrs->getValue (NTXT ("value")); + int value = str ? atoi (str) : -1; + str = attrs->getValue (NTXT ("name")); + const char *ustr = attrs->getValue (NTXT ("uname")); + propDscr->addState (value, str, ustr); + } + } + else if (strcmp (qName, SP_TAG_DTRACEFATAL) == 0) + pushElem (EL_DTRACEFATAL); + else + { + StringBuilder sb; + sb.sprintf (GTXT ("*** Warning: unrecognized element %s"), qName); + exp->warnq->append (new Emsg (CMSG_WARN, sb)); + pushElem (EL_NONE); + } +} + +void +Experiment::ExperimentHandler::characters (char *ch, int start, int length) +{ + switch (curElem) + { + case EL_COLLECTOR: + exp->cversion = dbe_strndup (ch + start, length); + break; + case EL_PROCESS: + exp->process_arglist_cmd (NULL, dbe_strndup (ch + start, length)); + break; + case EL_EVENT: + free (text); + text = dbe_strndup (ch + start, length); + break; + default: + break; + } +} + +void +Experiment::ExperimentHandler::endElement (char*, char*, char*) +{ + if (curElem == EL_EVENT && mkind >= 0 && mnum >= 0) + { + char *str; + if (mec > 0) + str = dbe_sprintf ("%s -- %s", text != NULL ? text : "", strerror (mec)); + else + str = dbe_sprintf ("%s", text != NULL ? text : ""); + Emsg *msg = new Emsg (mkind, mnum, str); + if (mkind == CMSG_WARN) + { + if (mnum != COL_WARN_FSTYPE + || dbeSession->check_ignore_fs_warn () == false) + exp->warnq->append (msg); + else + exp->commentq->append (msg); + } + else if (mkind == CMSG_ERROR || mkind == CMSG_FATAL) + exp->errorq->append (msg); + else if (mkind == CMSG_COMMENT) + exp->commentq->append (msg); + else + delete msg; + mkind = (Cmsg_warn) - 1; + mnum = -1; + mec = -1; + } + else if (curElem == EL_PROFILE) + dDscr = NULL; + else if (curElem == EL_PROFPCKT) + pDscr = NULL; + else if (curElem == EL_FIELD) + propDscr = NULL; + free (text); + text = NULL; + popElem (); +} + +void +Experiment::ExperimentHandler::error (SAXParseException *e) +{ + StringBuilder sb; + sb.sprintf (GTXT ("%s at line %d, column %d"), + e->getMessage (), e->getLineNumber (), e->getColumnNumber ()); + char *msg = sb.toString (); + SAXException *e1 = new SAXException (msg); + free (msg); + throw ( e1); +} + +//-------------------------------------------------- Experiment + +Experiment::Experiment () +{ + groupId = 0; + userExpId = expIdx = -1; + founder_exp = NULL; + baseFounder = NULL; + children_exps = new Vector<Experiment*>; + loadObjs = new Vector<LoadObject*>; + loadObjMap = new StringMap<LoadObject*>(128, 128); + sourcesMap = NULL; + + // Initialize configuration information. + status = FAILURE; + start_sec = 0; + mtime = 0; + hostname = NULL; + username = NULL; + architecture = NULL; + os_version = NULL; + uarglist = NULL; + utargname = NULL; + ucwd = NULL; + cversion = NULL; + dversion = NULL; + jversion = NULL; + exp_maj_version = 0; + exp_min_version = 0; + platform = Unknown; + wsize = Wnone; + page_size = 4096; + npages = 0; + stack_base = 0xf0000000; + broken = 1; + obsolete = 0; + hwc_bogus = 0; + hwc_lost_int = 0; + hwc_scanned = 0; + hwc_default = false; + invalid_packet = 0; + + // clear HWC event stats + dsevents = 0; + dsnoxhwcevents = 0; + + memset (&coll_params, 0, sizeof (coll_params)); + ncpus = 0; + minclock = 0; + maxclock = 0; + clock = 0; + varclock = 0; + exec_started = false; + timelineavail = true; + leaklistavail = false; + heapdataavail = false; + iodataavail = false; + dataspaceavail = false; + ifreqavail = false; + racelistavail = false; + deadlocklistavail = false; + ompavail = false; + tiny_threshold = -1; + pid = 0; + ppid = 0; + pgrp = 0; + sid = 0; + + gc_duration = ZERO_TIME; + exp_start_time = ZERO_TIME; // not known. Wall-clock hrtime (not zero based) + last_event = ZERO_TIME; // not known. Wall-clock hrtime (not zero based) + non_paused_time = 0; // 0 non-paused time (will sum as experiment is processed) + resume_ts = 0; // by default, collection is "resumed" (not paused) from time=0 + need_swap_endian = false; + exp_rel_start_time_set = false; + exp_rel_start_time = ZERO_TIME; + has_java = false; + hex_field_width = 8; + hw_cpuver = CPUVER_UNDEFINED; + machinemodel = NULL; + expt_name = NULL; + arch_name = NULL; + fndr_arch_name = NULL; + logFile = NULL; + + dataDscrs = new Vector<DataDescriptor*>; + for (int i = 0; i < DATA_LAST; ++i) + dataDscrs->append (NULL); + + pcktDscrs = new Vector<PacketDescriptor*>; + blksz = PROFILE_BUFFER_CHUNK; + jthreads = new Vector<JThread*>; + jthreads_idx = new Vector<JThread*>; + gcevents = new Vector<GCEvent*>; + gcevent_last_used = (GCEvent *) NULL; + heapUnmapEvents = new Vector<UnmapChunk*>; + cstack = NULL; + cstackShowHide = NULL; + frmpckts = new Vector<RawFramePacket*>; + typedef DefaultMap2D<uint32_t, hrtime_t, uint64_t> OmpMap0; + mapPRid = new OmpMap0 (OmpMap0::Interval); + typedef DefaultMap2D<uint32_t, hrtime_t, void*> OmpMap; + mapPReg = new OmpMap (OmpMap::Interval); + mapTask = new OmpMap (OmpMap::Interval); + openMPdata = NULL; + archiveMap = NULL; + nnodes = 0; + nchunks = 0; + chunks = 0; + uidHTable = NULL; + uidnodes = new Vector<UIDnode*>; + mrecs = new Vector<MapRecord*>; + samples = new Vector<Sample*>; + sample_last_used = (Sample *) NULL; + first_sample_label = (char*) NULL; + fDataMap = NULL; + vFdMap = NULL; + resolveFrameInfo = true; + discardTiny = false; + init (); +} + +Experiment::~Experiment () +{ + fini (); + free (coll_params.linetrace); + for (int i = 0; i < MAX_HWCOUNT; i++) + { + free (coll_params.hw_aux_name[i]); + free (coll_params.hw_username[i]); + } + free (hostname); + free (username); + free (architecture); + free (os_version); + free (uarglist); + free (utargname); + free (ucwd); + free (cversion); + free (dversion); + free (jversion); + delete logFile; + free (expt_name); + free (arch_name); + free (fndr_arch_name); + delete jthreads_idx; + delete cstack; + delete cstackShowHide; + delete mapPRid; + delete mapPReg; + delete mapTask; + delete openMPdata; + destroy_map (DbeFile *, archiveMap); + delete[] uidHTable; + delete uidnodes; + delete mrecs; + delete children_exps; + delete loadObjs; + delete loadObjMap; + delete sourcesMap; + free (first_sample_label); + free (machinemodel); + + dataDscrs->destroy (); + delete dataDscrs; + pcktDscrs->destroy (); + delete pcktDscrs; + jthreads->destroy (); + delete jthreads; + gcevents->destroy (); + delete gcevents; + heapUnmapEvents->destroy (); + delete heapUnmapEvents; + frmpckts->destroy (); + delete frmpckts; + samples->destroy (); + delete samples; + delete fDataMap; + delete vFdMap; + + for (long i = 0; i < nchunks; i++) + delete[] chunks[i]; + delete[] chunks; +} + +void +Experiment::init_cache () +{ + if (smemHTable) + return; + smemHTable = new SegMem*[HTableSize]; + instHTable = new DbeInstr*[HTableSize]; + for (int i = 0; i < HTableSize; i++) + { + smemHTable[i] = NULL; + instHTable[i] = NULL; + } + uidHTable = new UIDnode*[HTableSize]; + for (int i = 0; i < HTableSize; i++) + uidHTable[i] = NULL; + + cstack = CallStack::getInstance (this); + cstackShowHide = CallStack::getInstance (this); +} + +void +Experiment::init () +{ + userLabels = NULL; + seg_items = new Vector<SegMem*>; + maps = new PRBTree (); + jmaps = NULL; // used by JAVA_CLASSES only + jmidHTable = NULL; + smemHTable = NULL; + instHTable = NULL; + min_thread = (uint64_t) - 1; + max_thread = 0; + thread_cnt = 0; + min_lwp = (uint64_t) - 1; + max_lwp = 0; + lwp_cnt = 0; + min_cpu = (uint64_t) - 1; + max_cpu = 0; + cpu_cnt = 0; + + commentq = new Emsgqueue (NTXT ("commentq")); + runlogq = new Emsgqueue (NTXT ("runlogq")); + errorq = new Emsgqueue (NTXT ("errorq")); + warnq = new Emsgqueue (NTXT ("warnq")); + notesq = new Emsgqueue (NTXT ("notesq")); + pprocq = new Emsgqueue (NTXT ("pprocq")); + ifreqq = NULL; + + metrics = new Vector<BaseMetric*>; + tagObjs = new Vector<Vector<Histable*>*>; + tagObjs->store (PROP_THRID, new Vector<Histable*>); + tagObjs->store (PROP_LWPID, new Vector<Histable*>); + tagObjs->store (PROP_CPUID, new Vector<Histable*>); + tagObjs->store (PROP_EXPID, new Vector<Histable*>); + sparse_threads = false; +} + +void +Experiment::fini () +{ + seg_items->destroy (); + delete seg_items; + delete maps; + delete jmaps; + delete[] smemHTable; + delete[] instHTable; + delete jmidHTable; + delete commentq; + delete runlogq; + delete errorq; + delete warnq; + delete notesq; + delete pprocq; + if (ifreqq != NULL) + { + delete ifreqq; + ifreqq = NULL; + } + + int index; + BaseMetric *mtr; + Vec_loop (BaseMetric*, metrics, index, mtr) + { + dbeSession->drop_metric (mtr); + } + delete metrics; + tagObjs->fetch (PROP_THRID)->destroy (); + tagObjs->fetch (PROP_LWPID)->destroy (); + tagObjs->fetch (PROP_CPUID)->destroy (); + tagObjs->fetch (PROP_EXPID)->destroy (); + tagObjs->destroy (); + delete tagObjs; +} + +// These are the data files which can be read in parallel +// for multiple sub-experiments. +// Postpone calling resolve_frame_info() +void +Experiment::read_experiment_data (bool read_ahead) +{ + + read_frameinfo_file (); + if (read_ahead) + { + resolveFrameInfo = false; + (void) get_profile_events (); + resolveFrameInfo = true; + } +} + +Experiment::Exp_status +Experiment::open_epilogue () +{ + + // set up mapping for tagObj(PROP_EXPID) + (void) mapTagValue (PROP_EXPID, userExpId); + + post_process (); + if (last_event != ZERO_TIME) + { // if last_event is known + StringBuilder sb; + hrtime_t ts = last_event - exp_start_time; + sb.sprintf (GTXT ("Experiment Ended: %ld.%09ld\nData Collection Duration: %ld.%09ld"), + (long) (ts / NANOSEC), (long) (ts % NANOSEC), + (long) (non_paused_time / NANOSEC), + (long) (non_paused_time % NANOSEC)); + runlogq->append (new Emsg (CMSG_COMMENT, sb)); + } + + // Check for incomplete experiment, and inform the user + if (status == INCOMPLETE) + { + if (exec_started == true) + // experiment ended with the exec, not abnormally + status = SUCCESS; + else + { + char * cmnt = GTXT ("*** Note: experiment was not closed"); + commentq->append (new Emsg (CMSG_COMMENT, cmnt)); + // runlogq->append(new Emsg(CMSG_COMMENT, cmnt)); + } + } + // write a descriptive header for the experiment + write_header (); + return status; +} + +Experiment::Exp_status +Experiment::open (char *path) +{ + + // Find experiment directory + if (find_expdir (path) != SUCCESS) + // message will have been queued and status set + return status; + + // Get creation time for experiment + struct stat64 st; + if (dbe_stat (path, &st) == 0) + mtime = st.st_mtime; + + // Read the warnings file + read_warn_file (); + + // Open the log file + read_log_file (); + if (status == SUCCESS && last_event // last event is initialized + && (last_event - exp_start_time) / 1000000 < tiny_threshold) + { + // Process "tiny_threshold" (SP_ANALYZER_DISCARD_TINY_EXPERIMENTS) + // At this point, we've only processed log.xml. + // Note: if an experiment terminated abnormally, last_event will not yet + // represent events from clock profiling and other metrics. + // Other events will often have timestamps after the last log.xml entry. + discardTiny = true; + return status; + } + if (status == FAILURE) + { + if (logFile->get_status () == ExperimentFile::EF_FAILURE) + { + Emsg *m = new Emsg (CMSG_FATAL, GTXT ("*** Error: log file in experiment cannot be read")); + errorq->append (m); + } + else if (fetch_errors () == NULL) + { + if (broken == 1) + { + Emsg *m = new Emsg (CMSG_FATAL, GTXT ("*** Error: log does not show target starting")); + errorq->append (m); + } + else + { + Emsg *m = new Emsg (CMSG_FATAL, GTXT ("*** Error: log file in experiment could not be parsed")); + errorq->append (m); + } + } + return status; + } + init_cache (); + if (varclock != 0) + { + StringBuilder sb; + sb.sprintf ( + GTXT ("*** Warning: system has variable clock frequency, which may cause variable execution times and inaccurate conversions of cycle counts into time.")); + warnq->append (new Emsg (CMSG_WARN, sb)); + } + + // Read the notes file + read_notes_file (); + read_labels_file (); + read_archives (); + + // The log file shows experiment started + read_java_classes_file (); + + read_map_file (); + + // Dyntext file has to be processed after loadobjects file + // as we need to be able to map (vaddr,ts) to dynamic functions. + read_dyntext_file (); + + // Read the overview file and create samples. + // Profiling data hasn't been read yet so we may have + // events after the last recorded sample. + // We'll create a fake sample to cover all those + // events later. + read_overview_file (); + + // Check if instruction frequency data is available + read_ifreq_file (); + + // Check if OMP data is available + read_omp_file (); + + return status; +} + +/* XXX -- update() is a no-op now, but may be needed for auto-update */ +Experiment::Exp_status +Experiment::update () +{ + return status; +} + +void +Experiment::append (LoadObject *lo) +{ + loadObjs->append (lo); + char *obj_name = lo->get_pathname (); + char *bname = get_basename (obj_name); + loadObjMap->put (obj_name, lo); + loadObjMap->put (bname, lo); + if (lo->flags & SEG_FLAG_EXE) + loadObjMap->put (COMP_EXE_NAME, lo); +} + +void +Experiment::read_notes_file () +{ + Emsg *m; + + // Open log file: + char *fname = dbe_sprintf (NTXT ("%s/%s"), expt_name, SP_NOTES_FILE); + FILE *f = fopen (fname, NTXT ("r")); + free (fname); + if (f == NULL) + return; + if (!dbeSession->is_interactive ()) + { + m = new Emsg (CMSG_COMMENT, NTXT ("Notes:")); + notesq->append (m); + } + + while (1) + { + char str[MAXPATHLEN]; + char *e = fgets (str, ((int) sizeof (str)) - 1, f); + if (e == NULL) + { + if (!dbeSession->is_interactive ()) + { + m = new Emsg (CMSG_COMMENT, + "============================================================"); + notesq->append (m); + } + break; + } + size_t i = strlen (str); + if (i > 0 && str[i - 1] == '\n') + // remove trailing nl + str[i - 1] = 0; + m = new Emsg (CMSG_COMMENT, str); + notesq->append (m); + } + (void) fclose (f); +} + +int +Experiment::save_notes (char* text, bool handle_file) +{ + if (handle_file) + { + FILE *fnotes; + char *fname = dbe_sprintf (NTXT ("%s/%s"), expt_name, SP_NOTES_FILE); + fnotes = fopen (fname, NTXT ("w")); + free (fname); + if (fnotes != NULL) + { + (void) fprintf (fnotes, NTXT ("%s"), text); + fclose (fnotes); + } + else + return 1; // Cannot write file + } + notesq->clear (); + Emsg *m = new Emsg (CMSG_COMMENT, text); + notesq->append (m); + + return 0; +} + +int +Experiment::delete_notes (bool handle_file) +{ + if (handle_file) + { + char *fname = dbe_sprintf (NTXT ("%s/%s"), expt_name, SP_NOTES_FILE); + if (unlink (fname) != 0) + { + free (fname); + return 1; // Cannot delete file + } + free (fname); + } + notesq->clear (); + return 0; +} + +int +Experiment::read_warn_file () +{ + int local_status = SUCCESS; + + ExperimentFile *warnFile = new ExperimentFile (this, SP_WARN_FILE); + if (warnFile == NULL) + return FAILURE; + if (!warnFile->open ()) + { + delete warnFile; + return FAILURE; + } + SAXParserFactory *factory = SAXParserFactory::newInstance (); + SAXParser *saxParser = factory->newSAXParser (); + DefaultHandler *dh = new ExperimentHandler (this); + try + { + saxParser->parse ((File*) warnFile->fh, dh); + } + catch (SAXException *e) + { + // Fatal error in the parser + StringBuilder sb; + sb.sprintf (NTXT ("%s: %s"), SP_WARN_FILE, e->getMessage ()); + char *str = sb.toString (); + Emsg *m = new Emsg (CMSG_FATAL, str); + errorq->append (m); + local_status = FAILURE; + delete e; + } + delete warnFile; + delete dh; + delete saxParser; + delete factory; + + return local_status; +} + +int +Experiment::read_log_file () +{ + if (logFile == NULL) + logFile = new ExperimentFile (this, SP_LOG_FILE); + if (!logFile->open ()) + { + status = FAILURE; + return status; + } + + SAXParserFactory *factory = SAXParserFactory::newInstance (); + SAXParser *saxParser = factory->newSAXParser (); + DefaultHandler *dh = new ExperimentHandler (this); + try + { + saxParser->parse ((File*) logFile->fh, dh); + } + catch (SAXException *e) + { + // Fatal error in the parser + StringBuilder sb; + if (obsolete == 1) + sb.sprintf (NTXT ("%s"), e->getMessage ()); + else + sb.sprintf (NTXT ("%s: %s"), SP_LOG_FILE, e->getMessage ()); + char *str = sb.toString (); + Emsg *m = new Emsg (CMSG_FATAL, str); + errorq->append (m); + status = FAILURE; + delete e; + } + logFile->close (); + dbeSession->register_metric (GTXT ("IPC"), GTXT ("Instructions Per Cycle"), + NTXT ("insts/cycles")); + dbeSession->register_metric (GTXT ("CPI"), GTXT ("Cycles Per Instruction"), + NTXT ("cycles/insts")); + dbeSession->register_metric (GTXT ("K_IPC"), + GTXT ("Kernel Instructions Per Cycle"), + NTXT ("K_insts/K_cycles")); + dbeSession->register_metric (GTXT ("K_CPI"), + GTXT ("Kernel Cycles Per Instruction"), + NTXT ("K_cycles/K_insts")); + + delete dh; + delete saxParser; + delete factory; + + return status; +} + +//////////////////////////////////////////////////////////////////////////////// +// class Experiment::ExperimentLabelsHandler +// + +class Experiment::ExperimentLabelsHandler : public DefaultHandler +{ +public: + + ExperimentLabelsHandler (Experiment *_exp) + { + exp = _exp; + } + + ~ExperimentLabelsHandler () { }; + void startDocument () { } + void endDocument () { } + void endElement (char * /*uri*/, char * /*localName*/, char * /*qName*/) { } + void characters (char * /*ch*/, int /*start*/, int /*length*/) { } + void ignorableWhitespace (char*, int, int) { } + void error (SAXParseException * /*e*/) { } + + void startElement (char *uri, char *localName, char *qName, Attributes *attrs); + +private: + + inline const char * + s2s (const char *s) + { + return s ? s : "NULL"; + } + + Experiment *exp; + char *hostname; + hrtime_t time, tstamp; +}; + +void +Experiment::ExperimentLabelsHandler::startElement (char*, char*, char *qName, + Attributes *attrs) +{ + DEBUG_CODE if (DEBUG_SAXPARSER) dump_startElement (qName, attrs); + if (qName == NULL || strcmp (qName, NTXT ("id")) != 0) + return; + char *name = NULL, *all_times = NULL, *comment = NULL, *hostName = NULL; + long startSec = 0; + // long tm_zone = 0; + hrtime_t startHrtime = (hrtime_t) 0; + long long lbl_ts = 0; + int relative = 0; + timeval start_tv; + start_tv.tv_usec = start_tv.tv_sec = 0; + for (int i = 0, sz = attrs ? attrs->getLength () : 0; i < sz; i++) + { + const char *qn = attrs->getQName (i); + const char *vl = attrs->getValue (i); + if (strcmp (qn, NTXT ("name")) == 0) + name = dbe_xml2str (vl); + else if (strcmp (qn, NTXT ("cmd")) == 0) + all_times = dbe_xml2str (vl); + else if (strcmp (qn, NTXT ("comment")) == 0) + comment = dbe_xml2str (vl); + else if (strcmp (qn, NTXT ("relative")) == 0) + relative = atoi (vl); + else if (strcmp (qn, NTXT ("hostname")) == 0) + hostName = dbe_xml2str (vl); + else if (strcmp (qn, NTXT ("time")) == 0) + startSec = atol (vl); + else if (strcmp (qn, NTXT ("tstamp")) == 0) + startHrtime = parseTStamp (vl); + else if (strcmp (qn, NTXT ("lbl_ts")) == 0) + { + if (*vl == '-') + lbl_ts = -parseTStamp (vl + 1); + else + lbl_ts = parseTStamp (vl); + } + } + if (name == NULL || hostName == NULL || (all_times == NULL && comment == NULL)) + { + free (name); + free (hostName); + free (all_times); + free (comment); + return; + } + UserLabel *lbl = new UserLabel (name); + lbl->comment = comment; + lbl->hostname = hostName; + lbl->start_sec = startSec; + lbl->start_hrtime = startHrtime; + exp->userLabels->append (lbl); + if (all_times) + { + lbl->all_times = all_times; + lbl->start_tv = start_tv; + lbl->relative = relative; + if (relative == UserLabel::REL_TIME) + lbl->atime = lbl_ts; + else + { // relative == UserLabel::CUR_TIME + long long delta = 0; + if (exp->hostname && strcmp (lbl->hostname, exp->hostname) == 0) + delta = lbl_ts + (lbl->start_hrtime - exp->exp_start_time); + else + for (int i = 0; i < exp->userLabels->size (); i++) + { + UserLabel *firstLbl = exp->userLabels->fetch (i); + if (strcmp (lbl->hostname, firstLbl->hostname) == 0) + { + delta = lbl_ts + (lbl->start_hrtime - firstLbl->start_hrtime) + + ((long long) (firstLbl->start_sec - exp->start_sec)) * NANOSEC; + break; + } + } + lbl->atime = delta > 0 ? delta : 0; + } + } +} + +static int +sortUserLabels (const void *a, const void *b) +{ + UserLabel *l1 = *((UserLabel **) a); + UserLabel *l2 = *((UserLabel **) b); + int v = dbe_strcmp (l1->name, l2->name); + if (v != 0) + return v; + if (l1->atime < l2->atime) + return -1; + else if (l1->atime > l2->atime) + return 1; + if (l1->id < l2->id) + return -1; + else if (l1->id > l2->id) + return 1; + return 0; +} + +static char * +append_string (char *s, char *str) +{ + if (s == NULL) + return dbe_strdup (str); + char *new_s = dbe_sprintf (NTXT ("%s %s"), s, str); + free (s); + return new_s; +} + +void +Experiment::read_labels_file () +{ + ExperimentFile *fp = new ExperimentFile (this, SP_LABELS_FILE); + if (!fp->open ()) + { + delete fp; + return; + } + userLabels = new Vector<UserLabel*>(); + SAXParserFactory *factory = SAXParserFactory::newInstance (); + SAXParser *saxParser = factory->newSAXParser (); + DefaultHandler *dh = new ExperimentLabelsHandler (this); + try + { + saxParser->parse ((File*) fp->fh, dh); + } + catch (SAXException *e) + { + // Fatal error in the parser + StringBuilder sb; + sb.sprintf (NTXT ("%s: %s"), SP_LABELS_FILE, e->getMessage ()); + char *str = sb.toString (); + Emsg *m = new Emsg (CMSG_FATAL, str); + errorq->append (m); + delete e; + } + fp->close (); + delete fp; + delete dh; + delete saxParser; + delete factory; + + userLabels->sort (sortUserLabels); + UserLabel::dump ("After sortUserLabels:", userLabels); + UserLabel *ulbl = NULL; + for (int i = 0, sz = userLabels->size (); i < sz; i++) + { + UserLabel *lbl = userLabels->fetch (i); + if (ulbl == NULL) + ulbl = new UserLabel (lbl->name); + else if (dbe_strcmp (lbl->name, ulbl->name) != 0) + { // new Label + ulbl->register_user_label (groupId); + if (ulbl->expr == NULL) + delete ulbl; + ulbl = new UserLabel (lbl->name); + } + if (lbl->all_times) + { + if (strncmp (lbl->all_times, NTXT ("start"), 5) == 0) + { + if (!ulbl->start_f) + { + ulbl->start_f = true; + ulbl->timeStart = lbl->atime; + } + } + else + { // stop + if (!ulbl->start_f) + continue; + ulbl->all_times = append_string (ulbl->all_times, lbl->all_times); + ulbl->stop_f = true; + ulbl->timeStop = lbl->atime; + ulbl->gen_expr (); + } + } + if (lbl->comment != NULL) + ulbl->comment = append_string (ulbl->comment, lbl->comment); + } + if (ulbl) + { + ulbl->register_user_label (groupId); + if (ulbl->expr == NULL) + delete ulbl; + } + Destroy (userLabels); +} + +void +Experiment::read_archives () +{ + if (founder_exp) + return; + char *allocated_str = NULL; + char *nm = get_arch_name (); + DIR *exp_dir = opendir (nm); + if (exp_dir == NULL) + { + if (founder_exp == NULL) + { + // Check if the user uses a subexperiment only + nm = dbe_sprintf (NTXT ("%s/../%s"), expt_name, SP_ARCHIVES_DIR); + exp_dir = opendir (nm); + if (exp_dir == NULL) + { + free (nm); + return; + } + allocated_str = nm; + } + else + return; + } + + StringBuilder sb; + sb.append (nm); + sb.append ('/'); + int dlen = sb.length (); + free (allocated_str); + archiveMap = new StringMap<DbeFile *>(); + + struct dirent *entry = NULL; + while ((entry = readdir (exp_dir)) != NULL) + { + char *dname = entry->d_name; + if (dname[0] == '.' + && (dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0'))) + // skip links to ./ or ../ + continue; + sb.setLength (dlen); + sb.append (dname); + char *fnm = sb.toString (); + DbeFile *df = new DbeFile (fnm); + df->set_location (fnm); + df->filetype |= DbeFile::F_FILE; + df->inArchive = true; + df->experiment = this; + archiveMap->put (dname, df); + free (fnm); + } + closedir (exp_dir); +} + +static char * +gen_file_name (const char *packet_name, const char *src_name) +{ + char *fnm, *bname = get_basename (packet_name); + if (bname == packet_name) + fnm = dbe_strdup (src_name); + else + fnm = dbe_sprintf ("%.*s%s", (int) (bname - packet_name), + packet_name, src_name); + + // convert "java.lang.Object/Integer.java" => "java/lang/Object/Integer.java" + bname = get_basename (fnm); + for (char *s = fnm; s < bname; s++) + if (*s == '.') + *s = '/'; + return fnm; +} + +static char * +get_jlass_name (const char *nm) +{ + // Convert "Ljava/lang/Object;" => "java/lang/Object.class" + if (*nm == 'L') + { + size_t len = strlen (nm); + if (nm[len - 1] == ';') + return dbe_sprintf ("%.*s.class", (int) (len - 2), nm + 1); + } + return dbe_strdup (nm); +} + +static char * +get_jmodule_name (const char *nm) +{ + // convert "Ljava/lang/Object;" => "java.lang.Object" + if (*nm == 'L') + { + size_t len = strlen (nm); + if (nm[len - 1] == ';') + { + char *mname = dbe_sprintf (NTXT ("%.*s"), (int) (len - 2), nm + 1); + for (char *s = mname; *s; s++) + if (*s == '/') + *s = '.'; + return mname; + } + } + return dbe_strdup (nm); +} + +LoadObject * +Experiment::get_j_lo (const char *className, const char *fileName) +{ + char *class_name = get_jlass_name (className); + Dprintf (DUMP_JCLASS_READER, + "Experiment::get_j_lo: className='%s' class_name='%s' fileName='%s'\n", + STR (className), STR (class_name), STR (fileName)); + LoadObject *lo = loadObjMap->get (class_name); + if (lo == NULL) + { + lo = createLoadObject (class_name, fileName); + lo->type = LoadObject::SEG_TEXT; + lo->mtime = (time_t) 0; + lo->size = 0; + lo->set_platform (Java, wsize); + lo->dbeFile->filetype |= DbeFile::F_FILE | DbeFile::F_JAVACLASS; + append (lo); + Dprintf (DUMP_JCLASS_READER, + "Experiment::get_j_lo: creates '%s' location='%s'\n", + STR (lo->get_name ()), STR (lo->dbeFile->get_location (false))); + } + free (class_name); + return lo; +} + +Module * +Experiment::get_jclass (const char *className, const char *fileName) +{ + LoadObject *lo = get_j_lo (className, NULL); + char *mod_name = get_jmodule_name (className); + Module *mod = lo->find_module (mod_name); + if (mod == NULL) + { + mod = dbeSession->createClassFile (mod_name); + mod->loadobject = lo; + if (strcmp (fileName, NTXT ("<Unknown>")) != 0) + mod->set_file_name (gen_file_name (lo->get_pathname (), fileName)); + else + mod->set_file_name (dbe_strdup (fileName)); + lo->append_module (mod); + mod_name = NULL; + } + else if (mod->file_name && (strcmp (mod->file_name, "<Unknown>") == 0) + && strcmp (fileName, "<Unknown>") != 0) + mod->set_file_name (gen_file_name (lo->get_pathname (), fileName)); + Dprintf (DUMP_JCLASS_READER, + "Experiment::get_jclass: class_name='%s' mod_name='%s' fileName='%s'\n", + mod->loadobject->get_pathname (), mod->get_name (), mod->file_name); + free (mod_name); + return mod; +} + +#define ARCH_STRLEN(s) ( ( strlen(s) + 4 ) & ~0x3 ) + +int +Experiment::read_java_classes_file () +{ + char *data_file_name = dbe_sprintf (NTXT ("%s/%s"), expt_name, SP_JCLASSES_FILE); + Data_window *dwin = new Data_window (data_file_name); + free (data_file_name); + if (dwin->not_opened ()) + { + delete dwin; + return INCOMPLETE; + } + dwin->need_swap_endian = need_swap_endian; + jmaps = new PRBTree (); + jmidHTable = new DbeCacheMap<unsigned long long, JMethod>; + + hrtime_t cur_loaded = 0; + Module *cur_mod = NULL; + for (int64_t offset = 0;;) + { + CM_Packet *cpkt = (CM_Packet*) dwin->bind (offset, sizeof (CM_Packet)); + if (cpkt == NULL) + break; + uint16_t v16 = (uint16_t) cpkt->tsize; + size_t cpktsize = dwin->decode (v16); + cpkt = (CM_Packet*) dwin->bind (offset, cpktsize); + if ((cpkt == NULL) || (cpktsize == 0)) + { + char *buf = dbe_sprintf (GTXT ("archive file malformed %s"), + arch_name); + errorq->append (new Emsg (CMSG_ERROR, buf)); + free (buf); + break; + } + v16 = (uint16_t) cpkt->type; + v16 = dwin->decode (v16); + switch (v16) + { + case ARCH_JCLASS: + { + ARCH_jclass *ajcl = (ARCH_jclass*) cpkt; + uint64_t class_id = dwin->decode (ajcl->class_id); + char *className = ((char*) ajcl) + sizeof (*ajcl); + char *fileName = className + ARCH_STRLEN (className); + Dprintf (DUMP_JCLASS_READER, + "read_java_classes_file: ARCH_JCLASS(Ox%x)" + "class_id=Ox%llx className='%s' fileName='%s' \n", + (int) v16, (long long) class_id, className, fileName); + cur_mod = NULL; + if (*className == 'L') + { // Old libcollector generated '[' (one array dimension). + cur_mod = get_jclass (className, fileName); + cur_loaded = dwin->decode (ajcl->tstamp); + jmaps->insert (class_id, cur_loaded, cur_mod); + } + break; + } + case ARCH_JCLASS_LOCATION: + { + ARCH_jclass_location *ajcl = (ARCH_jclass_location *) cpkt; + uint64_t class_id = dwin->decode (ajcl->class_id); + char *className = ((char*) ajcl) + sizeof (*ajcl); + char *fileName = className + ARCH_STRLEN (className); + Dprintf (DUMP_JCLASS_READER, + "read_java_classes_file: ARCH_JCLASS_LOCATION(Ox%x)" + "class_id=Ox%llx className='%s' fileName='%s' \n", + (int) v16, (long long) class_id, className, fileName); + get_j_lo (className, fileName); + break; + } + case ARCH_JMETHOD: + { + if (cur_mod == NULL) + break; + ARCH_jmethod *ajmt = (ARCH_jmethod*) cpkt; + uint64_t method_id = dwin->decode (ajmt->method_id); + char *s_name = ((char*) ajmt) + sizeof (*ajmt); + char *s_signature = s_name + ARCH_STRLEN (s_name); + char *fullname = dbe_sprintf ("%s.%s", cur_mod->get_name (), s_name); + Dprintf (DUMP_JCLASS_READER, + "read_java_classes_file: ARCH_JMETHOD(Ox%x) " + "method_id=Ox%llx name='%s' signature='%s' fullname='%s'\n", + (int) v16, (long long) method_id, s_name, + s_signature, fullname); + JMethod *jmthd = cur_mod->find_jmethod (fullname, s_signature); + if (jmthd == NULL) + { + jmthd = dbeSession->createJMethod (); + jmthd->size = (unsigned) - 1; // unknown until later (maybe) + jmthd->module = cur_mod; + jmthd->set_signature (s_signature); + jmthd->set_name (fullname); + cur_mod->functions->append (jmthd); + cur_mod->loadobject->functions->append (jmthd); + Dprintf (DUMP_JCLASS_READER, + "read_java_classes_file: ARCH_JMETHOD CREATE fullname=%s\n", + fullname); + } + jmaps->insert (method_id, cur_loaded, jmthd); + free (fullname); + break; + } + default: + Dprintf (DUMP_JCLASS_READER, + "read_java_classes_file: type=Ox%x (%d) cpktsize=%d\n", + (int) v16, (int) v16, (int) cpktsize); + break; // ignore unknown packets + } + offset += cpktsize; + } + delete dwin; + return SUCCESS; +} + +void +Experiment::read_map_file () +{ + ExperimentFile *mapFile = new ExperimentFile (this, SP_MAP_FILE); + if (!mapFile->open ()) + { + delete mapFile; + return; + } + + SAXParserFactory *factory = SAXParserFactory::newInstance (); + SAXParser *saxParser = factory->newSAXParser (); + DefaultHandler *dh = new ExperimentHandler (this); + try + { + saxParser->parse ((File*) mapFile->fh, dh); + } + catch (SAXException *e) + { + // Fatal error in the parser + StringBuilder sb; + sb.sprintf (NTXT ("%s: %s"), SP_MAP_FILE, e->getMessage ()); + char *str = sb.toString (); + Emsg *m = new Emsg (CMSG_FATAL, str); + errorq->append (m); + status = FAILURE; + free (str); + delete e; + } + delete mapFile; + delete dh; + delete saxParser; + delete factory; + + for (int i = 0, sz = mrecs ? mrecs->size () : 0; i < sz; i++) + { + MapRecord *mrec = mrecs->fetch (i); + SegMem *smem, *sm_lo, *sm_hi; + switch (mrec->kind) + { + case MapRecord::LOAD: + smem = new SegMem; + smem->base = mrec->base; + smem->size = mrec->size; + smem->load_time = mrec->ts; + smem->unload_time = MAX_TIME; + smem->obj = mrec->obj; + smem->set_file_offset (mrec->foff); + seg_items->append (smem); // add to the master list + + // Check if the new segment overlaps other active segments + sm_lo = (SegMem*) maps->locate (smem->base, smem->load_time); + if (sm_lo && sm_lo->base + sm_lo->size > smem->base) + { + // check to see if it is a duplicate record: same address and size, and + if ((smem->base == sm_lo->base) && (smem->size == sm_lo->size)) + { + // addresses and sizes match, check name + if (strstr (smem->obj->get_name (), sm_lo->obj->get_name ()) != NULL + || strstr (sm_lo->obj->get_name (), smem->obj->get_name ()) != NULL) + // this is a duplicate; just move on the the next map record + continue; + fprintf (stderr, + GTXT ("*** Warning: Segment `%s' loaded with same address, size as `%s' [0x%llx-0x%llx]\n"), + smem->obj->get_name (), sm_lo->obj->get_name (), + sm_lo->base, sm_lo->base + sm_lo->size); + } + + // Not a duplicate; implicitly unload the old one + // Note: implicit unloading causes high <Unknown> + // when such overlapping is bogus + StringBuilder sb; + sb.sprintf (GTXT ("*** Warning: Segment %s [0x%llx-0x%llx] overlaps %s [0x%llx-0x%llx], which has been implicitly unloaded"), + smem->obj->get_name (), smem->base, smem->base + smem->size, + sm_lo->obj->get_name (), sm_lo->base, sm_lo->base + sm_lo->size); + warnq->append (new Emsg (CMSG_WARN, sb)); + } + + // now look for other segments with which this might overlap + sm_hi = (SegMem*) maps->locate_up (smem->base, smem->load_time); + while (sm_hi && sm_hi->base < smem->base + smem->size) + { + + // Note: implicit unloading causes high <Unknown> when such overlapping is bogus + // maps->remove( sm_hi->base, smem->load_time ); + StringBuilder sb; + sb.sprintf (GTXT ("*** Warning: Segment %s [0x%llx-0x%llx] overlaps %s [0x%llx-0x%llx], which has been implicitly unloaded"), + smem->obj->get_name (), smem->base, + smem->base + smem->size, sm_hi->obj->get_name (), + sm_hi->base, sm_hi->base + sm_hi->size); + warnq->append (new Emsg (CMSG_WARN, sb)); + sm_hi = (SegMem*) maps->locate_up (sm_hi->base + sm_hi->size, + smem->load_time); + } + + maps->insert (smem->base, smem->load_time, smem); + break; + case MapRecord::UNLOAD: + smem = (SegMem*) maps->locate (mrec->base, mrec->ts); + if (smem && smem->base == mrec->base) + { + smem->unload_time = mrec->ts; + maps->remove (mrec->base, mrec->ts); + } + break; + } + } + mrecs->destroy (); + + // See if there are comments or warnings for a load object; + // if so, queue them to Experiment + for (long i = 0, sz = loadObjs ? loadObjs->size () : 0; i < sz; i++) + { + LoadObject *lo = loadObjs->get (i); + for (Emsg *m = lo->fetch_warnings (); m; m = m->next) + warnq->append (m->get_warn (), m->get_msg ()); + for (Emsg *m = lo->fetch_comments (); m; m = m->next) + commentq->append (m->get_warn (), m->get_msg ()); + } +} + +void +Experiment::read_frameinfo_file () +{ + init_cache (); + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading CallStack Data: %s"), base_name); + read_data_file ("data." SP_FRINFO_FILE, msg); + free (msg); + frmpckts->sort (frUidCmp); + uidnodes->sort (uidNodeCmp); +} + +void +Experiment::read_omp_preg () +{ + // Parallel region descriptions + DataDescriptor *pregDdscr = getDataDescriptor (DATA_OMP4); + if (pregDdscr == NULL) + return; + DataView *pregData = pregDdscr->createView (); + pregData->sort (PROP_CPRID); // omptrace PROP_CPRID + + // OpenMP enter parreg events + DataDescriptor *dDscr = getDataDescriptor (DATA_OMP2); + if (dDscr == NULL || dDscr->getSize () == 0) + { + delete pregData; + return; + } + + char *idxname = NTXT ("OMP_preg"); + delete dbeSession->indxobj_define (idxname, GTXT ("OpenMP Parallel Region"), + NTXT ("CPRID"), NULL, NULL); + int idxtype = dbeSession->findIndexSpaceByName (idxname); + if (idxtype < 0) + { + delete pregData; + return; + } + ompavail = true; + + // Pre-create parallel region with id == 0 + Histable *preg0 = dbeSession->createIndexObject (idxtype, (int64_t) 0); + preg0->set_name (dbe_strdup (GTXT ("Implicit OpenMP Parallel Region"))); + + // Take care of the progress bar + char *msg = dbe_sprintf (GTXT ("Processing OpenMP Parallel Region Data: %s"), + get_basename (expt_name)); + theApplication->set_progress (0, msg); + free (msg); + long deltaReport = 1000; + long nextReport = 0; + long errors_found = 0; + Vector<Histable*> pregs; + + long size = dDscr->getSize (); + for (long i = 0; i < size; ++i) + { + if (i == nextReport) + { + int percent = (int) (i * 100 / size); + if (percent > 0) + theApplication->set_progress (percent, NULL); + nextReport += deltaReport; + } + + uint32_t thrid = dDscr->getIntValue (PROP_THRID, i); + hrtime_t tstamp = dDscr->getLongValue (PROP_TSTAMP, i); + uint64_t cprid = dDscr->getLongValue (PROP_CPRID, i); // omptrace CPRID + mapPRid->put (thrid, tstamp, cprid); + + pregs.reset (); + /* + * We will use 2 pointers to make sure there is no loop. + * First pointer "curpreg" goes to the next element, + * second pointer "curpreg_loop_control" goes to the next->next element. + * If these pointers have the same value - there is a loop. + */ + uint64_t curpreg_loop_control = cprid; + Datum tval_loop_control; + if (curpreg_loop_control != 0) + { + tval_loop_control.setUINT64 (curpreg_loop_control); + long idx = pregData->getIdxByVals (&tval_loop_control, DataView::REL_EQ); + if (idx < 0) + curpreg_loop_control = 0; + else + curpreg_loop_control = pregData->getLongValue (PROP_PPRID, idx); + } + for (uint64_t curpreg = cprid; curpreg != 0;) + { + Histable *val = NULL; + Datum tval; + tval.setUINT64 (curpreg); + long idx = pregData->getIdxByVals (&tval, DataView::REL_EQ); + if (idx < 0) + break; + /* + * Check if there is a loop + */ + if (0 != curpreg_loop_control) + { + if (curpreg == curpreg_loop_control) + { + errors_found++; + if (1 == errors_found) + { + Emsg *m = new Emsg (CMSG_WARN, GTXT ("*** Warning: circular links in OMP regions; data may not be correct.")); + warnq->append (m); + } + break; + } + } + uint64_t pragmapc = pregData->getLongValue (PROP_PRPC, idx); + DbeInstr *instr = map_Vaddr_to_PC (pragmapc, tstamp); + if (instr == NULL) + { + break; + } + val = instr; + DbeLine *dbeline = (DbeLine*) instr->convertto (Histable::LINE); + if (dbeline->lineno > 0) + { + if (instr->func->usrfunc) + dbeline = dbeline->sourceFile->find_dbeline + (instr->func->usrfunc, dbeline->lineno); + dbeline->set_flag (DbeLine::OMPPRAGMA); + val = dbeline; + } + val = dbeSession->createIndexObject (idxtype, val); + pregs.append (val); + + curpreg = pregData->getLongValue (PROP_PPRID, idx); + /* + * Update curpreg_loop_control + */ + if (0 != curpreg_loop_control) + { + tval_loop_control.setUINT64 (curpreg_loop_control); + idx = pregData->getIdxByVals + (&tval_loop_control, DataView::REL_EQ); + if (idx < 0) + curpreg_loop_control = 0; + else + { + curpreg_loop_control = pregData->getLongValue + (PROP_PPRID, idx); + tval_loop_control.setUINT64 (curpreg_loop_control); + idx = pregData->getIdxByVals + (&tval_loop_control, DataView::REL_EQ); + if (idx < 0) + curpreg_loop_control = 0; + else + curpreg_loop_control = pregData->getLongValue + (PROP_PPRID, idx); + } + } + } + pregs.append (preg0); + void *prstack = cstack->add_stack (&pregs); + mapPReg->put (thrid, tstamp, prstack); + } + theApplication->set_progress (0, NTXT ("")); + delete pregData; +} + +void +Experiment::read_omp_task () +{ + // Task description + DataDescriptor *taskDataDdscr = getDataDescriptor (DATA_OMP5); + if (taskDataDdscr == NULL) + return; + + //7035272: previously, DataView was global; now it's local...is this OK? + DataView *taskData = taskDataDdscr->createView (); + taskData->sort (PROP_TSKID); // omptrace PROP_TSKID + + // OpenMP enter task events + DataDescriptor *dDscr = getDataDescriptor (DATA_OMP3); + if (dDscr == NULL || dDscr->getSize () == 0) + { + delete taskData; + return; + } + + char *idxname = NTXT ("OMP_task"); + // delete a possible error message. Ugly. + delete dbeSession->indxobj_define (idxname, GTXT ("OpenMP Task"), NTXT ("TSKID"), NULL, NULL); + int idxtype = dbeSession->findIndexSpaceByName (idxname); + if (idxtype < 0) + { + delete taskData; + return; + } + ompavail = true; + + // Pre-create task with id == 0 + Histable *task0 = dbeSession->createIndexObject (idxtype, (int64_t) 0); + task0->set_name (dbe_strdup (GTXT ("OpenMP Task from Implicit Parallel Region"))); + + // Take care of the progress bar + char *msg = dbe_sprintf (GTXT ("Processing OpenMP Task Data: %s"), get_basename (expt_name)); + theApplication->set_progress (0, msg); + free (msg); + long deltaReport = 1000; + long nextReport = 0; + + Vector<Histable*> tasks; + long size = dDscr->getSize (); + long errors_found = 0; + for (long i = 0; i < size; ++i) + { + if (i == nextReport) + { + int percent = (int) (i * 100 / size); + if (percent > 0) + theApplication->set_progress (percent, NULL); + nextReport += deltaReport; + } + + uint32_t thrid = dDscr->getIntValue (PROP_THRID, i); + hrtime_t tstamp = dDscr->getLongValue (PROP_TSTAMP, i); + uint64_t tskid = dDscr->getLongValue (PROP_TSKID, i); //omptrace TSKID + tasks.reset (); + /* + * We will use 2 pointers to make sure there is no loop. + * First pointer "curtsk" goes to the next element, + * second pointer "curtsk_loop_control" goes to the next->next element. + * If these pointers have the same value - there is a loop. + */ + uint64_t curtsk_loop_control = tskid; + Datum tval_loop_control; + if (curtsk_loop_control != 0) + { + tval_loop_control.setUINT64 (curtsk_loop_control); + long idx = taskData->getIdxByVals (&tval_loop_control, DataView::REL_EQ); + if (idx < 0) + curtsk_loop_control = 0; + else + curtsk_loop_control = taskData->getLongValue (PROP_PTSKID, idx); + } + for (uint64_t curtsk = tskid; curtsk != 0;) + { + Histable *val = NULL; + + Datum tval; + tval.setUINT64 (curtsk); + long idx = taskData->getIdxByVals (&tval, DataView::REL_EQ); + if (idx < 0) + break; + /* + * Check if there is a loop + */ + if (0 != curtsk_loop_control) + { + if (curtsk == curtsk_loop_control) + { + errors_found++; + if (1 == errors_found) + { + Emsg *m = new Emsg (CMSG_WARN, GTXT ("*** Warning: circular links in OMP tasks; data may not be correct.")); + warnq->append (m); + } + break; + } + } + uint64_t pragmapc = taskData->getLongValue (PROP_PRPC, idx); + DbeInstr *instr = map_Vaddr_to_PC (pragmapc, tstamp); + if (instr == NULL) + break; + val = instr; + DbeLine *dbeline = (DbeLine*) instr->convertto (Histable::LINE); + if (dbeline->lineno > 0) + { + if (instr->func->usrfunc) + dbeline = dbeline->sourceFile->find_dbeline + (instr->func->usrfunc, dbeline->lineno); + dbeline->set_flag (DbeLine::OMPPRAGMA); + val = dbeline; + } + val = dbeSession->createIndexObject (idxtype, val); + tasks.append (val); + + curtsk = taskData->getLongValue (PROP_PTSKID, idx); + /* + * Update curtsk_loop_control + */ + if (0 != curtsk_loop_control) + { + tval_loop_control.setUINT64 (curtsk_loop_control); + idx = taskData->getIdxByVals (&tval_loop_control, DataView::REL_EQ); + if (idx < 0) + curtsk_loop_control = 0; + else + { + curtsk_loop_control = taskData->getLongValue (PROP_PTSKID, idx); + tval_loop_control.setUINT64 (curtsk_loop_control); + idx = taskData->getIdxByVals (&tval_loop_control, + DataView::REL_EQ); + if (idx < 0) + curtsk_loop_control = 0; + else + curtsk_loop_control = taskData->getLongValue (PROP_PTSKID, + idx); + } + } + } + tasks.append (task0); + void *tskstack = cstack->add_stack (&tasks); + mapTask->put (thrid, tstamp, tskstack); + } + theApplication->set_progress (0, NTXT ("")); + delete taskData; +} + +void +Experiment::read_omp_file () +{ + // DATA_OMP2 table is common between OpenMP 2.5 and 3.0 profiling + DataDescriptor *dDscr = getDataDescriptor (DATA_OMP2); + if (dDscr == NULL) + return; + if (dDscr->getSize () == 0) + { + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading OpenMP Data: %s"), base_name); + read_data_file (SP_OMPTRACE_FILE, msg); + free (msg); + + // OpenMP fork events + dDscr = getDataDescriptor (DATA_OMP); + long sz = dDscr->getSize (); + if (sz > 0) + { + // progress bar + msg = dbe_sprintf (GTXT ("Processing OpenMP Parallel Region Data: %s"), + base_name); + theApplication->set_progress (0, msg); + free (msg); + long deltaReport = 5000; + long nextReport = 0; + for (int i = 0; i < sz; ++i) + { + if (i == nextReport) + { + int percent = (int) (i * 100 / sz); + if (percent > 0) + theApplication->set_progress (percent, NULL); + nextReport += deltaReport; + } + uint32_t thrid = dDscr->getIntValue (PROP_THRID, i); + hrtime_t tstamp = dDscr->getLongValue (PROP_TSTAMP, i); + uint64_t cprid = dDscr->getLongValue (PROP_CPRID, i); //omptrace + mapPRid->put (thrid, tstamp, cprid); + } + theApplication->set_progress (0, NTXT ("")); + + ompavail = true; + openMPdata = dDscr->createView (); + openMPdata->sort (PROP_CPRID); // omptrace PROP_CPRID + + // thread enters parreg events + dDscr = getDataDescriptor (DATA_OMP2); + sz = dDscr->getSize (); + + // progress bar + msg = dbe_sprintf (GTXT ("Processing OpenMP Parallel Region Data: %s"), + base_name); + theApplication->set_progress (0, msg); + free (msg); + deltaReport = 5000; + nextReport = 0; + + for (int i = 0; i < sz; ++i) + { + if (i == nextReport) + { + int percent = (int) (i * 100 / sz); + if (percent > 0) + theApplication->set_progress (percent, NULL); + nextReport += deltaReport; + } + uint32_t thrid = dDscr->getIntValue (PROP_THRID, i); + hrtime_t tstamp = dDscr->getLongValue (PROP_TSTAMP, i); + uint64_t cprid = dDscr->getLongValue (PROP_CPRID, i); //omptrace + mapPRid->put (thrid, tstamp, cprid); + } + theApplication->set_progress (0, NTXT ("")); + } + else + { + read_omp_preg (); + read_omp_task (); + } + if (ompavail && coll_params.profile_mode) + { + dbeSession->status_ompavail = 1; + register_metric (Metric::OMP_WORK); + register_metric (Metric::OMP_WAIT); + register_metric (Metric::OMP_OVHD); + if (coll_params.lms_magic_id == LMS_MAGIC_ID_SOLARIS) + register_metric (Metric::OMP_MASTER_THREAD); + } + } +} + +void +Experiment::read_ifreq_file () +{ + char *fname = dbe_sprintf (NTXT ("%s/%s"), expt_name, SP_IFREQ_FILE); + FILE *f = fopen (fname, NTXT ("r")); + free (fname); + if (f == NULL) + { + ifreqavail = false; + return; + } + ifreqavail = true; + ifreqq = new Emsgqueue (NTXT ("ifreqq")); + + while (1) + { + Emsg *m; + char str[MAXPATHLEN]; + char *e = fgets (str, ((int) sizeof (str)) - 1, f); + if (e == NULL) + { + // end the list from the experiment + m = new Emsg (CMSG_COMMENT, + GTXT ("============================================================")); + ifreqq->append (m); + break; + } + // get the string + size_t i = strlen (str); + if (i > 0 && str[i - 1] == '\n') + // remove trailing nl + str[i - 1] = 0; + // and append it + m = new Emsg (CMSG_COMMENT, str); + ifreqq->append (m); + } + (void) fclose (f); +} + +Experiment * +Experiment::getBaseFounder () +{ + if (baseFounder) + return baseFounder; + Experiment *founder = this; + Experiment *parent = founder->founder_exp; + while (parent) + { + founder = parent; + parent = founder->founder_exp; + } + baseFounder = founder; + return baseFounder; +} + +hrtime_t +Experiment::getRelativeStartTime () +{ + if (exp_rel_start_time_set) + return exp_rel_start_time; + Experiment *founder = getBaseFounder (); + hrtime_t child_start = this->getStartTime (); + hrtime_t founder_start = founder->getStartTime (); + exp_rel_start_time = child_start - founder_start; + if (child_start == 0 && founder_start) + exp_rel_start_time = 0; // when descendents have incomplete log.xml + exp_rel_start_time_set = true; + return exp_rel_start_time; +} + +DataDescriptor * +Experiment::get_raw_events (int data_id) +{ + DataDescriptor *dDscr; + switch (data_id) + { + case DATA_CLOCK: + dDscr = get_profile_events (); + break; + case DATA_SYNCH: + dDscr = get_sync_events (); + break; + case DATA_HWC: + dDscr = get_hwc_events (); + break; + case DATA_HEAP: + dDscr = get_heap_events (); + break; + case DATA_HEAPSZ: + dDscr = get_heapsz_events (); + break; + case DATA_IOTRACE: + dDscr = get_iotrace_events (); + break; + case DATA_RACE: + dDscr = get_race_events (); + break; + case DATA_DLCK: + dDscr = get_deadlock_events (); + break; + case DATA_SAMPLE: + dDscr = get_sample_events (); + break; + case DATA_GCEVENT: + dDscr = get_gc_events (); + break; + default: + dDscr = NULL; + break; + } + return dDscr; +} + +int +Experiment::base_data_id (int data_id) +{ + switch (data_id) + { + case DATA_HEAPSZ: + return DATA_HEAP; // DATA_HEAPSZ DataView is based on DATA_HEAP's DataView + default: + break; + } + return data_id; +} + +DataView * +Experiment::create_derived_data_view (int data_id, DataView *dview) +{ + // dview contains filtered packets + switch (data_id) + { + case DATA_HEAPSZ: + return create_heapsz_data_view (dview); + default: + break; + } + return NULL; +} + +DataDescriptor * +Experiment::get_profile_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_CLOCK); + if (dDscr == NULL) + return NULL; + if (dDscr->getSize () == 0) + { + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading Profile Data: %s"), base_name); + read_data_file (SP_PROFILE_FILE, msg); + free (msg); + add_evt_time_to_profile_events (dDscr); + resolve_frame_info (dDscr); + } + else if (!dDscr->isResolveFrInfoDone ()) + resolve_frame_info (dDscr); + return dDscr; +} + +void +Experiment::add_evt_time_to_profile_events (DataDescriptor *dDscr) +{ + if (coll_params.lms_magic_id != LMS_MAGIC_ID_SOLARIS) + return; + + DataView *dview = dDscr->createView (); + dview->sort (PROP_THRID, PROP_TSTAMP); + + // add PROP_EVT_TIME + PropDescr* tmp_propDscr = new PropDescr (PROP_EVT_TIME, "EVT_TIME"); + tmp_propDscr->uname = dbe_strdup (GTXT ("Event duration")); + tmp_propDscr->vtype = TYPE_INT64; + dDscr->addProperty (tmp_propDscr); + + long sz = dview->getSize (); + long long ptimer_usec = get_params ()->ptimer_usec; + for (long i = 0; i < sz; i++) + { + int next_sample; + int jj; + { + hrtime_t this_tstamp = dview->getLongValue (PROP_TSTAMP, i); + long this_thrid = dview->getLongValue (PROP_THRID, i); + for (jj = i + 1; jj < sz; jj++) + { + hrtime_t tmp_tstamp = dview->getLongValue (PROP_TSTAMP, jj); + if (tmp_tstamp != this_tstamp) + break; + long tmp_thrid = dview->getLongValue (PROP_THRID, jj); + if (tmp_thrid != this_thrid) + break; + } + next_sample = jj; + } + + long nticks = 0; + for (jj = i; jj < next_sample; jj++) + nticks += dview->getLongValue (PROP_NTICK, jj); + if (nticks <= 1) + continue; // no duration + + nticks--; + hrtime_t duration = ptimer_usec * 1000LL * nticks; // nanoseconds + for (jj = i; jj < next_sample; jj++) + dview->setValue (PROP_EVT_TIME, jj, duration); + i = jj - 1; + } + delete dview; +} + +DataDescriptor * +Experiment::get_sync_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_SYNCH); + if (dDscr == NULL) + return NULL; + if (dDscr->getSize () > 0) + return dDscr; + + // fetch data + { + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading Synctrace Data: %s"), base_name); + read_data_file (SP_SYNCTRACE_FILE, msg); + free (msg); + resolve_frame_info (dDscr); + } + + // check for PROP_EVT_TIME + PropDescr *tmp_propDscr = dDscr->getProp (PROP_EVT_TIME); + if (tmp_propDscr) + return dDscr; + + // add PROP_EVT_TIME + tmp_propDscr = new PropDescr (PROP_EVT_TIME, "EVT_TIME"); + tmp_propDscr->uname = dbe_strdup (GTXT ("Event duration")); + tmp_propDscr->vtype = TYPE_INT64; + dDscr->addProperty (tmp_propDscr); + + long sz = dDscr->getSize (); + for (long i = 0; i < sz; i++) + { + uint64_t event_duration = dDscr->getLongValue (PROP_TSTAMP, i); + event_duration -= dDscr->getLongValue (PROP_SRQST, i); + dDscr->setValue (PROP_EVT_TIME, i, event_duration); + } + return dDscr; +} + +DataDescriptor * +Experiment::get_hwc_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_HWC); + if (dDscr == NULL) + return NULL; + if (dDscr->getSize () == 0) + { + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading HW Profile Data: %s"), base_name); + + // clear HWC event stats + dsevents = 0; + dsnoxhwcevents = 0; + read_data_file (SP_HWCNTR_FILE, msg); + free (msg); + resolve_frame_info (dDscr); + + // describe the HW counters in PropDescr + PropDescr *prop = dDscr->getProp (PROP_HWCTAG); + if (prop) + { + Collection_params *cparam = get_params (); + if (cparam->hw_mode != 0) + for (int aux = 0; aux < MAX_HWCOUNT; aux++) + if (cparam->hw_aux_name[aux]) + { + const char* cmdname = cparam->hw_aux_name[aux]; + const char* uname = cparam->hw_username[aux]; + prop->addState (aux, cmdname, uname); + } + } + else + assert (0); + + double dserrrate = 100.0 * ((double) dsnoxhwcevents) / ((double) dsevents); + if ((dsevents > 0) && (dserrrate > 10.0)) + { + // warn the user that rate is high + StringBuilder sb; + if (dbeSession->check_ignore_no_xhwcprof ()) + sb.sprintf ( + GTXT ("Warning: experiment %s has %.1f%%%% (%lld of %lld) dataspace events that were accepted\n without verification; data may be incorrect or misleading\n recompile with -xhwcprof and rerecord to get better data\n"), + base_name, dserrrate, (long long) dsnoxhwcevents, + (long long) dsevents); + else + sb.sprintf ( + GTXT ("Warning: experiment %s has %.1f%%%% (%lld of %lld) dataspace events that could not be verified\n recompile with -xhwcprof and rerecord to get better data\n"), + base_name, dserrrate, (long long) dsnoxhwcevents, + (long long) dsevents); + errorq->append (new Emsg (CMSG_WARN, sb)); + } + + // see if we've scanned the data + if (hwc_scanned == 0) + { + // no, scan the packets to see how many are bogus, or represent lost interrupts + long hwc_cnt = 0; + + // loop over the packets, counting the bad ones + if (hwc_bogus != 0 || hwc_lost_int != 0) + { + // hwc counter data had bogus packets and/or packets reflecting lost interrupts + double bogus_rate = 100. * (double) hwc_bogus / (double) hwc_cnt; + if (bogus_rate > 5.) + { + StringBuilder sb; + sb.sprintf ( + GTXT ("WARNING: Too many invalid HW counter profile events (%ld/%ld = %3.2f%%) in experiment %d (`%s'); data may be unreliable"), + (long) hwc_bogus, (long) hwc_cnt, bogus_rate, + (int) userExpId, base_name); + Emsg *m = new Emsg (CMSG_WARN, sb); + warnq->append (m); + } + hwc_scanned = 1; + } + } + } + return dDscr; +} + +DataDescriptor * +Experiment::get_iotrace_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_IOTRACE); + if (dDscr == NULL) + return NULL; + + if (dDscr->getSize () > 0) + return dDscr; + + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading IO Trace Data: %s"), base_name); + read_data_file (SP_IOTRACE_FILE, msg); + free (msg); + + if (dDscr->getSize () == 0) + return dDscr; + resolve_frame_info (dDscr); + + // check for PROP_EVT_TIME + PropDescr *tmp_propDscr = dDscr->getProp (PROP_EVT_TIME); + if (tmp_propDscr) + return dDscr; + + // add PROP_EVT_TIME + tmp_propDscr = new PropDescr (PROP_EVT_TIME, "EVT_TIME"); + tmp_propDscr->uname = dbe_strdup (GTXT ("Event duration")); + tmp_propDscr->vtype = TYPE_INT64; + dDscr->addProperty (tmp_propDscr); + + // add PROP_IOVFD + tmp_propDscr = new PropDescr (PROP_IOVFD, "IOVFD"); + tmp_propDscr->uname = dbe_strdup (GTXT ("Virtual File Descriptor")); + tmp_propDscr->vtype = TYPE_INT64; + dDscr->addProperty (tmp_propDscr); + + delete fDataMap; + fDataMap = new DefaultMap<int64_t, FileData*>; + + delete vFdMap; + vFdMap = new DefaultMap<int, int64_t>; + + static int64_t virtualFd = 0; + + FileData *fData; + virtualFd += 10; + fData = fDataMap->get (VIRTUAL_FD_STDIN); + if (fData == NULL) + { + fData = new FileData (STDIN_FILENAME); + fData->setVirtualFd (VIRTUAL_FD_STDIN); + fData->id = VIRTUAL_FD_STDIN; + fData->setFileDes (STDIN_FD); + fDataMap->put (VIRTUAL_FD_STDIN, fData); + vFdMap->put (STDIN_FD, VIRTUAL_FD_STDIN); + } + + fData = fDataMap->get (VIRTUAL_FD_STDOUT); + if (fData == NULL) + { + fData = new FileData (STDOUT_FILENAME); + fData->setVirtualFd (VIRTUAL_FD_STDOUT); + fData->id = VIRTUAL_FD_STDOUT; + fData->setFileDes (STDOUT_FD); + fDataMap->put (VIRTUAL_FD_STDOUT, fData); + vFdMap->put (STDOUT_FD, VIRTUAL_FD_STDOUT); + } + + fData = fDataMap->get (VIRTUAL_FD_STDERR); + if (fData == NULL) + { + fData = new FileData (STDERR_FILENAME); + fData->setVirtualFd (VIRTUAL_FD_STDERR); + fData->id = VIRTUAL_FD_STDERR; + fData->setFileDes (STDERR_FD); + fDataMap->put (VIRTUAL_FD_STDERR, fData); + vFdMap->put (STDERR_FD, VIRTUAL_FD_STDERR); + } + + fData = fDataMap->get (VIRTUAL_FD_OTHERIO); + if (fData == NULL) + { + fData = new FileData (OTHERIO_FILENAME); + fData->setVirtualFd (VIRTUAL_FD_OTHERIO); + fData->id = VIRTUAL_FD_OTHERIO; + fData->setFileDes (OTHERIO_FD); + fDataMap->put (VIRTUAL_FD_OTHERIO, fData); + } + + DataView *dview = dDscr->createView (); + dview->sort (PROP_TSTAMP); + long sz = dview->getSize (); + for (long i = 0; i < sz; i++) + { + hrtime_t event_duration = dview->getLongValue (PROP_TSTAMP, i); + hrtime_t event_start = dview->getLongValue (PROP_IORQST, i); + if (event_start > 0) + event_duration -= event_start; + else + event_duration = 0; + dview->setValue (PROP_EVT_TIME, i, event_duration); + + int32_t fd = -1; + int64_t vFd = VIRTUAL_FD_NONE; + char *fName = NULL; + int32_t origFd = -1; + StringBuilder *sb = NULL; + FileData *fDataOrig = NULL; + FileSystem_type fsType; + + IOTrace_type ioType = (IOTrace_type) dview->getIntValue (PROP_IOTYPE, i); + switch (ioType) + { + case READ_TRACE: + case WRITE_TRACE: + case READ_TRACE_ERROR: + case WRITE_TRACE_ERROR: + fd = dview->getIntValue (PROP_IOFD, i); + vFd = vFdMap->get (fd); + if (vFd == 0 || vFd == VIRTUAL_FD_NONE + || (fData = fDataMap->get (vFd)) == NULL) + { + fData = new FileData (UNKNOWNFD_FILENAME); + fData->setVirtualFd (virtualFd); + fData->setFsType ("N/A"); + fData->setFileDes (fd); + fDataMap->put (virtualFd, fData); + vFdMap->put (fd, virtualFd); + vFd = virtualFd; + virtualFd++; + } + dview->setValue (PROP_IOVFD, i, vFd); + break; + case OPEN_TRACE: + fName = NULL; + sb = (StringBuilder*) dview->getObjValue (PROP_IOFNAME, i); + if (sb != NULL && sb->length () > 0) + fName = sb->toString (); + fd = dview->getIntValue (PROP_IOFD, i); + origFd = dview->getIntValue (PROP_IOOFD, i); + fsType = (FileSystem_type) dview->getIntValue (PROP_IOFSTYPE, i); + + if (fName != NULL) + { + fData = new FileData (fName); + fDataMap->put (virtualFd, fData); + vFdMap->put (fd, virtualFd); + fData->setFileDes (fd); + fData->setFsType (fsType); + fData->setVirtualFd (virtualFd); + vFd = virtualFd; + virtualFd++; + } + else if (origFd > 0) + { + vFd = vFdMap->get (origFd); + if (vFd == 0 || vFd == VIRTUAL_FD_NONE) + { + Dprintf (DEBUG_IO, + "*** Error I/O tracing: (open) cannot get the virtual file descriptor, fd=%d origFd=%d\n", + fd, origFd); + continue; + } + else if ((fDataOrig = fDataMap->get (vFd)) == NULL) + { + Dprintf (DEBUG_IO, + "*** Error IO tracing: (open) cannot get original FileData object, fd=%d origFd=%d\n", + fd, origFd); + continue; + } + else + { + fName = fDataOrig->getFileName (); + fData = new FileData (fName); + fData->setFileDes (fd); + fData->setFsType (fDataOrig->getFsType ()); + fData->setVirtualFd (virtualFd); + fDataMap->put (virtualFd, fData); + vFdMap->put (fd, virtualFd); + vFd = virtualFd; + virtualFd++; + } + } + else if (fd >= 0) + { + vFd = vFdMap->get (fd); + if (vFd == 0 || vFd == VIRTUAL_FD_NONE + || (fData = fDataMap->get (vFd)) == NULL) + { + fData = new FileData (UNKNOWNFD_FILENAME); + fData->setVirtualFd (virtualFd); + fData->setFsType ("N/A"); + fData->setFileDes (fd); + fDataMap->put (virtualFd, fData); + vFdMap->put (fd, virtualFd); + vFd = virtualFd; + virtualFd++; + } + } + else + { + Dprintf (DEBUG_IO, + NTXT ("*** Error IO tracing: (open) unknown open IO type, fd=%d origFd=%d\n"), fd, origFd); + continue; + } + + dview->setValue (PROP_IOVFD, i, vFd); + break; + + case OPEN_TRACE_ERROR: + fName = NULL; + + sb = (StringBuilder*) dview->getObjValue (PROP_IOFNAME, i); + if (sb != NULL && sb->length () > 0) + fName = sb->toString (); + fd = dview->getIntValue (PROP_IOFD, i); + origFd = dview->getIntValue (PROP_IOOFD, i); + fsType = (FileSystem_type) dview->getIntValue (PROP_IOFSTYPE, i); + + if (fName != NULL) + { + fData = new FileData (fName); + fDataMap->put (virtualFd, fData); + fData->setFileDes (fd); + fData->setFsType (fsType); + fData->setVirtualFd (virtualFd); + vFd = virtualFd; + virtualFd++; + } + else if (origFd > 0) + { + vFd = vFdMap->get (origFd); + if (vFd == 0 || vFd == VIRTUAL_FD_NONE) + { + Dprintf (DEBUG_IO, + "*** Error IO tracing: (open error) cannot get the virtual file descriptor, fd=%d origFd=%d\n", + fd, origFd); + continue; + } + else if ((fDataOrig = fDataMap->get (vFd)) == NULL) + { + Dprintf (DEBUG_IO, + "*** Error IO tracing: (open error) cannot get original FileData object, fd=%d origFd=%d\n", + fd, origFd); + continue; + } + else + { + fName = fDataOrig->getFileName (); + fData = new FileData (fName); + fData->setFileDes (fd); + fData->setFsType (fDataOrig->getFsType ()); + fData->setVirtualFd (virtualFd); + fDataMap->put (virtualFd, fData); + vFd = virtualFd; + virtualFd++; + } + } + + dview->setValue (PROP_IOVFD, i, vFd); + break; + + case CLOSE_TRACE: + case CLOSE_TRACE_ERROR: + fd = dview->getIntValue (PROP_IOFD, i); + vFd = vFdMap->get (fd); + if (vFd == 0 || vFd == VIRTUAL_FD_NONE) + { + Dprintf (DEBUG_IO, + "*** Error IO tracing: (close) cannot get the virtual file descriptor, fd=%d\n", + fd); + continue; + } + fData = fDataMap->get (vFd); + if (fData == NULL) + { + Dprintf (DEBUG_IO, + "*** Error IO tracing: (close) cannot get the FileData object, fd=%d\n", + fd); + continue; + } + + vFdMap->put (fd, VIRTUAL_FD_NONE); + dview->setValue (PROP_IOVFD, i, vFd); + break; + + case OTHERIO_TRACE: + case OTHERIO_TRACE_ERROR: + vFd = VIRTUAL_FD_OTHERIO; + fData = fDataMap->get (vFd); + if (fData == NULL) + { + Dprintf (DEBUG_IO, + "*** Error IO tracing: (other IO) cannot get the FileData object\n"); + continue; + } + + dview->setValue (PROP_IOVFD, i, vFd); + break; + case IOTRACETYPE_LAST: + break; + } + } + + delete dview; + + return dDscr; +} + +DataDescriptor * +Experiment::get_heap_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_HEAP); + if (dDscr == NULL) + return NULL; + if (dDscr->getSize () > 0) + return dDscr; + + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading Heap Trace Data: %s"), base_name); + read_data_file (SP_HEAPTRACE_FILE, msg); + free (msg); + + if (dDscr->getSize () == 0) + return dDscr; + resolve_frame_info (dDscr); + + // Match FREE to MALLOC + PropDescr *prop = new PropDescr (PROP_HLEAKED, NTXT ("HLEAKED")); + prop->uname = dbe_strdup (GTXT ("Bytes Leaked")); + prop->vtype = TYPE_UINT64; + dDscr->addProperty (prop); + + prop = new PropDescr (PROP_HMEM_USAGE, NTXT ("HMEM_USAGE")); + prop->uname = dbe_strdup (GTXT ("Heap Memory Usage")); + prop->vtype = TYPE_UINT64; + dDscr->addProperty (prop); + + prop = new PropDescr (PROP_HFREED, NTXT ("HFREED")); + prop->uname = dbe_strdup (GTXT ("Bytes Freed")); + prop->vtype = TYPE_UINT64; + dDscr->addProperty (prop); + + prop = new PropDescr (PROP_HCUR_ALLOCS, NTXT ("HCUR_ALLOCS")); + prop->uname = dbe_strdup (GTXT ("Net Bytes Allocated")); + prop->vtype = TYPE_INT64; + dDscr->addProperty (prop); + + prop = new PropDescr (PROP_HCUR_LEAKS, NTXT ("HCUR_LEAKS")); + prop->uname = dbe_strdup (GTXT ("Net Bytes Leaked")); + prop->vtype = TYPE_UINT64; + dDscr->addProperty (prop); + + prop = new PropDescr (PROP_HCUR_NET_ALLOC, NTXT ("HCUR_NET_ALLOC")); + prop->vtype = TYPE_INT64; + prop->flags = DDFLAG_NOSHOW; + dDscr->addProperty (prop); + + prop = new PropDescr (PROP_DDSCR_LNK, NTXT ("DDSCR_LNK")); + prop->vtype = TYPE_UINT64; + prop->flags = DDFLAG_NOSHOW; + dDscr->addProperty (prop); + + prop = new PropDescr (PROP_VOIDP_OBJ, NTXT ("VOIDP_OBJ")); + prop->vtype = TYPE_OBJ; + prop->flags = DDFLAG_NOSHOW; + dDscr->addProperty (prop); + + prop = new PropDescr (PROP_TSTAMP2, NTXT ("TSTAMP2")); + prop->uname = dbe_strdup (GTXT ("End Timestamp (nanoseconds)")); + prop->vtype = TYPE_UINT64; + prop->flags = DDFLAG_NOSHOW; + dDscr->addProperty (prop); + + DataView *dview = dDscr->createView (); + dview->sort (PROP_TSTAMP); + + // Keep track of memory usage + Size memoryUsage = 0; + + HeapMap *heapmap = new HeapMap (); + long sz = dview->getSize (); + for (long i = 0; i < sz; i++) + { + + Heap_type mtype = (Heap_type) dview->getIntValue (PROP_HTYPE, i); + Vaddr vaddr = dview->getULongValue (PROP_HVADDR, i); + Vaddr ovaddr = dview->getULongValue (PROP_HOVADDR, i); + Size hsize = dview->getULongValue (PROP_HSIZE, i); + hrtime_t tstamp = dview->getLongValue (PROP_TSTAMP, i); + + switch (mtype) + { + case MALLOC_TRACE: + dview->setValue (PROP_TSTAMP2, i, (uint64_t) MAX_TIME); + if (vaddr) + { + dview->setValue (PROP_HLEAKED, i, hsize); + heapmap->allocate (vaddr, i + 1); + + // Increase heap size + memoryUsage += hsize; + dview->setValue (PROP_HMEM_USAGE, i, memoryUsage); + } + break; + + case FREE_TRACE: + if (vaddr) + { + long idx = heapmap->deallocate (vaddr) - 1; + if (idx >= 0) + { + // Decrease heap size + Size leaked = dview->getLongValue (PROP_HLEAKED, idx); + memoryUsage -= leaked; + dview->setValue (PROP_HMEM_USAGE, i, memoryUsage); + + Size alloc = dview->getLongValue (PROP_HSIZE, idx); + // update allocation + dview->setValue (PROP_HLEAKED, idx, (uint64_t) 0); + dview->setValue (PROP_TSTAMP2, idx, tstamp); + dview->setValue (PROP_DDSCR_LNK, idx, dview->getIdByIdx (i) + 1); + // update this event + dview->setValue (PROP_HFREED, i, alloc); + } + } + break; + + case REALLOC_TRACE: + dview->setValue (PROP_TSTAMP2, i, (uint64_t) MAX_TIME); + if (ovaddr) + { + long idx = heapmap->deallocate (ovaddr) - 1; + if (idx >= 0) + { + // Decrease heap size + Size leaked = dview->getLongValue (PROP_HLEAKED, idx); + memoryUsage -= leaked; + dview->setValue (PROP_HMEM_USAGE, i, memoryUsage); + + Size alloc = dview->getLongValue (PROP_HSIZE, idx); + // update allocation + dview->setValue (PROP_HLEAKED, idx, (uint64_t) 0); + dview->setValue (PROP_TSTAMP2, idx, tstamp); + dview->setValue (PROP_DDSCR_LNK, idx, dview->getIdByIdx (i) + 1); + // update this event + dview->setValue (PROP_HFREED, i, alloc); + } + } + if (vaddr) + { + dview->setValue (PROP_HLEAKED, i, hsize); + heapmap->allocate (vaddr, i + 1); + + // Increase heap size + memoryUsage += hsize; + dview->setValue (PROP_HMEM_USAGE, i, memoryUsage); + } + break; + case MMAP_TRACE: + case MUNMAP_TRACE: + // Adjust the size to be multiple of page_size + //hsize = (( hsize - 1 ) / page_size + 1 ) * page_size; + if (vaddr) + { + UnmapChunk *list; + if (mtype == MMAP_TRACE) + { + dview->setValue (PROP_TSTAMP2, i, (uint64_t) MAX_TIME); + dview->setValue (PROP_HLEAKED, i, hsize); + list = heapmap->mmap (vaddr, hsize, i); + + // Increase heap size + memoryUsage += hsize; + dview->setValue (PROP_HMEM_USAGE, i, memoryUsage); + } + else + { // MUNMAP_TRACE + list = heapmap->munmap (vaddr, hsize); + + // Set allocation size to zero + // Note: We're currently reusing PROP_HSIZE to mean allocation size + // If we ever need to save the original HSIZE, we'll need to + // create a new PROP_* to represent event allocation size + // + // For now, tuck the original size away as HOVADDR + dview->setValue (PROP_HOVADDR, i, (uint64_t) hsize); + dview->setValue (PROP_HSIZE, i, (uint64_t) 0); + } + Size total_freed = 0; + while (list) + { + long idx = list->val; + total_freed += list->size; + Size leaked = dview->getLongValue (PROP_HLEAKED, idx); + + // Decrease heap size + memoryUsage -= list->size; + dview->setValue (PROP_HMEM_USAGE, i, memoryUsage); + + Size leak_update = leaked - list->size; + // update allocation + dview->setValue (PROP_HLEAKED, idx, leak_update); + // update allocation's list of frees + { + UnmapChunk *copy = new UnmapChunk; + heapUnmapEvents->append (copy); + copy->val = dview->getIdByIdx (i); + copy->size = list->size; + copy->next = (UnmapChunk *) dview->getObjValue (PROP_VOIDP_OBJ, idx); + dview->setObjValue (PROP_VOIDP_OBJ, idx, copy); + } + if (leak_update <= 0) + if (leak_update == 0) + dview->setValue (PROP_TSTAMP2, idx, tstamp); + UnmapChunk *t = list; + list = list->next; + delete t; + } + // update this event + if (total_freed) + // only need to write value if it is non-zero + dview->setValue (PROP_HFREED, i, total_freed); + } + break; + // ignoring HEAPTYPE_LAST, which will never be recorded + case HEAPTYPE_LAST: + break; + } + } + delete heapmap; + delete dview; + + return dDscr; +} + +DataDescriptor * +Experiment::get_heapsz_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_HEAPSZ); + if (dDscr) + return dDscr; + dDscr = get_heap_events (); // derived from DATA_HEAP + if (dDscr == NULL) + return NULL; + dDscr = newDataDescriptor (DATA_HEAPSZ, 0, dDscr); + return dDscr; +} + +static void +update_heapsz_packet (std::set<long> &pkt_id_set, DataView *dview, + long alloc_pkt_id, int64_t net_alloc, uint64_t leaks) +{ + // pkt_id_set: set is updated to include packet + // alloc_pkt_id: data descriptor id (NOT dview idx) + // net_alloc: adjustment to net allocation for this packet (note: signed value) + // leaks: leak bytes to attribute to alloc_pkt_id + std::pair < std::set<long>::iterator, bool> ret; + ret = pkt_id_set.insert (alloc_pkt_id); // add to set + bool new_to_set = ret.second; // was not in set + if (!new_to_set) + { + // Has been seen before, update values + net_alloc += dview->getDataDescriptorValue (PROP_HCUR_NET_ALLOC, alloc_pkt_id); + if (leaks) + { + uint64_t old = dview->getDataDescriptorValue (PROP_HCUR_LEAKS, alloc_pkt_id); + if (old != 0) + leaks = old; + } + } + dview->setDataDescriptorValue (PROP_HCUR_NET_ALLOC, alloc_pkt_id, net_alloc); + dview->setDataDescriptorValue (PROP_HCUR_LEAKS, alloc_pkt_id, leaks); +} + +DataView * +Experiment::create_heapsz_data_view (DataView *heap_dview) +{ + // heap_dview has DATA_HEAP _filtered_ packets. + // This creates, populates, and returns DATA_HEAPSZ DataView + DataDescriptor *dDscr = get_heapsz_events (); + if (dDscr == NULL) + return NULL; + std::set<long> pkt_id_set; + DataView *dview = heap_dview; + long sz = dview->getSize (); + for (long i = 0; i < sz; i++) + { + int64_t hsize = (int64_t) dview->getULongValue (PROP_HSIZE, i); + uint64_t leaks = dview->getULongValue (PROP_HLEAKED, i); + long alloc_pkt_id = dview->getIdByIdx (i); + update_heapsz_packet (pkt_id_set, dview, alloc_pkt_id, hsize, leaks); + + // linked free + UnmapChunk *mmap_frees = (UnmapChunk *) dview->getObjValue (PROP_VOIDP_OBJ, i); // mmap metadata + if (mmap_frees) + { + // mmap: all frees associated with this packet + while (mmap_frees) + { + long free_pkt_id = mmap_frees->val; + int64_t free_sz = mmap_frees->size; + update_heapsz_packet (pkt_id_set, dview, free_pkt_id, -free_sz, 0); + mmap_frees = mmap_frees->next; + } + } + else + { + // malloc: check for associated free + long free_pkt_id = dview->getLongValue (PROP_DDSCR_LNK, i) - 1; + if (free_pkt_id >= 0) + update_heapsz_packet (pkt_id_set, dview, free_pkt_id, -hsize, 0); + } + } + + // create a new DataView based on the filtered-in and associated free events + std::set<long>::iterator it; + DataView *heapsz_dview = dDscr->createExtManagedView (); + for (it = pkt_id_set.begin (); it != pkt_id_set.end (); ++it) + { + long ddscr_pkt_id = *it; + heapsz_dview->appendDataDescriptorId (ddscr_pkt_id); + } + compute_heapsz_data_view (heapsz_dview); + return heapsz_dview; +} + +void +Experiment::compute_heapsz_data_view (DataView *heapsz_dview) +{ + DataView *dview = heapsz_dview; + + // Keep track of memory usage + int64_t currentAllocs = 0; + Size currentLeaks = 0; + dview->sort (PROP_TSTAMP); + long sz = dview->getSize (); + for (long i = 0; i < sz; i++) + { + int64_t net_alloc = dview->getLongValue (PROP_HCUR_NET_ALLOC, i); + currentAllocs += net_alloc; + dview->setValue (PROP_HCUR_ALLOCS, i, currentAllocs); + + Size leaks = dview->getULongValue (PROP_HCUR_LEAKS, i); + currentLeaks += leaks; + dview->setValue (PROP_HCUR_LEAKS, i, currentLeaks); + } +} + +void +Experiment::DBG_memuse (Sample * s) +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_HEAP); + if (dDscr == NULL || dDscr->getSize () == 0) + return; + + DataView *dview = dDscr->createView (); + dview->sort (PROP_TSTAMP); + hrtime_t ts1 = s->get_start_time (); + hrtime_t ts2 = s->get_end_time (); + + HeapMap *heapmap = new HeapMap (); + long sz = dview->getSize (); + Size maxSize = 0; + Size curSize = 0; + hrtime_t maxTime = 0; + for (long i = 0; i < sz; i++) + { + hrtime_t tstamp = dview->getLongValue (PROP_TSTAMP, i); + if (tstamp < ts1) + continue; + if (tstamp >= ts2) + break; + + Heap_type mtype = (Heap_type) dview->getIntValue (PROP_HTYPE, i); + Vaddr vaddr = dview->getULongValue (PROP_HVADDR, i); + Vaddr ovaddr = dview->getULongValue (PROP_HOVADDR, i); + switch (mtype) + { + case REALLOC_TRACE: + break; + case MALLOC_TRACE: + ovaddr = 0; + break; + case FREE_TRACE: + ovaddr = vaddr; + vaddr = 0; + break; + default: + vaddr = 0; + ovaddr = 0; + break; + } + if (ovaddr) + { + long idx = heapmap->deallocate (ovaddr) - 1; + if (idx >= 0) + curSize -= dview->getULongValue (PROP_HSIZE, idx); + } + if (vaddr) + { + heapmap->allocate (vaddr, i + 1); + curSize += dview->getULongValue (PROP_HSIZE, i); + if (curSize > maxSize) + { + maxSize = curSize; + maxTime = tstamp; + } + } + } + printf ("SAMPLE=%s (id=%d) MEMUSE=%lld TSTAMP=%lld\n", s->get_start_label (), + s->get_number (), maxSize, maxTime - getStartTime ()); + delete dview; + delete heapmap; +} + +void +Experiment::DBG_memuse (const char *sname) +{ + for (int i = 0; i < samples->size (); ++i) + { + Sample *sample = samples->fetch (i); + if (streq (sname, sample->get_start_label ())) + { + DBG_memuse (sample); + break; + } + } +} + +DataDescriptor * +Experiment::get_race_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_RACE); + if (dDscr == NULL) + return NULL; + if (dDscr->getSize () == 0) + { + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading Race Data: %s"), base_name); + read_data_file (SP_RACETRACE_FILE, msg); + free (msg); + resolve_frame_info (dDscr); + } + return dDscr; +} + +DataDescriptor * +Experiment::get_deadlock_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_DLCK); + if (dDscr == NULL) + return NULL; + if (dDscr->getSize () == 0) + { + char *base_name = get_basename (expt_name); + char *msg = dbe_sprintf (GTXT ("Loading Deadlocks Data: %s"), base_name); + read_data_file (SP_DEADLOCK_FILE, msg); + free (msg); + resolve_frame_info (dDscr); + } + return dDscr; +} + +DataDescriptor * +Experiment::get_sample_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_SAMPLE); + if (dDscr == NULL) + return NULL; + if (dDscr->getSize () > 0) + return dDscr; + + // read_overview_file(); //YXXX do this here at some point instead of: + PropDescr *tmp_propDscr; + tmp_propDscr = new PropDescr (PROP_SMPLOBJ, NTXT ("SMPLOBJ")); + tmp_propDscr->uname = NULL; + tmp_propDscr->vtype = TYPE_OBJ; + dDscr->addProperty (tmp_propDscr); + + tmp_propDscr = new PropDescr (PROP_TSTAMP, NTXT ("TSTAMP")); + tmp_propDscr->uname = dbe_strdup ("High resolution timestamp"); + tmp_propDscr->vtype = TYPE_UINT64; + dDscr->addProperty (tmp_propDscr); + + tmp_propDscr = new PropDescr (PROP_SAMPLE, NTXT ("SAMPLE")); + tmp_propDscr->uname = dbe_strdup ("Sample number"); + tmp_propDscr->vtype = TYPE_UINT64; + dDscr->addProperty (tmp_propDscr); + + tmp_propDscr = new PropDescr (PROP_EVT_TIME, NTXT ("EVT_TIME")); + tmp_propDscr->uname = dbe_strdup ("Event duration"); + tmp_propDscr->vtype = TYPE_UINT64; + dDscr->addProperty (tmp_propDscr); + + long ssize = samples->size (); + for (long ii = 0; ii < ssize; ii++) + { + Sample * sample = samples->fetch (ii); + long recn = dDscr->addRecord (); + hrtime_t sduration = sample->get_end_time () - sample->get_start_time (); + dDscr->setObjValue (PROP_SMPLOBJ, recn, sample); + dDscr->setValue (PROP_SAMPLE, recn, sample->get_number ()); + dDscr->setValue (PROP_TSTAMP, recn, sample->get_end_time ()); + dDscr->setValue (PROP_EVT_TIME, recn, sduration); + } + return dDscr; +} + +DataDescriptor * +Experiment::get_gc_events () +{ + DataDescriptor *dDscr = getDataDescriptor (DATA_GCEVENT); + if (dDscr == NULL) + return NULL; + if (dDscr->getSize () > 0) + return dDscr; + + // read_overview_file(); //YXXX do this here at some point instead of: + PropDescr *tmp_propDscr; + tmp_propDscr = new PropDescr (PROP_GCEVENTOBJ, NTXT ("GCEVENTOBJ")); + tmp_propDscr->uname = NULL; + tmp_propDscr->vtype = TYPE_OBJ; + dDscr->addProperty (tmp_propDscr); + + tmp_propDscr = new PropDescr (PROP_TSTAMP, NTXT ("TSTAMP")); + tmp_propDscr->uname = dbe_strdup ("High resolution timestamp"); + tmp_propDscr->vtype = TYPE_UINT64; + dDscr->addProperty (tmp_propDscr); + + tmp_propDscr = new PropDescr (PROP_GCEVENT, NTXT ("GCEVENT")); + tmp_propDscr->uname = dbe_strdup ("GCEvent number"); + tmp_propDscr->vtype = TYPE_UINT64; + dDscr->addProperty (tmp_propDscr); + + tmp_propDscr = new PropDescr (PROP_EVT_TIME, NTXT ("EVT_TIME")); + tmp_propDscr->uname = dbe_strdup ("Event duration"); + tmp_propDscr->vtype = TYPE_UINT64; + dDscr->addProperty (tmp_propDscr); + + long ssize = gcevents->size (); + for (long ii = 0; ii < ssize; ii++) + { + GCEvent * gcevent = gcevents->fetch (ii); + long recn = dDscr->addRecord (); + hrtime_t sduration = gcevent->end - gcevent->start; + dDscr->setObjValue (PROP_GCEVENTOBJ, recn, gcevent); + dDscr->setValue (PROP_GCEVENT, recn, gcevent->id); + dDscr->setValue (PROP_TSTAMP, recn, gcevent->end); + dDscr->setValue (PROP_EVT_TIME, recn, sduration); + } + return dDscr; +} + +void +Experiment::update_last_event (hrtime_t ts/*wall_ts*/) +{ + if (last_event == ZERO_TIME) + { + // not yet initialized + last_event = ts; + } + if (last_event - exp_start_time < ts - exp_start_time) + // compare deltas to avoid hrtime_t wrap + last_event = ts; +} + +void +Experiment::write_header () +{ + StringBuilder sb; + + // write commentary to the experiment, describing the parameters + if (dbeSession->ipc_mode || dbeSession->rdt_mode) + { + // In GUI: print start time at the beginning + char *start_time = ctime (&start_sec); + if (start_time != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT ("Experiment started %s"), start_time); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + } + // write message with target arglist + if (uarglist != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT ("\nTarget command (%s): '%s'"), + (wsize == W32 ? "32-bit" : "64-bit"), uarglist); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + + sb.setLength (0); + sb.sprintf (GTXT ("Process pid %d, ppid %d, pgrp %d, sid %d"), + pid, ppid, pgrp, sid); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + + // add comment for user name, if set + if (username != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT ("User: `%s'"), username); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + + // add comment for current working directory + if (ucwd != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT ("Current working directory: %s"), ucwd); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + + // add comment for collector version string + if (cversion != NULL) + { + char *wstring; + switch (wsize) + { + case Wnone: + wstring = NTXT ("?"); + break; + case W32: + wstring = GTXT ("32-bit"); + break; + case W64: + wstring = GTXT ("64-bit"); + break; + default: + wstring = NTXT ("??"); + break; + } + sb.setLength (0); + sb.sprintf (GTXT ("Collector version: `%s'; experiment version %d.%d (%s)"), + cversion, exp_maj_version, exp_min_version, wstring); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + + // add comment for driver version string (er_kernel) + if (dversion != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT ("Kernel driver version: `%s'"), dversion); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + + if (jversion != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT ("JVM version: `%s'"), jversion); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + + // add comment for hostname, parameters + if (hostname == NULL) + hostname = dbe_strdup (GTXT ("unknown")); + if (os_version == NULL) + os_version = dbe_strdup (GTXT ("unknown")); + if (architecture == NULL) + architecture = dbe_strdup (GTXT ("unknown")); + sb.setLength (0); + sb.sprintf (GTXT ("Host `%s', OS `%s', page size %d, architecture `%s'"), + hostname, os_version, page_size, architecture); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + + sb.setLength (0); + if (maxclock != minclock) + { + clock = maxclock; + sb.sprintf ( + GTXT (" %d CPUs, with clocks ranging from %d to %d MHz.; max of %d MHz. assumed"), + ncpus, minclock, maxclock, clock); + } + else + sb.sprintf (GTXT (" %d CPU%s, clock speed %d MHz."), + ncpus, (ncpus == 1 ? NTXT ("") : "s"), clock); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + + // add comment for machine memory size + if (page_size > 0 && npages > 0) + { + long long memsize = ((long long) npages * page_size) / (1024 * 1024); + sb.setLength (0); + sb.sprintf (GTXT (" Memory: %d pages @ %d = %lld MB."), + npages, page_size, memsize); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + + // add comment for machine memory size + if (machinemodel != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT (" Machine model: %s"), machinemodel); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + + // add comment for start time + char *p = ctime (&start_sec); + sb.setLength (0); + if (p != NULL) + sb.sprintf (GTXT ("Experiment started %s"), p); + else + sb.sprintf (GTXT ("\nExperiment start not recorded")); + write_coll_params (); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + commentq->appendqueue (runlogq); + runlogq->mark_clear (); +} + +void +Experiment::write_coll_params () +{ + StringBuilder sb; + + // now write the various collection parameters as comments + sb.setLength (0); + sb.append (GTXT ("Data collection parameters:")); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + if (coll_params.profile_mode == 1) + { + sb.setLength (0); + sb.sprintf (GTXT (" Clock-profiling, interval = %d microsecs."), + (int) (coll_params.ptimer_usec)); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.sync_mode == 1) + { + sb.setLength (0); + char *scope_str = NTXT (""); + switch (coll_params.sync_scope) + { + case 0: + scope_str = GTXT ("Native- and Java-APIs"); + break; + case SYNCSCOPE_JAVA: + scope_str = GTXT ("JAVA-APIs"); + break; + case SYNCSCOPE_NATIVE: + scope_str = GTXT ("Native-APIs"); + break; + case SYNCSCOPE_JAVA | SYNCSCOPE_NATIVE: + scope_str = GTXT ("Native- and Java-APIs"); + break; + } + if (coll_params.sync_threshold < 0) + sb.sprintf (GTXT (" Synchronization tracing, threshold = %d microsecs. (calibrated); %s"), + -coll_params.sync_threshold, scope_str); + else + sb.sprintf (GTXT (" Synchronization tracing, threshold = %d microsecs.; %s"), + coll_params.sync_threshold, scope_str); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.heap_mode == 1) + { + sb.setLength (0); + sb.append (GTXT (" Heap tracing")); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.io_mode == 1) + { + sb.setLength (0); + sb.append (GTXT (" IO tracing")); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.race_mode == 1) + { + sb.setLength (0); + char *race_stack_name; + switch (coll_params.race_stack) + { + case 0: + race_stack_name = GTXT ("dual-stack"); + break; + case 1: + race_stack_name = GTXT ("single-stack"); + break; + case 2: + race_stack_name = GTXT ("leaf"); + break; + default: + abort (); + } + sb.sprintf (GTXT (" Datarace detection, %s"), race_stack_name); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.deadlock_mode == 1) + { + sb.setLength (0); + sb.append (GTXT (" Deadlock detection")); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.hw_mode == 1) + { + sb.setLength (0); + if (hwc_default == true) + sb.append (GTXT (" HW counter-profiling (default); counters:")); + else + sb.append (GTXT (" HW counter-profiling; counters:")); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + for (int i = 0; i < MAX_HWCOUNT; i++) + { + if (!coll_params.hw_aux_name[i]) + continue; + sb.setLength (0); + sb.sprintf (GTXT (" %s, tag %d, interval %d, memop %d"), + coll_params.hw_aux_name[i], i, + coll_params.hw_interval[i], coll_params.hw_tpc[i]); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + } + if (coll_params.sample_periodic == 1) + { + sb.setLength (0); + sb.sprintf (GTXT (" Periodic sampling, %d secs."), + coll_params.sample_timer); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.limit != 0) + { + sb.setLength (0); + sb.sprintf (GTXT (" Experiment size limit, %d"), + coll_params.limit); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.linetrace != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT (" Follow descendant processes from: %s"), + coll_params.linetrace); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.pause_sig != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT (" Pause signal %s"), coll_params.pause_sig); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.sample_sig != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT (" Sample signal %s"), coll_params.sample_sig); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.start_delay != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT (" Data collection delay start %s seconds"), coll_params.start_delay); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + if (coll_params.terminate != NULL) + { + sb.setLength (0); + sb.sprintf (GTXT (" Data collection termination after %s seconds"), coll_params.terminate); + commentq->append (new Emsg (CMSG_COMMENT, sb)); + } + // add a blank line after data description + commentq->append (new Emsg (CMSG_COMMENT, NTXT (""))); +} + + +/* + * Raw packet processing + */ +static int +check_mstate (char *ptr, PacketDescriptor *pDscr, int arg) +{ + switch (arg) + { + case PROP_UCPU: + case PROP_SCPU: + case PROP_TRAP: + case PROP_TFLT: + case PROP_DFLT: + case PROP_KFLT: + case PROP_ULCK: + case PROP_TSLP: + case PROP_WCPU: + case PROP_TSTP: + break; + default: + return 0; + } + Vector<FieldDescr*> *fields = pDscr->getFields (); + for (int i = 0, sz = fields->size (); i < sz; i++) + { + FieldDescr *fDscr = fields->fetch (i); + if (fDscr->propID == arg) + return *((int*) (ptr + fDscr->offset)); + } + return 0; +} + +#define PACKET_ALIGNMENT 4 + +uint64_t +Experiment::readPacket (Data_window *dwin, Data_window::Span *span) +{ + Common_packet *rcp = (Common_packet *) dwin->bind (span, + sizeof (CommonHead_packet)); + uint16_t v16; + uint64_t size = 0; + if (rcp) + { + if ((((long) rcp) % PACKET_ALIGNMENT) != 0) + { + invalid_packet++; + size = PROFILE_BUFFER_CHUNK - span->offset % PROFILE_BUFFER_CHUNK; + return size; + } + v16 = (uint16_t) rcp->tsize; + size = dwin->decode (v16); + if (size == 0) + { + size = PROFILE_BUFFER_CHUNK - span->offset % PROFILE_BUFFER_CHUNK; + return size; + } + rcp = (Common_packet *) dwin->bind (span, size); + } + if (rcp == NULL) + return 0; + + if ((((long) rcp) % PACKET_ALIGNMENT) != 0) + { + invalid_packet++; + size = PROFILE_BUFFER_CHUNK - span->offset % PROFILE_BUFFER_CHUNK; + return size; + } + v16 = (uint16_t) rcp->type; + uint32_t rcptype = dwin->decode (v16); + if (rcptype == EMPTY_PCKT) + return size; + if (rcptype == FRAME_PCKT) + { + RawFramePacket *fp = new RawFramePacket; + fp->uid = dwin->decode (((Frame_packet*) rcp)->uid); + fp->uidn = NULL; + fp->uidj = NULL; + fp->omp_uid = NULL; + fp->omp_state = 0; + char *ptr = (char*) rcp + dwin->decode (((Frame_packet*) rcp)->hsize); + if ((((long) ptr) % PACKET_ALIGNMENT) != 0) + { + invalid_packet++; + delete fp; + return size; + } + v16 = (uint16_t) ((Frame_packet*) rcp)->tsize; + char *end = (char*) rcp + dwin->decode (v16); + for (; ptr < end;) + { + Common_info *cinfo = (Common_info*) ptr; + uint32_t hsize = dwin->decode (cinfo->hsize); + if (hsize == 0 || ptr + hsize > end) + break; + int kind = dwin->decode (cinfo->kind); + bool compressed = false; + if (kind & COMPRESSED_INFO) + { + compressed = true; + kind &= ~COMPRESSED_INFO; + } + switch (kind) + { + case STACK_INFO: + { + char *stack = ptr + sizeof (Stack_info); + size_t stack_size = hsize - sizeof (Stack_info); + uint64_t uidn = dwin->decode (((Stack_info*) cinfo)->uid); + if (stack_size <= 0) + { + fp->uidn = get_uid_node (uidn); + break; + } + uint64_t link_uid = (uint64_t) 0; + if (compressed) + { + stack_size -= sizeof (uint64_t); + unsigned char *s = (unsigned char*) (stack + stack_size); + int shift = 0; + for (size_t i = 0; i<sizeof (link_uid); i++) + { + link_uid |= (uint64_t) * s++ << shift; + shift += 8; + } + } + if (wsize == W32) + fp->uidn = add_uid (dwin, uidn, + (int) (stack_size / sizeof (uint32_t)), + (uint32_t*) stack, link_uid); + else + fp->uidn = add_uid (dwin, uidn, + (int) (stack_size / sizeof (uint64_t)), + (uint64_t*) stack, link_uid); + break; + } + case JAVA_INFO: + { + char *stack = ptr + sizeof (Java_info); + size_t stack_size = hsize - sizeof (Java_info); + uint64_t uidj = dwin->decode (((Java_info*) cinfo)->uid); + if (stack_size <= 0) + { + fp->uidj = get_uid_node (uidj); + break; + } + + uint64_t link_uid = (uint64_t) 0; + if (compressed) + { + stack_size -= sizeof (uint64_t); + unsigned char *s = (unsigned char*) (stack + stack_size); + int shift = 0; + for (size_t i = 0; i<sizeof (link_uid); i++) + { + link_uid |= (uint64_t) * s++ << shift; + shift += 8; + } + } + if (wsize == W32) + fp->uidj = add_uid (dwin, uidj, + (int) (stack_size / sizeof (uint32_t)), + (uint32_t*) stack, link_uid); + else + { + // bug 6909545: garbage in 64-bit JAVA_INFO + char *nstack = (char*) malloc (stack_size); + char *dst = nstack; + char *srcmax = stack + stack_size - sizeof (uint64_t); + for (char *src = stack; src <= srcmax;) + { + int64_t val = dwin->decode (*(int32_t*) src); + *(uint64_t*) dst = dwin->decode (val); + src += sizeof (uint64_t); + dst += sizeof (uint64_t); + if (src > srcmax) + { + fprintf (stderr, "er_print: Experiment::readPacket: Error in data: src=%llx greater than %llx\n", + (long long) src, (long long) srcmax); + break; + } + *(uint64_t*) dst = *(uint64_t*) src; + src += sizeof (uint64_t); + dst += sizeof (uint64_t); + } + fp->uidj = add_uid (dwin, uidj, + (int) (stack_size / sizeof (uint64_t)), + (uint64_t*) nstack, link_uid); + free (nstack); + } + break; + } + case OMP_INFO: + fp->omp_state = dwin->decode (((OMP_info*) ptr)->omp_state); + break; + case OMP2_INFO: + { + uint64_t omp_uid = dwin->decode (((OMP2_info*) ptr)->uid); + fp->omp_uid = get_uid_node (omp_uid); + fp->omp_state = dwin->decode (((OMP2_info*) ptr)->omp_state); + break; + } + default: + break; + } + ptr += hsize; + } + frmpckts->append (fp); + return size; + } + else if (rcptype == UID_PCKT) + { + Uid_packet *uidp = (Uid_packet*) rcp; + uint64_t uid = dwin->decode (uidp->uid); + char *arr_bytes = (char*) (uidp + 1); + v16 = (uint16_t) rcp->tsize; + size_t arr_length = dwin->decode (v16) - sizeof (Uid_packet); + if (arr_length <= 0) + return size; + uint64_t link_uid = (uint64_t) 0; + if (dwin->decode (uidp->flags) & COMPRESSED_INFO) + { + arr_length -= sizeof (uint64_t); + unsigned char *s = (unsigned char*) (arr_bytes + arr_length); + int shift = 0; + for (size_t i = 0; i<sizeof (link_uid); i++) + { + link_uid |= (uint64_t) * s++ << shift; + shift += 8; + } + } + if (wsize == W32) + add_uid (dwin, uid, (int) (arr_length / sizeof (uint32_t)), + (uint32_t*) arr_bytes, link_uid); + else + add_uid (dwin, uid, (int) (arr_length / sizeof (uint64_t)), + (uint64_t*) arr_bytes, link_uid); + return size; + } + + PacketDescriptor *pcktDescr = getPacketDescriptor (rcptype); + if (pcktDescr == NULL) + return size; + DataDescriptor *dataDescr = pcktDescr->getDataDescriptor (); + if (dataDescr == NULL) + return size; + + /* omazur: TBR START -- old experiment */ + if (rcptype == PROF_PCKT) + { + // For backward compatibility with older SS12 experiments + int numstates = get_params ()->lms_magic_id; // ugly, for old experiments + if (numstates > LMS_NUM_SOLARIS_MSTATES) + numstates = LMS_NUM_SOLARIS_MSTATES; + for (int i = 0; i < numstates; i++) + if (check_mstate ((char*) rcp, pcktDescr, PROP_UCPU + i)) + readPacket (dwin, (char*) rcp, pcktDescr, dataDescr, PROP_UCPU + i, + size); + } + else + readPacket (dwin, (char*) rcp, pcktDescr, dataDescr, 0, size); + return size; +} + +void +Experiment::readPacket (Data_window *dwin, char *ptr, PacketDescriptor *pDscr, + DataDescriptor *dDscr, int arg, uint64_t pktsz) +{ + union Value + { + uint32_t val32; + uint64_t val64; + } *v; + + long recn = dDscr->addRecord (); + Vector<FieldDescr*> *fields = pDscr->getFields (); + int sz = fields->size (); + for (int i = 0; i < sz; i++) + { + FieldDescr *field = fields->fetch (i); + v = (Value*) (ptr + field->offset); + if (field->propID == arg) + { + dDscr->setValue (PROP_NTICK, recn, dwin->decode (v->val32)); + dDscr->setValue (PROP_MSTATE, recn, (uint32_t) (field->propID - PROP_UCPU)); + } + if (field->propID == PROP_THRID || field->propID == PROP_LWPID + || field->propID == PROP_CPUID) + { + uint64_t tmp64 = 0; + switch (field->vtype) + { + case TYPE_INT32: + case TYPE_UINT32: + tmp64 = dwin->decode (v->val32); + break; + case TYPE_INT64: + case TYPE_UINT64: + tmp64 = dwin->decode (v->val64); + break; + case TYPE_STRING: + case TYPE_DOUBLE: + case TYPE_OBJ: + case TYPE_DATE: + case TYPE_BOOL: + case TYPE_ENUM: + case TYPE_LAST: + case TYPE_NONE: + break; + } + uint32_t tag = mapTagValue ((Prop_type) field->propID, tmp64); + dDscr->setValue (field->propID, recn, tag); + } + else + { + switch (field->vtype) + { + case TYPE_INT32: + case TYPE_UINT32: + dDscr->setValue (field->propID, recn, dwin->decode (v->val32)); + break; + case TYPE_INT64: + case TYPE_UINT64: + dDscr->setValue (field->propID, recn, dwin->decode (v->val64)); + break; + case TYPE_STRING: + { + int len = (int) (pktsz - field->offset); + if ((len > 0) && (ptr[field->offset] != 0)) + { + StringBuilder *sb = new StringBuilder (); + sb->append (ptr + field->offset, 0, len); + dDscr->setObjValue (field->propID, recn, sb); + } + break; + } + // ignoring the following cases (why?) + case TYPE_DOUBLE: + case TYPE_OBJ: + case TYPE_DATE: + case TYPE_BOOL: + case TYPE_ENUM: + case TYPE_LAST: + case TYPE_NONE: + break; + } + } + } +} + +#define PROG_BYTE 102400 // update progress bar every PROG_BYTE bytes + +void +Experiment::read_data_file (const char *fname, const char *msg) +{ + Data_window::Span span; + off64_t total_len, remain_len; + char *progress_bar_msg; + int progress_bar_percent = -1; + + char *data_file_name = dbe_sprintf (NTXT ("%s/%s"), expt_name, fname); + Data_window *dwin = new Data_window (data_file_name); + // Here we can call stat(data_file_name) to get file size, + // and call a function to reallocate vectors for clock profiling data + free (data_file_name); + if (dwin->not_opened ()) + { + delete dwin; + return; + } + dwin->need_swap_endian = need_swap_endian; + + span.offset = 0; + span.length = dwin->get_fsize (); + total_len = remain_len = span.length; + progress_bar_msg = dbe_sprintf (NTXT ("%s %s"), NTXT (" "), msg); + invalid_packet = 0; + for (;;) + { + uint64_t pcktsz = readPacket (dwin, &span); + if (pcktsz == 0) + break; + // Update progress bar + if ((span.length <= remain_len) && (remain_len > 0)) + { + int percent = (int) (100 * (total_len - remain_len) / total_len); + if (percent > progress_bar_percent) + { + progress_bar_percent += 10; + theApplication->set_progress (percent, progress_bar_msg); + } + remain_len -= PROG_BYTE; + } + span.length -= pcktsz; + span.offset += pcktsz; + } + delete dwin; + + if (invalid_packet) + { + StringBuilder sb; + sb.sprintf (GTXT ("WARNING: There are %d invalid packet(s) in the %s file"), + invalid_packet, fname); + Emsg *m = new Emsg (CMSG_WARN, sb); + warnq->append (m); + } + + theApplication->set_progress (0, NTXT ("")); + free (progress_bar_msg); +} + +int +Experiment::read_overview_file () +{ + char *data_file_name = dbe_sprintf ("%s/%s", expt_name, SP_OVERVIEW_FILE); + Data_window *dwin = new Data_window (data_file_name); + free (data_file_name); + if (dwin->not_opened ()) + { + delete dwin; + return 0; + } + dwin->need_swap_endian = need_swap_endian; + newDataDescriptor (DATA_SAMPLE); + + Data_window::Span span; + span.offset = 0; + span.length = dwin->get_fsize (); + + PrUsage *data = NULL, *data_prev = NULL; + Sample *sample; + int sample_number = 1; + + int64_t prDataSize; + if (wsize == W32) + prDataSize = PrUsage::bind32Size (); + else + prDataSize = PrUsage::bind64Size (); + + while (span.length > 0) + { + data_prev = data; + data = new PrUsage (); + + void *dw = dwin->bind (&span, prDataSize); + if ((dw == NULL) || (prDataSize > span.length)) + { + Emsg *m = new Emsg (CMSG_ERROR, GTXT ("Warning: overview data file can't be read")); + warnq->append (m); + status = FAILURE; + delete dwin; + return status; + } + + if (wsize == W32) + data->bind32 (dw, need_swap_endian); + else + data->bind64 (dw, need_swap_endian); + span.length -= prDataSize; + span.offset += prDataSize; + + // Skip the first packet + if (data_prev == NULL) + continue; + if (sample_number > samples->size ()) + { // inconsistent log/overview + sample = new Sample (sample_number); + char * label = GTXT ("<unknown>"); + sample->start_label = dbe_strdup (label); + sample->end_label = dbe_strdup (label); + samples->append (sample); + } + else + sample = samples->fetch (sample_number - 1); + sample_number++; + sample->start_time = data_prev->pr_tstamp + 1; + sample->end_time = data->pr_tstamp; + sample->prusage = data_prev; + + data_prev->pr_rtime = data->pr_rtime - data_prev->pr_rtime; + data_prev->pr_utime = data->pr_utime - data_prev->pr_utime; + data_prev->pr_stime = data->pr_stime - data_prev->pr_stime; + data_prev->pr_ttime = data->pr_ttime - data_prev->pr_ttime; + data_prev->pr_tftime = data->pr_tftime - data_prev->pr_tftime; + data_prev->pr_dftime = data->pr_dftime - data_prev->pr_dftime; + data_prev->pr_kftime = data->pr_kftime - data_prev->pr_kftime; + data_prev->pr_ltime = data->pr_ltime - data_prev->pr_ltime; + data_prev->pr_slptime = data->pr_slptime - data_prev->pr_slptime; + data_prev->pr_wtime = data->pr_wtime - data_prev->pr_wtime; + data_prev->pr_stoptime = data->pr_stoptime - data_prev->pr_stoptime; + data_prev->pr_minf = data->pr_minf - data_prev->pr_minf; + data_prev->pr_majf = data->pr_majf - data_prev->pr_majf; + data_prev->pr_nswap = data->pr_nswap - data_prev->pr_nswap; + data_prev->pr_inblk = data->pr_inblk - data_prev->pr_inblk; + data_prev->pr_oublk = data->pr_oublk - data_prev->pr_oublk; + data_prev->pr_msnd = data->pr_msnd - data_prev->pr_msnd; + data_prev->pr_mrcv = data->pr_mrcv - data_prev->pr_mrcv; + data_prev->pr_sigs = data->pr_sigs - data_prev->pr_sigs; + data_prev->pr_vctx = data->pr_vctx - data_prev->pr_vctx; + data_prev->pr_ictx = data->pr_ictx - data_prev->pr_ictx; + data_prev->pr_sysc = data->pr_sysc - data_prev->pr_sysc; + data_prev->pr_ioch = data->pr_ioch - data_prev->pr_ioch; + sample->get_usage (); // force validation + } + + for (long smpNum = samples->size (); smpNum >= sample_number; smpNum--) + { + // overview file was truncated + sample = samples->remove (smpNum - 1); + delete sample; + } + + if (data) + { + // Update last_event so that getEndTime() covers + // all loadobjects, too. + update_last_event (data->pr_tstamp); + delete data; + } + delete dwin; + return SUCCESS; +} + +int +Experiment::uidNodeCmp (const void *a, const void *b) +{ + UIDnode *nd1 = *(UIDnode**) a; + UIDnode *nd2 = *(UIDnode**) b; + if (nd1->uid == nd2->uid) + return 0; + return nd1->uid < nd2->uid ? -1 : 1; +} + +static uint64_t +funcAddr (uint32_t val) +{ + if (val == (uint32_t) SP_LEAF_CHECK_MARKER) + return (uint64_t) SP_LEAF_CHECK_MARKER; + if (val == (uint32_t) SP_TRUNC_STACK_MARKER) + return (uint64_t) SP_TRUNC_STACK_MARKER; + if (val == (uint32_t) SP_FAILED_UNWIND_MARKER) + return (uint64_t) SP_FAILED_UNWIND_MARKER; + return val; +} + +Experiment::UIDnode * +Experiment::add_uid (Data_window *dwin, uint64_t uid, int size, + uint32_t *array, uint64_t link_uid) +{ + if (uid == (uint64_t) 0) + return NULL; + uint64_t val = funcAddr (dwin->decode (array[0])); + UIDnode *node = NULL; + UIDnode *res = get_uid_node (uid, val); + UIDnode *next = res; + for (int i = 0; i < size; i++) + { + val = funcAddr (dwin->decode (array[i])); + if (next == NULL) + { + next = get_uid_node ((uint64_t) 0, val); + if (node != NULL) + node->next = next; + } + node = next; + next = node->next; + if (node->val == 0) + node->val = val; + else if (node->val != val) // Algorithmic error (should never happen) + node->val = (uint64_t) SP_LEAF_CHECK_MARKER; + } + if (next == NULL && link_uid != (uint64_t) 0 && node != NULL) + node->next = get_uid_node (link_uid); + return res; +} + +Experiment::UIDnode * +Experiment::add_uid (Data_window *dwin, uint64_t uid, int size, uint64_t *array, uint64_t link_uid) +{ + if (uid == (uint64_t) 0) + return NULL; + UIDnode *node = NULL; + uint64_t val = dwin->decode (array[0]); + UIDnode *res = get_uid_node (uid, val); + UIDnode *next = res; + for (int i = 0; i < size; i++) + { + val = dwin->decode (array[i]); + if (next == NULL) + { + next = get_uid_node ((uint64_t) 0, val); + if (node != NULL) + node->next = next; + } + node = next; + next = node->next; + if (node->val == (uint64_t) 0) + node->val = val; + else if (node->val != val) // Algorithmic error (should never happen) + node->val = (uint64_t) - 1; + } + if (next == NULL && link_uid != (uint64_t) 0 && node != NULL) + node->next = get_uid_node (link_uid); + return res; +} + +Experiment::UIDnode * +Experiment::new_uid_node (uint64_t uid, uint64_t val) +{ +#define NCHUNKSTEP 1024 + if (nnodes >= nchunks * CHUNKSZ) + { + // Reallocate Node chunk array + UIDnode** old_chunks = chunks; + chunks = new UIDnode*[nchunks + NCHUNKSTEP]; + memcpy (chunks, old_chunks, nchunks * sizeof (UIDnode*)); + nchunks += NCHUNKSTEP; + delete[] old_chunks; + // Clean future pointers + memset (&chunks[nchunks - NCHUNKSTEP], 0, NCHUNKSTEP * sizeof (UIDnode*)); + } + + if (NULL == chunks[nnodes / CHUNKSZ]) // Allocate new chunk for nodes. + chunks[nnodes / CHUNKSZ] = new UIDnode[CHUNKSZ]; + UIDnode *node = &chunks[nnodes / CHUNKSZ][nnodes % CHUNKSZ]; + node->uid = uid; + node->val = val; + node->next = NULL; + nnodes++; + return node; +} + +Experiment::UIDnode * +Experiment::get_uid_node (uint64_t uid, uint64_t val) +{ + int hash = (((int) uid) >> 4) & (HTableSize - 1); + if (uid != (uint64_t) 0) + { + UIDnode *node = uidHTable[hash]; + if (node && node->uid == uid) + return node; + } + UIDnode *node = new_uid_node (uid, val); + if (uid != (uint64_t) 0) + { + uidHTable[hash] = node; + uidnodes->append (node); + } + return node; +} + +Experiment::UIDnode * +Experiment::get_uid_node (uint64_t uid) +{ + if (uid == (uint64_t) 0) + return NULL; + int hash = (((int) uid) >> 4) & (HTableSize - 1); + UIDnode *node = uidHTable[hash]; + if (node && node->uid == uid) + return node; + node = new_uid_node (uid, (uint64_t) 0); + node->next = node; + return node; +} + +Experiment::UIDnode * +Experiment::find_uid_node (uint64_t uid) +{ + int hash = (((int) uid) >> 4) & (HTableSize - 1); + UIDnode *node = uidHTable[hash]; + if (node && node->uid == uid) + return node; + int lt = 0; + int rt = uidnodes->size () - 1; + while (lt <= rt) + { + int md = (lt + rt) / 2; + node = uidnodes->fetch (md); + if (node->uid < uid) + lt = md + 1; + else if (node->uid > uid) + rt = md - 1; + else + { + uidHTable[hash] = node; + return node; + } + } + return NULL; +} + +int +Experiment::frUidCmp (const void *a, const void *b) +{ + RawFramePacket *fp1 = *(RawFramePacket**) a; + RawFramePacket *fp2 = *(RawFramePacket**) b; + if (fp1->uid == fp2->uid) + return 0; + return fp1->uid < fp2->uid ? -1 : 1; +} + +Experiment::RawFramePacket * +Experiment::find_frame_packet (uint64_t uid) +{ + int lt = 0; + int rt = frmpckts->size () - 1; + while (lt <= rt) + { + int md = (lt + rt) / 2; + RawFramePacket *fp = frmpckts->fetch (md); + if (fp->uid < uid) + lt = md + 1; + else if (fp->uid > uid) + rt = md - 1; + else + return fp; + } + + return NULL; +} + +#define FRINFO_CACHEOPT_SIZE_LIMIT 4000000 +#define FRINFO_PIPELINE_SIZE_LIMIT 500000 +#define FRINFO_PIPELINE_NUM_STAGES 3 + +// Pipelined execution of resolve_frame_info() and add_stack(). +// Since this is the largest time consuming part of loading an experiment (especially +// so for large java experiments) - executing this part as a 3 stage pipeline can +// give significant performance gain - and this concept can be aggressively applied +// to enhance the gain further in future. The three stages are: +// Phase 1: resolve_frame_info() +// Phase 2: first part of add_stack() where the native stack is built +// Phase 3: second part og add_stack() where the java stack is built +// Phase 4: insert the native and java stacks into the stack map +// The main thread operates in the first Phase and the other stages are +// operated by a ssplib sequential queue - The threads working on the queues run concurrently +// with each other and with the main thread. But within a particular queue, jobs are +// executed sequentially + + +// This is the second phase of the pipeline of resolve_frame_info and add_stack +// It works on a chunk of iterations (size CSTCTX_CHUNK_SZ) and invokes add_stack() +// for each one of them + +void +Experiment::resolve_frame_info (DataDescriptor *dDscr) +{ + if (!resolveFrameInfo) + return; + if (NULL == cstack) + return; + dDscr->setResolveFrInfoDone (); + + // Check for TSTAMP + int propID = dbeSession->getPropIdByName (NTXT ("TSTAMP")); + Data *dataTStamp = dDscr->getData (propID); + if (dataTStamp == NULL) + return; + + propID = dbeSession->getPropIdByName (NTXT ("FRINFO")); + Data *dataFrinfo = dDscr->getData (propID); + + propID = dbeSession->getPropIdByName (NTXT ("THRID")); + Data *dataThrId = dDscr->getData (propID); + + // We can get frame info either by FRINFO or by [THRID,STKIDX] + if (dataFrinfo == NULL) + return; + + char *propName = NTXT ("MSTACK"); + propID = dbeSession->getPropIdByName (propName); + PropDescr *prMStack = new PropDescr (propID, propName); + prMStack->uname = dbe_strdup (GTXT ("Machine Call Stack")); + prMStack->vtype = TYPE_OBJ; + dDscr->addProperty (prMStack); + + propName = NTXT ("USTACK"); + propID = dbeSession->getPropIdByName (propName); + PropDescr *prUStack = new PropDescr (propID, propName); + prUStack->uname = dbe_strdup (GTXT ("User Call Stack")); + prUStack->vtype = TYPE_OBJ; + dDscr->addProperty (prUStack); + + propName = NTXT ("XSTACK"); + propID = dbeSession->getPropIdByName (propName); + PropDescr *prXStack = new PropDescr (propID, propName); + prXStack->uname = dbe_strdup (GTXT ("Expert Call Stack")); + prXStack->vtype = TYPE_OBJ; + dDscr->addProperty (prXStack); + + propName = NTXT ("HSTACK"); + propID = dbeSession->getPropIdByName (propName); + PropDescr *prHStack = new PropDescr (propID, propName); + prHStack->uname = dbe_strdup (GTXT ("ShowHide Call Stack")); + prHStack->vtype = TYPE_OBJ; + dDscr->addProperty (prHStack); + + if (has_java) + { + propName = NTXT ("JTHREAD"); + propID = dbeSession->getPropIdByName (propName); + PropDescr *prJThread = new PropDescr (propID, propName); + prJThread->uname = dbe_strdup (GTXT ("Java Thread")); + prJThread->vtype = TYPE_OBJ; + dDscr->addProperty (prJThread); + } + + if (ompavail) + { + PropDescr *prop = new PropDescr (PROP_OMPSTATE, NTXT ("OMPSTATE")); + prop->uname = dbe_strdup (GTXT ("OpenMP state")); + prop->vtype = TYPE_UINT32; + char * stateNames [OMP_LAST_STATE] = OMP_THR_STATE_STRINGS; + char * stateUNames[OMP_LAST_STATE] = OMP_THR_STATE_USTRINGS; + for (int ii = 0; ii < OMP_LAST_STATE; ii++) + prop->addState (ii, stateNames[ii], stateUNames[ii]); + dDscr->addProperty (prop); + + // add PROP_CPRID to profiling data (not same as omptrace's PROP_CPRID) + prop = dDscr->getProp (PROP_CPRID); + if (prop) + { + VType_type type = prop->vtype; + assert (type == TYPE_OBJ); //see 7040526 + } + prop = new PropDescr (PROP_CPRID, NTXT ("CPRID")); //profiling PROP_CPRID + prop->uname = dbe_strdup (GTXT ("OpenMP parallel region")); + prop->vtype = TYPE_OBJ; + dDscr->addProperty (prop); + + // add PROP_TSKID to profiling data (not same as omptrace's PROP_TSKID) + prop = dDscr->getProp (PROP_TSKID); + if (prop) + { + VType_type type = prop->vtype; + assert (type == TYPE_OBJ); //see 7040526 + } + prop = new PropDescr (PROP_TSKID, NTXT ("TSKID")); //profiling PROP_TSKID + prop->uname = dbe_strdup (GTXT ("OpenMP task")); + prop->vtype = TYPE_OBJ; + dDscr->addProperty (prop); + } + char *progress_bar_msg = dbe_sprintf (NTXT ("%s %s: %s"), NTXT (" "), + GTXT ("Processing CallStack Data"), + get_basename (expt_name)); + int progress_bar_percent = -1; + long deltaReport = 5000; + long nextReport = 0; + + long size = dDscr->getSize (); + // bool resolve_frinfo_pipelined = size > FRINFO_PIPELINE_SIZE_LIMIT && !ompavail; + bool resolve_frinfo_pipelined = false; + + Map<uint64_t, uint64_t> *nodeCache = NULL; + Map<uint64_t, uint64_t> *frameInfoCache = NULL; + if (size > FRINFO_CACHEOPT_SIZE_LIMIT && dversion == NULL) + { + frameInfoCache = new CacheMap<uint64_t, uint64_t>; + nodeCache = new CacheMap<uint64_t, uint64_t>; + } + + pushCnt = popCnt = pushCnt3 = popCnt3 = 0; + nPush = nPop = 0; + + FramePacket *fp = NULL; + // DbeThreadPool * threadPool = new DbeThreadPool(5); + fp = new FramePacket; + fp->stack = new Vector<Vaddr>; + fp->jstack = new Vector<Vaddr>; + fp->ompstack = new Vector<Vaddr>; + fp->omp_state = 0; + fp->mpi_state = 0; + + // piggyback on post-processing to calculate exp->last_event + const hrtime_t _exp_start_time = getStartTime (); // wall clock time + hrtime_t exp_duration = getLastEvent () == ZERO_TIME ? 0 + : getLastEvent () - _exp_start_time; // zero-based + + int missed_fi = 0; + int total_fi = 0; + + for (long i = 0; i < size; i++) + { + if (i == nextReport) + { + int percent = (int) (i * 100 / size); + if (percent > progress_bar_percent) + { + progress_bar_percent += 10; + theApplication->set_progress (percent, progress_bar_msg); + } + nextReport += deltaReport; + } + + uint32_t thrid = (uint32_t) dataThrId->fetchInt (i); + hrtime_t tstamp = (hrtime_t) dataTStamp->fetchLong (i); + + // piggyback on post-processing to calculate exp->last_event + { + hrtime_t relative_timestamp = tstamp - _exp_start_time; + if (exp_duration < relative_timestamp) + exp_duration = relative_timestamp; + } + uint64_t frinfo = (uint64_t) dataFrinfo->fetchLong (i); + + RawFramePacket *rfp = NULL; + if (frinfo) + { + // CacheMap does not work with NULL key + if (frameInfoCache != NULL) + rfp = (RawFramePacket *) frameInfoCache->get (frinfo); + } + if (rfp == 0) + { + rfp = find_frame_packet (frinfo); + if (rfp != 0) + { + if (frameInfoCache != NULL) + frameInfoCache->put (frinfo, (uint64_t) rfp); + } + else + missed_fi++; + total_fi++; + } + + // Process OpenMP properties + if (ompavail) + { + fp->omp_state = rfp ? rfp->omp_state : 0; + dDscr->setValue (PROP_OMPSTATE, i, fp->omp_state); + + fp->omp_cprid = mapPRid->get (thrid, tstamp, mapPRid->REL_EQLE); + void *omp_preg = mapPReg->get (thrid, tstamp, mapPReg->REL_EQLE); + if (!omp_preg) + { + char *idxname = NTXT ("OMP_preg"); + int idxtype = dbeSession->findIndexSpaceByName (idxname); + if (idxtype != -1) + { + Histable *preg0 = dbeSession->findObjectById (Histable::INDEXOBJ, idxtype, (int64_t) 0); + if (preg0) + { + Vector<Histable*> pregs; + pregs.append (preg0); + omp_preg = cstack->add_stack (&pregs); + mapPReg->put (thrid, tstamp, omp_preg); + } + } + } + dDscr->setObjValue (PROP_CPRID, i, omp_preg); //profiling PROP_CPRID + void *omp_task = mapTask->get (thrid, tstamp, mapTask->REL_EQLE); + if (!omp_task) + { + char *idxname = NTXT ("OMP_task"); + int idxtype = dbeSession->findIndexSpaceByName (idxname); + if (idxtype != -1) + { + Histable *task0 = dbeSession->findObjectById (Histable::INDEXOBJ, idxtype, (int64_t) 0); + if (task0) + { + Vector<Histable*> tasks; + tasks.append (task0); + omp_task = cstack->add_stack (&tasks); + mapTask->put (thrid, tstamp, omp_task); + } + } + } + dDscr->setObjValue (PROP_TSKID, i, omp_task); //profiling PROP_TSKID + } + else + { + fp->omp_state = 0; + fp->omp_cprid = 0; + } + + // Construct the native stack + fp->stack->reset (); + Vaddr leafpc = dDscr->getULongValue (PROP_LEAFPC, i); + if (leafpc) + fp->stack->append (leafpc); + UIDnode *node = rfp ? rfp->uidn : NULL; + while (node) + { + if (node->next == node) + // this node contains link_uid + node = find_uid_node (node->uid); + else + { + fp->stack->append (node->val); + node = node->next; + } + } + fp->truncated = 0; + int last = fp->stack->size () - 1; + if (last >= 0) + { + switch (fp->stack->fetch (last)) + { + case SP_TRUNC_STACK_MARKER: + fp->truncated = (Vaddr) SP_TRUNC_STACK_MARKER; + fp->stack->remove (last); + break; + case SP_FAILED_UNWIND_MARKER: + fp->truncated = (Vaddr) SP_FAILED_UNWIND_MARKER; + fp->stack->remove (last); + break; + } + } + + // Construct the Java stack + fp->jstack->reset (); + node = rfp ? rfp->uidj : NULL; + while (node) + { + if (node->next == node) + { + // this node contains link_uid + UIDnode *n = NULL; + if (node->uid) + { + // CacheMap does not work with NULL key + if (nodeCache != NULL) + n = (UIDnode *) nodeCache->get (node->uid); + } + if (n == NULL) + { + n = find_uid_node (node->uid); + if (n != NULL) + { + if (nodeCache != NULL) + nodeCache->put (node->uid, (uint64_t) n); + } + } + node = n; + } + else + { + fp->jstack->append (node->val); + node = node->next; + } + } + fp->jtruncated = false; + last = fp->jstack->size () - 1; + if (last >= 1 && fp->jstack->fetch (last) == SP_TRUNC_STACK_MARKER) + { + fp->jtruncated = true; + fp->jstack->remove (last); + fp->jstack->remove (last - 1); + } + + // Construct the OpenMP stack + if (ompavail) + { + fp->ompstack->reset (); + if (rfp && rfp->omp_uid) + { + if (leafpc) + fp->ompstack->append (leafpc); + node = rfp->omp_uid; + while (node) + { + if (node->next == node) + // this node contains link_uid + node = find_uid_node (node->uid); + else + { + fp->ompstack->append (node->val); + node = node->next; + } + } + fp->omptruncated = false; + last = fp->ompstack->size () - 1; + if (last >= 0) + { + switch (fp->ompstack->fetch (last)) + { + case SP_TRUNC_STACK_MARKER: + fp->omptruncated = (Vaddr) SP_TRUNC_STACK_MARKER; + fp->ompstack->remove (last); + break; + case SP_FAILED_UNWIND_MARKER: + fp->omptruncated = (Vaddr) SP_FAILED_UNWIND_MARKER; + fp->ompstack->remove (last); + break; + } + } + } + } + cstack->add_stack (dDscr, i, fp, NULL); + } + + // piggyback on post-processing to calculate exp->last_event + { + hrtime_t exp_end_time = _exp_start_time + exp_duration; + update_last_event (exp_end_time); + } + + if (missed_fi > 0) + { + StringBuilder sb; + sb.sprintf ( + GTXT ("*** Warning: %d frameinfo packets are missing from total of %d when resolving %s."), + missed_fi, total_fi, dDscr->getName ()); + warnq->append (new Emsg (CMSG_WARN, sb)); + } + + // threadPool->wait_group(); + // delete threadPool; + theApplication->set_progress (0, NTXT ("")); + free (progress_bar_msg); + if (!resolve_frinfo_pipelined && fp != NULL) + { + delete fp->ompstack; + delete fp->jstack; + delete fp->stack; + delete fp; + } + delete frameInfoCache; + frameInfoCache = NULL; + delete nodeCache; + nodeCache = NULL; +} + +void +Experiment::post_process () +{ + // update non_paused_time after final update to "last_event" + if (resume_ts != MAX_TIME && last_event) + { + hrtime_t ts = last_event - exp_start_time; + hrtime_t delta = ts - resume_ts; + non_paused_time += delta; + resume_ts = MAX_TIME; // collection is paused + } + + // GC: prune events outside of experiment duration, calculate GC duration, update indices + int index; + GCEvent * gcevent; + gc_duration = ZERO_TIME; + if (gcevents != NULL) + { + // delete events that finish before exp_start_time or start after last_event + for (int ii = 0; ii < gcevents->size ();) + { + gcevent = gcevents->fetch (ii); + if (gcevent->end - exp_start_time < 0 + || last_event - gcevent->start < 0) + delete gcevents->remove (ii); + else + ii++; + } + } + Vec_loop (GCEvent*, gcevents, index, gcevent) + { + gcevent->id = index + 1; // renumber to account for any deleted events + if (gcevent->start - exp_start_time < 0 || gcevent->start == ZERO_TIME) + // truncate events that start before experiment start + gcevent->start = exp_start_time; + if (last_event - gcevent->end < 0) + // truncate events that end after experiment end + gcevent->end = last_event; + gc_duration += gcevent->end - gcevent->start; + } +} + +Experiment::Exp_status +Experiment::find_expdir (char *path) +{ + // This function checks that the experiment directory + // is of the proper form, and accessible + struct stat64 sbuf; + + // Save the name + expt_name = dbe_strdup (path); + + // Check that the name ends in .er + size_t i = strlen (path); + if (i > 0 && path[i - 1] == '/') + path[--i] = '\0'; + + if (i < 4 || strcmp (&path[i - 3], NTXT (".er")) != 0) + { + Emsg *m = new Emsg (CMSG_FATAL, + GTXT ("*** Error: not a valid experiment name")); + errorq->append (m); + status = FAILURE; + return FAILURE; + } + + // Check if new directory structure (i.e., no pointer file) + if (dbe_stat (path, &sbuf)) + { + Emsg *m = new Emsg (CMSG_FATAL, GTXT ("*** Error: experiment not found")); + errorq->append (m); + status = FAILURE; + return FAILURE; + } + if (S_ISDIR (sbuf.st_mode) == 0) + { + // ignore pointer-file experiments + Emsg *m = new Emsg (CMSG_FATAL, + GTXT ("*** Error: experiment was recorded with an earlier version, and can not be read")); + errorq->append (m); + obsolete = 1; + status = FAILURE; + return FAILURE; + } + return SUCCESS; +} + +void +Experiment::purge () +{ + // This routine will purge all of the caches of releasable storage. + for (int i = 0; i < dataDscrs->size (); ++i) + { + DataDescriptor *dataDscr = dataDscrs->fetch (i); + if (dataDscr == NULL) + continue; + dataDscr->reset (); + } + delete cstack; + delete cstackShowHide; + cstack = CallStack::getInstance (this); + cstackShowHide = CallStack::getInstance (this); +} + +void +Experiment::resetShowHideStack () +{ + delete cstackShowHide; + cstackShowHide = CallStack::getInstance (this); +} + +#define GET_INT_VAL(v, s, len) \ + for (v = len = 0; isdigit(*s); s++, len++) { v = v * 10 + (*s -'0'); } + +static int +dir_name_cmp (const void *a, const void *b) +{ + char *s1 = *((char **) a); + char *s2 = *((char **) b); + while (*s1) + { + if (isdigit (*s1) && isdigit (*s2)) + { + int v1, v2, len1, len2; + GET_INT_VAL (v1, s1, len1); + GET_INT_VAL (v2, s2, len2); + if (v1 != v2) + return v1 - v2; + if (len1 != len2) + return len2 - len1; + continue; + } + if (*s1 != *s2) + break; + s1++; + s2++; + } + return *s1 - *s2; +} + +Vector<char*> * +Experiment::get_descendants_names () +{ + char *dir_name = get_expt_name (); + if (dir_name == NULL) + return NULL; + DIR *exp_dir = opendir (dir_name); + if (exp_dir == NULL) + return NULL; + Vector<char*> *exp_names = new Vector<char*>(); + for (struct dirent *entry = readdir (exp_dir); entry; + entry = readdir (exp_dir)) + { + if (entry->d_name[0] == '_' || strncmp (entry->d_name, "M_r", 3) == 0) + { + char *dpath = dbe_sprintf (NTXT ("%s/%s"), dir_name, entry->d_name); + struct stat64 sbuf; + if (dbe_stat (dpath, &sbuf) == 0 && S_ISDIR (sbuf.st_mode)) + exp_names->append (dpath); + else + free (dpath); + } + } + closedir (exp_dir); + if (exp_names->size () == 0) + { + delete exp_names; + return NULL; + } + exp_names->sort (dir_name_cmp); + return exp_names; +} + +bool +Experiment::create_dir (char *dname) +{ + if (mkdir (dname, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0) + { + return true; + } + struct stat64 sbuf; + if (dbe_stat (dname, &sbuf) != 0 || S_ISDIR (sbuf.st_mode) == 0) + { + char *buf = dbe_sprintf (GTXT ("Unable to create directory `%s'\n"), + dname); + errorq->append (new Emsg (CMSG_ERROR, buf)); + free (buf); + return false; + } + return true; +} + +char * +Experiment::get_arch_name () +{ + if (arch_name == NULL) + { + // Determine the master experiment directory. + // omazur: should do it in a less hacky way. XXXX + char *ptr = strstr_r (expt_name, DESCENDANT_EXPT_KEY); + ptr = ptr ? ptr + 3 : expt_name + strlen (expt_name); + arch_name = dbe_sprintf (NTXT ("%.*s/%s"), (int) (ptr - expt_name), + expt_name, SP_ARCHIVES_DIR); + } + return arch_name; +} + +char * +Experiment::get_fndr_arch_name () +{ + if (fndr_arch_name == NULL) + // Determine the founder experiment directory. + fndr_arch_name = dbe_strdup (get_arch_name ()); + return fndr_arch_name; +} + +enum +{ + HASH_NAME_LEN = 11 // (64 / 6 + 1) = 11 +}; + +static char * +get_hash_string (char buf[HASH_NAME_LEN + 1], uint64_t hash) +{ + static const char *har = + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"; + for (int i = 0; i < HASH_NAME_LEN; i++) + { + buf[i] = har[hash & 0x3f]; + hash = hash >> 6; + } + buf[HASH_NAME_LEN] = 0; + return buf; +} + +char * +Experiment::getNameInArchive (const char *fname, bool archiveFile) +{ + char *aname = get_archived_name (fname, archiveFile); + char *ret = dbe_sprintf (NTXT ("%s/%s"), get_arch_name (), aname); + free (aname); + return ret; +} + +#define MAX_ARCHIVE_FILENAME_LEN (256 - HASH_NAME_LEN - 2) + +char * +Experiment::get_archived_name (const char *fname, bool archiveFile) +{ + char *bname = get_basename (fname); + + // dirname_hash: + char dirnameHash[HASH_NAME_LEN + 1]; + // Treat "a.out" and "./a.out" equally + uint64_t hash = bname != fname ? crc64 (fname, bname - fname) + : crc64 (NTXT ("./"), 2); + get_hash_string (dirnameHash, hash); + + char *ret; + long bname_len = dbe_sstrlen (bname); + if (bname_len > MAX_ARCHIVE_FILENAME_LEN) + { + char basenameHash[HASH_NAME_LEN + 1]; + hash = crc64 (bname, bname_len); + get_hash_string (basenameHash, hash); + ret = dbe_sprintf ("%.*s%c%s_%s", + MAX_ARCHIVE_FILENAME_LEN - HASH_NAME_LEN - 1, + bname, archiveFile ? '.' : '_', + dirnameHash, basenameHash); + } + else + ret = dbe_sprintf ("%s%c%s", bname, archiveFile ? '.' : '_', dirnameHash); + return ret; +} + +char * +Experiment::checkFileInArchive (const char *fname, bool archiveFile) +{ + if (archiveMap) + { + char *aname = get_archived_name (fname, archiveFile); + DbeFile *df = archiveMap->get (aname); + free (aname); + if (df) + return strdup (df->get_location ()); + return NULL; + } + if (founder_exp) + return founder_exp->checkFileInArchive (fname, archiveFile); + return NULL; +} + + +// Comparing SegMem + +static int +SegMemCmp (const void *a, const void *b) +{ + SegMem *item1 = *((SegMem **) a); + SegMem *item2 = *((SegMem **) b); + return item1->unload_time > item2->unload_time ? 1 : + item1->unload_time == item2->unload_time ? 0 : -1; +} + +SegMem* +Experiment::update_ts_in_maps (Vaddr addr, hrtime_t ts) +{ + Vector<SegMem *> *segMems = (Vector<SegMem *> *) maps->values (); + if (!segMems->is_sorted ()) + { + Dprintf (DEBUG_MAPS, NTXT ("update_ts_in_maps: segMems.size=%lld\n"), (long long) segMems->size ()); + segMems->sort (SegMemCmp); + } + for (int i = 0, sz = segMems ? segMems->size () : 0; i < sz; i++) + { + SegMem *sm = segMems->fetch (i); + if (ts < sm->unload_time) + { + for (; i < sz; i++) + { + sm = segMems->fetch (i); + if ((addr >= sm->base) && (addr < sm->base + sm->size)) + { + Dprintf (DEBUG_MAPS, + "update_ts_in_maps: old:%u.%09u -> %u.%09u addr=0x%08llx size=%lld\n", + (unsigned) (sm->load_time / NANOSEC), + (unsigned) (sm->load_time % NANOSEC), + (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), + (unsigned long long) sm->base, (long long) sm->size); + maps->remove (sm->base, sm->load_time); + sm->load_time = ts; + maps->insert (sm->base, ts, sm); + return sm; + } + } + } + } + Dprintf (DEBUG_MAPS, "update_ts_in_maps: NOT FOUND %u.%09u addr=0x%08llx\n", + (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), + (unsigned long long) addr); + return NULL; +} + +DbeInstr* +Experiment::map_Vaddr_to_PC (Vaddr addr, hrtime_t ts) +{ + // Look up in the hash table first + int hash = (((int) addr) >> 8) & (HTableSize - 1); + SegMem *si = smemHTable[hash]; + if (si == NULL || addr < si->base || addr >= si->base + si->size + || ts < si->load_time || ts >= si->unload_time) + { + // Not in the hash table + si = (SegMem*) maps->locate (addr, ts); + if (si == NULL || addr < si->base || addr >= si->base + si->size + || ts < si->load_time || ts >= si->unload_time) + { + si = update_ts_in_maps (addr, ts); + if (si == NULL) + return dbeSession->get_Unknown_Function ()->find_dbeinstr (PCInvlFlag, addr); + } + smemHTable[hash] = si; + } + + // Calculate the file offset of 'addr' + uint64_t f_offset = si->get_file_offset () + (addr - si->base); + + DbeInstr *instr; + if (si->obj->get_type () == Histable::LOADOBJECT) + { + LoadObject *lo = (LoadObject*) si->obj; + lo->sync_read_stabs (); + instr = lo->find_dbeinstr (f_offset); + } + else + { + int hash2 = ((((int) addr) & 0xFFFC00) | (((int) f_offset) >> 2)) + & (HTableSize - 1); + instr = instHTable[hash2]; + if (instr == NULL || instr->func != si->obj || instr->addr != f_offset) + { + // Not in the hash table + Function *fp = (Function *) si->obj; + instr = fp->find_dbeinstr (0, f_offset); + instHTable[hash2] = instr; + } + } + if (!instr->func->isUsed) + { + instr->func->isUsed = true; + instr->func->module->isUsed = true; + instr->func->module->loadobject->isUsed = true; + } + return instr; +} + +Sample * +Experiment::map_event_to_Sample (hrtime_t ts) +{ + Sample *sample; + int index; + + // Check if the last used sample is the right one, + // if not then find it. + if (sample_last_used && ts >= sample_last_used->start_time + && ts <= sample_last_used->end_time) + return sample_last_used; + + Vec_loop (Sample*, samples, index, sample) + { + if ((ts >= sample->start_time) && + (ts <= sample->end_time)) + { + sample_last_used = sample; + return sample; + } + } + return (Sample*) NULL; +} + +GCEvent * +Experiment::map_event_to_GCEvent (hrtime_t ts) +{ + GCEvent *gcevent; + int index; + + // Check if the last used sample is the right one, + // if not then find it. + if (gcevent_last_used && ts >= gcevent_last_used->start + && ts <= gcevent_last_used->end) + return gcevent_last_used; + Vec_loop (GCEvent*, gcevents, index, gcevent) + { + if ((ts >= gcevent->start) && + (ts <= gcevent->end)) + { + gcevent_last_used = gcevent; + return gcevent; + } + } + return (GCEvent*) NULL; +} + +DbeInstr* +Experiment::map_jmid_to_PC (Vaddr mid, int bci, hrtime_t ts) +{ + if (mid == 0 || jmaps == NULL) + // special case: no Java stack was recorded, bci - error code + return dbeSession->get_JUnknown_Function ()->find_dbeinstr (0, bci); + + JMethod *jmthd = jmidHTable->get (mid); + if (jmthd == NULL) + { + jmthd = (JMethod *) jmaps->locate_exact_match (mid, ts); + if (jmthd) + jmidHTable->put (mid, jmthd); + } + if (jmthd == NULL || jmthd->get_type () != Histable::FUNCTION) + return dbeSession->get_JUnknown_Function ()->find_dbeinstr (0, (uint64_t) mid); + return jmthd->find_dbeinstr (0, bci); +} + +Emsg * +Experiment::fetch_comments () +{ + return commentq->fetch (); +} + +Emsg * +Experiment::fetch_runlogq () +{ + return runlogq->fetch (); +} + +Emsg * +Experiment::fetch_errors () +{ + return errorq->fetch (); +} + +Emsg * +Experiment::fetch_warnings () +{ + return warnq->fetch (); +} + +Emsg * +Experiment::fetch_notes () +{ + return notesq->fetch (); +} + +Emsg * +Experiment::fetch_ifreq () +{ + return ifreqq->fetch (); +} + +Emsg * +Experiment::fetch_pprocq () +{ + return pprocq->fetch (); +} + +int +Experiment::read_dyntext_file () +{ + char *data_file_name = dbe_sprintf ("%s/%s", expt_name, SP_DYNTEXT_FILE); + Data_window *dwin = new Data_window (data_file_name); + if (dwin->not_opened ()) + { + free (data_file_name); + delete dwin; + return 1; + } + dwin->need_swap_endian = need_swap_endian; + + Function *fp = NULL; + char *progress_msg = NULL; // Message for the progress bar + for (int64_t offset = 0;;) + { + DT_common *cpckt = (DT_common *) dwin->bind (offset, sizeof (DT_common)); + if (cpckt == NULL) + break; + size_t cpcktsize = dwin->decode (cpckt->size); + cpckt = (DT_common *) dwin->bind (offset, cpcktsize); + if (cpckt == NULL) + break; + switch (dwin->decode (cpckt->type)) + { + case DT_HEADER: + { + DT_header *hdr = (DT_header*) cpckt; + hrtime_t ts = dwin->decode (hdr->time) + exp_start_time; + SegMem *si = (SegMem*) maps->locate (dwin->decode (hdr->vaddr), ts); + fp = si ? (Function *) si->obj : NULL; + if (fp && (fp->get_type () != Histable::FUNCTION + || !(fp->flags & FUNC_FLAG_DYNAMIC))) + fp = NULL; + break; + } + case DT_CODE: + if (fp) + { + fp->img_fname = data_file_name; + fp->img_offset = offset + sizeof (DT_common); + if ((platform != Intel) && (platform != Amd64)) + { //ARCH(SPARC) + // Find out 'save' instruction address for SPARC + char *ptr = ((char*) cpckt) + sizeof (DT_common); + size_t img_size = cpcktsize - sizeof (DT_common); + for (size_t i = 0; i < img_size; i += 4) + if (ptr[i] == (char) 0x9d && ptr[i + 1] == (char) 0xe3) + { + fp->save_addr = i; + break; + } + } + } + break; + case DT_SRCFILE: + if (fp) + { + char *srcname = dbe_strndup (((char*) cpckt) + sizeof (DT_common), + cpcktsize - sizeof (DT_common)); + LoadObject *ds = fp->module ? fp->module->loadobject : NULL; + assert (ds != NULL); + Module *mod = dbeSession->createModule (ds, NULL); + mod->set_file_name (srcname); + //} + if (fp->module) + { + // It's most likely (unknown). Remove fp from it. + long idx = fp->module->functions->find (fp); + if (idx >= 0) + fp->module->functions->remove (idx); + } + fp->module = mod; + mod->functions->append (fp); + } + break; + case DT_LTABLE: + if (fp) + { + DT_lineno *ltab = (DT_lineno*) ((char*) cpckt + sizeof (DT_common)); + size_t sz = (cpcktsize - sizeof (DT_common)) / sizeof (DT_lineno); + if (sz <= 0) + break; + // Take care of the progress bar + static int percent = 0; + static long deltaReport = sz / 100; // 1000; + static long nextReport = 0; + static long progress_count = 0; + fp->pushSrcFile (fp->getDefSrc (), 0); + for (size_t i = 0; i < sz; i++) + { + int lineno = dwin->decode (ltab[i].lineno); + if (fp->usrfunc != NULL) + { + // Update progress bar + if (dbeSession->is_interactive ()) + { + if (progress_count == nextReport) + { + if (percent < 99) + { + percent++; + if (NULL == progress_msg) + { + progress_msg = dbe_sprintf (GTXT ("Processing Dynamic Text: %s"), + get_basename (expt_name)); + } + theApplication->set_progress (percent, progress_msg); + nextReport += deltaReport; + } + } + progress_count++; + } + DbeLine *dbeline = fp->usrfunc->mapPCtoLine (lineno, NULL); + lineno = dbeline != NULL ? dbeline->lineno : -1; + } + fp->add_PC_info (dwin->decode (ltab[i].offset), lineno); + } + fp->popSrcFile (); + } + break; + default: + // skip unknown records + break; + } + offset += cpcktsize; + } + free (progress_msg); + free (data_file_name); + delete dwin; + return 0; +} + +uint32_t +Experiment::mapTagValue (Prop_type prop, uint64_t value) +{ + Vector<Histable*> *objs = tagObjs->fetch (prop); + int lt = 0; + int rt = objs->size () - 1; + while (lt <= rt) + { + int md = (lt + rt) / 2; + Other *obj = (Other*) objs->fetch (md); + if (obj->value64 < value) + lt = md + 1; + else if (obj->value64 > value) + rt = md - 1; + else + return obj->tag; + } + + uint32_t tag; + if (sparse_threads && (prop == PROP_THRID || prop == PROP_LWPID)) + tag = objs->size () + 1; // "+ 1" related to 7038295 + else + tag = (int) value; // truncation; See 6788767 + + Other *obj = new Other (); + obj->value64 = value; + obj->tag = tag; + if (lt == objs->size ()) + objs->append (obj); + else + objs->insert (lt, obj); + + // Update min and max tags + if (prop == PROP_LWPID) + { + if ((uint64_t) tag < min_lwp) + min_lwp = (uint64_t) tag; + if ((uint64_t) tag > max_lwp) + max_lwp = (uint64_t) tag; + lwp_cnt++; + } + else if (prop == PROP_THRID) + { + if ((uint64_t) tag < min_thread) + min_thread = (uint64_t) tag; + if ((uint64_t) tag > max_thread) + max_thread = (uint64_t) tag; + thread_cnt++; + } + else if (prop == PROP_CPUID) + { + // On Solaris 8, we don't get CPU id -- don't change + if (value != (uint64_t) - 1) + {//YXXX is this related only to solaris 8? + if ((uint64_t) tag < min_cpu) + min_cpu = (uint64_t) tag; + if ((uint64_t) tag > max_cpu) + max_cpu = (uint64_t) tag; + } + cpu_cnt++; + } + return obj->tag; +} + +Vector<Histable*> * +Experiment::getTagObjs (Prop_type prop) +{ + return tagObjs->fetch (prop); +} + +Histable * +Experiment::getTagObj (Prop_type prop, uint32_t tag) +{ + Vector<Histable*> *objs = tagObjs->fetch (prop); + if (objs == NULL) + return NULL; + for (int i = 0; i < objs->size (); i++) + { + Other *obj = (Other*) objs->fetch (i); + if (obj->tag == tag) + return obj; + } + return NULL; +} + +JThread * +Experiment::map_pckt_to_Jthread (uint32_t tid, hrtime_t tstamp) +{ + if (!has_java) + return JTHREAD_DEFAULT; + int lt = 0; + int rt = jthreads_idx->size () - 1; + while (lt <= rt) + { + int md = (lt + rt) / 2; + JThread *jthread = jthreads_idx->fetch (md); + if (jthread->tid < tid) + lt = md + 1; + else if (jthread->tid > tid) + rt = md - 1; + else + { + for (; jthread; jthread = jthread->next) + if (tstamp >= jthread->start && tstamp < jthread->end) + return jthread; + break; + } + } + + return JTHREAD_NONE; +} + +JThread* +Experiment::get_jthread (uint32_t tid) +{ + if (!has_java) + return JTHREAD_DEFAULT; + int lt = 0; + int rt = jthreads_idx->size () - 1; + while (lt <= rt) + { + int md = (lt + rt) / 2; + JThread *jthread = jthreads_idx->fetch (md); + if (jthread->tid < tid) + lt = md + 1; + else if (jthread->tid > tid) + rt = md - 1; + else + { + JThread *jthread_first = jthread; + while ((jthread = jthread->next) != NULL) + if (!jthread->is_system () && + jthread->jthr_id < jthread_first->jthr_id) + jthread_first = jthread; + return jthread_first; + } + } + + return JTHREAD_NONE; +} + +// SS12 experiment +DataDescriptor * +Experiment::newDataDescriptor (int data_id, int flags, + DataDescriptor *master_dDscr) +{ + DataDescriptor *dataDscr = NULL; + if (data_id >= 0 && data_id < dataDscrs->size ()) + { + dataDscr = dataDscrs->fetch (data_id); + if (dataDscr != NULL) + return dataDscr; + } + + assert (data_id >= 0 && data_id < DATA_LAST); + const char *nm = get_prof_data_type_name (data_id); + const char *uname = get_prof_data_type_uname (data_id); + + if (master_dDscr) + dataDscr = new DataDescriptor (data_id, nm, uname, master_dDscr); + else + dataDscr = new DataDescriptor (data_id, nm, uname, flags); + dataDscrs->store (data_id, dataDscr); + return dataDscr; +} + +Vector<DataDescriptor*> * +Experiment::getDataDescriptors () +{ + Vector<DataDescriptor*> *result = new Vector<DataDescriptor*>; + for (int i = 0; i < dataDscrs->size (); ++i) + { + DataDescriptor *dd; + dd = get_raw_events (i); // force data fetch + if (dd != NULL) + result->append (dd); + } + return result; +} + +DataDescriptor * +Experiment::getDataDescriptor (int data_id) +{ + if (data_id < 0 || data_id >= dataDscrs->size ()) + return NULL; + return dataDscrs->fetch (data_id); +} + +PacketDescriptor * +Experiment::newPacketDescriptor (int kind, DataDescriptor *dDscr) +{ + PacketDescriptor *pDscr = new PacketDescriptor (dDscr); + pcktDscrs->store (kind, pDscr); + return pDscr; +} + +PacketDescriptor * +Experiment::getPacketDescriptor (int kind) +{ + if (kind < 0 || kind >= pcktDscrs->size ()) + return NULL; + return pcktDscrs->fetch (kind); +} + +void +Experiment::set_clock (int clk) +{ + if (clk > 0) + { + if (maxclock < clk) + { + maxclock = clk; + clock = maxclock; + } + if (minclock == 0 || minclock > clk) + minclock = clk; + } +} + +bool +JThread::is_system () +{ + if (group_name == NULL) + return false; + return strcmp (group_name, NTXT ("system")) == 0; +} + +void +Experiment::dump_stacks (FILE *outfile) +{ + cstack->print (outfile); +} + +void +Experiment::dump_map (FILE *outfile) +{ + int index; + SegMem *s; + fprintf (outfile, GTXT ("Experiment %s\n"), get_expt_name ()); + fprintf (outfile, GTXT ("Address Size (hex) Load time Unload time Checksum Name\n")); + Vec_loop (SegMem*, seg_items, index, s) + { + timestruc_t load; + timestruc_t unload; + hr2timestruc (&load, (s->load_time - exp_start_time)); + if (load.tv_nsec < 0) + { + load.tv_sec--; + load.tv_nsec += NANOSEC; + } + if (s->unload_time == MAX_TIME) + { + unload.tv_sec = 0; + unload.tv_nsec = 0; + } + else + hr2timestruc (&unload, (s->unload_time - exp_start_time)); + if (load.tv_nsec < 0) + { + load.tv_sec--; + load.tv_nsec += NANOSEC; + } + fprintf (outfile, + "0x%08llx %8lld (0x%08llx) %5ld.%09ld %5ld.%09ld \"%s\"\n", + s->base, s->size, s->size, load.tv_sec, load.tv_nsec, + unload.tv_sec, unload.tv_nsec, s->obj->get_name ()); + } + fprintf (outfile, NTXT ("\n")); +} + +/** + * Copy file to archive + * @param name + * @param aname + * @param hide_msg + * @return 0 - success, 1 - error + */ +int +Experiment::copy_file_to_archive (const char *name, const char *aname, int hide_msg) +{ + errno = 0; + int fd_w = open64 (aname, O_WRONLY | O_CREAT | O_EXCL, 0644); + if (fd_w == -1) + { + if (errno == EEXIST) + return 0; + fprintf (stderr, GTXT ("er_archive: unable to copy `%s': %s\n"), + name, STR (strerror (errno))); + return 1; + } + + if (dbe_stat_file (name, NULL) != 0) + { + fprintf (stderr, GTXT ("er_archive: cannot access file `%s': %s\n"), + name, STR (strerror (errno))); + close (fd_w); + return 1; + } + + int fd_r = open64 (name, O_RDONLY); + if (fd_r == -1) + { + fprintf (stderr, GTXT ("er_archive: unable to open `%s': %s\n"), + name, strerror (errno)); + close (fd_w); + unlink (aname); + return 1; + } + + if (!hide_msg) + fprintf (stderr, GTXT ("Copying `%s' to `%s'\n"), name, aname); + bool do_unlink = false; + for (;;) + { + unsigned char buf[65536]; + int n, n1; + n = (int) read (fd_r, (void *) buf, sizeof (buf)); + if (n <= 0) + break; + n1 = (int) write (fd_w, buf, n); + if (n != n1) + { + fprintf (stderr, GTXT ("er_archive: unable to write %d bytes to `%s': %s\n"), + n, aname, STR (strerror (errno))); + do_unlink = true; + break; + } + } + close (fd_w); + + struct stat64 s_buf; + if (fstat64 (fd_r, &s_buf) == 0) + { + struct utimbuf u_buf; + u_buf.actime = s_buf.st_atime; + u_buf.modtime = s_buf.st_mtime; + utime (aname, &u_buf); + } + close (fd_r); + if (do_unlink) + { + if (!hide_msg) + fprintf (stderr, GTXT ("er_archive: remove %s\n"), aname); + unlink (aname); + return 1; + } + return 0; +} + +/** + * Copy file to common archive + * Algorithm: + * Calculate checksum + * Generate file name to be created in common archive + * Check if it is not in common archive yet + * Copy file to the common archive directory if it is not there yet + * Create symbolic link: "aname" -> "caname", where "caname" is the name in common archive + * @param name - original file name + * @param aname - file name in experiment archive + * @param common_archive - common archive directory + * @return 0 - success, 1 - error + */ +int +Experiment::copy_file_to_common_archive (const char *name, const char *aname, + int hide_msg, + const char *common_archive, + int relative_path) +{ + if (!name || !aname || !common_archive) + { + if (!name) + fprintf (stderr, GTXT ("er_archive: Internal error: file name is NULL\n")); + if (!aname) + fprintf (stderr, GTXT ("er_archive: Internal error: file name in archive is NULL\n")); + if (!common_archive) + fprintf (stderr, GTXT ("er_archive: Internal error: path to common archive is NULL\n")); + return 1; + } + // Check if file is already archived + if (dbe_stat (aname, NULL) == 0) + return 0; // File is already archived + // Generate full path to common archive directory + char *cad = NULL; + char *abs_aname = NULL; + if ((common_archive[0] != '/') || (aname[0] != '/')) + { + long size = pathconf (NTXT ("."), _PC_PATH_MAX); + if (size < 0) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: pathconf(\".\", _PC_PATH_MAX) failed\n")); + return 1; + } + char *buf = (char *) malloc ((size_t) size); + if (buf == NULL) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: unable to allocate memory\n")); + return 1; + } + char *ptr = getcwd (buf, (size_t) size); + if (ptr == NULL) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: cannot determine current directory\n")); + free (buf); + return 1; + } + if (common_archive[0] != '/') + cad = dbe_sprintf (NTXT ("%s/%s"), ptr, common_archive); + else + cad = dbe_strdup (common_archive); + if (aname[0] != '/') + abs_aname = dbe_sprintf (NTXT ("%s/%s"), ptr, aname); + else + abs_aname = dbe_strdup (aname); + free (buf); + } + else + { + cad = dbe_strdup (common_archive); + abs_aname = dbe_strdup (aname); + } + // Calculate checksum + char * errmsg = NULL; + uint32_t crcval = get_cksum (name, &errmsg); + if (0 == crcval) + { // error + free (cad); + free (abs_aname); + if (NULL != errmsg) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: %s\n"), errmsg); + free (errmsg); + return 1; + } + fprintf (stderr, + GTXT ("er_archive: Fatal error: get_cksum(%s) returned %d\n"), + name, crcval); + return 1; + } + // Generate file name to be created in common archive + char *fname = get_basename (name); + char *abs_caname = dbe_sprintf (NTXT ("%s/%u_%s"), cad, crcval, fname); + if (abs_caname == NULL) + { + free (cad); + free (abs_aname); + fprintf (stderr, + GTXT ("er_archive: Fatal error: unable to allocate memory\n")); + return 1; + } + // Check if full name is not too long + long len = dbe_sstrlen (abs_caname); + long max = pathconf (cad, _PC_PATH_MAX); + if ((max < 0) || (len <= 0)) + { // unknown error + fprintf (stderr, GTXT ("er_archive: Fatal error: pathconf(%s, _PC_PATH_MAX) failed\n"), + cad); + free (abs_caname); + free (cad); + free (abs_aname); + return 1; + } + if (len >= max) + { + // Try to truncate the name + if ((len - max) <= dbe_sstrlen (fname)) + { + // Yes, we can do it + abs_caname[max - 1] = 0; + if (!hide_msg) + fprintf (stderr, GTXT ("er_archive: file path is too long - truncated:%s\n"), + abs_caname); + } + } + // Check if file name is not too long + char *cafname = get_basename (abs_caname); + len = dbe_sstrlen (cafname); + max = pathconf (cad, _PC_NAME_MAX); + if ((max < 0) || (len <= 0)) + { // unknown error + fprintf (stderr, GTXT ("er_archive: Fatal error: pathconf(%s, _PC_NAME_MAX) failed\n"), + cad); + free (abs_caname); + free (cad); + free (abs_aname); + return 1; + } + if (len >= max) + { + // Try to truncate the name + if ((len - max) <= dbe_sstrlen (fname)) + { + // Yes, we can do it + cafname[max - 1] = 0; + if (!hide_msg) + fprintf (stderr, GTXT ("er_archive: file name is too long - truncated:%s\n"), + abs_caname); + } + } + // Copy file to the common archive directory if it is not there yet + int res = 0; + if (dbe_stat_file (abs_caname, NULL) != 0) + { + // Use temporary file to avoid synchronization problems + char *t = dbe_sprintf ("%s/archive_%llx", cad, (unsigned long long) gethrtime()); + free (cad); + // Copy file to temporary file + res = copy_file_to_archive (name, t, hide_msg); // hide messages + if (res != 0) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: cannot copy file %s to temporary file: %s\n"), + name, t); + unlink (t); + free (t); + free (abs_caname); + free (abs_aname); + return 1; + } + // Set read-only permissions + struct stat64 statbuf; + if (0 == dbe_stat_file (name, &statbuf)) + { + mode_t mask = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + mode_t mode = statbuf.st_mode & mask; + chmod (t, mode); + } + // Try to rename temporary file "t" to "abs_caname" + // res = link(t, abs_caname); // link() fails on some f/s - use rename() + res = rename (t, abs_caname); + if (res != 0) + { + if (errno != EEXIST) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: rename(%s, %s) returned error: %d\n"), + t, abs_caname, res); + unlink (t); + free (t); + free (abs_caname); + free (abs_aname); + return 1; + } + // File "abs_caname" is already there - continue + } + unlink (t); + free (t); + } + else + free (cad); + char *lname = NULL; + if (relative_path) + { + if (common_archive[0] != '/' && aname[0] != '/') + { + // compare one relative path to another and find common beginning + char *rel_caname = dbe_sprintf ("%s/%s", common_archive, cafname); + if (rel_caname == NULL) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: unable to allocate memory\n")); + return 1; + } + lname = get_relative_link (rel_caname, aname); + free (rel_caname); + } + else + { + if (abs_aname == NULL) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: unable to allocate memory\n")); + return 1; + } + lname = get_relative_link (abs_caname, abs_aname); + } + } + else // absolute path + lname = dbe_strdup (abs_caname); + free (abs_aname); + if (lname == NULL) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: unable to allocate memory\n")); + return 1; + } + // Create symbolic link: aname -> lname + if (dbe_stat_file (abs_caname, NULL) == 0) + { + res = symlink (lname, aname); + if (res != 0) + { + fprintf (stderr, GTXT ("er_archive: Fatal error: symlink(%s, %s) returned error: %d (errno=%s)\n"), + lname, aname, res, strerror (errno)); + free (abs_caname); + free (lname); + return 1; + } + if (!hide_msg) + fprintf (stderr, GTXT ("Created symbolic link %s to file in common archive: %s\n"), + aname, lname); + } + else + { + fprintf (stderr, GTXT ("er_archive: Internal error: file does not exist in common archive: %s\n"), + abs_caname); + res = 1; + } + free (abs_caname); + free (lname); + return res; +} + +/** + * Copy file to archive + * @param name + * @param aname + * @param hide_msg + * @param common_archive + * @return 0 - success + */ +int +Experiment::copy_file (char *name, char *aname, int hide_msg, char *common_archive, int relative_path) +{ + if (common_archive) + { + if (0 == copy_file_to_common_archive (name, aname, hide_msg, + common_archive, relative_path)) + return 0; + // Error. For now - fatal error. Message is already printed. + fprintf (stderr, GTXT ("er_archive: Fatal error: cannot copy file %s to common archive %s\n"), + name, common_archive); + return 1; + } + return (copy_file_to_archive (name, aname, hide_msg)); +} + +LoadObject * +Experiment::createLoadObject (const char *path, uint64_t chksum) +{ + LoadObject *lo = dbeSession->createLoadObject (path, chksum); + if (lo->firstExp == NULL) + lo->firstExp = this; + return lo; +} + +LoadObject * +Experiment::createLoadObject (const char *path, const char *runTimePath) +{ + DbeFile *df = findFileInArchive (path, runTimePath); + if (df && (df->get_stat () == NULL)) + df = NULL; // No access to file + LoadObject *lo = dbeSession->createLoadObject (path, runTimePath, df); + if (df && (lo->dbeFile->get_location (false) == NULL)) + { + lo->dbeFile->set_location (df->get_location ()); + lo->dbeFile->inArchive = df->inArchive; + lo->dbeFile->sbuf = df->sbuf; + lo->dbeFile->experiment = df->experiment; + lo->firstExp = df->experiment; + } + if (lo->firstExp == NULL) + { + lo->firstExp = this; + lo->dbeFile->experiment = this; + } + return lo; +} + +SourceFile * +Experiment::get_source (const char *path) +{ + if (founder_exp && (founder_exp != this)) + return founder_exp->get_source (path); + if (sourcesMap == NULL) + sourcesMap = new StringMap<SourceFile*>(1024, 1024); + if (strncmp (path, NTXT ("./"), 2) == 0) + path += 2; + SourceFile *sf = sourcesMap->get (path); + if (sf) + return sf; + char *fnm = checkFileInArchive (path, false); + if (fnm) + { + sf = new SourceFile (path); + dbeSession->append (sf); + DbeFile *df = sf->dbeFile; + df->set_location (fnm); + df->inArchive = true; + df->check_access (fnm); // init 'sbuf' + df->sbuf.st_mtime = 0; // Don't check timestamps + free (fnm); + } + else + sf = dbeSession->createSourceFile (path); + sourcesMap->put (path, sf); + return sf; +} + +Vector<Histable*> * +Experiment::get_comparable_objs () +{ + update_comparable_objs (); + if (comparable_objs || dbeSession->expGroups->size () <= 1) + return comparable_objs; + comparable_objs = new Vector<Histable*>(dbeSession->expGroups->size ()); + for (long i = 0, sz = dbeSession->expGroups->size (); i < sz; i++) + { + ExpGroup *gr = dbeSession->expGroups->get (i); + if (groupId == gr->groupId) + { + comparable_objs->append (this); + continue; + } + Histable *h = NULL; + for (long i1 = 0, sz1 = gr->exps ? gr->exps->size () : 0; i1 < sz1; i1++) + { + Experiment *exp = gr->exps->get (i1); + if ((exp->comparable_objs == NULL) && (dbe_strcmp (utargname, exp->utargname) == 0)) + { + exp->phaseCompareIdx = phaseCompareIdx; + h = exp; + h->comparable_objs = comparable_objs; + break; + } + } + comparable_objs->append (h); + } + dump_comparable_objs (); + return comparable_objs; +} + +DbeFile * +Experiment::findFileInArchive (const char *fname) +{ + if (archiveMap) + { + char *aname = get_archived_name (fname); + DbeFile *df = archiveMap->get (aname); + free (aname); + return df; + } + if (founder_exp) + return founder_exp->findFileInArchive (fname); + return NULL; +} + +DbeFile * +Experiment::findFileInArchive (const char *className, const char *runTimePath) +{ + DbeFile *df = NULL; + if (runTimePath) + { + const char *fnm = NULL; + if (strncmp (runTimePath, NTXT ("zip:"), 4) == 0) + fnm = runTimePath + 4; + else if (strncmp (runTimePath, NTXT ("jar:file:"), 9) == 0) + fnm = runTimePath + 9; + if (fnm) + { + const char *s = strchr (fnm, '!'); + if (s) + { + char *s1 = dbe_strndup (fnm, s - fnm); + df = findFileInArchive (s1); + free (s1); + } + else + df = findFileInArchive (fnm); + if (df) + df->filetype |= DbeFile::F_JAR_FILE; + } + else if (strncmp (runTimePath, NTXT ("file:"), 5) == 0) + { + fnm = runTimePath + 5; + df = findFileInArchive (fnm); + } + else + df = findFileInArchive (runTimePath); + } + if (df == NULL) + df = findFileInArchive (className); + return df; +} diff --git a/gprofng/src/Experiment.h b/gprofng/src/Experiment.h new file mode 100644 index 0000000..41c44e4 --- /dev/null +++ b/gprofng/src/Experiment.h @@ -0,0 +1,689 @@ +/* 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. */ + +#ifndef _EEXPERIMENT_H +#define _EEXPERIMENT_H + +// The experiment class is responsible for managing all the data +// for an individual experiment + +#include "Metric.h" +#include "Histable.h" +#include "Stats_data.h" +#include "DefaultMap.h" +#include "HeapMap.h" + +class Data_window; +class DbeFile; +class CallStack; +class JMethod; +class Sample; +class SegMem; +class LoadObject; +class SourceFile; +class UserLabel; +class PRBTree; +class Emsg; +class Emsgqueue; +struct JThread; +struct GCEvent; +class FileData; +class Module; +class Experiment; +template <class ITEM> class Vector; + +#define JTHREAD_DEFAULT ((JThread*)0) +#define JTHREAD_NONE ((JThread*)-1) + +// When we perform the pipelined optimization on resolve_frame_info() and add_stack() +// this is the number of iterations one phase works on before passing on the work to +// the next phase + +#define CSTCTX_CHUNK_SZ 10000 +#define PIPELINE_QUEUE_SZ_HI 8 +#define PIPELINE_QUEUE_SZ_LOW 2 + +// the add_stack_ctx structure contains the intermediate state (context) after +// CSTCTX_CHUNK_SZ number of iterations to pass on the work to another thread to +// operate on the next stage +typedef struct +{ + Vector<DbeInstr*> *natpcs; + Vector<Histable*> *jpcs; + long idx; + FramePacket *frp; + hrtime_t tstamp; + uint32_t thrid; + bool last_ctx; +} cstk_ctx; + +// To minimize threadpool overhead, the granularity of a job submitted is made larger: +// containing a chunk of iterations (of size CSTCTX_CHUNK_SZ) +typedef struct +{ + cstk_ctx* cstCtxAr[CSTCTX_CHUNK_SZ]; + int last_idx; + long idx_begin; + long idx_end; + DataDescriptor *dDscr; + Experiment *exp; + void *cstk; +} cstk_ctx_chunk; + +class Experiment : public Histable, public DbeMessages +{ +public: + + enum Exp_status + { + SUCCESS, + INCOMPLETE, + FAILURE + }; + + Experiment (); + virtual ~Experiment (); + + virtual Histable_type + get_type () + { + return EXPERIMENT; + }; + virtual Vector<Histable*> *get_comparable_objs (); + + int groupId; + Experiment *founder_exp; // parent of this experiment + Vector<Experiment*> *children_exps; // children of this experiment + + // Configuration Information + char *hostname; // Hosthame (e.g. mymachine) + long start_sec; // Starting timeval secs. + char *username; // name of person performing the test + char *architecture; // Architecture name ("sun4") + Platform_t platform; // Sparc,Sparcv9,Intel + WSize_t wsize; // word size: may be w32 or w64 + int clock; // CPU clock frequency, Mhz + int varclock; // Set if CPU clock frequency can change: turbo-mode + int maxclock; // max. CPU clock frequency on MP machine + int minclock; // min. CPU clock frequency on MP machine + int ncpus; // count of CPUs where expt was recorded + int hw_cpuver; // CPU version from libcpc + char *machinemodel; // machine model of machine on which experiment was recorded + char *os_version; // Operating system name + int page_size; // Page size (bytes) + int npages; // Number of page size + int exp_maj_version; // major version number of current experiment + int exp_min_version; // minor version number of current experiment + int hex_field_width; // number of digits in hex form of address + // for current experiment, i.e. 8 for 32bit addresses + int broken; // If SP_JCMD_RUN line not seen + int obsolete; // If pointer file experiment detected + bool hwc_default; // True if HW counters were enabled by default + int hwc_bogus; // Count of bogus HWC packets + int hwc_lost_int; // Count of packets reflecting lost interrupt + int hwc_scanned; // If the HWC packets have been scanned + int invalid_packet; // Count of invalid packets + bool exec_started; // True if exec was called, and exec error not yet seen + bool dataspaceavail; // True if dataspace data is in the experiment + bool leaklistavail; // True if leaklist data is in the experiment + bool heapdataavail; // True if heap data is in the experiment + bool racelistavail; // true if there are race events in the experiment + bool iodataavail; // true if there are io events in the experiment + bool deadlocklistavail; // true if there are deadlock events in the experiment + bool timelineavail; // true if there are valid timestamps in the experiment + bool ifreqavail; // True if instruction-frequency data is in the experiment + bool ompavail; // true if there is OpenMP data in the experiment + bool has_java; + char *uarglist; // argv[] array, as a string + char *utargname; // basename of argv[0] extracted from uarglist + char *ucwd; // working directory + char *cversion; // collector version string + char *dversion; // driver version string (er_kernel) + char *jversion; // Java version string (java profiling) + + // Open the named experiment record and process log file + Exp_status open (char *directory_name); + + // Update experiment (read and process new data) + Exp_status update (); + + // Returns collector parameters for the current sample selection + Collection_params * + get_params () + { + return &coll_params; + } + + Exp_status + get_status () + { + return status; + } + + // Returns the number of samples. For use by FilterNumeric + int + nsamples () + { + return samples->size (); + } + + // Release any releasable memory. + void purge (); + + void resetShowHideStack (); + int save_notes (char*, bool); + int delete_notes (bool); + Experiment *getBaseFounder (); // returns topmost founder or this if no descendents + + hrtime_t + getStartTime () + { + return exp_start_time; + } + hrtime_t getRelativeStartTime (); // delta between start and founder's start + + hrtime_t + getWallStartSec () + { + return start_sec; + } + + hrtime_t + getLastEvent () + { + if (last_event != ZERO_TIME) + return last_event; + return exp_start_time; + } + + hrtime_t + getGCDuration () + { + return gc_duration; + } + + int + getPID () + { + return pid; + } + + int + getUserExpId () + { + return userExpId; + } + + int + getExpIdx () + { + return expIdx; + } + + void + setExpIdx (int idx) + { + expIdx = idx; + } + + void + setUserExpId (int idx) + { + userExpId = idx; + } + + void + setTinyThreshold (int limit) + { + tiny_threshold = limit; + } + + bool + isDiscardedTinyExperiment () + { + return discardTiny; + } + + Exp_status open_epilogue (); + void read_experiment_data (bool read_ahead); + static int copy_file_to_archive (const char *name, const char *aname, int hide_msg); + static int copy_file_to_common_archive (const char *name, const char *aname, + int hide_msg, const char *common_archive, int relative_path = 0); + static int copy_file (char *name, char *aname, int hide_msg, + char *common_archive = NULL, int relative_path = 0); + + // get_raw_events() + // action: get unfiltered packets, loading them if required + // parameters: data_id (see ProfData_type) + DataDescriptor *get_raw_events (int data_id); + Vector<DataDescriptor*> *getDataDescriptors (); + + // Some DATA_* types are derived from others, e.g. DATA_HEAPSZ is derived from DATA_HEAP + // The following hooks support derived DataViews + int base_data_id (int data_id); // returns base data_id type (ProfData_type DATA_*) + DataView *create_derived_data_view (int data_id, DataView *dview); + + Vector<BaseMetric*>* + get_metric_list () + { + return metrics; + } + + char * + get_expt_name () + { + return expt_name; // Return the pathname to the experiment + }; + + Vector<char*> *get_descendants_names (); + char *get_fndr_arch_name (); + char *get_arch_name (); + char *getNameInArchive (const char *fname, bool archiveFile = false); + char *checkFileInArchive (const char *fname, bool archiveFile = false); + DbeFile *findFileInArchive (const char *className, const char *runTimePath); + DbeFile *findFileInArchive (const char *fname); + bool create_dir (char *dname); + + Vaddr + ret_stack_base () + { + return stack_base; + }; + + // Map a virtual address to a PC pair + DbeInstr *map_Vaddr_to_PC (Vaddr addr, hrtime_t ts); + DbeInstr *map_jmid_to_PC (Vaddr mid, int lineno, hrtime_t ts); + Sample *map_event_to_Sample (hrtime_t ts); + GCEvent *map_event_to_GCEvent (hrtime_t ts); + + DataView * + getOpenMPdata () + { + return openMPdata; + } + + time_t + get_mtime () + { + return mtime; + } + + Emsg *fetch_comments (void); // fetch the queue of comment messages + Emsg *fetch_runlogq (void); // fetch the queue of run log messages + Emsg *fetch_errors (void); // fetch the queue of error messages + Emsg *fetch_warnings (void); // fetch the queue of warning messages + Emsg *fetch_notes (void); // fetch the queue of notes messages + Emsg *fetch_ifreq (void); // fetch the queue of ifreq messages + Emsg *fetch_pprocq (void); // fetch the queue of post-processing messages + + // message queues + Emsgqueue *commentq; // comments for the experiment header + Emsgqueue *runlogq; // used temporarily; after log file processing, + // messages are appended to the commentq + Emsgqueue *errorq; // error messages + Emsgqueue *warnq; // warning messages + Emsgqueue *notesq; // user-written notes messages + Emsgqueue *pprocq; // postprocessing messages + Emsgqueue *ifreqq; // Instruction frequency data, from count experiment + Map<const char*, LoadObject*> *loadObjMap; + Vector<LoadObject*> *loadObjs; + void append (LoadObject *lo); + LoadObject *createLoadObject (const char *path, uint64_t chksum = 0); + LoadObject *createLoadObject (const char *path, const char *runTimePath); + SourceFile *get_source (const char *path); + void set_clock (int clk); + + CallStack * + callTree () + { + return cstack; + } + + CallStack * + callTreeShowHide () + { + return cstackShowHide; + } + + uint32_t mapTagValue (Prop_type, uint64_t value); + Histable *getTagObj (Prop_type, uint32_t idx); + Vector<Histable*> *getTagObjs (Prop_type); + + JThread *map_pckt_to_Jthread (uint32_t tid, hrtime_t tstamp); + JThread *get_jthread (uint32_t tid); + + Vector<JThread*> * + get_jthreads () + { + return jthreads; + } + + Vector<GCEvent*> * + get_gcevents () + { + return gcevents; + } + + bool need_swap_endian; + Collection_params coll_params; // Collection params + + // Ranges for threads, lwps, cpu + uint64_t min_thread; + uint64_t max_thread; + uint64_t thread_cnt; + uint64_t min_lwp; + uint64_t max_lwp; + uint64_t lwp_cnt; + uint64_t min_cpu; + uint64_t max_cpu; + uint64_t cpu_cnt; + uint64_t dsevents; // count of dataspace events + uint64_t dsnoxhwcevents; /* count of ds events that could be be validated + * because of no branch target info */ + + PacketDescriptor *newPacketDescriptor (int kind, DataDescriptor *dDscr); + PacketDescriptor *getPacketDescriptor (int kind); + + // debugging aids -- dump_stacks, dump_map + void dump_stacks (FILE *); + void dump_map (FILE *); + + // These methods are used in nightly performance regression testing + void DBG_memuse (Sample *); + void DBG_memuse (const char *sname); + void init_cache (); + + DefaultMap<int64_t, FileData*> * + getFDataMap () + { + return fDataMap; + } + CallStack *cstack; + +protected: + + Exp_status status; // Error status + Vector<SegMem*> *seg_items; // Master list of seg_items + CallStack *cstackShowHide; + PRBTree *maps; // All maps in (Vaddr,time) + + hrtime_t gc_duration; // wall-clock hrtime of total GC intervals + hrtime_t exp_start_time; // wall-clock hrtime at exp start + hrtime_t last_event; // wall-clock hrtime of last known sample or log.xml entry + hrtime_t non_paused_time; // sum of periods where data collection is active (not paused) + hrtime_t resume_ts; // tracks log.xml start/resume times + void update_last_event (hrtime_t ts /*wall time (not 0-based)*/); + + char *expt_name; // name of experiment + char *arch_name; // <experiment>/archive + char *fndr_arch_name; // <founder_experiment>/archive + //TBR? hrtime_t sample_time; // total of sample durations + int yyparse (); // Allow yyparse actions to access + Vaddr stack_base; // Stack base + + // Write experiment header to comment queue + void write_header (); + void write_coll_params (); + + Exp_status find_expdir (char *directory_name); + + // Invoke the parser to process a file. + void read_data_file (const char*, const char*); + int read_log_file (); + void read_labels_file (); + void read_notes_file (); + void read_archives (); + int read_java_classes_file (); + void read_map_file (); + int read_overview_file (); + int read_dyntext_file (); + void read_omp_file (); + void read_omp_preg (); + void read_omp_task (); + void read_ifreq_file (); + void read_frameinfo_file (); + + // Functions to process the log and loadobjects file entries + // They are deliberately made virtual to overload them + // in er_export. + virtual int process_arglist_cmd (char *, char *); + virtual int process_desc_start_cmd (char *, hrtime_t, char *, char *, int, char *); + virtual int process_desc_started_cmd (char *, hrtime_t, char *, char *, int, char *); + virtual int process_fn_load_cmd (Module *mod, char *fname, Vaddr vaddr, int fsize, hrtime_t ts); + virtual int process_fn_unload_cmd (char *, Vaddr, hrtime_t); + virtual int process_hwcounter_cmd (char *, int, char *, char *, int, int, int, char *); + virtual int process_hwsimctr_cmd (char *, int, char *, char *, char*, int, int, int, int, int); + virtual int process_jcm_load_cmd (char*, Vaddr, Vaddr, int, hrtime_t); + virtual int process_jcm_unload_cmd (char*, Vaddr, hrtime_t); + virtual int process_Linux_kernel_cmd (hrtime_t); + virtual int process_jthr_end_cmd (char *, uint64_t, Vaddr, Vaddr, hrtime_t); + virtual int process_jthr_start_cmd (char *, char *, char *, char *, uint64_t, Vaddr, Vaddr, hrtime_t); + virtual int process_gc_end_cmd (hrtime_t); + virtual int process_gc_start_cmd (hrtime_t); + virtual int process_sample_cmd (char *, hrtime_t, int id, char *lbl); + virtual int process_sample_sig_cmd (char *, int); + virtual int process_seg_map_cmd (char *, hrtime_t, Vaddr, int, int, int64_t, int64_t, int64_t, char *); + virtual int process_seg_unmap_cmd (char *, hrtime_t, Vaddr); + + // creation time for experiment + time_t mtime; + hrtime_t exp_rel_start_time; // start of exp. relative to founder + bool exp_rel_start_time_set; + Vector<UserLabel*> *userLabels; // List of er_labels + int userExpId; // user value for EXPID + int expIdx; // DbeSession exp identifier + PRBTree *jmaps; // JAVA_CLASSES: (id,time)->Histable + Experiment* baseFounder; // outermost experiment (null until lazily computed) + + // Represents a file in experiment + class ExperimentFile; + + // XML handler to parse various experiment files + class ExperimentHandler; + class ExperimentLabelsHandler; + + uint64_t readPacket (Data_window *dwin, Data_window::Span *span); + void readPacket (Data_window *dwin, char *ptr, PacketDescriptor *pDscr, + DataDescriptor *dDscr, int arg, uint64_t pktsz); + + // read data + DataDescriptor *get_profile_events (); + DataDescriptor *get_sync_events (); + DataDescriptor *get_hwc_events (); + DataDescriptor *get_heap_events (); + DataDescriptor *get_heapsz_events (); + DataDescriptor *get_iotrace_events (); + DataDescriptor *get_race_events (); + DataDescriptor *get_deadlock_events (); + DataDescriptor *get_sample_events (); + DataDescriptor *get_gc_events (); + DataDescriptor *getDataDescriptor (int data_id); + DataDescriptor *newDataDescriptor (int data_id, int flags = 0, + DataDescriptor *master_dDscr = NULL); + + // Frame info data structures and methods + struct UIDnode; + struct RawFramePacket; + + Vector<RawFramePacket*>*frmpckts; // frame info data + static int frUidCmp (const void*, const void*); + RawFramePacket *find_frame_packet (uint64_t uid); + + static const int CHUNKSZ = 16384; + long nnodes; + long nchunks; + UIDnode **chunks; + UIDnode **uidHTable; + Vector<UIDnode*> *uidnodes; + bool resolveFrameInfo; + bool discardTiny; + int tiny_threshold; /* optimize away tiny experiments which ran + * for less than specified time (ms): default 0 */ + + static int uidNodeCmp (const void *a, const void *b); + UIDnode *add_uid (Data_window *dwin, uint64_t uid, int size, uint32_t *array, uint64_t link_uid); + UIDnode *add_uid (Data_window *dwin, uint64_t uid, int size, uint64_t *array, uint64_t link_uid); + UIDnode *new_uid_node (uint64_t uid, uint64_t val); + UIDnode *get_uid_node (uint64_t uid, uint64_t val); + UIDnode *get_uid_node (uint64_t uid); + UIDnode *find_uid_node (uint64_t uid); + + ExperimentFile *logFile; + + // Data descriptors + Vector<DataDescriptor*> *dataDscrs; + Vector<PacketDescriptor*> *pcktDscrs; + long blksz; // binary data file block size + + // Processed data packets + DataView *openMPdata; // OMP fork events + + // Map events to OpenMP parallel regions and tasks + Map2D<uint32_t, hrtime_t, uint64_t> *mapPRid; + Map2D<uint32_t, hrtime_t, void*> *mapPReg; + Map2D<uint32_t, hrtime_t, void*> *mapTask; + + // Archive content + Map<const char*, DbeFile *> *archiveMap; + Map<const char*, SourceFile*>*sourcesMap; + + void init (); + void fini (); + void post_process (); + void constructJavaStack (FramePacket *, UIDnode *, Map<uint64_t, uint64_t> *); + void resolve_frame_info (DataDescriptor*); + void cleanup_cstk_ctx_chunk (); + void register_metric (Metric::Type type); + void register_metric (Hwcentry *ctr, const char* aux, const char* username); + + Sample *sample_last_used; + GCEvent *gcevent_last_used; + char *first_sample_label; + Module *get_jclass (const char *className, const char *fileName); + LoadObject *get_j_lo (const char *className, const char *fileName); + + Vector<BaseMetric*> *metrics; + Vector<JThread*> *jthreads; // master list of Java threads + Vector<JThread*> *jthreads_idx; // index in the master list + Vector<GCEvent*> *gcevents; + Vector<UnmapChunk*> *heapUnmapEvents; + Vector<Sample*> *samples; // Array of Sample pointers + + DefaultMap<int64_t, FileData*> *fDataMap; // list of FileData objects using the virtual File descriptor as the key + DefaultMap<int, int64_t> *vFdMap; // list of virtual file descrptors using the file descriptor as the key + + Vector<Vector<Histable*>*> *tagObjs; // tag objects + bool sparse_threads; + + SegMem **smemHTable; // hash table for SegMem's + DbeInstr **instHTable; // hash table for DbeInstr + Map<unsigned long long, JMethod*> *jmidHTable; // hash table for jmid + + // identity of target process + int pid; + int ppid; + int pgrp; + int sid; + + // Map file processing related data + struct MapRecord + { + + enum + { + LOAD, UNLOAD + } kind; + Histable *obj; + Vaddr base; + Size size; + hrtime_t ts; + uint64_t foff; + }; + + void mrec_insert (MapRecord *mrec); + SegMem *update_ts_in_maps (Vaddr addr, hrtime_t ts); + int read_warn_file (); + LoadObject *get_dynfunc_lo (const char *loName); + Function *create_dynfunc (Module *mod, char *fname, int64_t vaddr, int64_t fsize); + char *get_archived_name (const char *fname, bool archiveFile = false); + + Vector<MapRecord*> *mrecs; + +private: + void add_evt_time_to_profile_events (DataDescriptor *dDscr); + DataView *create_heapsz_data_view (DataView *heap_dview); + void compute_heapsz_data_view (DataView *heapsz_dview); +}; + +struct JThread +{ + JThread *next; + char *name; + char *group_name; + char *parent_name; + uint32_t tid; // system thread id + Vaddr jthr; // recorded Java thread id + Vaddr jenv; // recorded JNIEnv id + uint32_t jthr_id; // internal JThread object id + hrtime_t start; + hrtime_t end; + + JThread () + { + name = NULL; + group_name = NULL; + parent_name = NULL; + } + + ~JThread () + { + free (name); + free (group_name); + free (parent_name); + } + bool is_system (); +}; + +struct GCEvent +{ + + GCEvent () + { + id = -1; + } + + ~GCEvent () { } + + hrtime_t start; + hrtime_t end; + int id; +}; + +class ExperimentLoadCancelException +{ +public: + + ExperimentLoadCancelException () { }; + + ~ExperimentLoadCancelException () { }; +}; + + +#endif /* _EEXPERIMENT_H */ diff --git a/gprofng/src/Expression.cc b/gprofng/src/Expression.cc new file mode 100644 index 0000000..49c94a8 --- /dev/null +++ b/gprofng/src/Expression.cc @@ -0,0 +1,1279 @@ +/* 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 <assert.h> +#include "CallStack.h" +#include "DbeSession.h" +#include "DbeView.h" +#include "DataObject.h" +#include "Exp_Layout.h" +#include "Experiment.h" +#include "Module.h" +#include "LoadObject.h" +#include "Expression.h" +#include "Function.h" +#include "Histable.h" +#include "Sample.h" +#include "Table.h" + +////////////////////////////////////////////////////////// +// class Expression::Context + +static const uint64_t INDXOBJ_EXPGRID_SHIFT = 60; +static const uint64_t INDXOBJ_EXPID_SHIFT = 32; + +Expression::Context::Context (DbeView *_dbev, Experiment *_exp) +{ + dbev = _dbev; + exp = _exp; + dview = NULL; + eventId = 0; +} + +Expression::Context::Context (DbeView *_dbev, Experiment *_exp, + DataView *_dview, long _eventId) +{ + dbev = _dbev; + exp = _exp; + dview = _dview; + eventId = _eventId; +} + +////////////////////////////////////////////////////////// +// class Expression +Expression::Expression (OpCode _op, uint64_t _v) +{ + op = _op; + v = Value (_v); + arg0 = NULL; + arg1 = NULL; +} + +Expression::Expression (OpCode _op, const Expression *_arg0, + const Expression *_arg1) +{ + op = _op; + v = Value (); + arg0 = NULL; + arg1 = NULL; + if (_arg0) + arg0 = _arg0->copy (); + if (_arg1) + arg1 = _arg1->copy (); +} + +Expression::~Expression () +{ + delete arg0; + delete arg1; +} + +Expression::Expression (const Expression &rhs) +{ + op = rhs.op; + arg0 = NULL; + arg1 = NULL; + if (rhs.arg0) + arg0 = rhs.arg0->copy (); + if (rhs.arg1) + arg1 = rhs.arg1->copy (); + v = Value (rhs.v); + fixupValues (); +} + +Expression::Expression (const Expression *rhs) +{ + arg0 = NULL; + arg1 = NULL; + copy (rhs); +} + +void +Expression::copy (const Expression *rhs) +{ + op = rhs->op; + delete arg0; + delete arg1; + arg0 = NULL; + arg1 = NULL; + if (rhs->arg0) + arg0 = rhs->arg0->copy (); + if (rhs->arg1) + arg1 = rhs->arg1->copy (); + v = Value (rhs->v); + fixupValues (); +} + +Expression & +Expression::operator= (const Expression &rhs) +{ + if (this == &rhs) + return *this; + copy (&rhs); + return *this; +} + +void +Expression::fixupValues () +{ + if (v.next) + { + assert (arg0 && v.next == &(arg0->v)); + v.next = &(arg0->v); + } +} + +bool +Expression::getVal (int propId, Context *ctx) +{ + v.val = 0; + v.next = NULL; + int origPropId = propId; + switch (propId) + { + default: + { + if (!ctx->dview) + return false; + PropDescr *propDscr = ctx->dview->getProp (propId); + if (!propDscr) + return false; + switch (propDscr->vtype) + { + case TYPE_INT32: + v.val = ctx->dview->getIntValue (propId, ctx->eventId); + break; + case TYPE_UINT32: + v.val = (uint32_t) ctx->dview->getIntValue (propId, ctx->eventId); //prevent sign extension + break; + case TYPE_INT64: + case TYPE_UINT64: + v.val = ctx->dview->getLongValue (propId, ctx->eventId); + break; + case TYPE_OBJ: + // YM: not sure if we should allow this + v.val = (long long) ctx->dview->getObjValue (propId, ctx->eventId); + break; + case TYPE_STRING: + case TYPE_DOUBLE: + default: + return false; // Weird, programming error? + } + break; + } + case PROP_FREQ_MHZ: + if (ctx->exp && ctx->exp->clock) + v.val = ctx->exp->clock; + else + return false; + break; + case PROP_PID: + if (ctx->exp == NULL) + return false; + v.val = ctx->exp->getPID (); + break; + case PROP_EXPID: + if (ctx->exp == NULL) + return false; + v.val = ctx->exp->getUserExpId (); + break; + case PROP_EXPID_CMP: + if (ctx->exp == NULL) + return false; + else + { + Experiment *exp = ctx->exp; + if (ctx->dbev && ctx->dbev->comparingExperiments ()) + exp = (Experiment *) exp->get_compare_obj (); + v.val = exp->getUserExpId (); + } + break; + case PROP_EXPGRID: + if (ctx->exp == NULL) + return false; + v.val = ctx->exp->groupId; + break; + case PROP_NTICK_USEC: + if (ctx->exp == NULL) + return false; + if (ctx->dview && ctx->dview->getProp (PROP_NTICK)) + v.val = ctx->dview->getIntValue (PROP_NTICK, ctx->eventId) + * ctx->exp->get_params ()->ptimer_usec; + else + return false; + break; + case PROP_ATSTAMP: + case PROP_ETSTAMP: + if (ctx->exp == NULL) + return false; + if (ctx->dview && ctx->dview->getProp (PROP_TSTAMP)) + v.val = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId); + else + return false; + if (propId == PROP_ATSTAMP) + break; // absolute time, no adjustments + // propId==PROP_ETSTAMP + // calculate relative time from start of this experiment + v.val -= ctx->exp->getStartTime (); + break; + case PROP_TSTAMP: + case PROP_TSTAMP_LO: + case PROP_TSTAMP_HI: + { + if (ctx->exp == NULL) + return false; + if (!(ctx->dview && ctx->dview->getProp (PROP_TSTAMP))) + return false; + hrtime_t tstamp = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId); + // compute relative time from start of founder experiment + v.val = tstamp - ctx->exp->getStartTime () + + ctx->exp->getRelativeStartTime (); + if (propId == PROP_TSTAMP) + break; + if (ctx->dview->getProp (PROP_EVT_TIME)) + { + hrtime_t delta = ctx->dview->getLongValue (PROP_EVT_TIME, ctx->eventId); + if (propId == PROP_TSTAMP_LO) + { + if (delta > 0) + { // positive delta means TSTAMP is at end + // TSTAMP_LO = TSTAMP-delta + v.val -= delta; + break; + } + break; + } + else + { // PROP_TSTAMP_HI + if (delta < 0) + { // negative delta means TSTAMP is at start + // TSTAMP_HI = TSTAMP+(-delta) + v.val -= delta; + break; + } + break; + } + } + else if (ctx->dview->getProp (PROP_TSTAMP2)) + { + if (propId == PROP_TSTAMP_HI) + { + hrtime_t tstamp2 = ctx->dview->getLongValue (PROP_TSTAMP2, + ctx->eventId); + if (tstamp2 == 0) + break; // if not initialized, event does not have duration + if (tstamp2 == MAX_TIME) + tstamp2 = ctx->exp->getLastEvent (); + hrtime_t delta = tstamp2 - tstamp; + if (delta >= 0) + { + v.val += delta; + break; + } + break; // weird, delta should not be negative + } + break; // PROP_TSTAMP_LO, no modification needed + } + break; // should never be hit + } + case PROP_IOHEAPBYTES: + { + propId = PROP_IONBYTE; + if (ctx->dview == NULL) + return false; + if (!ctx->dview->getProp (propId)) + { // has property? + propId = PROP_HSIZE; + if (!ctx->dview->getProp (propId)) + return false; + } + v.val = ctx->dview->getLongValue (propId, ctx->eventId); + break; + } + case PROP_SAMPLE_MAP: + { + if (ctx->exp == NULL) + return false; + if (ctx->dview == NULL) + return false; + if (ctx->dview->getProp (PROP_SAMPLE)) + v.val = ctx->dview->getIntValue (PROP_SAMPLE, ctx->eventId); + else + { // does not have property, convert to time. + uint64_t tstamp; + tstamp = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId); + Sample *sample = ctx->exp->map_event_to_Sample (tstamp); + v.val = sample ? sample->get_number () : -1; + } + break; + } + case PROP_GCEVENT_MAP: + { + if (ctx->exp == NULL) + return false; + if (ctx->dview == NULL) + return false; + if (ctx->dview->getProp (PROP_GCEVENT)) + v.val = ctx->dview->getIntValue (PROP_GCEVENT, ctx->eventId); + else + { // does not have property, convert to time. + uint64_t tstamp; + tstamp = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId); + GCEvent *gcevent = ctx->exp->map_event_to_GCEvent (tstamp); + v.val = gcevent ? gcevent->id : 0; + } + break; + } + case PROP_LEAF: + { + if (ctx->dview == NULL) + return false; + VMode vmode = ctx->dbev ? ctx->dbev->get_view_mode () : VMODE_USER; + int prop_id; + if (vmode == VMODE_MACHINE) + prop_id = PROP_MSTACK; + else if (vmode == VMODE_EXPERT) + prop_id = PROP_XSTACK; + else + prop_id = PROP_USTACK; + if (!ctx->dview->getProp (prop_id)) + return false; + Histable *obj = CallStack::getStackPC (ctx->dview->getObjValue (prop_id, ctx->eventId), 0); + Function *func = (Function*) obj->convertto (Histable::FUNCTION); + v.val = func->id; // LEAF + break; + } + case PROP_STACKID: + { + VMode vmode = ctx->dbev ? ctx->dbev->get_view_mode () : VMODE_USER; + if (vmode == VMODE_MACHINE) + propId = PROP_MSTACK; + else if (vmode == VMODE_EXPERT) + propId = PROP_XSTACK; + else + propId = PROP_USTACK; + if (ctx->dview == NULL) + return false; + if (!ctx->dview->getProp (propId)) + return false; + v.val = (long) ctx->dview->getObjValue (propId, ctx->eventId); + break; + } + case PROP_STACKL: + case PROP_STACKI: + case PROP_STACK: + { + VMode vmode = ctx->dbev ? ctx->dbev->get_view_mode () : VMODE_USER; + if (vmode == VMODE_MACHINE) + propId = PROP_MSTACK; + else if (vmode == VMODE_EXPERT) + propId = PROP_XSTACK; + else + propId = PROP_USTACK; + } + // no break; + case PROP_MSTACKL: + case PROP_XSTACKL: + case PROP_USTACKL: + case PROP_MSTACKI: + case PROP_XSTACKI: + case PROP_USTACKI: + switch (propId) + { + case PROP_MSTACKL: + case PROP_MSTACKI: + propId = PROP_MSTACK; + break; + case PROP_XSTACKL: + case PROP_XSTACKI: + propId = PROP_XSTACK; + break; + case PROP_USTACKL: + case PROP_USTACKI: + propId = PROP_USTACK; + break; + default: + break; + } + // no break; + case PROP_MSTACK: + case PROP_XSTACK: + case PROP_USTACK: + { + if (ctx->dview == NULL) + return false; + if (!ctx->dview->getProp (propId)) + return false; + bool hide_mode = !ctx->dbev->isShowAll () + || ctx->dbev->isFilterHideMode (); + Expression *cur = this; + for (CallStackNode *stack = (CallStackNode *) + ctx->dview->getObjValue (propId, ctx->eventId); + stack; stack = stack->get_ancestor ()) + { + Histable *hist = stack->get_instr (); + if (origPropId == PROP_STACK || origPropId == PROP_MSTACK + || origPropId == PROP_XSTACK || origPropId == PROP_USTACK) + { + cur->v.val = hist->convertto (Histable::FUNCTION)->id; + cur->v.fn = cur->v.val; + } + else if (origPropId == PROP_STACKL || origPropId == PROP_MSTACKL + || origPropId == PROP_XSTACKL || origPropId == PROP_USTACKL) + { + cur->v.val = hist->convertto (Histable::LINE)->id; + if (hide_mode) + cur->v.fn = hist->convertto (Histable::FUNCTION)->id; + else + cur->v.fn = 0; + } + else if (origPropId == PROP_STACKI || origPropId == PROP_MSTACKI + || origPropId == PROP_XSTACKI || origPropId == PROP_USTACKI) + { + cur->v.val = hist->convertto (Histable::INSTR)->id; + if (hide_mode) + cur->v.fn = hist->convertto (Histable::FUNCTION)->id; + else + cur->v.fn = 0; + } + if (cur->arg1 == NULL) + cur->arg1 = new Expression (OP_NONE, (uint64_t) 0); + if (stack->get_ancestor () == NULL) + { + if (origPropId == PROP_STACKL || origPropId == PROP_MSTACKL + || origPropId == PROP_XSTACKL || origPropId == PROP_USTACKL + || origPropId == PROP_STACKI || origPropId == PROP_MSTACKI + || origPropId == PROP_XSTACKI || origPropId == PROP_USTACKI) + { + cur->v.next = NULL; + continue; + } + } + cur->v.next = &cur->arg1->v; + cur = cur->arg1; + } + if (origPropId == PROP_STACK || origPropId == PROP_MSTACK + || origPropId == PROP_XSTACK || origPropId == PROP_USTACK) + { + cur->v.val = dbeSession->get_Total_Function ()->id; + cur->v.fn = cur->v.val; + cur->v.next = NULL; + } + break; + } + case PROP_DOBJ: + { + if (ctx->dview == NULL) + return false; + if (!ctx->dview->getProp (PROP_DOBJ)) + return false; + DataObject *dobj = (DataObject*) + ctx->dview->getObjValue (PROP_DOBJ, ctx->eventId); + if (dobj != NULL) + { + Expression *cur = this; + for (;;) + { + cur->v.val = dobj->id; + dobj = dobj->parent; + if (dobj == NULL) + break; + if (cur->arg1 == NULL) + cur->arg1 = new Expression (OP_NONE, (uint64_t) 0); + cur->v.next = &cur->arg1->v; + cur = cur->arg1; + } + cur->v.next = NULL; + } + break; + } + case PROP_CPRID: + case PROP_TSKID: + { + if (ctx->dview == NULL) + return false; + if (!ctx->dview->getProp (propId)) + return false; + CallStackNode *ompstack = (CallStackNode *) + ctx->dview->getObjValue (propId, ctx->eventId); + Histable *hobj = ompstack->get_instr (); + if (hobj != NULL) + v.val = hobj->id; + break; + } + case PROP_JTHREAD: + { + if (ctx->exp == NULL) + return false; + if (ctx->dview == NULL) + return false; + if (!ctx->dview->getProp (propId)) + return false; + uint64_t tstamp; + tstamp = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId); + uint32_t thrid; + uint64_t jthr_id = 0; + thrid = ctx->dview->getIntValue (PROP_THRID, ctx->eventId); + JThread *jthread = ctx->exp->map_pckt_to_Jthread (thrid, tstamp); + if (jthread != JTHREAD_NONE && jthread != JTHREAD_DEFAULT) + { + jthr_id = jthread->jthr_id; + uint64_t grid = ctx->exp->groupId; + uint64_t expid = ctx->exp->getUserExpId (); + v.val = (grid << INDXOBJ_EXPGRID_SHIFT) | + (expid << INDXOBJ_EXPID_SHIFT) | jthr_id; + } + break; + } + } + return true; +} + +bool +Expression::bEval (Context *ctx) +{ + uint64_t v0, v1; + switch (op) + { + case OP_DEG: + if (!arg1->bEval (ctx)) + return false; + if (arg1->v.val < 0) + { + v.val = 0; + return true; + } + if (!arg0->bEval (ctx)) + { + return false; + } + v0 = arg0->v.val; + v1 = arg1->v.val; + for (v.val = 1; v1 > 0; v1--) + v.val *= v0; + return true; + case OP_MUL: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val * arg1->v.val; + return true; + } + return false; + case OP_DIV: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v1 = arg1->v.val; + v.val = (v1 == 0) ? 0 : (arg0->v.val / v1); + return true; + } + return false; + case OP_REM: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v1 = arg1->v.val; + v.val = (v1 == 0) ? 0 : (arg0->v.val % v1); + return true; + } + return false; + case OP_ADD: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val + arg1->v.val; + // DBFIXME LIBRARY VISIBILITY + // hack to pass v.fn value to new expression for leaf filters USTACK+0 + v.fn = arg0->v.fn + arg1->v.fn; + return true; + } + return false; + case OP_MINUS: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val - arg1->v.val; + return true; + } + return false; + case OP_LS: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val << arg1->v.val; + return true; + } + return false; + case OP_RS: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val >> arg1->v.val; + return true; + } + return false; + case OP_LT: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val < arg1->v.val ? 1 : 0; + return true; + } + return false; + case OP_LE: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val <= arg1->v.val ? 1 : 0; + return true; + } + return false; + case OP_GT: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val > arg1->v.val ? 1 : 0; + return true; + } + return false; + case OP_GE: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val >= arg1->v.val ? 1 : 0; + return true; + } + return false; + case OP_EQ: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val == arg1->v.val ? 1 : 0; + return true; + } + return false; + case OP_NE: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val != arg1->v.val ? 1 : 0; + return true; + } + return false; + case OP_BITAND: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val & arg1->v.val; + return true; + } + return false; + case OP_BITXOR: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val ^ arg1->v.val; + return true; + } + return false; + case OP_BITOR: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v.val = arg0->v.val | arg1->v.val; + return true; + } + return false; + case OP_AND: + if (arg0->bEval (ctx)) + { + if (arg0->v.val == 0) + { + v.val = 0; + return true; + } + if (arg1->bEval (ctx)) + { + v.val = arg1->v.val == 0 ? 0 : 1; + return true; + } + return false; + } + if (arg1->bEval (ctx) && arg1->v.val == 0) + { + v.val = 0; + return true; + } + return false; + case OP_OR: + if (arg0->bEval (ctx)) + { + if (arg0->v.val != 0) + { + v.val = 1; + return true; + } + if (arg1->bEval (ctx)) + { + v.val = arg1->v.val == 0 ? 0 : 1; + return true; + } + return false; + } + if (arg1->bEval (ctx) && arg1->v.val != 0) + { + v.val = 1; + return true; + } + return false; + case OP_NEQV: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v0 = arg0->v.val; + v1 = arg1->v.val; + v.val = (v0 == 0 && v1 != 0) || (v0 != 0 && v1 == 0) ? 1 : 0; + return true; + } + return false; + case OP_EQV: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + v0 = arg0->v.val; + v1 = arg1->v.val; + v.val = (v0 == 0 && v1 == 0) || (v0 != 0 && v1 != 0) ? 1 : 0; + return true; + } + return false; + case OP_QWE: + if (arg0->bEval (ctx)) + { + if (arg0->v.val != 0) + { + if (arg1->arg0->bEval (ctx)) + { + v.val = arg1->arg0->v.val; + return true; + } + } + else + { + if (arg1->arg1->bEval (ctx)) + { + v.val = arg1->arg1->v.val; + return true; + } + } + } + return false; + case OP_COMMA: + if (arg0->bEval (ctx)) + { + v.next = &arg0->v; + if (arg1->bEval (ctx)) + { + v.val = arg1->v.val; + return true; + } + } + return false; + case OP_IN: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + for (Value *s = &arg0->v; s; s = s->next) + { + bool found = false; + for (Value *t = &arg1->v; t; t = t->next) + { + if (t->val == s->val) + { + found = true; + break; + } + } + if (!found) + { + v.val = 0; + return true; + } + } + v.val = 1; + return true; + } + return false; + case OP_SOMEIN: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + for (Value *s = &arg0->v; s; s = s->next) + { + for (Value *t = &arg1->v; t; t = t->next) + { + if (t->val == s->val) + { + v.val = 1; + return true; + } + } + } + v.val = 0; + return true; + } + return false; + case OP_ORDRIN: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + for (Value *t0 = &arg1->v; t0; t0 = t0->next) + { + bool found = true; + for (Value *s = &arg0->v, *t = t0; s; s = s->next, t = t->next) + { + if (t == NULL || t->val != s->val) + { + found = false; + break; + } + } + if (found) + { + v.val = 1; + return true; + } + } + v.val = 0; + return true; + } + return false; + // LIBRARY_VISIBILITY + case OP_LIBRARY_IN: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + for (Value *s = &arg0->v; s; s = s->next) + { + bool found = false; + uint64_t objId = s->val; + Histable *obj = dbeSession->findObjectById (objId); + bool libraryFound = false; + Function *fn; + if (obj != NULL && obj->get_type () == Histable::FUNCTION) + { + fn = (Function *) obj; + if (fn->isHideFunc) + // this belongss to a loadobject in hide/library mode + libraryFound = true; + } + + if (libraryFound) + { + uint64_t lo_id = fn->module->loadobject->id; + for (Value *t = &arg1->v; t; t = t->next) + { + uint64_t t_id = t->fn; + Histable *obj2 = dbeSession->findObjectById (t_id); + if (obj2 != NULL + && obj2->get_type () == Histable::FUNCTION) + { + Function *func2 = (Function *) obj2; + uint64_t lo_id2 = func2->module->loadobject->id; + if (lo_id2 == lo_id) + { + found = true; + break; + } + } + } + } + else + { + // Not a loadobject + for (Value *t = &arg1->v; t; t = t->next) + { + if (t->val == s->val) + { + found = true; + break; + } + } + } + if (!found) + { + v.val = 0; + return true; + } + } + v.val = 1; + return true; + } + return false; + case OP_LIBRARY_SOMEIN: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + for (Value *s = &arg0->v; s; s = s->next) + { + uint64_t objId = s->val; + Histable *obj = dbeSession->findObjectById (objId); + bool libraryFound = false; + Function *fn; + if (obj != NULL && obj->get_type () == Histable::FUNCTION) + { + fn = (Function *) obj; + if (fn->isHideFunc) + // this belongs to a loadobject in hide/library mode + libraryFound = true; + } + + if (libraryFound) + { + uint64_t lo_id = fn->module->loadobject->id; + for (Value *t = &arg1->v; t; t = t->next) + { + uint64_t t_id = t->fn; + Histable *obj2 = dbeSession->findObjectById (t_id); + if (obj2 != NULL && obj2->get_type () == Histable::FUNCTION) + { + Function *func2 = (Function *) obj2; + uint64_t lo_id2 = func2->module->loadobject->id; + if (lo_id2 == lo_id) + { + v.val = 1; + return true; + } + } + } + } + else + { + for (Value *t = &arg1->v; t; t = t->next) + if (t->val == s->val) + { + v.val = 1; + return true; + } + } + } + v.val = 0; + return true; + } + return false; + case OP_LIBRARY_ORDRIN: + if (arg0->bEval (ctx) && arg1->bEval (ctx)) + { + for (Value *t0 = &arg1->v; t0; t0 = t0->next) + { + bool found = true; + Value *t = t0; + for (Value *s = &arg0->v; s; s = s->next) + { + // start comparing s->val with t->val + // if matches move on to s->next and t->next + uint64_t objId = s->val; + Histable *obj = dbeSession->findObjectById (objId); + bool libraryFound = false; + Function *fn; + if (obj != NULL && obj->get_type () == Histable::FUNCTION) + { + fn = (Function *) obj; + if (fn->isHideFunc) + libraryFound = true; + } + if (libraryFound) + { + // s->val is from a loadobject + // check if t->val is a func whose loadobject matches s->val + uint64_t lo_id = fn->module->loadobject->id; + uint64_t t_id = t->fn; + Histable *obj2 = dbeSession->findObjectById (t_id); + if (obj2 != NULL + && obj2->get_type () == Histable::FUNCTION) + { + Function *func2 = (Function *) obj2; + uint64_t lo_id2 = func2->module->loadobject->id; + if (lo_id2 != lo_id) + { + // no match + found = false; + break; + } + else + { + // t->val is a func whose loadobject matches s->val + while (t != NULL && lo_id2 == lo_id) + { + // skip frames with same load object + t = t->next; + t_id = t->fn; + obj2 = dbeSession->findObjectById (t_id); + if (obj2 != NULL + && obj2->get_type () == Histable::FUNCTION) + { + func2 = (Function *) obj2; + lo_id2 = func2->module->loadobject->id; + } + } + } + } + } + else + { + if (t == NULL || t->val != s->val) + { + found = false; + break; + } + t = t->next; + } + } + if (found) + { + v.val = 1; + return true; + } + } + v.val = 0; + return true; + } + return false; + case OP_BITNOT: + if (arg0->bEval (ctx)) + { + v.val = ~arg0->v.val; + return true; + } + return false; + case OP_NOT: + if (arg0->bEval (ctx)) + { + v.val = !arg0->v.val; + return true; + } + return false; + case OP_NUM: + return true; + case OP_NAME: + if (ctx && arg0->bEval (ctx) && getVal ((int) arg0->v.val, ctx)) + return true; + return false; + case OP_FUNC: + // FNAME is completely processed by pEval for now + v.val = 0; + return true; + case OP_HASPROP: + if (!ctx || !ctx->dview) + return false; // can't be resolved (occurs during pEval() ) + else if (arg0->op != OP_NAME || !arg0->arg0) + return false; // weird, wrong arg type + else + { + int propId = (int) arg0->arg0->v.val; + if (ctx->dview->getProp (propId)) + v.val = 1; + else + v.val = 0; + return true; + } + case OP_FILE: + // FILENAME is completely processed by pEval for now + v.val = 0; + return true; + case OP_JAVA: + //JGROUP & JPARENT is completely processed by pEval for now + v.val = 0; + return true; + case OP_COLON: + return false; // OK for arg1 of OP_QWE + default: +#ifdef IPC_LOG + fprintf (stderr, "INTERNAL ERROR: Expression::eval op=%d\n", op); +#endif + return false; + } + return false; +} + +Expression * +Expression::pEval (Context *ctx) // partial evaluation (dview may be NULL) +{ + Expression *res = NULL; + switch (op) + { + case OP_FUNC: + { + Vector<Histable*> *objs = NULL; + if (arg0->v.val == FUNC_FNAME) + { + Histable::NameFormat nfmt = ctx ? ctx->dbev->get_name_format () : Histable::NA; + objs = (Vector<Histable*>*)dbeSession->match_func_names ((char*) arg1->v.val, nfmt); + } + else if (arg0->v.val == FUNC_DNAME) + objs = (Vector<Histable*>*)dbeSession->match_dobj_names ((char*) arg1->v.val); + Expression *cur = new Expression (Expression::OP_NUM, (uint64_t) 0); + res = cur; + int i = objs ? objs->size () - 1 : -1; + for (; i >= 0; i--) + { + cur->v.val = objs->fetch (i)->id; + if (i == 0) + break; + cur->arg0 = new Expression (OP_NONE, (uint64_t) 0); + cur->v.next = &cur->arg0->v; + cur = cur->arg0; + } + cur->v.next = NULL; + if (objs) + delete objs; + break; + } + case OP_JAVA: + { + Vector<JThread*> *objs = NULL; + Vector<uint64_t> *grids = NULL; + Vector<uint64_t> *expids = NULL; + if (arg0->v.val == JAVA_JGROUP) + objs = dbeSession->match_java_threads ((char*) arg1->v.val, 0, grids, + expids); + else if (arg0->v.val == JAVA_JPARENT) + objs = dbeSession->match_java_threads ((char*) arg1->v.val, 1, grids, + expids); + Expression *cur = new Expression (Expression::OP_NUM, (uint64_t) 0); + res = cur; + int i = objs ? objs->size () - 1 : -1; + for (; i >= 0; i--) + { + uint64_t jthr_id = 0; + JThread *jthread = (JThread *) (objs->fetch (i)); + jthr_id = jthread->jthr_id; + uint64_t grid = grids->fetch (i); + uint64_t expid = expids->fetch (i); + cur->v.val = (grid << INDXOBJ_EXPGRID_SHIFT) | + (expid << INDXOBJ_EXPID_SHIFT) | jthr_id; + if (i == 0) + break; + cur->arg0 = new Expression (OP_NONE, (uint64_t) 0); + cur->v.next = &cur->arg0->v; + cur = cur->arg0; + } + cur->v.next = NULL; + delete objs; + delete grids; + delete expids; + break; + } + case OP_FILE: + { + Vector<Histable*> *objs = NULL; + Histable::NameFormat nfmt = ctx ? ctx->dbev->get_name_format () : Histable::NA; + if (ctx) + objs = (Vector<Histable*>*)dbeSession->match_file_names ((char*) arg1->v.val, nfmt); + Expression *cur = new Expression (Expression::OP_NUM, (uint64_t) 0); + res = cur; + int i = objs ? objs->size () - 1 : -1; + for (; i >= 0; i--) + { + cur->v.val = objs->fetch (i)->id; + if (i == 0) + break; + cur->arg0 = new Expression (OP_NONE, (uint64_t) 0); + cur->v.next = &cur->arg0->v; + cur = cur->arg0; + } + cur->v.next = NULL; + if (objs) + delete objs; + break; + } + case OP_NUM: + case OP_COMMA: + res = copy (); + break; + case OP_IN: + case OP_SOMEIN: + case OP_ORDRIN: + { + // LIBRARY_VISIBILITY: + // Evaluate the arg0 of OP_IN, OP_SOMEIN, OP_ORDRIN to see if it has any library/loadobject + // Change it to OP_LIBRARY_IN, OP_LIBRARY_SOMEIN or OP_LIBRARY_ORDRIN respectively + if (dbeSession->is_lib_visibility_used () && (arg0->hasLoadObject () + || arg1->hasLoadObject ())) + { + OpCode new_op; + switch (op) + { + case OP_IN: + new_op = OP_LIBRARY_IN; + break; + case OP_SOMEIN: + new_op = OP_LIBRARY_SOMEIN; + break; + case OP_ORDRIN: + new_op = OP_LIBRARY_ORDRIN; + break; + default: + new_op = op; // Should never reach here + break; + } + if (arg1->hasLoadObject ()) + res = new Expression (new_op, arg1 ? arg1->pEval (ctx) : NULL, + arg0 ? arg0->pEval (ctx) : NULL); + else + res = new Expression (new_op, arg0 ? arg0->pEval (ctx) : NULL, + arg1 ? arg1->pEval (ctx) : NULL); + res->v = v; + ctx->dbev->setFilterHideMode (); + return res; + } + } + // no break; if no loadobjects found fall thru to the default case + default: + if (bEval (ctx)) + { + res = new Expression (OP_NUM, v.val); + break; + } + res = new Expression (op, arg0 ? arg0->pEval (ctx) : NULL, + arg1 ? arg1->pEval (ctx) : NULL); + res->v = v; + break; + } + return res; +} + +bool +Expression::verifyObjectInExpr (Histable *obj) +{ + uint64_t id = ((uint64_t) obj->id); + if (op == OP_NUM && v.val == id) + return true; + bool inArg0 = false; + bool inArg1 = false; + if (arg0 != NULL) + inArg0 = arg0->verifyObjectInExpr (obj); + if (inArg0) + return true; + if (arg1 != NULL) + inArg1 = arg1->verifyObjectInExpr (obj); + if (inArg1) + return true; + return false; +} + +bool +Expression::hasLoadObject () +{ + if (op == OP_NUM) + { + uint64_t id = v.val; + Histable *obj = dbeSession->findObjectById (id); + if (obj != NULL && obj->get_type () == Histable::FUNCTION) + { + Function *func = (Function *) obj; + if (func->isHideFunc) + return true; + } + } + if (arg0 && arg0->hasLoadObject ()) + return true; + if (arg1 && arg1->hasLoadObject ()) + return true; + return false; +} diff --git a/gprofng/src/Expression.h b/gprofng/src/Expression.h new file mode 100644 index 0000000..7542853 --- /dev/null +++ b/gprofng/src/Expression.h @@ -0,0 +1,180 @@ +/* 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. */ + +#ifndef _EXPRESSION_H +#define _EXPRESSION_H + +#include <inttypes.h> + +class Experiment; +class DataView; +class DbeView; +class Histable; + +class Expression +{ +public: + + class Context + { + public: + Context (DbeView *_dbev, Experiment *_exp = 0); + Context (DbeView *_dbev, Experiment *_exp, DataView *_dview, long _eventId); + + ~Context () { }; + + void + put (DataView *d, long id) + { + dview = d; + eventId = id; + }; + + void + put (Experiment *_exp) + { + exp = _exp; + }; + + Experiment *exp; + DataView *dview; + DbeView *dbev; + long eventId; + }; + + enum OpCode + { + OP_NONE, + OP_QWE, + OP_COLON, + OP_OR, + OP_AND, + OP_NOT, + OP_EQV, + OP_NEQV, + OP_BITOR, + OP_BITAND, + OP_BITXOR, + OP_BITNOT, + OP_EQ, + OP_NE, + OP_LT, + OP_GT, + OP_LE, + OP_GE, + OP_LS, + OP_RS, + OP_ADD, + OP_MINUS, + OP_MUL, + OP_DIV, + OP_REM, + OP_DEG, + OP_COMMA, + OP_IN, + OP_SOMEIN, + OP_ORDRIN, + OP_NUM, + OP_NAME, + OP_FUNC, + OP_FILE, + OP_JAVA, + OP_HASPROP, + OP_LIBRARY_IN, + OP_LIBRARY_SOMEIN, + OP_LIBRARY_ORDRIN + }; + + enum FuncCode + { + FUNC_FNAME, + FUNC_DNAME + }; + + enum JavaCode + { + JAVA_JGROUP, + JAVA_JPARENT + }; + + Expression (OpCode, const Expression*, const Expression* = 0); + Expression (OpCode, uint64_t); + Expression (const Expression &rhs); + Expression (const Expression *rhs); + Expression &operator= (const Expression &rhs); + ~Expression (); + + Expression * + copy () const + { + return new Expression (this); + } + void copy (const Expression *rhs); + + uint64_t + eval (Context *ctx) + { + return bEval (ctx) ? v.val : 0; + }; + + bool + passes (Context *ctx) + { + return bEval (ctx) ? v.val != 0 : true; + }; + + bool + complete () + { + return op == OP_NUM; + }; + + bool verifyObjectInExpr (Histable *obj); + Expression * + pEval (Context *ctx); // Partial evaluation to simplify expression + +private: + + struct Value + { + + Value (uint64_t _val = 0, Value *_next = 0) : val (_val), next (_next) + { + fn = 0; + } + uint64_t val; + uint64_t fn; + Value *next; + }; + + bool getVal (int propId, Context *ctx); + bool bEval (Context *ctx); + + bool hasLoadObject (); + void fixupValues (); + + OpCode op; + Value v; + Expression *arg0; + Expression *arg1; +}; + + +#endif /* _EXPRESSION_H */ diff --git a/gprofng/src/FileData.cc b/gprofng/src/FileData.cc new file mode 100644 index 0000000..7a941f5 --- /dev/null +++ b/gprofng/src/FileData.cc @@ -0,0 +1,400 @@ +/* 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 <assert.h> +#include <string.h> + +#include "util.h" +#include "FileData.h" + +void +FileData::init () +{ + readTime = 0; + writeTime = 0; + otherTime = 0; + errorTime = 0; + readBytes = 0; + writeBytes = 0; + readCnt = 0; + writeCnt = 0; + otherCnt = 0; + errorCnt = 0; + wSlowestBytes = 0; + wSmallestBytes = _10TB; + wLargestBytes = 0; + w0KB1KBCnt = 0; + w1KB8KBCnt = 0; + w8KB32KBCnt = 0; + w32KB128KBCnt = 0; + w128KB256KBCnt = 0; + w256KB512KBCnt = 0; + w512KB1000KBCnt = 0; + w1000KB10MBCnt = 0; + w10MB100MBCnt = 0; + w100MB1GBCnt = 0; + w1GB10GBCnt = 0; + w10GB100GBCnt = 0; + w100GB1TBCnt = 0; + w1TB10TBCnt = 0; + rSlowestBytes = 0; + rSmallestBytes = _10TB; + rLargestBytes = 0; + r0KB1KBCnt = 0; + r1KB8KBCnt = 0; + r8KB32KBCnt = 0; + r32KB128KBCnt = 0; + r128KB256KBCnt = 0; + r256KB512KBCnt = 0; + r512KB1000KBCnt = 0; + r1000KB10MBCnt = 0; + r10MB100MBCnt = 0; + r100MB1GBCnt = 0; + r1GB10GBCnt = 0; + r10GB100GBCnt = 0; + r100GB1TBCnt = 0; + r1TB10TBCnt = 0; +} + +FileData::FileData (const char *fName) +{ + fileName = dbe_strdup (fName); + fileDesList = new Vector<int>; + virtualFds = new Vector<int64_t>; + virtualFd = -1; + fileDes = -1; + fsType[0] = '\0'; + histType = Histable::IOACTVFD; + init (); +} + +FileData::FileData (FileData *fData) +{ + fileName = dbe_strdup (fData->fileName); + fileDesList = new Vector<int>; + Vector<int> *fdList = fData->fileDesList; + int fd; + if (fdList != NULL) + for (int i = 0; i < fdList->size (); i++) + if ((fd = fdList->fetch (i)) == -1) + fileDesList->append (fd); + + virtualFds = new Vector<int64_t>; + Vector<int64_t> *vfds = fData->virtualFds; + int64_t vfd; + if (vfds != NULL) + for (int i = 0; i < vfds->size (); i++) + if ((vfd = vfds->fetch (i)) == -1) + virtualFds->append (vfd); + virtualFd = fData->virtualFd; + fileDes = fData->fileDes; + histType = fData->histType; + + for (int i = 0; i < FSTYPESZ; i++) + fsType[i] = fData->fsType[i]; + + readTime = fData->readTime; + writeTime = fData->writeTime; + otherTime = fData->otherTime; + errorTime = fData->errorTime; + readBytes = fData->readBytes; + writeBytes = fData->writeBytes; + readCnt = fData->readCnt; + writeCnt = fData->writeCnt; + otherCnt = fData->otherCnt; + errorCnt = fData->errorCnt; + wSlowestBytes = fData->wSlowestBytes; + wSmallestBytes = fData->wSmallestBytes; + wLargestBytes = fData->wLargestBytes; + w0KB1KBCnt = fData->w0KB1KBCnt; + w1KB8KBCnt = fData->w1KB8KBCnt; + w8KB32KBCnt = fData->w8KB32KBCnt; + w32KB128KBCnt = fData->w32KB128KBCnt; + w128KB256KBCnt = fData->w128KB256KBCnt; + w256KB512KBCnt = fData->w256KB512KBCnt; + w512KB1000KBCnt = fData->w512KB1000KBCnt; + w1000KB10MBCnt = fData->w1000KB10MBCnt; + w10MB100MBCnt = fData->w10MB100MBCnt; + w100MB1GBCnt = fData->w100MB1GBCnt; + w1GB10GBCnt = fData->w1GB10GBCnt; + w10GB100GBCnt = fData->w10GB100GBCnt; + w100GB1TBCnt = fData->w100GB1TBCnt; + w1TB10TBCnt = fData->w1TB10TBCnt; + rSlowestBytes = fData->rSlowestBytes; + rSmallestBytes = fData->rSmallestBytes; + rLargestBytes = fData->rLargestBytes; + r0KB1KBCnt = fData->r0KB1KBCnt; + r1KB8KBCnt = fData->r1KB8KBCnt; + r8KB32KBCnt = fData->r8KB32KBCnt; + r32KB128KBCnt = fData->r32KB128KBCnt; + r128KB256KBCnt = fData->r128KB256KBCnt; + r256KB512KBCnt = fData->r256KB512KBCnt; + r512KB1000KBCnt = fData->r512KB1000KBCnt; + r1000KB10MBCnt = fData->r1000KB10MBCnt; + r10MB100MBCnt = fData->r10MB100MBCnt; + r100MB1GBCnt = fData->r100MB1GBCnt; + r1GB10GBCnt = fData->r1GB10GBCnt; + r10GB100GBCnt = fData->r10GB100GBCnt; + r100GB1TBCnt = fData->r100GB1TBCnt; + r1TB10TBCnt = fData->r1TB10TBCnt; +} + +FileData::~FileData () +{ + free (fileName); + delete fileDesList; + delete virtualFds; +} + +void +FileData::setVirtualFds (int64_t vfd) +{ + for (int i = 0; i < virtualFds->size (); i++) + if (vfd == virtualFds->fetch (i)) + return; + virtualFds->append (vfd); +} + +void +FileData::setFileDesList (int fd) +{ + for (int i = 0; i < fileDesList->size (); i++) + if (fd == fileDesList->fetch (i)) + return; + fileDesList->append (fd); +} + +void +FileData::setFsType (const char* fst) +{ + size_t len = strlen (fst); + if (len > 0 && len < FSTYPESZ) + snprintf (fsType, sizeof (fsType), NTXT ("%s"), fst); + else + snprintf (fsType, sizeof (fsType), GTXT ("error")); +} + +Histable* +FileData::convertto (Histable_type type, Histable*) +{ + return (type == histType ? this : NULL); +} + +char* +FileData::get_name (Histable::NameFormat /*_nfmt*/) +{ + if (histType == Histable::IOACTVFD) + { + if (!streq (fileName, NTXT ("<Total>"))) + { + if (fileDes >= 0) + return dbe_sprintf (GTXT ("%s (IOVFD=%lld, FD=%d)"), fileName, + (long long) virtualFd, (int) fileDes); + return dbe_sprintf (GTXT ("%s (IOVFD=%lld)"), fileName, + (long long) virtualFd); + } + else + return fileName; + } + else if (histType == Histable::IOACTFILE) + { + if (!streq (fileName, NTXT ("<Total>"))) + { + if (!streq (fsType, NTXT ("N/A"))) + return dbe_sprintf (GTXT ("%s (FS=%s)"), fileName, fsType); + return fileName; + } + return fileName; + } + return fileName; +} + +char* +FileData::get_raw_name (Histable::NameFormat /*_nfmt*/) +{ + return fileName; +} + +void +FileData::setFsType (FileSystem_type fst) +{ + const char *fsName; + switch (fst) + { + case ZFS_TYPE: + fsName = "zfs"; + break; + case NFS_TYPE: + fsName = "nfs"; + break; + case UFS_TYPE: + fsName = "ufs"; + break; + case UDFS_TYPE: + fsName = "udfs"; + break; + case LOFS_TYPE: + fsName = "lofs"; + break; + case VXFS_TYPE: + fsName = "vxfs"; + break; + case TMPFS_TYPE: + fsName = "tmpfs"; + break; + case PCFS_TYPE: + fsName = "pcfs"; + break; + case HSFS_TYPE: + fsName = "hsfs"; + break; + case PROCFS_TYPE: + fsName = "procfs"; + break; + case FIFOFS_TYPE: + fsName = "fifofs"; + break; + case SWAPFS_TYPE: + fsName = "swapfs"; + break; + case CACHEFS_TYPE: + fsName = "cachefs"; + break; + case AUTOFS_TYPE: + fsName = "autofs"; + break; + case SPECFS_TYPE: + fsName = "specfs"; + break; + case SOCKFS_TYPE: + fsName = "sockfs"; + break; + case FDFS_TYPE: + fsName = "fdfs"; + break; + case MNTFS_TYPE: + fsName = "mntfs"; + break; + case NAMEFS_TYPE: + fsName = "namefs"; + break; + case OBJFS_TYPE: + fsName = "objfs"; + break; + case SHAREFS_TYPE: + fsName = "sharefs"; + break; + case EXT2FS_TYPE: + fsName = "ext2"; + break; + case EXT3FS_TYPE: + fsName = "ext3"; + break; + case EXT4FS_TYPE: + fsName = "ext4"; + break; + case UNKNOWNFS_TYPE: + fsName = "N/A"; + break; + default: + fsName = "N/A"; + break; + } + setFsType (fsName); +} + +void +FileData::setWriteStat (hrtime_t wt, int64_t nb) +{ + if (wSlowestBytes < wt) + wSlowestBytes = wt; + if (nb != 0 && wSmallestBytes > nb) + wSmallestBytes = nb; + if (wLargestBytes < nb) + wLargestBytes = nb; + if (nb >= 0 && nb <= _1KB) + w0KB1KBCnt++; + else if (nb <= _8KB) + w1KB8KBCnt++; + else if (nb <= _32KB) + w8KB32KBCnt++; + else if (nb <= _128KB) + w32KB128KBCnt++; + else if (nb <= _256KB) + w128KB256KBCnt++; + else if (nb <= _512KB) + w256KB512KBCnt++; + else if (nb <= _1000KB) + w512KB1000KBCnt++; + else if (nb <= _10MB) + w1000KB10MBCnt++; + else if (nb <= _100MB) + w10MB100MBCnt++; + else if (nb <= _1GB) + w100MB1GBCnt++; + else if (nb <= _10GB) + w1GB10GBCnt++; + else if (nb <= _100GB) + w10GB100GBCnt++; + else if (nb <= _1TB) + w100GB1TBCnt++; + else if (nb <= _10TB) + w1TB10TBCnt++; +} + +void +FileData::setReadStat (hrtime_t rt, int64_t nb) +{ + if (rSlowestBytes < rt) + rSlowestBytes = rt; + if (nb != 0 && rSmallestBytes > nb) + rSmallestBytes = nb; + if (rLargestBytes < nb) + rLargestBytes = nb; + if (nb >= 0 && nb <= _1KB) + r0KB1KBCnt++; + else if (nb <= _8KB) + r1KB8KBCnt++; + else if (nb <= _32KB) + r8KB32KBCnt++; + else if (nb <= _128KB) + r32KB128KBCnt++; + else if (nb <= _256KB) + r128KB256KBCnt++; + else if (nb <= _512KB) + r256KB512KBCnt++; + else if (nb <= _1000KB) + r512KB1000KBCnt++; + else if (nb <= _10MB) + r1000KB10MBCnt++; + else if (nb <= _100MB) + r10MB100MBCnt++; + else if (nb <= _1GB) + r100MB1GBCnt++; + else if (nb <= _10GB) + r1GB10GBCnt++; + else if (nb <= _100GB) + r10GB100GBCnt++; + else if (nb <= _1TB) + r100GB1TBCnt++; + else if (nb <= _10TB) + r1TB10TBCnt++; +} diff --git a/gprofng/src/FileData.h b/gprofng/src/FileData.h new file mode 100644 index 0000000..67de689 --- /dev/null +++ b/gprofng/src/FileData.h @@ -0,0 +1,522 @@ +/* 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. */ + +#ifndef _FILEDATA_H +#define _FILEDATA_H + +#include "gp-defs.h" +#include "gp-time.h" + +#include "vec.h" +#include "data_pckts.h" +#include "Histable.h" + +#define FSTYPESZ 16 + +#define VIRTUAL_FD_TOTAL 0 +#define VIRTUAL_FD_STDIN 1 +#define VIRTUAL_FD_STDOUT 2 +#define VIRTUAL_FD_STDERR 3 +#define VIRTUAL_FD_OTHERIO 4 +#define VIRTUAL_FD_NONE -1 + +#define STDIN_FD 0 +#define STDOUT_FD 1 +#define STDERR_FD 2 +#define OTHERIO_FD -1 + +#define OTHERIO_FILENAME "<Other IO activity>" +#define STDIN_FILENAME "<stdin>" +#define STDOUT_FILENAME "<stdout>" +#define STDERR_FILENAME "<stderr>" +#define TOTAL_FILENAME NTXT("<Total>") +#define UNKNOWNFD_FILENAME "<pipe(), socket(), or other fds>" + +#define _1KB 1024 +#define _8KB 8192 +#define _32KB 32768 +#define _128KB 131072 +#define _256KB 262144 +#define _512KB 524288 +#define _1000KB 1048576 +#define _10MB 10485760 +#define _100MB 104857600 +#define _1GB 1073741824 +#define _10GB 10737418240 +#define _100GB 107374182400 +#define _1TB 1099511627776 +#define _10TB 10995116277760 + +class FileData : public Histable +{ + friend class IOActivity; +public: + FileData (const char *fName); + FileData (FileData *fData); + ~FileData (); + + virtual char *get_name (Histable::NameFormat nfmt); + virtual Histable *convertto (Histable_type, Histable* = NULL); + + char *get_raw_name (Histable::NameFormat nfmt); + void setFsType (FileSystem_type fst); + void setFsType (const char* fst); + + virtual Histable_type + get_type () + { + return histType; + }; + + virtual uint64_t + get_addr () + { + return virtualFd; + }; + + uint64_t + get_index () + { + return virtualFd; + }; + + void init (); + + char * + getFileName () + { + return fileName; + } + + void + addReadEvent (hrtime_t rt, int64_t nb) + { + readTime += rt; + readBytes += nb; + readCnt++; + } + + hrtime_t + getReadTime () + { + return readTime; + } + + int64_t + getReadBytes () + { + return readBytes; + } + + int32_t + getReadCnt () + { + return readCnt; + } + + void + addWriteEvent (hrtime_t wt, int64_t nb) + { + writeTime += wt; + writeBytes += nb; + writeCnt++; + } + + hrtime_t + getWriteTime () + { + return writeTime; + } + + int64_t + getWriteBytes () + { + return writeBytes; + } + + int32_t + getWriteCnt () + { + return writeCnt; + } + + void + addOtherEvent (hrtime_t ot) + { + otherTime += ot; + otherCnt++; + } + + hrtime_t + getOtherTime () + { + return otherTime; + } + + int32_t + getOtherCnt () + { + return otherCnt; + } + + void + addErrorEvent (hrtime_t er) + { + errorTime += er; + errorCnt++; + } + + hrtime_t + getErrorTime () + { + return errorTime; + } + + int32_t + getErrorCnt () + { + return errorCnt; + } + + void setFileDesList (int fd); + + Vector<int> * + getFileDesList () + { + return fileDesList; + } + + void + setFileDes (int fd) + { + fileDes = fd; + } + + int32_t + getFileDes () + { + return fileDes; + } + + void setVirtualFds (int64_t vfd); + + Vector<int64_t> * + getVirtualFds () + { + return virtualFds; + } + + char * + getFsType () + { + return fsType; + } + + void + setVirtualFd (int64_t vFd) + { + virtualFd = vFd; + } + + int64_t + getVirtualFd () + { + return virtualFd; + } + + void + setHistType (Histable::Type hType) + { + histType = hType; + } + + Histable::Type + getHistType () + { + return histType; + } + + void setWriteStat (hrtime_t wt, int64_t nb); + + hrtime_t + getWSlowestBytes () + { + return wSlowestBytes; + } + + int64_t + getWSmallestBytes () + { + return wSmallestBytes; + } + + int64_t + getWLargestBytes () + { + return wLargestBytes; + } + + int32_t + getW0KB1KBCnt () + { + return w0KB1KBCnt; + } + + int32_t + getW1KB8KBCnt () + { + return w1KB8KBCnt; + } + + int32_t + getW8KB32KBCnt () + { + return w8KB32KBCnt; + } + + int32_t + getW32KB128KBCnt () + { + return w32KB128KBCnt; + } + + int32_t + getW128KB256KBCnt () + { + return w128KB256KBCnt; + } + + int32_t + getW256KB512KBCnt () + { + return w256KB512KBCnt; + } + + int32_t + getW512KB1000KBCnt () + { + return w512KB1000KBCnt; + } + + int32_t + getW1000KB10MBCnt () + { + return w1000KB10MBCnt; + } + + int32_t + getW10MB100MBCnt () + { + return w10MB100MBCnt; + } + + int32_t + getW100MB1GBCnt () + { + return w100MB1GBCnt; + } + + int32_t + getW1GB10GBCnt () + { + return w1GB10GBCnt; + } + + int32_t + getW10GB100GBCnt () + { + return w10GB100GBCnt; + } + + int32_t + getW100GB1TBCnt () + { + return w100GB1TBCnt; + } + + int32_t + getW1TB10TBCnt () + { + return w1TB10TBCnt; + } + + void setReadStat (hrtime_t rt, int64_t nb); + + hrtime_t + getRSlowestBytes () + { + return rSlowestBytes; + } + + int64_t + getRSmallestBytes () + { + return rSmallestBytes; + } + + int64_t + getRLargestBytes () + { + return rLargestBytes; + } + + int32_t + getR0KB1KBCnt () + { + return r0KB1KBCnt; + } + + int32_t + getR1KB8KBCnt () + { + return r1KB8KBCnt; + } + + int32_t + getR8KB32KBCnt () + { + return r8KB32KBCnt; + } + + int32_t + getR32KB128KBCnt () + { + return r32KB128KBCnt; + } + + int32_t + getR128KB256KBCnt () + { + return r128KB256KBCnt; + } + + int32_t + getR256KB512KBCnt () + { + return r256KB512KBCnt; + } + + int32_t + getR512KB1000KBCnt () + { + return r512KB1000KBCnt; + } + + int32_t + getR1000KB10MBCnt () + { + return r1000KB10MBCnt; + } + + int32_t + getR10MB100MBCnt () + { + return r10MB100MBCnt; + } + + int32_t + getR100MB1GBCnt () + { + return r100MB1GBCnt; + } + + int32_t + getR1GB10GBCnt () + { + return r1GB10GBCnt; + } + + int32_t + getR10GB100GBCnt () + { + return r10GB100GBCnt; + } + + int32_t + getR100GB1TBCnt () + { + return r100GB1TBCnt; + } + + int32_t + getR1TB10TBCnt () + { + return r1TB10TBCnt; + } + +private: + char *fileName; // File name + hrtime_t readTime; // The Total time for read operations; + hrtime_t writeTime; // The Total time for write operations; + hrtime_t otherTime; // The Total time for other IO operations; + hrtime_t errorTime; // The Total time for failed IO operations; + int64_t readBytes; //The total bytes read + int64_t writeBytes; //The total bytes written + int32_t readCnt; // The read count + int32_t writeCnt; // The write count + int32_t otherCnt; // The other IO count + int32_t errorCnt; // The failed IO count + Vector<int> *fileDesList; // The list of file descriptors + Vector<int64_t> *virtualFds; // The list of file virtual descriptors + char fsType[FSTYPESZ]; // The file system type + int64_t virtualFd; // The virtual file descriptor + int32_t fileDes; // The file descriptor + Histable::Type histType; // The Histable type: IOACTFILE, IOACTVFD, ... + + // Write statistics + hrtime_t wSlowestBytes; + int64_t wSmallestBytes; + int64_t wLargestBytes; + int32_t w0KB1KBCnt; + int32_t w1KB8KBCnt; + int32_t w8KB32KBCnt; + int32_t w32KB128KBCnt; + int32_t w128KB256KBCnt; + int32_t w256KB512KBCnt; + int32_t w512KB1000KBCnt; + int32_t w1000KB10MBCnt; + int32_t w10MB100MBCnt; + int32_t w100MB1GBCnt; + int32_t w1GB10GBCnt; + int32_t w10GB100GBCnt; + int32_t w100GB1TBCnt; + int32_t w1TB10TBCnt; + + // Read statistics + hrtime_t rSlowestBytes; + int64_t rSmallestBytes; + int64_t rLargestBytes; + int32_t r0KB1KBCnt; + int32_t r1KB8KBCnt; + int32_t r8KB32KBCnt; + int32_t r32KB128KBCnt; + int32_t r128KB256KBCnt; + int32_t r256KB512KBCnt; + int32_t r512KB1000KBCnt; + int32_t r1000KB10MBCnt; + int32_t r10MB100MBCnt; + int32_t r100MB1GBCnt; + int32_t r1GB10GBCnt; + int32_t r10GB100GBCnt; + int32_t r100GB1TBCnt; + int32_t r1TB10TBCnt; +}; + +#endif diff --git a/gprofng/src/Filter.cc b/gprofng/src/Filter.cc new file mode 100644 index 0000000..34eda0d --- /dev/null +++ b/gprofng/src/Filter.cc @@ -0,0 +1,514 @@ +/* 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 <stdio.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include "Filter.h" +#include "util.h" +#include "i18n.h" +#include "data_pckts.h" +#include "StringBuilder.h" +#include "Experiment.h" + + +// ======================================================================== +// Subclass: FilterNumeric +// Public Methods + +FilterNumeric::FilterNumeric (Experiment *_exp, const char *_cmd, + const char *_name) +{ + exp = _exp; + cmd = dbe_strdup (_cmd); + name = dbe_strdup (_name); + pattern = NULL; + status = NULL; + items = NULL; + prop_name = NULL; + first = (uint64_t) - 1; + last = (uint64_t) - 1; + nselected = 0; + nitems = 0; +} + +FilterNumeric::~FilterNumeric () +{ + free (cmd); + free (name); + free (pattern); + free (status); + Destroy (items); +} + +// sets min and max for this filter; should be called when the range is +// known -- that comes after the first PathTree build, in the current +// sequence of things +void +FilterNumeric::set_range (uint64_t findex, uint64_t lindex, uint64_t total) +{ + if (first == findex && last == lindex) + return; + first = findex; + last = lindex; + nitems = total; + nselected = nitems; + if (pattern) + { + free (pattern); + pattern = NULL; + } + if (status) + { + free (status); + status = NULL; + } +} + +void +FilterNumeric::update_range () +{ + if (exp == NULL) + return; + if (streq (cmd, NTXT ("sample"))) + set_range (1, (uint64_t) exp->nsamples (), exp->nsamples ()); + else if (streq (cmd, NTXT ("thread"))) + set_range (exp->min_thread, exp->max_thread, exp->thread_cnt); + else if (streq (cmd, NTXT ("LWP"))) + set_range (exp->min_lwp, exp->max_lwp, exp->lwp_cnt); + else if (streq (cmd, NTXT ("cpu"))) + { + if (exp->min_cpu != (uint64_t) - 1) + set_range (exp->min_cpu, exp->max_cpu, exp->cpu_cnt); + } +} + +// get_advanced_filter -- returns a string matching the current setting +char * +FilterNumeric::get_advanced_filter () +{ + if (items == NULL) + return NULL; + if (items->size () == 0) + return dbe_strdup (NTXT ("0")); + + StringBuilder sb; + if (items->size () > 1) + sb.append ('('); + for (int i = 0; i < items->size (); i++) + { + RangePair *rp = items->fetch (i); + if (i > 0) + sb.append (NTXT (" || ")); + sb.append ('('); + sb.append (prop_name); + if (rp->first == rp->last) + { + sb.append (NTXT ("==")); + sb.append ((long long) rp->first); + } + else + { + sb.append (NTXT (">=")); + sb.append ((long long) rp->first); + sb.append (NTXT (" && ")); + sb.append (prop_name); + sb.append (NTXT ("<=")); + sb.append ((long long) rp->last); + } + sb.append (')'); + } + if (items->size () > 1) + sb.append (')'); + return sb.toString (); +} + + +// get_pattern -- returns a string matching the current setting + +char * +FilterNumeric::get_pattern () +{ + update_range (); + if (pattern) + return pattern; + StringBuilder sb; + if (items == NULL) + { + if (last == (uint64_t) - 1 && last == first) + // neither set; data not available + sb.append (GTXT ("(data not recorded)")); + else + sb.append (GTXT ("all")); + } + else if (items->size () == 0) + sb.append (GTXT ("none")); + else + { + for (int i = 0; i < items->size (); i++) + { + RangePair *rp = items->fetch (i); + if (i > 0) + sb.append (','); + sb.append ((long long) rp->first); + if (rp->first != rp->last) + { + sb.append ('-'); + sb.append ((long long) rp->last); + } + } + } + pattern = sb.toString (); + return pattern; +} + +char * +FilterNumeric::get_status () +{ + update_range (); + if (status == NULL) + update_status (); + return dbe_strdup (status); +} + +// set_pattern -- set the filter to a new pattern +// set error true/false if there was or was not an error parsing string +// Returns true/false if the filter changed, implying a rebuild of data +bool +FilterNumeric::set_pattern (char *str, bool *error) +{ + update_range (); + // save the old filter + Vector<RangePair *> *olditems = items; + *error = false; + if (strcmp (str, NTXT ("all")) == 0) + // if all, leave items NULL + items = NULL; + else if (strcmp (str, NTXT ("none")) == 0) + // if none, leave items as a zero-length vector + items = new Vector<RangePair *>(0); + else + { + uint64_t val, val2; + char *s = str; + char *nexts = s; + items = NULL; + for (bool done = false; done == false;) + { + // tokenize the string + // Does it start with a "-" ? + if (*nexts == '-') + val = first; // yes, set val to first, and see what follows + else + { + // it must start with a number + val = get_next_number (s, &nexts, error); + if (*error == true) + break; + } + + // look at the next character + switch (*nexts) + { + case ',': + s = ++nexts; + *error = include_range (val, val); + if (*error == true) + done = true; + break; + case '-': + s = ++nexts; + if (*nexts == ',' || *nexts == '\0') + val2 = last; + else + { + val2 = get_next_number (s, &nexts, error); + if (*error == true) + { + done = true; + break; + } + } + if (val > val2) + { + *error = true; + done = true; + break; + } + *error = include_range (val, val2); + if (*error == true) + { + done = true; + break; + } + if (*nexts == ',') + { + s = ++nexts; + break; + } + if (*nexts == '\0') + { + done = true; + break; + } + break; + case '\0': + *error = include_range (val, val); + done = true; + break; + default: + *error = true; + done = true; + break; + } + } + // if there was a parser error leave old setting + if (*error == true) + { + if (items) + { + items->destroy (); + delete items; + } + items = olditems; + return false; + } + } + + if (first != (uint64_t) - 1 && last != (uint64_t) - 1) + { + for (long i = VecSize (items) - 1; i >= 0; i--) + { + RangePair *rp = items->get (i); + if ((rp->first > last) || (rp->last < first)) + { + delete rp; + items->remove (i); + continue; + } + if (rp->first < first) + rp->first = first; + if (rp->last > last) + rp->last = last; + } + if (VecSize (items) == 1) + { + RangePair *rp = items->get (0); + if ((rp->first == first) && (rp->last == last)) + { + // All, leave items NULL + items->destroy (); + delete items; + items = NULL; + } + } + } + + // no error, delete the old setting + if (olditems != NULL) + { + olditems->destroy (); + delete olditems; + } + + bool changed; + // regenerate the pattern + if (pattern == NULL) + changed = true; + else + { + char *oldpattern = pattern; + pattern = NULL; // to force a recompute with new values + (void) get_pattern (); + changed = strcmp (pattern, oldpattern) != 0; + free (oldpattern); + } + return changed; +} + +//================================================================ +// Protected methods + +// set_status -- regenerate the status line, describing the current setting +void +FilterNumeric::update_status () +{ + // regenerate the status line + free (status); + nselected = 0; + if (items == NULL) + { + if (last == (uint64_t) - 1 && last == first) + // neither set; data not available + status = dbe_sprintf (GTXT ("(data not recorded)")); + else if (first == (uint64_t) - 1 || last == (uint64_t) - 1) + // range was not set + status = dbe_sprintf (GTXT ("(all)")); + else + // range was set, compute percentage + status = dbe_sprintf (GTXT ("total %lld, range: %lld-%lld"), + (long long) nitems, (long long) first, + (long long) last); + } + else + { + // some are selected + int index; + RangePair *rp; + Vec_loop (RangePair *, items, index, rp) + { + nselected += rp->last - rp->first + 1; + } + if (last == (uint64_t) - 1) + // range was not set + status = dbe_sprintf (GTXT ("(%lld items selected)"), + (long long) nselected); + else + // range was set + status = dbe_sprintf (GTXT ("total %lld, range: %lld-%lld"), + (long long) nitems, (long long) first, + (long long) last); + } +} + +// Add a range to the filter; called from set_pattern for each index, +// or index pair +bool +FilterNumeric::include_range (uint64_t findex, uint64_t lindex) +{ + int index; + RangePair *rp; + if (findex > lindex) + return true; + + bool done = false; + if (items == NULL) + items = new Vector<RangePair *>(0); + + Vec_loop (RangePair *, items, index, rp) + { + if (findex < rp->first) + { + // Case where the new pair starts before the old + if (lindex + 1 < rp->first) + { + // this pair comes cleanly in front of the current item + RangePair *rp2 = new RangePair (); + rp2->first = findex; + rp2->last = lindex; + items->insert (index, rp2); + done = true; + break; + } + // This new one extends the previous from the front + rp->first = findex; +chkextend: + if (lindex <= rp->last) + { + // but does not extend the back + done = true; + break; + } + // extend this one out + rp->last = lindex; + + // does it go into the next range? + if (index == items->size () - 1) + { + // this is the last range, so it does not + done = true; + break; + } + RangePair *next = items->fetch (index + 1); + if (lindex + 1 < next->first) + { + // no extension, we're done + done = true; + break; + } + // it does extend the next one + next->first = rp->first; + rp = next; + // remove the current one, promoting next + items->remove (index); + goto chkextend; + } + else if (findex > rp->last + 1) + // the new one is completely beyond the current + continue; + else + { + // the new one may start at or after the current, but it + // extends it out; set the current + // this pair overlaps the current item + // rp-> first is OK -- it's equal or less than findex + goto chkextend; + } + } + + if (done != true) + { + // fall through -- append to list + rp = new RangePair (); + rp->first = findex; + rp->last = lindex; + items->append (rp); + } + + return false; +} + +// Scan the filter to see if the number given is filtered in or out +// return true if number is in, false if it's out +bool +FilterNumeric::is_selected (uint64_t number) +{ + int index; + RangePair *rp; + if (items == NULL) + return true; + if (items->size () == 0) + return false; + + Vec_loop (RangePair *, items, index, rp) + { + if (number >= rp->first && number <= rp->last) + return true; + } + return false; +} + +// get_next_number +// Called from parser to extract a number from the current string position +// Sets fail true if there was an error, false otherwise +// returns the number as parsed +uint64_t +FilterNumeric::get_next_number (char *s, char **e, bool *fail) +{ + errno = 0; + *fail = false; + uint64_t val = strtoll (s, e, 10); + if (errno == EINVAL) + *fail = true; + return (val); +} diff --git a/gprofng/src/Filter.h b/gprofng/src/Filter.h new file mode 100644 index 0000000..1338218 --- /dev/null +++ b/gprofng/src/Filter.h @@ -0,0 +1,111 @@ +/* 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. */ + +#ifndef _FILTER_H +#define _FILTER_H + +// A sample selection specifies a set of samples the user is interested in +// viewing as a whole. + +#include "vec.h" +#include "data_pckts.h" + +class Experiment; + +class FilterNumeric +{ +public: + FilterNumeric (Experiment *, const char *, const char *); + ~FilterNumeric (); + + // set or update the range of items first and last + void set_range (uint64_t findex, uint64_t lindex, uint64_t total); + + // Return a string representation of the current ranges + // E.g. "1-5,7,9,10,12-13,73" + char *get_pattern (); + + // Return a string for the current status: %, range, ... + // E.g. "100%" "100% [1-7]" "25% [1-4]" + char *get_status (); + + char *get_advanced_filter (); + + // Sets selection according to the string representation + // See above for return values and error handling + bool set_pattern (char *, bool *); + + // Return true if "number" is included in selection + bool is_selected (uint64_t number); + + char * + get_cmd () + { + return cmd; + }; + + char * + get_name () + { + return name; + }; + + uint64_t + nelem () + { + return nitems; + }; + + char *prop_name; // name in advanced filter + +private: + + typedef struct + { + uint64_t first; + uint64_t last; + } RangePair; + + void update_status (); + void update_range (); + + // Include "range" in selection + bool include_range (uint64_t findex, uint64_t lindex); + + // Parse a number from the string + uint64_t get_next_number (char *s, char **e, bool *fail); + + // Data + Vector<RangePair *> *items; // sorted array of items + uint64_t nselected; + uint64_t nitems; + + Experiment *exp; + char *cmd; + char *name; + char *pattern; + char *status; + + // First and Last items in selection + uint64_t first; + uint64_t last; +}; + +#endif /* _FILTER_H */ diff --git a/gprofng/src/FilterExp.h b/gprofng/src/FilterExp.h new file mode 100644 index 0000000..b186af1 --- /dev/null +++ b/gprofng/src/FilterExp.h @@ -0,0 +1,56 @@ +/* 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. */ + +#ifndef _FILTEREXP_H +#define _FILTEREXP_H + +#include "Expression.h" + +class FilterExp +{ +public: + + FilterExp (Expression *_expr, Expression::Context *_ctx, bool _noParFilter) : + expr (_expr), ctx (_ctx), noParFilter (_noParFilter) { }; + + ~FilterExp () + { + delete ctx; + } + + bool + passes () + { + return expr ? expr->passes (ctx) : true; + } + + void + put (DataView *dview, long eventId) + { + ctx->put (dview, eventId); + } + + Expression *expr; + Expression::Context *ctx; + bool noParFilter; +}; + + +#endif /* _FILTEREXP_H */ diff --git a/gprofng/src/FilterSet.cc b/gprofng/src/FilterSet.cc new file mode 100644 index 0000000..29dc437 --- /dev/null +++ b/gprofng/src/FilterSet.cc @@ -0,0 +1,106 @@ +/* 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 "Experiment.h" +#include "StringBuilder.h" +#include "FilterSet.h" +#include "Filter.h" +#include "i18n.h" + +FilterSet::FilterSet (DbeView *_dbev, Experiment *_exp) +{ + dbev = _dbev; + exp = _exp; + enbl = false; + dfilter = new Vector<FilterNumeric *>; + FilterNumeric *f; + f = new FilterNumeric (exp, "sample", GTXT ("Samples")); + f->prop_name = NTXT ("SAMPLE_MAP"); + dfilter->append (f); + f = new FilterNumeric (exp, "thread", GTXT ("Threads")); + f->prop_name = NTXT ("THRID"); + dfilter->append (f); + f = new FilterNumeric (exp, "LWP", GTXT ("LWPs")); + f->prop_name = NTXT ("LWPID"); + dfilter->append (f); + f = new FilterNumeric (exp, "cpu", GTXT ("CPUs")); + f->prop_name = NTXT ("CPUID"); + dfilter->append (f); + f = new FilterNumeric (exp, "gcevent", GTXT ("GCEvents")); + f->prop_name = NTXT ("GCEVENT_MAP"); + dfilter->append (f); // must add new numeric below +} + +FilterSet::~FilterSet () +{ + dfilter->destroy (); + delete dfilter; +} + +FilterNumeric * +FilterSet::get_filter (int index) +{ + if (index < dfilter->size () && index >= 0) + return dfilter->fetch (index); + return NULL; +} + +char * +FilterSet::get_advanced_filter () +{ + StringBuilder sb; + bool filtrIsFalse = false; + + if (get_enabled ()) + { + Vector<FilterNumeric*> *filts = get_all_filters (); + if (filts == NULL) + return NULL; + for (int i = 0; i < filts->size (); i++) + { + FilterNumeric *f = filts->fetch (i); + if (f == NULL) + continue; + char *s = f->get_advanced_filter (); + if (s == NULL) + continue; + if (streq (s, NTXT ("0"))) + { + free (s); + sb.setLength (0); + filtrIsFalse = true; + break; + } + if (sb.length () != 0) + sb.append (NTXT (" && ")); + sb.append (s); + free (s); + } + } + else + filtrIsFalse = true; + if (filtrIsFalse) + sb.append ('0'); + else if (sb.length () == 0) + return NULL; + return sb.toString (); +} + diff --git a/gprofng/src/FilterSet.h b/gprofng/src/FilterSet.h new file mode 100644 index 0000000..6908565 --- /dev/null +++ b/gprofng/src/FilterSet.h @@ -0,0 +1,72 @@ +/* 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. */ + +#ifndef _FILTERSET_H +#define _FILTERSET_H + +#include "dbe_types.h" +#include "vec.h" + +class Experiment; +class FilterNumeric; +class DbeView; + +#define SAMPLE_FILTER_IDX 0 +#define THREAD_FILTER_IDX 1 +#define LWP_FILTER_IDX 2 +#define CPU_FILTER_IDX 3 + +class FilterSet +{ +public: + + FilterSet (DbeView *_dbev, Experiment *_exp); + ~FilterSet (); + char *get_advanced_filter (); + FilterNumeric *get_filter (int); + + bool + get_enabled () + { + return enbl; + } + + void + set_enabled (bool b) + { + enbl = b; + } + + Vector<FilterNumeric*> * + get_all_filters () + { + return dfilter; + } + +private: + + DbeView *dbev; + Experiment *exp; + bool enbl; + Vector<FilterNumeric*> *dfilter; +}; + +#endif /* _FILTERSET_H */ + diff --git a/gprofng/src/Function.cc b/gprofng/src/Function.cc new file mode 100644 index 0000000..b0e4a8f --- /dev/null +++ b/gprofng/src/Function.cc @@ -0,0 +1,1160 @@ +/* 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 <assert.h> +#include <string.h> +#include <ctype.h> + +#include "demangle.h" +#include "util.h" +#include "DbeSession.h" +#include "Function.h" +#include "Module.h" +#include "LoadObject.h" +#include "Settings.h" +#include "DbeFile.h" +#include "DbeView.h" + +struct SrcInfo +{ + DbeLine *src_line; + SrcInfo *included_from; + SrcInfo *next; +}; + +struct PCInfo +{ + int64_t offset; + int64_t size; + SrcInfo *src_info; +}; + +Function::Function (uint64_t _id) +{ + id = _id; + instr_id = id << 32; + derivedNode = NULL; + module = NULL; + line_first = line_last = -1; + isOutlineFunction = false; + name = NULL; + mangled_name = NULL; + match_name = NULL; + comparable_name = NULL; + img_fname = NULL; + img_offset = 0; + chksum = 0; + flags = 0; + size = 0; + save_addr = FUNC_NO_SAVE; + linetab = NULL; + def_source = NULL; + indexStabsLink = NULL; + elfSym = NULL; + sources = NULL; + instrs = new Vector<DbeInstr*>; + addrs = NULL; + name_buf = NULL; + current_name_format = Histable::NA; + curr_srcinfo = NULL; + curr_srcfile = NULL; + srcinfo_list = NULL; + defaultDbeLine = NULL; + usrfunc = NULL; + alias = NULL; + instHTable = NULL; + addrIndexHTable = NULL; + isUsed = false; + isHideFunc = false; + inlinedSubr = NULL; + inlinedSubrCnt = 0; +} + +Function::~Function () +{ + free (mangled_name); + free (match_name); + free (comparable_name); + free (name_buf); + Destroy (linetab); + Destroy (instrs); + + while (srcinfo_list) + { + SrcInfo *t = srcinfo_list; + srcinfo_list = t->next; + delete t; + } + delete sources; + delete addrs; + delete[] instHTable; + delete[] addrIndexHTable; + if (indexStabsLink) + // Remove a link to the current function + indexStabsLink->indexStabsLink = NULL; +} + +char * +Function::get_name (NameFormat nfmt) +{ + if (nfmt == Histable::NA) + { + DbeView *dbeView = dbeSession->getView (0); + if (dbeView) + nfmt = dbeView->get_name_format (); + } + if (name_buf && (nfmt == current_name_format || nfmt == Histable::NA)) + return name_buf; + free (name_buf); + current_name_format = nfmt; + + bool soname_fmt = Histable::soname_fmt (nfmt); + int fname_fmt = Histable::fname_fmt (nfmt); + if (fname_fmt == Histable::MANGLED) + name_buf = strdup (mangled_name); + else + { + if (module && module->is_fortran () + && (streq (name, "MAIN") || streq (name, "MAIN_"))) + name_buf = strdup (match_name); + else + name_buf = strdup (name); + + if (fname_fmt == Histable::SHORT) + { + int i = get_paren (name_buf); + if (i != -1) + name_buf[i] = (char) 0; + } + } + if (soname_fmt) + { + char *fname = dbe_sprintf (NTXT ("%s [%s]"), name_buf, module->loadobject->get_name ()); + free (name_buf); + name_buf = fname; + } + return name_buf; +} + +uint64_t +Function::get_addr () +{ + LoadObject *lo = module ? module->loadobject : NULL; + int seg_idx = lo ? lo->seg_idx : -1; + return MAKE_ADDRESS (seg_idx, img_offset); +} + +Histable * +Function::convertto (Histable_type type, Histable *obj) +{ + Histable *res = NULL; + SourceFile *source = (SourceFile*) obj; + switch (type) + { + case INSTR: + res = find_dbeinstr (0, 0); + break; + case LINE: + { + // mapPCtoLine will implicitly read line info if necessary + res = mapPCtoLine (0, source); + break; + } + case FUNCTION: + res = this; + break; + case SOURCEFILE: + res = def_source; + break; + default: + assert (0); + } + return res; +} + +void +Function::set_name (char *string) +{ + if (string == NULL) + return; + set_mangled_name (string); + + // strip away any globalization prefix, and save result for matching + char *mname = string; + if (strncmp (string, "$X", 2) == 0 || strncmp (string, ".X", 2) == 0) + { + // name was globalized + char *n = strchr (string + 2, (int) '.'); + if (n != NULL) + mname = n + 1; + } + set_match_name (mname); + name = NULL; + if (module) + { + if (name == NULL && *match_name == '_') + { + int flag = DMGL_PARAMS; + if (module->lang_code == Sp_lang_java) + flag |= DMGL_JAVA; + name = cplus_demangle (match_name, flag); + } + } + if (name == NULL) // got demangled string + name = dbe_strdup (match_name); + set_comparable_name (name); +} + +void +Function::set_mangled_name (const char *string) +{ + if (string) + { + free (mangled_name); + mangled_name = dbe_strdup (string); + } +} + +void +Function::set_match_name (const char *string) +{ + if (string) + { + free (match_name); + match_name = dbe_strdup (string); + } +} + +void +Function::set_comparable_name (const char *string) +{ + if (string) + { + free (comparable_name); + comparable_name = dbe_strdup (string); + + // remove blanks from comparable_name + for (char *s = comparable_name, *s1 = comparable_name;;) + { + if (*s == 0) + { + *s1 = 0; + break; + } + else if (*s != ' ') + { + *s1 = *s; + s1++; + } + s++; + } + } +} + +// This function looks at the name of a function, and determines whether +// or not it may be a derived function -- outline, mtask, or clone -- +// If it is, it writes the function name as demangled, +// and sets a pointer to the function from which it was derived +void +Function::findDerivedFunctions () + +{ + MPFuncTypes ftype; + int index; + Function *fitem; + unsigned long long line_no; + char *namefmt; + char *subname = mangled_name; + char *demname; + + // see if we've already done this + if ((flags & FUNC_FLAG_RESDER) != 0) + return; + + // set flag for future + flags = flags | FUNC_FLAG_RESDER; + if (module == NULL) + return; + if (*subname != '_' || subname[1] != '$') // Not a specially named function + return; + + // look for the current versions of naming + if (strncmp (subname + 2, NTXT ("d1"), 2) == 0) // doall function + ftype = MPF_DOALL; + else if (strncmp (subname + 2, "p1", 2) == 0) // parallel region function + ftype = MPF_PAR; + else if (strncmp (subname + 2, "l1", 2) == 0) // single thread loop setup + ftype = MPF_DOALL; + else if (strncmp (subname + 2, "s1", 2) == 0) // parallel section function + ftype = MPF_SECT; + else if (strncmp (subname + 2, "t1", 2) == 0) // task function + ftype = MPF_TASK; + else if (strncmp (subname + 2, "o1", 2) == 0) // outline function + { + ftype = MPF_OUTL; + isOutlineFunction = true; + } + else if (strncmp (subname + 2, "c1", 2) == 0) // clone function + ftype = MPF_CLONE; + else // Not an encoded name, just return + return; + + // we know it's one of the above prefixes + char *sub = dbe_strdup (name + 4); // starting with base-26 number + char *p = sub; + + // skip the base-26 number, and extract the line number + while (isalpha ((int) (*p)) != 0 && *p != 0) + p++; + line_no = atoll (p); + + // skip past the number to to the . + while (*p != '.' && *p != 0) + p++; + if (*p == 0) + { + // can't be right + free (sub); + return; + } + // skip the trailing . + p++; + subname = p; + bool foundmatch = false; + + // Find the function from which it is derived -- the one that matched subname + Vec_loop (Function*, module->functions, index, fitem) + { + if (streq (subname, fitem->mangled_name)) + { // found it + foundmatch = true; + usrfunc = fitem; + + // set the derived node + if ((fitem->flags & FUNC_FLAG_RESDER) == 0) + // ensure that it, too, is resolved if derived + fitem->findDerivedFunctions (); + + // Build a demangled name + switch (ftype) + { + case MPF_OUTL: + isOutlineFunction = true; + namefmt = GTXT ("%s -- outline code from line %lld [%s]"); + derivedNode = fitem->find_dbeinstr (PCLineFlag, line_no); + break; + case MPF_PAR: + namefmt = GTXT ("%s -- OMP parallel region from line %lld [%s]"); + break; + case MPF_DOALL: + namefmt = GTXT ("%s -- Parallel loop from line %lld [%s]"); + break; + case MPF_SECT: + namefmt = GTXT ("%s -- OMP sections from line %lld [%s]"); + break; + case MPF_CLONE: + // Note that clones are handled differently -- no line number and + // clones are NOT shown as called from the original + // so after constructing the name, just return + // later, establish link from clone to parent + demname = dbe_sprintf (GTXT ("%s -- cloned version [%s]"), + fitem->get_name (), name); + free (name); + // set the name to the demangled version + name = demname; + free (sub); + derivedNode = fitem->find_dbeinstr (PCLineFlag, line_no); + return; + case MPF_TASK: + namefmt = GTXT ("%s -- OMP task from line %lld [%s]"); + break; + default: + free (sub); + return; + + } + + // Finally, construct the demangled name + demname = dbe_sprintf (namefmt, fitem->get_name (), line_no, name); + free (name); + name = demname; + setLineFirst ((int) line_no); + break; + } + } + + if (foundmatch == false && ftype == MPF_OUTL) + { + // Even if derived node was not found, we can demangle + demname = dbe_sprintf (GTXT ("%s -- outline code [%s]"), subname, + mangled_name); + free (name); + name = demname; + } + free (sub); +} + +SrcInfo * +Function::new_srcInfo () +{ + SrcInfo *t = new SrcInfo (); + t->next = srcinfo_list; + srcinfo_list = t; + return t; +} + +void +Function::pushSrcFile (SourceFile* source, int /*lineno*/) +{ + // create new file stack + if (curr_srcfile == NULL) + { + curr_srcfile = source; + return; + } + + SrcInfo *src_info = new_srcInfo (); + // In the ideal world, we need a DbeLine(III) here, + // but right now it would make us later believe that there are + // instructions generated for #include lines. To avoid that, + // we ask for a DbeLine(II). + src_info->src_line = curr_srcfile->find_dbeline (this, 0 /*lineno*/); + if (src_info->src_line) + { + src_info->included_from = curr_srcinfo; + curr_srcinfo = src_info; + } + curr_srcfile = source; + setSource (); +} + +SourceFile * +Function::popSrcFile () +{ + if (curr_srcinfo != NULL) + { + curr_srcfile = curr_srcinfo->src_line->sourceFile; + curr_srcinfo = curr_srcinfo->included_from; + } + else + curr_srcfile = NULL; + return curr_srcfile; +} + +void +Function::copy_PCInfo (Function *from) +{ + if (line_first <= 0) + line_first = from->line_first; + if (line_last <= 0) + line_last = from->line_last; + if (def_source == NULL) + def_source = from->def_source; + for (int i = 0, sz = from->linetab ? from->linetab->size () : 0; i < sz; i++) + { + PCInfo *pcinf = from->linetab->fetch (i); + DbeLine *dbeLine = pcinf->src_info->src_line; + add_PC_info (pcinf->offset, dbeLine->lineno, dbeLine->sourceFile); + } +} + +void +Function::add_PC_info (uint64_t offset, int lineno, SourceFile *cur_src) +{ + if (lineno <= 0 || size < 0 || offset >= (uint64_t) size) + return; + if (cur_src == NULL) + cur_src = curr_srcfile ? curr_srcfile : def_source; + if (linetab == NULL) + linetab = new Vector<PCInfo*>; + + int left = 0; + int right = linetab->size () - 1; + DbeLine *dbeline; + while (left <= right) + { + int x = (left + right) / 2; + PCInfo *pcinf = linetab->fetch (x); + uint64_t pcinf_offset = ((uint64_t) pcinf->offset); + if (offset == pcinf_offset) + { + dbeline = cur_src->find_dbeline (this, lineno); + dbeline->init_Offset (offset); + pcinf->src_info->src_line = dbeline; + // Ignore duplicate offset + return; + } + else if (offset > pcinf_offset) + left = x + 1; + else + right = x - 1; + } + PCInfo *pcinfo = new PCInfo; + pcinfo->offset = offset; + + // Form new SrcInfo + SrcInfo *srcInfo = new_srcInfo (); + dbeline = cur_src->find_dbeline (this, lineno); + dbeline->init_Offset (offset); + srcInfo->src_line = dbeline; + // For now don't build included_from list. + // We need better compiler support for that. + //srcInfo->included_from = curr_srcinfo; + srcInfo->included_from = NULL; + pcinfo->src_info = srcInfo; + + // Update the size of the current line in both structures: + // current PCInfo and corresponding DbeLine. + if (left < linetab->size ()) + pcinfo->size = linetab->fetch (left)->offset - offset; + else + pcinfo->size = size - offset; + pcinfo->src_info->src_line->size += pcinfo->size; + + // If not the first line, update the size of the previous line + if (left > 0) + { + PCInfo *pcinfo_prev = linetab->fetch (left - 1); + int64_t delta = (offset - pcinfo_prev->offset) - pcinfo_prev->size; + pcinfo_prev->size += delta; + pcinfo_prev->src_info->src_line->size += delta; + } + + linetab->insert (left, pcinfo); + if (cur_src == def_source) + { + if (line_first <= 0) + setLineFirst (lineno); + if (line_last <= 0 || lineno > line_last) + line_last = lineno; + } +} + +PCInfo * +Function::lookup_PCInfo (uint64_t offset) +{ + module->read_stabs (); + if (linetab == NULL) + linetab = new Vector<PCInfo*>; + + int left = 0; + int right = linetab->size () - 1; + while (left <= right) + { + int x = (left + right) / 2; + PCInfo *pcinfo = linetab->fetch (x); + if (offset >= ((uint64_t) pcinfo->offset)) + { + if (offset < (uint64_t) (pcinfo->offset + pcinfo->size)) + return pcinfo; + left = x + 1; + } + else + right = x - 1; + } + return NULL; +} + +DbeInstr* +Function::mapLineToPc (DbeLine *dbeLine) +{ + if (dbeLine && linetab) + { + DbeLine *dbl = dbeLine->dbeline_base; + for (int i = 0, sz = linetab->size (); i < sz; i++) + { + PCInfo *pcinfo = linetab->get (i); + if (pcinfo->src_info + && (pcinfo->src_info->src_line->dbeline_base == dbl)) + { + DbeInstr *dbeInstr = find_dbeinstr (PCLineFlag, pcinfo->offset); + if (dbeInstr) + { + dbeInstr->lineno = dbeLine->lineno; + return dbeInstr; + } + } + } + } + return NULL; +} + +DbeLine* +Function::mapPCtoLine (uint64_t addr, SourceFile *src) +{ + PCInfo *pcinfo = lookup_PCInfo (addr); + if (pcinfo == NULL) + { + if (defaultDbeLine == NULL) + defaultDbeLine = getDefSrc ()->find_dbeline (this, 0); + return defaultDbeLine; + } + DbeLine *dbeline = pcinfo->src_info->src_line; + + // If source-context is not specified return the line + // from which this pc has been generated. + if (src == NULL) + return dbeline; + if (dbeline->sourceFile == src) + return dbeline->dbeline_base; + return src->find_dbeline (this, 0); +} + +DbeInstr * +Function::find_dbeinstr (int flag, uint64_t addr) +{ + DbeInstr *instr; + + enum + { + FuncInstHTableSize = 128 + }; + + int hash = (((int) addr) >> 2) & (FuncInstHTableSize - 1); + if (instHTable == NULL) + { + if (size > 2048) + { + instHTable = new DbeInstr*[FuncInstHTableSize]; + for (int i = 0; i < FuncInstHTableSize; i++) + instHTable[i] = NULL; + } + } + else + { + instr = instHTable[hash]; + if (instr && instr->addr == addr && instr->flags == flag) + return instr; + } + + int left = 0; + int right = instrs->size () - 1; + while (left <= right) + { + int index = (left + right) / 2; + instr = instrs->fetch (index); + if (addr < instr->addr) + right = index - 1; + else if (addr > instr->addr) + left = index + 1; + else + { + if (flag == instr->flags) + { + if (instHTable) + instHTable[hash] = instr; + return instr; + } + else if (flag < instr->flags) + right = index - 1; + else + left = index + 1; + } + } + + // None found, create a new one + instr = new DbeInstr (instr_id++, flag, this, addr); + instrs->insert (left, instr); + if (instHTable) + instHTable[hash] = instr; + return instr; +} + +// LIBRARY_VISIBILITY +DbeInstr * +Function::create_hide_instr (DbeInstr *instr) +{ + DbeInstr *new_instr = new DbeInstr (instr_id++, 0, this, instr->addr); + return new_instr; +} + +uint64_t +Function::find_previous_addr (uint64_t addr) +{ + if (addrs == NULL) + { + addrs = module->getAddrs (this); + if (addrs == NULL) + return addr; + } + + int index = -1, not_found = 1; + + enum + { + FuncAddrIndexHTableSize = 128 + }; + int hash = (((int) addr) >> 2) & (FuncAddrIndexHTableSize - 1); + if (addrIndexHTable == NULL) + { + if (size > 2048) + { + addrIndexHTable = new int[FuncAddrIndexHTableSize]; + for (int i = 0; i < FuncAddrIndexHTableSize; i++) + addrIndexHTable[i] = -1; + } + } + else + { + index = addrIndexHTable[hash]; + if (index >= 0 && addrs->fetch (index) == addr) + not_found = 0; + } + + int left = 0; + int right = addrs->size () - 1; + while (not_found && left <= right) + { + index = (left + right) / 2; + uint64_t addr_test = addrs->fetch (index); + if (addr < addr_test) + right = index - 1; + else if (addr > addr_test) + left = index + 1; + else + { + if (addrIndexHTable) + addrIndexHTable[hash] = index; + not_found = 0; + } + } + if (not_found) + return addr; + if (index > 0) + index--; + return addrs->fetch (index); +} + +void +Function::setSource () +{ + SourceFile *sf = module->getIncludeFile (); + if (sf == NULL) + sf = getDefSrc (); + if (def_source == NULL) + setDefSrc (sf); + if (sf == def_source) + return; + if (sources == NULL) + { + sources = new Vector<SourceFile*>; + sources->append (def_source); + sources->append (sf); + } + else if (sources->find (sf) < 0) + sources->append (sf); +} + +void +Function::setDefSrc (SourceFile *sf) +{ + if (sf) + { + def_source = sf; + if (line_first > 0) + add_PC_info (0, line_first, def_source); + } +} + +void +Function::setLineFirst (int lineno) +{ + if (lineno > 0) + { + line_first = lineno; + if (line_last <= 0) + line_last = lineno; + if (def_source) + add_PC_info (0, line_first, def_source); + } +} + +Vector<SourceFile*> * +Function::get_sources () +{ + if (module) + module->read_stabs (); + if (sources == NULL) + { + sources = new Vector<SourceFile*>; + sources->append (getDefSrc ()); + } + return sources; +} + +SourceFile* +Function::getDefSrc () +{ + if (module) + module->read_stabs (); + if (def_source == NULL) + setDefSrc (module->getMainSrc ()); + return def_source; +} + +char * +Function::getDefSrcName () +{ + SourceFile *sf = getDefSrc (); + if (sf) + return sf->dbeFile->getResolvedPath (); + if (module) + return module->file_name; + sf = dbeSession->get_Unknown_Source (); + return sf->get_name (); +} + +#define cmpValue(a, b) ((a) > (b) ? 1 : (a) == (b) ? 0 : -1) + +int +Function::func_cmp (Function *func, SourceFile *srcContext) +{ + if (def_source != func->def_source) + { + if (srcContext == NULL) + srcContext = getDefSrc (); + if (def_source == srcContext) + return -1; + if (func->def_source == srcContext) + return 1; + return cmpValue (img_offset, func->img_offset); + } + + if (line_first == func->line_first) + return cmpValue (img_offset, func->img_offset); + if (line_first <= 0) + { + if (func->line_first > 0) + return 1; + return cmpValue (img_offset, func->img_offset); + } + if (func->line_first <= 0) + return -1; + return cmpValue (line_first, func->line_first); +} + +Vector<Histable*> * +Function::get_comparable_objs () +{ + update_comparable_objs (); + if (comparable_objs || dbeSession->expGroups->size () <= 1 || module == NULL) + return comparable_objs; + if (module == NULL || module->loadobject == NULL) + return NULL; + Vector<Histable*> *comparableModules = module->get_comparable_objs (); + if (comparableModules == NULL) + { + return NULL; + } + comparable_objs = new Vector<Histable*>(comparableModules->size ()); + for (long i = 0, sz = comparableModules->size (); i < sz; i++) + { + Function *func = NULL; + comparable_objs->store (i, func); + Module *mod = (Module*) comparableModules->fetch (i); + if (mod == NULL) + continue; + if (mod == module) + func = this; + else + { + for (long i1 = 0, sz1 = VecSize (mod->functions); i1 < sz1; i1++) + { + Function *f = mod->functions->get (i1); + if ((f->comparable_objs == NULL) + && (strcmp (f->comparable_name, comparable_name) == 0)) + { + func = f; + func->comparable_objs = comparable_objs; + break; + } + } + } + comparable_objs->store (i, func); + } + Vector<Histable*> *comparableLoadObjs = + module->loadobject->get_comparable_objs (); + if (VecSize (comparableLoadObjs) == VecSize (comparable_objs)) + { + for (long i = 0, sz = VecSize (comparableLoadObjs); i < sz; i++) + { + LoadObject *lo = (LoadObject *) comparableLoadObjs->get (i); + Function *func = (Function *) comparable_objs->get (i); + if (func || (lo == NULL)) + continue; + if (module->loadobject == lo) + func = this; + else + { + for (long i1 = 0, sz1 = VecSize (lo->functions); i1 < sz1; i1++) + { + Function *f = lo->functions->fetch (i1); + if ((f->comparable_objs == NULL) + && (strcmp (f->comparable_name, comparable_name) == 0)) + { + func = f; + func->comparable_objs = comparable_objs; + break; + } + } + } + comparable_objs->store (i, func); + } + } + dump_comparable_objs (); + return comparable_objs; +} + +JMethod::JMethod (uint64_t _id) : Function (_id) +{ + mid = 0LL; + addr = (Vaddr) 0; + signature = NULL; + jni_function = NULL; +} + +JMethod::~JMethod () +{ + free (signature); +} + +uint64_t +JMethod::get_addr () +{ + if (addr != (Vaddr) 0) + return addr; + else + return Function::get_addr (); +} + +typedef struct +{ + size_t used_in; + size_t used_out; +} MethodField; + +static void +write_buf (char* buf, char* str) +{ + while ((*buf++ = *str++)); +} + +/** Translate one field from the nane buffer. + * return how many chars were read from name and how many bytes were used in buf. + */ +static MethodField +translate_method_field (const char* name, char* buf) +{ + MethodField out, t; + switch (*name) + { + case 'L': + name++; + out.used_in = 1; + out.used_out = 0; + while (*name != ';') + { + *buf = *name++; + if (*buf == '/') + *buf = '.'; + buf++; + out.used_in++; + out.used_out++; + } + out.used_in++; /* the ';' is also used. */ + break; + case 'Z': + write_buf (buf, NTXT ("boolean")); + out.used_out = 7; + out.used_in = 1; + break; + case 'B': + write_buf (buf, NTXT ("byte")); + out.used_out = 4; + out.used_in = 1; + break; + case 'C': + write_buf (buf, NTXT ("char")); + out.used_out = 4; + out.used_in = 1; + break; + case 'S': + write_buf (buf, NTXT ("short")); + out.used_out = 5; + out.used_in = 1; + break; + case 'I': + write_buf (buf, NTXT ("int")); + out.used_out = 3; + out.used_in = 1; + break; + case 'J': + write_buf (buf, NTXT ("long")); + out.used_out = 4; + out.used_in = 1; + break; + case 'F': + write_buf (buf, NTXT ("float")); + out.used_out = 5; + out.used_in = 1; + break; + case 'D': + write_buf (buf, NTXT ("double")); + out.used_out = 6; + out.used_in = 1; + break; + case 'V': + write_buf (buf, NTXT ("void")); + out.used_out = 4; + out.used_in = 1; + break; + case '[': + t = translate_method_field (name + 1, buf); + write_buf (buf + t.used_out, NTXT ("[]")); + out.used_out = t.used_out + 2; + out.used_in = t.used_in + 1; + break; + default: + out.used_out = 0; + out.used_in = 0; + } + return out; +} + +/** + * translate method name to full method signature + * into the output buffer (buf). + * ret_type - true for printing result type + */ +static bool +translate_method (char* mname, char *signature, bool ret_type, char* buf) +{ + MethodField p; + size_t l; + int first = 1; + if (signature == NULL) + return false; + + const char *c = strchr (signature, ')'); + if (c == NULL) + return false; + if (ret_type) + { + p = translate_method_field (++c, buf); + buf += p.used_out; + *buf++ = ' '; + } + + l = strlen (mname); + memcpy (buf, mname, l + 1); + buf += l; + // *buf++ = ' '; // space before () + *buf++ = '('; + + c = signature + 1; + while (*c != ')') + { + if (!first) + { + *buf++ = ','; + *buf++ = ' '; + } + first = 0; + p = translate_method_field (c, buf); + c += p.used_in; + buf += p.used_out; + } + + *buf++ = ')'; + *buf = '\0'; + return true; +} + +void +JMethod::set_name (char *string) +{ + if (string == NULL) + return; + set_mangled_name (string); + + char buf[MAXDBUF]; + *buf = '\0'; + if (translate_method (string, signature, false, buf)) + { + name = dbe_strdup (buf); // got translated string + Dprintf (DUMP_JCLASS_READER, + "JMethod::set_name: true name=%s string=%s signature=%s\n", + STR (name), STR (string), STR (signature)); + } + else + { + name = dbe_strdup (string); + Dprintf (DUMP_JCLASS_READER, + "JMethod::set_name: false name=%s signature=%s\n", + STR (name), STR (signature)); + } + set_match_name (name); + set_comparable_name (name); +} + +bool +JMethod::jni_match (Function *func) +{ + if (func == NULL || (func->flags & FUNC_NOT_JNI) != 0) + return false; + if (jni_function == func) + return true; + + char *fname = func->get_name (); + if ((func->flags & FUNC_JNI_CHECKED) == 0) + { + func->flags |= FUNC_JNI_CHECKED; + if (strncmp (func->get_name (), NTXT ("Java_"), 5) != 0) + { + func->flags |= FUNC_NOT_JNI; + return false; + } + } + + char *d = name; + char *s = fname + 5; + while (*d && *d != '(' && *d != ' ') + { + if (*d == '.') + { + if (*s++ != '_') + return false; + d++; + } + else if (*d == '_') + { + if (*s++ != '_') + return false; + if (*s++ != '1') + return false; + d++; + } + else if (*d++ != *s++) + return false; + } + jni_function = func; + return true; +} diff --git a/gprofng/src/Function.h b/gprofng/src/Function.h new file mode 100644 index 0000000..b0a856b --- /dev/null +++ b/gprofng/src/Function.h @@ -0,0 +1,222 @@ +/* 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. */ + +#ifndef _DBE_FUNCTION_H +#define _DBE_FUNCTION_H + +// A Function object represents an individual function in a .o file. + +#include "util.h" +#include "Histable.h" +#include "SourceFile.h" + +class Module; +class Symbol; +class InlinedSubr; +struct SrcInfo; +struct PCInfo; +template <class ITEM> class Vector; + +const uint64_t FUNC_NO_SAVE = (uint64_t) - 1; +const uint64_t FUNC_ROOT = (uint64_t) - 2; + +enum +{ + FUNC_FLAG_PLT = 1, + FUNC_FLAG_DYNAMIC = 2, + FUNC_FLAG_RESDER = 4, // set if derived function resolution has been done + FUNC_FLAG_NO_OFFSET = 8, // set if disassembly should not show offset from function + FUNC_FLAG_SIMULATED = 16, // not a real function like <Total>, <Unknown>, etc. + FUNC_FLAG_NATIVE = 32, // no byte code for JMethod + FUNC_NOT_JNI = 64, // a function name is not "Java_..." + FUNC_JNI_CHECKED = 128 // already checked for "Java_..." +}; + +const int MAXDBUF = 32768; // the longest demangled name handled + +class Function : public Histable +{ +public: + + enum MPFuncTypes + { + MPF_DOALL, + MPF_PAR, + MPF_SECT, + MPF_TASK, + MPF_CLONE, + MPF_OUTL + }; + + Function (uint64_t _id); + virtual ~Function (); + + virtual uint64_t get_addr (); + virtual char *get_name (NameFormat = NA); + virtual Vector<Histable*> *get_comparable_objs (); + virtual void set_name (char *); // Set the demangled name + virtual Histable *convertto (Histable_type type, Histable *obj = NULL); + + virtual Histable_type + get_type () + { + return FUNCTION; + }; + + virtual int64_t + get_size () + { + return size; + }; + + void set_comparable_name (const char *string); + void set_mangled_name (const char *string); + void set_match_name (const char *string); + + // Find any derived functions, and set their derivedNode + void findDerivedFunctions (); + void findKrakatoaDerivedFunctions (); + void add_PC_info (uint64_t offset, int lineno, SourceFile *cur_src = NULL); + void pushSrcFile (SourceFile* source, int lineno); + SourceFile *popSrcFile (); + int func_cmp (Function *func, SourceFile *srcContext = NULL); + void copy_PCInfo (Function *f); + DbeLine *mapPCtoLine (uint64_t addr, SourceFile *src = NULL); + DbeInstr *mapLineToPc (DbeLine *dbeLine); + DbeInstr *find_dbeinstr (int flag, uint64_t addr); + DbeInstr *create_hide_instr (DbeInstr *instr); + uint64_t find_previous_addr (uint64_t addr); + SourceFile *getDefSrc (); + char *getDefSrcName (); + void setDefSrc (SourceFile *sf); + void setLineFirst (int lineno); + Vector<SourceFile*> *get_sources (); + + char * + get_mangled_name () + { + return mangled_name; + } + + char * + get_match_name () + { + return match_name; + } + + inline Function * + cardinal () + { + return alias ? alias : this; + } + + unsigned int flags; // FUNC_FLAG_* + Module *module; // pointer to module containing source + int line_first; // range of line numbers for function + int line_last; + int64_t size; // size of the function in bytes + uint64_t save_addr; // used for detection of leaf routines + DbeInstr *derivedNode; // If derived from another function + bool isOutlineFunction; // if outline (save assumed) + unsigned int chksum; // check sum of instructions + char *img_fname; // file containing function image + uint64_t img_offset; // file offset of the image + SourceFile *curr_srcfile; + DbeLine *defaultDbeLine; + Function *usrfunc; // User function + Function *alias; + bool isUsed; + bool isHideFunc; + SourceFile *def_source; + Function *indexStabsLink; // correspondent function for the .o file + Symbol *elfSym; + InlinedSubr *inlinedSubr; + int inlinedSubrCnt; + +private: + DbeInstr **instHTable; // hash table for DbeInstr + int *addrIndexHTable; // hash table for addrs index + void setSource (); + PCInfo *lookup_PCInfo (uint64_t offset); + SrcInfo *new_srcInfo (); + + char *mangled_name; + char *match_name; // mangled name, with globalization stripped + char *comparable_name; // demangled name, with globalization and blanks stripped + char *name_buf; + NameFormat current_name_format; + Vector<PCInfo*> *linetab; + Vector<DbeInstr*> *instrs; + Vector<uint64_t> *addrs; + uint64_t instr_id; + Vector<SourceFile*> *sources; + SrcInfo *curr_srcinfo; // the current source stack of the function + SrcInfo *srcinfo_list; // master list for SrcInfo +}; + +class JMethod : public Function +{ +public: + JMethod (uint64_t _id); + virtual ~JMethod (); + virtual void set_name (char *); + virtual uint64_t get_addr (); + + void + set_addr (Vaddr _addr) + { + addr = _addr; + } + + uint64_t + get_mid () + { + return mid; + } + + void + set_mid (uint64_t _mid) + { + mid = _mid; + } + + char * + get_signature () + { + return signature; + } + + void + set_signature (const char *s) + { + signature = dbe_strdup (s); + } + + // Returns true if func's name matches method's as its JNI implementation + bool jni_match (Function *func); + +private: + uint64_t mid; + Vaddr addr; + char *signature; + Function *jni_function; +}; + +#endif /* _DBE_FUNCTION_H */ diff --git a/gprofng/src/HashMap.h b/gprofng/src/HashMap.h new file mode 100644 index 0000000..f66a6fe --- /dev/null +++ b/gprofng/src/HashMap.h @@ -0,0 +1,435 @@ +/* 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. */ + +// java.util.HashMap + +#ifndef _DBE_HASHMAP_H +#define _DBE_HASHMAP_H + +#include "vec.h" +#include "util.h" +#include "StringBuilder.h" +#include "Histable.h" +#include "MemObject.h" + +template <typename Key_t> inline int get_hash_code (Key_t a); +template <typename Key_t> inline bool is_equals (Key_t a, Key_t b); +template <typename Key_t> inline Key_t copy_key (Key_t a); +template <typename Key_t> inline void delete_key (Key_t a); + +// Specialization for char* +template<> inline int +get_hash_code (char *a) +{ + return (int) (crc64 (a, strlen (a)) & 0x7fffffff); +} + +template<> inline bool +is_equals (char *a, char *b) +{ + return dbe_strcmp (a, b) == 0; +} + +template<> inline char * +copy_key (char *a) +{ + return dbe_strdup (a); +} + +template<> inline void +delete_key (char *a) +{ + return free (a); +} + +template<> inline int +get_hash_code (uint64_t a) +{ + return (int) (a & 0x7fffffff); +} + +template<> inline bool +is_equals (uint64_t a, uint64_t b) +{ + return a == b; +} + +template<> inline uint64_t +copy_key (uint64_t a) +{ + return a; +} + +template<> inline void +delete_key (uint64_t a) +{ + a = a; +} + +template<> inline int +get_hash_code (Histable* a) +{ + return (int) (a->id & 0x7fffffff); +} + +template<> inline bool +is_equals (Histable* a, Histable* b) +{ + return a == b; +} + +template<> inline Histable* +copy_key (Histable* a) +{ + return a; +} + +template<> inline void +delete_key (Histable* a) +{ + a->id = a->id; +} + +template<> inline int +get_hash_code (MemObj* a) +{ + return (int) (a->id & 0x7fffffff); +} + +template<> inline bool +is_equals (MemObj* a, MemObj* b) +{ + return a == b; +} + +template<> inline MemObj* +copy_key (MemObj* a) +{ + return a; +} + +template<> inline void +delete_key (MemObj* a) +{ + a->id = a->id; +} + +template <typename Key_t, typename Value_t> +class HashMap +{ +public: + HashMap (int initialCapacity = 0); + + ~HashMap () + { + clear (); + delete vals; + delete[] hashTable; + } + + Value_t put (Key_t key, Value_t val); + Value_t get (Key_t key); + Value_t get (Key_t key, Value_t val); // Create a new map if key is not here + void clear (); + Value_t remove (Key_t); + Vector<Value_t> *values (Key_t key); // Return a list of values for 'key' + + bool + containsKey (Key_t key) + { + Value_t p = get (key); + return p != NULL; + }; + + Vector<Value_t> * + values () + { + return vals; + } + + void + reset () + { + clear (); + } + + int + get_phaseIdx () + { + return phaseIdx; + } + + void + set_phaseIdx (int phase) + { + if (phase == 0) clear (); + phaseIdx = phase; + } + char *dump (); + +private: + + enum + { + HASH_SIZE = 511, + MAX_HASH_SIZE = 1048575 + }; + + typedef struct Hash + { + Key_t key; + Value_t val; + struct Hash *next; + } Hash_t; + + void resize (); + + int + hashCode (Key_t key) + { + return get_hash_code (key) % hash_sz; + } + + bool + equals (Key_t a, Key_t b) + { + return is_equals (a, b); + } + + Key_t + copy (Key_t key) + { + return copy_key (key); + } + + Hash_t **hashTable; + Vector<Value_t> *vals; + int phaseIdx; + int hash_sz; + int nelem; +}; + +template <typename Key_t, typename Value_t> +HashMap<Key_t, Value_t> +::HashMap (int initialCapacity) +{ + if (initialCapacity > 0) + vals = new Vector<Value_t>(initialCapacity); + else + vals = new Vector<Value_t>(); + phaseIdx = 0; + nelem = 0; + hash_sz = HASH_SIZE; + hashTable = new Hash_t*[hash_sz]; + for (int i = 0; i < hash_sz; i++) + hashTable[i] = NULL; +} + +template <typename Key_t, typename Value_t> +void +HashMap<Key_t, Value_t>::clear () +{ + vals->reset (); + phaseIdx = 0; + nelem = 0; + for (int i = 0; i < hash_sz; i++) + { + Hash_t *next; + for (Hash_t *p = hashTable[i]; p; p = next) + { + next = p->next; + delete_key (p->key); + delete p; + } + hashTable[i] = NULL; + } +} + +template <typename Key_t, typename Value_t> +void +HashMap<Key_t, Value_t>::resize () +{ + int old_hash_sz = hash_sz; + hash_sz = old_hash_sz * 2 + 1; + Hash_t **old_hash_table = hashTable; + hashTable = new Hash_t*[hash_sz]; + for (int i = 0; i < hash_sz; i++) + hashTable[i] = NULL; + nelem = 0; + for (int i = 0; i < old_hash_sz; i++) + { + if (old_hash_table[i] != NULL) + { + Hash_t *old_p; + Hash_t *p = old_hash_table[i]; + while (p != NULL) + { + put (p->key, p->val); + old_p = p; + p = p->next; + delete old_p; + } + } + } + delete[] old_hash_table; +} + +template <typename Key_t, typename Value_t> +Value_t +HashMap<Key_t, Value_t>::get (Key_t key) +{ + int hash_code = hashCode (key); + for (Hash_t *p = hashTable[hash_code]; p; p = p->next) + if (equals (key, p->key)) + return p->val; + return NULL; +} + +template <typename Key_t, typename Value_t> +Vector<Value_t> * +HashMap<Key_t, Value_t>::values (Key_t key) +{ + Vector<Value_t> *list = new Vector<Value_t>(); + int hash_code = hashCode (key); + for (Hash_t *p = hashTable[hash_code]; p; p = p->next) + { + if (equals (key, p->key)) + list->append (p->val); + } + return list; +} + +template <typename Key_t, typename Value_t> +Value_t +HashMap<Key_t, Value_t>::get (const Key_t key, Value_t val) +{ + int hash_code = hashCode (key); + Hash_t *p, *first = NULL; + for (p = hashTable[hash_code]; p; p = p->next) + { + if (equals (key, p->key)) + { + if (first == NULL) + first = p; + if (val == p->val) + return first->val; // Always return the first value + } + } + vals->append (val); + p = new Hash_t (); + p->val = val; + p->key = copy (key); + if (first) + { + p->next = first->next; + first->next = p; + return first->val; // Always return the first value + } + else + { + p->next = hashTable[hash_code]; + hashTable[hash_code] = p; + return val; + } +} + +template <typename Key_t, typename Value_t> +Value_t +HashMap<Key_t, Value_t>::remove (Key_t key) +{ + int hash_code = hashCode (key); + Value_t val = NULL; + for (Hash_t *prev = NULL, *p = hashTable[hash_code]; p != NULL;) + { + if (equals (key, p->key)) + { + if (prev == NULL) + hashTable[hash_code] = p->next; + else + prev->next = p->next; + if (val == NULL) + val = p->val; + delete_key (p->key); + delete p; + } + else + { + prev = p; + p = p->next; + } + } + return val; +} + +template <typename Key_t, typename Value_t> +Value_t +HashMap<Key_t, Value_t>::put (Key_t key, Value_t val) +{ + int hash_code = hashCode (key); + vals->append (val); + for (Hash_t *p = hashTable[hash_code]; p != NULL; p = p->next) + { + if (equals (key, p->key)) + { + Value_t v = p->val; + p->val = val; + return v; + } + } + Hash_t *p = new Hash_t (); + p->val = val; + p->key = copy (key); + p->next = hashTable[hash_code]; + hashTable[hash_code] = p; + nelem++; + if (nelem == hash_sz) + resize (); + return val; +} + +template <typename Key_t, typename Value_t> +char * +HashMap<Key_t, Value_t>::dump () +{ + StringBuilder sb; + char buf[128]; + snprintf (buf, sizeof (buf), "HashMap: size=%d ##########\n", vals->size ()); + sb.append (buf); + for (int i = 0; i < hash_sz; i++) + { + if (hashTable[i] == NULL) + continue; + snprintf (buf, sizeof (buf), "%3d:", i); + sb.append (buf); + char *s = NTXT (" "); + for (Hash_t *p = hashTable[i]; p; p = p->next) + { + sb.append (s); + s = NTXT (" "); + sb.append (p->key); + snprintf (buf, sizeof (buf), " --> 0x%p '%s'\n", + p->val, p->val->get_name ()); + sb.append (buf); + } + } + return sb.toString (); +} + +#endif // _DBE_HASHMAP_H diff --git a/gprofng/src/HeapActivity.cc b/gprofng/src/HeapActivity.cc new file mode 100644 index 0000000..943183d --- /dev/null +++ b/gprofng/src/HeapActivity.cc @@ -0,0 +1,408 @@ +/* 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 "DbeSession.h" +#include "HeapData.h" +#include "StringBuilder.h" +#include "i18n.h" +#include "util.h" +#include "HeapActivity.h" +#include "MetricList.h" +#include "Application.h" +#include "Experiment.h" +#include "DbeView.h" +#include "Exp_Layout.h" +#include "i18n.h" + +HeapActivity::HeapActivity (DbeView *_dbev) +{ + dbev = _dbev; + hDataTotal = NULL; + hDataObjs = NULL; + hDataObjsCallStack = NULL; + hasCallStack = false; + hDataCalStkMap = NULL; + hist_data_callstack_all = NULL; +} + +void +HeapActivity::reset () +{ + delete hDataTotal; + hDataTotal = NULL; + delete hDataObjsCallStack; + hDataObjsCallStack = NULL; + hasCallStack = false; + hDataObjs = NULL; + delete hDataCalStkMap; + hDataCalStkMap = NULL; + hist_data_callstack_all = NULL; +} + +void +HeapActivity::createHistItemTotals (Hist_data *hist_data, MetricList *mlist, + Histable::Type hType, bool empty) +{ + int mIndex; + Metric *mtr; + Hist_data::HistItem *hi; + HeapData *hData = NULL; + if (hDataTotal == NULL) + { + hDataTotal = new HeapData (TOTAL_HEAPNAME); + hDataTotal->setHistType (hType); + hDataTotal->setStackId (TOTAL_STACK_ID); + hDataTotal->id = 0; + } + + hData = new HeapData (hDataTotal); + hData->setHistType (hType); + hi = hist_data->append_hist_item (hData); + + Vec_loop (Metric *, mlist->get_items (), mIndex, mtr) + { + if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ()) + continue; + + Metric::Type mtype = mtr->get_type (); + ValueTag vType = mtr->get_vtype (); + + hist_data->total->value[mIndex].tag = vType; + hi->value[mIndex].tag = vType; + switch (mtype) + { + case BaseMetric::HEAP_ALLOC_BYTES: + if (!empty) + { + hist_data->total->value[mIndex].ll = hDataTotal->getAllocBytes (); + hi->value[mIndex].ll = hDataTotal->getAllocBytes (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::HEAP_ALLOC_CNT: + if (!empty) + { + hist_data->total->value[mIndex].ll = hDataTotal->getAllocCnt (); + hi->value[mIndex].ll = hDataTotal->getAllocCnt (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::HEAP_LEAK_BYTES: + if (!empty) + { + hist_data->total->value[mIndex].ll = hDataTotal->getLeakBytes (); + hi->value[mIndex].ll = hDataTotal->getLeakBytes (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::HEAP_LEAK_CNT: + if (!empty) + { + hist_data->total->value[mIndex].ll = hDataTotal->getLeakCnt (); + hi->value[mIndex].ll = hDataTotal->getLeakCnt (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + default: + break; + } + } +} + +void +HeapActivity::computeHistTotals (Hist_data *hist_data, MetricList *mlist) +{ + int mIndex; + Metric *mtr; + Vec_loop (Metric *, mlist->get_items (), mIndex, mtr) + { + if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ()) + continue; + + Metric::Type mtype = mtr->get_type (); + ValueTag vType = mtr->get_vtype (); + + hist_data->total->value[mIndex].tag = vType; + switch (mtype) + { + case BaseMetric::HEAP_ALLOC_BYTES: + hist_data->total->value[mIndex].ll = hDataTotal->getAllocBytes (); + break; + case BaseMetric::HEAP_ALLOC_CNT: + hist_data->total->value[mIndex].ll = hDataTotal->getAllocCnt (); + break; + case BaseMetric::HEAP_LEAK_BYTES: + hist_data->total->value[mIndex].ll = hDataTotal->getLeakBytes (); + break; + case BaseMetric::HEAP_LEAK_CNT: + hist_data->total->value[mIndex].ll = hDataTotal->getLeakCnt (); + break; + default: + break; + } + } +} + +void +HeapActivity::computeHistData (Hist_data *hist_data, MetricList *mlist, + Hist_data::Mode mode, Histable *selObj) +{ + + Hist_data::HistItem *hi = NULL; + + int numObjs = hDataObjs->size (); + int numMetrics = mlist->get_items ()->size (); + for (int i = 0; i < numObjs; i++) + { + HeapData *hData = hDataObjs->fetch (i); + if (mode == Hist_data::ALL) + hi = hist_data->append_hist_item (hData); + else if (mode == Hist_data::SELF) + { + if (hData->id == selObj->id) + hi = hist_data->append_hist_item (hData); + else + continue; + } + + for (int mIndex = 0; mIndex < numMetrics; mIndex++) + { + Metric *mtr = mlist->get_items ()->fetch (mIndex); + if (!mtr->is_visible () && !mtr->is_tvisible () + && !mtr->is_pvisible ()) + continue; + + Metric::Type mtype = mtr->get_type (); + ValueTag vType = mtr->get_vtype (); + hi->value[mIndex].tag = vType; + switch (mtype) + { + case BaseMetric::HEAP_ALLOC_BYTES: + hi->value[mIndex].ll = hData->getAllocBytes (); + break; + case BaseMetric::HEAP_ALLOC_CNT: + hi->value[mIndex].ll = hData->getAllocCnt (); + break; + case BaseMetric::HEAP_LEAK_BYTES: + hi->value[mIndex].ll = hData->getLeakBytes (); + break; + case BaseMetric::HEAP_LEAK_CNT: + hi->value[mIndex].ll = hData->getLeakCnt (); + break; + default: + break; + } + } + } +} + +Hist_data * +HeapActivity::compute_metrics (MetricList *mlist, Histable::Type type, + Hist_data::Mode mode, Histable *selObj) +{ + // it's already there, just return it + if (mode == Hist_data::ALL && type == Histable::HEAPCALLSTACK + && hist_data_callstack_all != NULL) + return hist_data_callstack_all; + + bool has_data = false; + Hist_data *hist_data = NULL; + VMode viewMode = dbev->get_view_mode (); + switch (type) + { + case Histable::HEAPCALLSTACK: + if (!hasCallStack) // It is not computed yet + computeCallStack (type, viewMode); + + // computeCallStack() creates hDataObjsCallStack + // hDataObjsCallStack contains the list of call stack objects + if (hDataObjsCallStack != NULL) + { + hDataObjs = hDataObjsCallStack; + has_data = true; + } + else + has_data = false; + + if (has_data && mode == Hist_data::ALL && hist_data_callstack_all == NULL) + { + hist_data_callstack_all = new Hist_data (mlist, type, mode, true); + hist_data = hist_data_callstack_all; + } + else if (has_data) + hist_data = new Hist_data (mlist, type, mode, false); + else + { + hist_data = new Hist_data (mlist, type, mode, false); + createHistItemTotals (hist_data, mlist, type, true); + return hist_data; + } + break; + default: + fprintf (stderr, + "HeapActivity cannot process data due to wrong Histable (type=%d) \n", + type); + abort (); + } + + if (mode == Hist_data::ALL || (mode == Hist_data::SELF && selObj->id == 0)) + createHistItemTotals (hist_data, mlist, type, false); + else + computeHistTotals (hist_data, mlist); + computeHistData (hist_data, mlist, mode, selObj); + + // Determine by which metric to sort if any + bool rev_sort = mlist->get_sort_rev (); + int sort_ind = -1; + int nmetrics = mlist->get_items ()->size (); + + for (int mind = 0; mind < nmetrics; mind++) + if (mlist->get_sort_ref_index () == mind) + sort_ind = mind; + + hist_data->sort (sort_ind, rev_sort); + hist_data->compute_minmax (); + + return hist_data; +} + +void +HeapActivity::computeCallStack (Histable::Type type, VMode viewMode) +{ + bool has_data = false; + reset (); + uint64_t stackIndex = 0; + HeapData *hData = NULL; + + delete hDataCalStkMap; + hDataCalStkMap = new DefaultMap<uint64_t, HeapData*>; + + delete hDataTotal; + hDataTotal = new HeapData (TOTAL_HEAPNAME); + hDataTotal->setHistType (type); + + // There is no call stack for total, use the index for id + hDataTotal->id = stackIndex++; + + // get the list of io events from DbeView + int numExps = dbeSession->nexps (); + + for (int k = 0; k < numExps; k++) + { + // Investigate the performance impact of processing the heap events twice. + // This is a 2*n performance issue + dbev->get_filtered_events (k, DATA_HEAPSZ); + + DataView *heapPkts = dbev->get_filtered_events (k, DATA_HEAP); + if (heapPkts == NULL) + continue; + + Experiment *exp = dbeSession->get_exp (k); + long sz = heapPkts->getSize (); + int pid = 0; + int userExpId = 0; + if (sz > 0) + { + pid = exp->getPID (); + userExpId = exp->getUserExpId (); + } + for (long i = 0; i < sz; ++i) + { + uint64_t nByte = heapPkts->getULongValue (PROP_HSIZE, i); + uint64_t stackId = (uint64_t) getStack (viewMode, heapPkts, i); + Heap_type heapType = (Heap_type) heapPkts->getIntValue (PROP_HTYPE, i); + uint64_t leaked = heapPkts->getULongValue (PROP_HLEAKED, i); + int64_t heapSize = heapPkts->getLongValue (PROP_HCUR_ALLOCS, i); + hrtime_t packetTimestamp = heapPkts->getLongValue (PROP_TSTAMP, i); + hrtime_t timestamp = packetTimestamp - exp->getStartTime () + + exp->getRelativeStartTime (); + + switch (heapType) + { + case MMAP_TRACE: + case MALLOC_TRACE: + case REALLOC_TRACE: + if (stackId != 0) + { + hData = hDataCalStkMap->get (stackId); + if (hData == NULL) + { + char *stkName = dbe_sprintf (GTXT ("Stack 0x%llx"), + (unsigned long long) stackId); + hData = new HeapData (stkName); + hDataCalStkMap->put (stackId, hData); + hData->id = (int64_t) stackId; + hData->setStackId (stackIndex); + stackIndex++; + hData->setHistType (type); + } + } + else + continue; + + hData->addAllocEvent (nByte); + hDataTotal->addAllocEvent (nByte); + hDataTotal->setAllocStat (nByte); + hDataTotal->setPeakMemUsage (heapSize, hData->getStackId (), + timestamp, pid, userExpId); + if (leaked > 0) + { + hData->addLeakEvent (leaked); + hDataTotal->addLeakEvent (leaked); + hDataTotal->setLeakStat (leaked); + } + break; + case MUNMAP_TRACE: + case FREE_TRACE: + if (hData == NULL) + hData = new HeapData (TOTAL_HEAPNAME); + hDataTotal->setPeakMemUsage (heapSize, hData->getStackId (), + timestamp, pid, userExpId); + break; + case HEAPTYPE_LAST: + break; + } + has_data = true; + } + } + + if (has_data) + { + hDataObjsCallStack = hDataCalStkMap->values ()->copy (); + hasCallStack = true; + } +} diff --git a/gprofng/src/HeapActivity.h b/gprofng/src/HeapActivity.h new file mode 100644 index 0000000..582b0b7 --- /dev/null +++ b/gprofng/src/HeapActivity.h @@ -0,0 +1,76 @@ +/* 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. */ + +#ifndef _HEAPACTIVITY_H +#define _HEAPACTIVITY_H + + +#include <stdio.h> + +#include "vec.h" +#include "Histable.h" +#include "Hist_data.h" +#include "Metric.h" +#include "HeapData.h" +#include "DefaultMap.h" +#include "dbe_types.h" + +class Experiment; +class Expression; +class DataView; +class DbeView; + +class HeapActivity +{ +public: + + HeapActivity (DbeView *_dbev); + + ~HeapActivity () + { + this->reset (); + } + + void reset (void); + Hist_data *compute_metrics (MetricList *, Histable::Type, + Hist_data::Mode, Histable*); + +private: + + void computeCallStack (Histable::Type, VMode viewMode); + void createHistItemTotals (Hist_data *, MetricList *, Histable::Type, bool); + void computeHistTotals (Hist_data *, MetricList *); + void computeHistData (Hist_data *, MetricList *, Hist_data::Mode, Histable *); + + Vector<HeapData*> *hDataObjs; + Vector<HeapData*> *hDataObjsCallStack; + bool hasCallStack; + HeapData *hDataTotal; + + // list of HeapData objects using the stack id as the key + DefaultMap<uint64_t, HeapData*> *hDataCalStkMap; + + // the cached data for mode=Hist_Data::ALL + Hist_data *hist_data_callstack_all; + + DbeView *dbev; +}; + +#endif /* _HEAPACTIVITY_H */ diff --git a/gprofng/src/HeapData.cc b/gprofng/src/HeapData.cc new file mode 100644 index 0000000..5f001ad --- /dev/null +++ b/gprofng/src/HeapData.cc @@ -0,0 +1,284 @@ +/* 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 <assert.h> +#include <string.h> + +#include "util.h" +#include "HeapData.h" + +void +HeapData::init () +{ + allocBytes = 0; + leakBytes = 0; + allocCnt = 0; + leakCnt = 0; + stackId = 0; + histType = Histable::HEAPCALLSTACK; + peakMemUsage = 0; + timestamp = 0; + pid = 0; + userExpId = 0; + aSmallestBytes = _10TB; + aLargestBytes = 0; + a0KB1KBCnt = 0; + a1KB8KBCnt = 0; + a8KB32KBCnt = 0; + a32KB128KBCnt = 0; + a128KB256KBCnt = 0; + a256KB512KBCnt = 0; + a512KB1000KBCnt = 0; + a1000KB10MBCnt = 0; + a10MB100MBCnt = 0; + a100MB1GBCnt = 0; + a1GB10GBCnt = 0; + a10GB100GBCnt = 0; + a100GB1TBCnt = 0; + a1TB10TBCnt = 0; + + lSmallestBytes = _10TB; + lLargestBytes = 0; + l0KB1KBCnt = 0; + l1KB8KBCnt = 0; + l8KB32KBCnt = 0; + l32KB128KBCnt = 0; + l128KB256KBCnt = 0; + l256KB512KBCnt = 0; + l512KB1000KBCnt = 0; + l1000KB10MBCnt = 0; + l10MB100MBCnt = 0; + l100MB1GBCnt = 0; + l1GB10GBCnt = 0; + l10GB100GBCnt = 0; + l100GB1TBCnt = 0; + l1TB10TBCnt = 0; +} + +HeapData::HeapData (char *sName) +{ + stackName = dbe_strdup (sName); + peakStackIds = new Vector<uint64_t>; + peakTimestamps = new Vector<hrtime_t>; + init (); +} + +HeapData::HeapData (HeapData *hData) +{ + stackName = dbe_strdup (hData->stackName); + stackId = hData->stackId; + histType = hData->histType; + allocBytes = hData->allocBytes; + leakBytes = hData->leakBytes; + allocCnt = hData->allocCnt; + leakCnt = hData->leakCnt; + peakMemUsage = hData->peakMemUsage; + timestamp = hData->timestamp; + pid = hData->getPid (); + userExpId = hData->getUserExpId (); + peakStackIds = new Vector<uint64_t>; + Vector<uint64_t> *sIds = hData->peakStackIds; + uint64_t sId; + if (sIds != NULL) + for (int i = 0; i < sIds->size (); i++) + { + sId = sIds->fetch (i); + peakStackIds->append (sId); + } + + peakTimestamps = new Vector<hrtime_t>; + Vector<hrtime_t> *pts = hData->peakTimestamps; + hrtime_t ts; + if (pts != NULL) + for (int i = 0; i < pts->size (); i++) + { + ts = pts->fetch (i); + peakTimestamps->append (ts); + } + + aSmallestBytes = hData->aSmallestBytes; + aLargestBytes = hData->aLargestBytes; + a0KB1KBCnt = hData->a0KB1KBCnt; + a1KB8KBCnt = hData->a1KB8KBCnt; + a8KB32KBCnt = hData->a8KB32KBCnt; + a32KB128KBCnt = hData->a32KB128KBCnt; + a128KB256KBCnt = hData->a128KB256KBCnt; + a256KB512KBCnt = hData->a256KB512KBCnt; + a512KB1000KBCnt = hData->a512KB1000KBCnt; + a1000KB10MBCnt = hData->a1000KB10MBCnt; + a10MB100MBCnt = hData->a10MB100MBCnt; + a100MB1GBCnt = hData->a100MB1GBCnt; + a1GB10GBCnt = hData->a1GB10GBCnt; + a10GB100GBCnt = hData->a10GB100GBCnt; + a100GB1TBCnt = hData->a100GB1TBCnt; + a1TB10TBCnt = hData->a1TB10TBCnt; + + lSmallestBytes = hData->lSmallestBytes; + lLargestBytes = hData->lLargestBytes; + l0KB1KBCnt = hData->l0KB1KBCnt; + l1KB8KBCnt = hData->l1KB8KBCnt; + l8KB32KBCnt = hData->l8KB32KBCnt; + l32KB128KBCnt = hData->l32KB128KBCnt; + l128KB256KBCnt = hData->l128KB256KBCnt; + l256KB512KBCnt = hData->l256KB512KBCnt; + l512KB1000KBCnt = hData->l512KB1000KBCnt; + l1000KB10MBCnt = hData->l1000KB10MBCnt; + l10MB100MBCnt = hData->l10MB100MBCnt; + l100MB1GBCnt = hData->l100MB1GBCnt; + l1GB10GBCnt = hData->l1GB10GBCnt; + l10GB100GBCnt = hData->l10GB100GBCnt; + l100GB1TBCnt = hData->l100GB1TBCnt; + l1TB10TBCnt = hData->l1TB10TBCnt; +} + +HeapData::~HeapData () +{ + free (stackName); + delete peakStackIds; + delete peakTimestamps; +} + +Histable* +HeapData::convertto (Histable_type type, Histable*) +{ + return type == histType ? this : NULL; +} + +char* +HeapData::get_name (Histable::NameFormat /*_nfmt*/) +{ + return stackName; +} + +char* +HeapData::get_raw_name (Histable::NameFormat /*_nfmt*/) +{ + return stackName; +} + +void +HeapData::set_name (char* _name) +{ + free (stackName); + stackName = dbe_strdup (_name); +} + +void +HeapData::setPeakMemUsage (int64_t pmu, uint64_t sId, hrtime_t ts, int procId, int uei) +{ + if (peakMemUsage < pmu) + { + peakMemUsage = pmu; + peakStackIds->reset (); + peakStackIds->append (sId); + peakTimestamps->reset (); + peakTimestamps->append (ts); + pid = procId; + userExpId = uei; + } + else if (peakMemUsage == pmu) + { + for (int i = 0; i < peakStackIds->size (); i++) + { + uint64_t curSId = peakStackIds->fetch (i); + if (curSId == sId) + return; + } + peakStackIds->append (sId); + peakTimestamps->append (ts); + pid = procId; + userExpId = uei; + } +} + +void +HeapData::setAllocStat (int64_t nb) +{ + if (aSmallestBytes > nb) + aSmallestBytes = nb; + if (aLargestBytes < nb) + aLargestBytes = nb; + if (nb >= 0 && nb <= _1KB) + a0KB1KBCnt++; + else if (nb <= _8KB) + a1KB8KBCnt++; + else if (nb <= _32KB) + a8KB32KBCnt++; + else if (nb <= _128KB) + a32KB128KBCnt++; + else if (nb <= _256KB) + a128KB256KBCnt++; + else if (nb <= _512KB) + a256KB512KBCnt++; + else if (nb <= _1000KB) + a512KB1000KBCnt++; + else if (nb <= _10MB) + a1000KB10MBCnt++; + else if (nb <= _100MB) + a10MB100MBCnt++; + else if (nb <= _1GB) + a100MB1GBCnt++; + else if (nb <= _10GB) + a1GB10GBCnt++; + else if (nb <= _100GB) + a10GB100GBCnt++; + else if (nb <= _1TB) + a100GB1TBCnt++; + else if (nb <= _10TB) + a1TB10TBCnt++; +} + +void +HeapData::setLeakStat (int64_t nb) +{ + if (lSmallestBytes > nb) + lSmallestBytes = nb; + if (lLargestBytes < nb) + lLargestBytes = nb; + if (nb >= 0 && nb <= _1KB) + l0KB1KBCnt++; + else if (nb <= _8KB) + l1KB8KBCnt++; + else if (nb <= _32KB) + l8KB32KBCnt++; + else if (nb <= _128KB) + l32KB128KBCnt++; + else if (nb <= _256KB) + l128KB256KBCnt++; + else if (nb <= _512KB) + l256KB512KBCnt++; + else if (nb <= _1000KB) + l512KB1000KBCnt++; + else if (nb <= _10MB) + l1000KB10MBCnt++; + else if (nb <= _100MB) + l10MB100MBCnt++; + else if (nb <= _1GB) + l100MB1GBCnt++; + else if (nb <= _10GB) + l1GB10GBCnt++; + else if (nb <= _100GB) + l10GB100GBCnt++; + else if (nb <= _1TB) + l100GB1TBCnt++; + else if (nb <= _10TB) + l1TB10TBCnt++; +} diff --git a/gprofng/src/HeapData.h b/gprofng/src/HeapData.h new file mode 100644 index 0000000..7ff68e9 --- /dev/null +++ b/gprofng/src/HeapData.h @@ -0,0 +1,450 @@ +/* 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. */ + +#ifndef _HEAPDATA_H +#define _HEAPDATA_H + +#include "gp-defs.h" +#include "gp-time.h" + +#include "vec.h" +#include "data_pckts.h" +#include "Histable.h" + +#define TOTAL_HEAPNAME NTXT("<Total>") +#define TOTAL_STACK_ID 0 +#define _1KB 1024 +#define _8KB 8192 +#define _32KB 32768 +#define _128KB 131072 +#define _256KB 262144 +#define _512KB 524288 +#define _1000KB 1048576 +#define _10MB 10485760 +#define _100MB 104857600 +#define _1GB 1073741824 +#define _10GB 10737418240 +#define _100GB 107374182400 +#define _1TB 1099511627776 +#define _10TB 10995116277760 + +class HeapData : public Histable +{ + friend class HeapActivity; +public: + HeapData (char *sName); + HeapData (HeapData *hData); + ~HeapData (); + char *get_raw_name (Histable::NameFormat nfmt); + void init (); + void setStackName (char* sName); + void setPeakMemUsage (int64_t pmu, uint64_t sId, hrtime_t ts, int procId, int uei); + + virtual char *get_name (Histable::NameFormat nfmt); + virtual void set_name (char * _name); + virtual Histable *convertto (Histable_type, Histable* = NULL); + + virtual Histable_type + get_type () + { + return histType; + } + + virtual uint64_t + get_addr () + { + return stackId; + } + + uint64_t + get_index () + { + return stackId; + } + + char * + getStackName () + { + return stackName; + } + + void + addAllocEvent (uint64_t nb) + { + allocBytes += nb; + allocCnt++; + } + + uint64_t + getAllocBytes () + { + return allocBytes; + } + + int32_t + getAllocCnt () + { + return allocCnt; + } + + void + addLeakEvent (uint64_t nb) + { + leakBytes += nb; + leakCnt++; + } + + uint64_t + getLeakBytes () + { + return leakBytes; + } + + int32_t + getLeakCnt () + { + return leakCnt; + } + + void + setStackId (uint64_t sId) + { + stackId = sId; + } + + uint64_t + getStackId () + { + return stackId; + } + + void + setTimestamp (hrtime_t ts) + { + timestamp = ts; + } + + hrtime_t + getTimestamp () + { + return timestamp; + } + + void + setHistType (Histable::Type hType) + { + histType = hType; + } + + Histable::Type + getHistType () + { + return histType; + } + + int64_t + getPeakMemUsage () + { + return peakMemUsage; + } + + Vector<uint64_t> * + getPeakStackIds () + { + return peakStackIds; + } + + Vector<hrtime_t> * + getPeakTimestamps () + { + return peakTimestamps; + } + + void + setPid (int procId) + { + pid = procId; + } + + int + getPid () + { + return pid; + } + + void + setUserExpId (int uei) + { + userExpId = uei; + } + + int + getUserExpId () + { + return userExpId; + } + + void setAllocStat (int64_t nb); + + int64_t + getASmallestBytes () + { + return aSmallestBytes; + } + + int64_t + getALargestBytes () + { + return aLargestBytes; + } + + int32_t + getA0KB1KBCnt () + { + return a0KB1KBCnt; + } + + int32_t + getA1KB8KBCnt () + { + return a1KB8KBCnt; + } + + int32_t + getA8KB32KBCnt () + { + return a8KB32KBCnt; + } + + int32_t + getA32KB128KBCnt () + { + return a32KB128KBCnt; + } + + int32_t + getA128KB256KBCnt () + { + return a128KB256KBCnt; + } + + int32_t + getA256KB512KBCnt () + { + return a256KB512KBCnt; + } + + int32_t + getA512KB1000KBCnt () + { + return a512KB1000KBCnt; + } + + int32_t + getA1000KB10MBCnt () + { + return a1000KB10MBCnt; + } + + int32_t + getA10MB100MBCnt () + { + return a10MB100MBCnt; + } + + int32_t + getA100MB1GBCnt () + { + return a100MB1GBCnt; + } + + int32_t + getA1GB10GBCnt () + { + return a1GB10GBCnt; + } + + int32_t + getA10GB100GBCnt () + { + return a10GB100GBCnt; + } + + int32_t + getA100GB1TBCnt () + { + return a100GB1TBCnt; + } + + int32_t + getA1TB10TBCnt () + { + return a1TB10TBCnt; + } + + void setLeakStat (int64_t nb); + + int64_t + getLSmallestBytes () + { + return lSmallestBytes; + } + + int64_t + getLLargestBytes () + { + return lLargestBytes; + } + + int32_t + getL0KB1KBCnt () + { + return l0KB1KBCnt; + } + + int32_t + getL1KB8KBCnt () + { + return l1KB8KBCnt; + } + + int32_t + getL8KB32KBCnt () + { + return l8KB32KBCnt; + } + + int32_t + getL32KB128KBCnt () + { + return l32KB128KBCnt; + } + + int32_t + getL128KB256KBCnt () + { + return l128KB256KBCnt; + } + + int32_t + getL256KB512KBCnt () + { + return l256KB512KBCnt; + } + + int32_t + getL512KB1000KBCnt () + { + return l512KB1000KBCnt; + } + + int32_t + getL1000KB10MBCnt () + { + return l1000KB10MBCnt; + } + + int32_t + getL10MB100MBCnt () + { + return l10MB100MBCnt; + } + + int32_t + getL100MB1GBCnt () + { + return l100MB1GBCnt; + } + + int32_t + getL1GB10GBCnt () + { + return l1GB10GBCnt; + } + + int32_t + getL10GB100GBCnt () + { + return l10GB100GBCnt; + } + + int32_t + getL100GB1TBCnt () + { + return l100GB1TBCnt; + } + + int32_t + getL1TB10TBCnt () + { + return l1TB10TBCnt; + } + +private: + char *stackName; // stack name + uint64_t allocBytes; // The total bytes allocated + uint64_t leakBytes; // The total bytes leaked + int32_t allocCnt; // The alloc count + int32_t leakCnt; // The leak count + Histable::Type histType; // The Histable type: HEAPCALLSTACK + int64_t peakMemUsage; // Keep track of peak memory usage + uint64_t stackId; + Vector<uint64_t> *peakStackIds; // The peak memory usage stack ids + hrtime_t timestamp; + Vector<hrtime_t> *peakTimestamps; // The peak data + int pid; // The process id + int userExpId; // The experiment id + + int64_t aSmallestBytes; + int64_t aLargestBytes; + int32_t a0KB1KBCnt; + int32_t a1KB8KBCnt; + int32_t a8KB32KBCnt; + int32_t a32KB128KBCnt; + int32_t a128KB256KBCnt; + int32_t a256KB512KBCnt; + int32_t a512KB1000KBCnt; + int32_t a1000KB10MBCnt; + int32_t a10MB100MBCnt; + int32_t a100MB1GBCnt; + int32_t a1GB10GBCnt; + int32_t a10GB100GBCnt; + int32_t a100GB1TBCnt; + int32_t a1TB10TBCnt; + + int64_t lSmallestBytes; + int64_t lLargestBytes; + int32_t l0KB1KBCnt; + int32_t l1KB8KBCnt; + int32_t l8KB32KBCnt; + int32_t l32KB128KBCnt; + int32_t l128KB256KBCnt; + int32_t l256KB512KBCnt; + int32_t l512KB1000KBCnt; + int32_t l1000KB10MBCnt; + int32_t l10MB100MBCnt; + int32_t l100MB1GBCnt; + int32_t l1GB10GBCnt; + int32_t l10GB100GBCnt; + int32_t l100GB1TBCnt; + int32_t l1TB10TBCnt; +}; + +#endif diff --git a/gprofng/src/HeapMap.cc b/gprofng/src/HeapMap.cc new file mode 100644 index 0000000..8e6c009 --- /dev/null +++ b/gprofng/src/HeapMap.cc @@ -0,0 +1,325 @@ +/* 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 "util.h" +#include "HeapMap.h" + +#define HEAPCHUNKSZ 1024 // number of HeapObj's in a chunk +#define HEAPCHAINS 9192 // number of address-based chains +#define hash(x) (((x) >> 6) % HEAPCHAINS) + +typedef struct HeapObj +{ + uint64_t addr; + uint64_t size; + long val; + HeapObj *next; +} HeapObj; + +typedef struct HeapChunk +{ + void *addr; + HeapChunk *next; +} HeapChunk; + +HeapMap::HeapMap () +{ + chunks = NULL; + empty = NULL; + chain = new HeapObj*[HEAPCHAINS]; + for (int i = 0; i < HEAPCHAINS; i++) + chain[i] = NULL; + + mmaps = new HeapObj; + mmaps->addr = (uint64_t) 0; + mmaps->size = (uint64_t) 0; + mmaps->val = -1; + mmaps->next = NULL; +} + +HeapMap::~HeapMap () +{ + // free up all the chunks + HeapChunk *c = chunks; + while (c != NULL) + { + HeapChunk *next = c->next; + delete c; + c = next; + } + delete[] chain; + delete mmaps; +} + +void +HeapMap::allocate (uint64_t addr, long val) +{ + // get a HeapObj, and set it up for the allocated block + HeapObj *incoming = getHeapObj (); + incoming->addr = addr; + incoming->val = val; + incoming->next = NULL; + + // determine which chain the block belongs on + int ichain = (int) hash (addr); + + // if this is the first, just set it up and return + if (chain[ichain] == NULL) + { + chain[ichain] = incoming; + return; + } + // chain is non-empty -- find the slot for this one + // chain is maintained in reverse address order + HeapObj *prev = NULL; + HeapObj *next = chain[ichain]; + + for (;;) + { + if ((next == NULL) || (next->addr < incoming->addr)) + { + // we've found the spot + incoming->next = next; + if (prev == NULL) + chain[ichain] = incoming; + else + prev->next = incoming; + return; + } + if (next->addr == incoming->addr) + { + // error -- two blocks with same address active + // ignore the new one + releaseHeapObj (incoming); + return; + } + // not yet, keep looking + prev = next; + next = next->next; + } +} + +long +HeapMap::deallocate (uint64_t addr) +{ + int ichain = (int) hash (addr); + HeapObj *cur = chain[ichain]; + HeapObj *prev = NULL; + while (cur != NULL) + { + if (cur->addr == addr) + { + // we've found the block + long val = cur->val; + + // delete the block from the chain + if (prev == NULL) + chain[ichain] = cur->next; + else + prev->next = cur->next; + releaseHeapObj (cur); + return val; + } + prev = cur; + cur = cur->next; + } + + // block not found + return 0; +} + +void +HeapMap::allocateChunk () +{ + // allocate the memory + HeapChunk *c = new HeapChunk; + HeapObj *newc = new HeapObj[HEAPCHUNKSZ]; + + // set up the chunk, and queue it for destructor's use + c->addr = (void *) newc; + c->next = chunks; + chunks = c; + + // Initialize the HeapObj's in the chunk to one chain + // last entry is left NULL, terminating the chain + for (int i = 0; i < (HEAPCHUNKSZ - 1); i++) + newc[i].next = newc + i + 1; + newc[HEAPCHUNKSZ - 1].next = NULL; + + // put that chain on the empty queue + empty = newc; +} + +HeapObj * +HeapMap::getHeapObj () +{ + if (empty == NULL) + allocateChunk (); + HeapObj *ret = empty; + empty = ret->next; + return ret; +} + +void +HeapMap::releaseHeapObj (HeapObj *obj) +{ + obj->next = empty; + empty = obj; +} + +UnmapChunk* +HeapMap::mmap (uint64_t addr, int64_t size, long val) +{ + HeapObj *incoming = getHeapObj (); + incoming->addr = addr; + incoming->size = size; + incoming->val = val; + incoming->next = NULL; + UnmapChunk* list = process (incoming, addr, size); + return list; +} + +UnmapChunk* +HeapMap::munmap (uint64_t addr, int64_t size) +{ + UnmapChunk* list = process (NULL, addr, size); + return list; +} + +UnmapChunk* +HeapMap::process (HeapObj *obj, uint64_t addr, int64_t size) +{ + // Some graphics are used to clarify the alignment of mmap regions + // obj, shown as consecutive pages: "NNNNNN" + // cur, shown as consecutive pages: "CCCCCC" + + // Find the first overlap, start of N is before end of C. Examples: + // CCCCC + // NNNNN + // or + // CCCCC + // NNN + // or + // CCCCC + // NNNNN + // or + // CCCCC + // NNNNNNN + HeapObj *prev = mmaps; + HeapObj *cur = prev->next; + while (cur) + { + if (addr < cur->addr + cur->size) + break; + prev = cur; + cur = prev->next; + } + + // None found + if (cur == NULL) + { + prev->next = obj; + return NULL; + } + + UnmapChunk* list = NULL; + if (addr > cur->addr) + { + if (cur->addr + cur->size <= addr + size) + { + // Process overlap on the left (low side) of new allocation + // New range: N-start to C-end (gets freed below) + prev = cur; + cur = getHeapObj (); + cur->addr = addr; + cur->size = prev->addr + prev->size - addr; + cur->val = prev->val; + cur->next = prev->next; + + // Truncate range: C-start to N-start + prev->size = addr - prev->addr; + } + else + { + // Process new allocation that fits completely within old allocation + // New range: N-start to N-end (freed below) + int64_t c_end = cur->addr + cur->size; + prev = cur; + cur = getHeapObj (); + cur->addr = addr; + cur->size = size; + cur->val = prev->val; + cur->next = prev->next; + + // Truncate range: C-start to N-start + prev->size = addr - prev->addr; + + // New range: N-end to C-end. + HeapObj *next = getHeapObj (); + next->addr = addr + size; + next->size = c_end - next->addr; + next->val = cur->val; + next->next = cur->next; + cur->next = next; + } + } + + // Process all full overlaps. + // Copy details of cur to UnmapChunk list, remove cur from mmaps + while (cur && cur->addr + cur->size <= addr + size) + { + + UnmapChunk* uc = new UnmapChunk; + uc->val = cur->val; + uc->size = cur->size; + uc->next = list; + list = uc; + + HeapObj *t = cur; + cur = cur->next; + releaseHeapObj (t); + } + + if (cur && cur->addr < addr + size) + { + // Process the last overlap (right side of new allocation) + // Copy details of cur to UnmapChunk list + UnmapChunk* uc = new UnmapChunk; + uc->val = cur->val; + uc->size = addr + size - cur->addr; + uc->next = list; + list = uc; + + // Truncate left side of cur + cur->size -= uc->size; + cur->addr = addr + size; + } + + // Insert new allocation + if (obj) + { + prev->next = obj; + obj->next = cur; + } + else + prev->next = cur; + return list; +} diff --git a/gprofng/src/HeapMap.h b/gprofng/src/HeapMap.h new file mode 100644 index 0000000..c46129d --- /dev/null +++ b/gprofng/src/HeapMap.h @@ -0,0 +1,59 @@ +/* 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. */ + +#ifndef _HEAPMAP_H +#define _HEAPMAP_H + +#include "dbe_types.h" +#include "vec.h" + +struct HeapObj; +struct HeapChunk; + +typedef struct UnmapChunk +{ + long val; + int64_t size; + UnmapChunk *next; +} UnmapChunk; + +class HeapMap +{ +public: + HeapMap (); + ~HeapMap (); + void allocate (uint64_t addr, long val); + long deallocate (uint64_t addr); + UnmapChunk *mmap (uint64_t addr, int64_t size, long val); + UnmapChunk *munmap (uint64_t addr, int64_t size); + +private: + void allocateChunk (); + HeapObj *getHeapObj (); + void releaseHeapObj (HeapObj*); + UnmapChunk *process (HeapObj *obj, uint64_t addr, int64_t size); + + HeapChunk *chunks; + HeapObj *empty; + HeapObj **chain; + HeapObj *mmaps; +}; + +#endif /* _HEAPMAP_H */ diff --git a/gprofng/src/Hist_data.cc b/gprofng/src/Hist_data.cc new file mode 100644 index 0000000..4412203 --- /dev/null +++ b/gprofng/src/Hist_data.cc @@ -0,0 +1,1886 @@ +/* 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 <assert.h> + +#include "util.h" +#include "DefaultMap.h" +#include "DbeSession.h" +#include "Experiment.h" +#include "DataObject.h" +#include "Function.h" +#include "Hist_data.h" +#include "Histable.h" +#include "MemObject.h" +#include "IndexObject.h" +#include "MetricList.h" +#include "Metric.h" +#include "Module.h" +#include "LoadObject.h" +#include "Settings.h" +#include "StringBuilder.h" +#include "ExpGroup.h" +#include "PathTree.h" +#include "DbeView.h" +#include "FileData.h" + +Hist_data::HistItem::HistItem (long n) +{ + obj = NULL; + type = 0; + size = n; + value = new TValue[n]; + memset (value, 0, sizeof (TValue) * n); +} + +Hist_data::HistItem::~HistItem () +{ + for (long i = 0; i < size; i++) + if (value[i].tag == VT_LABEL) + free (value[i].l); + delete[] value; +} + +long +Hist_data::size () +{ + // If the data values have not been computed, do so + // Return the total number of items + return hist_items->size (); +} + +Hist_data::HistItem * +Hist_data::fetch (long index) +{ + return (index < VecSize (hist_items)) ? hist_items->get (index) : NULL; +} + +int +Hist_data::sort_compare (HistItem *hi_1, HistItem *hi_2, Sort_type stype, + long ind, Hist_data *hdata) +{ + // Sort the data depending upon order and type + int result = 0; + Histable::Type type = hi_1->obj->get_type (); + if (stype == ALPHA) + { + if (type != Histable::MEMOBJ && type != Histable::INDEXOBJ + && type != Histable::IOACTVFD && type != Histable::IOACTFILE + && type != Histable::IOCALLSTACK) + { + char *nm1 = hi_1->obj->get_name (); + char *nm2 = hi_2->obj->get_name (); + if (nm1 != NULL && nm2 != NULL) + result = strcoll (nm1, nm2); + } + else if (type == Histable::IOCALLSTACK || type == Histable::IOACTVFD + || type == Histable::IOACTFILE) + { + uint64_t idx1, idx2; + idx1 = ((FileData *) (hi_1->obj))->get_index (); + idx2 = ((FileData *) (hi_2->obj))->get_index (); + if (idx1 < idx2) + result = -1; + else if (idx1 > idx2) + result = 1; + else + result = 0; + } + else + { + // for memory and index objects, "alphabetic" is really by index + // <Total> has index -2, and always comes first + // <Unknown> has index -1, and always comes second. + uint64_t i1, i2; + bool needsStringCompare = false; + if (type == Histable::MEMOBJ) + { + i1 = ((MemObj *) (hi_1->obj))->get_index (); + i2 = ((MemObj *) (hi_2->obj))->get_index (); + } + else if (type == Histable::INDEXOBJ) + { + i1 = ((IndexObject *) (hi_1->obj))->get_index (); + i2 = ((IndexObject *) (hi_2->obj))->get_index (); + needsStringCompare = + ((IndexObject *) (hi_1->obj))->requires_string_sort (); + } + else + abort (); + if (i1 == (uint64_t) - 2) + result = -1; + else if (i2 == (uint64_t) - 2) + result = 1; + else if (i1 == (uint64_t) - 1) + result = -1; + else if (i2 == (uint64_t) - 1) + result = 1; + else if (needsStringCompare) + { + char *nm1 = hi_1->obj->get_name (); + char *nm2 = hi_2->obj->get_name (); + if (nm1 != NULL && nm2 != NULL) + { + char nm1_lead = nm1[0]; + char nm2_lead = nm2[0]; + // put "(unknown)" and friends at end of list + if (nm1_lead == '(' && nm1_lead != nm2_lead) + result = 1; + else if (nm2_lead == '(' && nm1_lead != nm2_lead) + result = -1; + else + result = strcoll (nm1, nm2); + } + } + if (result == 0) + { // matches, resolve by index + if (i1 < i2) + result = -1; + else if (i1 > i2) + result = 1; + } + } + } + else if (stype == AUX) + { + switch (type) + { + case Histable::INSTR: + { + DbeInstr *instr1 = (DbeInstr*) hi_1->obj; + DbeInstr *instr2 = (DbeInstr*) hi_2->obj; + result = instr1 ? instr1->pc_cmp (instr2) : instr2 ? 1 : 0; + break; + } + case Histable::LINE: + { + DbeLine *dbl1 = (DbeLine*) hi_1->obj; + DbeLine *dbl2 = (DbeLine*) hi_2->obj; + result = dbl1->line_cmp (dbl2); + } + break; + default: + assert (0); + } + } + else if (stype == VALUE) + { + Metric *m = hdata->get_metric_list ()->get (ind); + if ((m->get_visbits () & (VAL_DELTA | VAL_RATIO)) != 0) + { + TValue v1, v2; + int first_ind = hdata->hist_metrics[ind].indFirstExp; + if ((m->get_visbits () & VAL_DELTA) != 0) + { + v1.make_delta (hi_1->value + ind, hi_1->value + first_ind); + v2.make_delta (hi_2->value + ind, hi_2->value + first_ind); + } + else + { + v1.make_ratio (hi_1->value + ind, hi_1->value + first_ind); + v2.make_ratio (hi_2->value + ind, hi_2->value + first_ind); + } + result = v1.compare (&v2); + } + else + result = hi_1->value[ind].compare (hi_2->value + ind); + } + return result; +} + +int +Hist_data::sort_compare_all (const void *a, const void *b, const void *arg) +{ + HistItem *hi_1 = *((HistItem **) a); + HistItem *hi_2 = *((HistItem **) b); + + Hist_data *hdata = (Hist_data*) arg; + int result = sort_compare (hi_1, hi_2, hdata->sort_type, hdata->sort_ind, hdata); + if (hdata->sort_order == DESCEND) + result = -result; + + // Use the name as the 2d sort key (always ASCEND) + // except for MemoryObjects and IndexObjects, where the index is used + // For the Alphabetic sort + if (result == 0) + { + result = sort_compare (hi_1, hi_2, ALPHA, 0, NULL); + if (result == 0) + { + for (long i = 0, sz = hdata->metrics->size (); i < sz; i++) + { + Metric *m = hdata->metrics->get (i); + if (m->get_type () != Metric::ONAME) + { + result = sort_compare (hi_1, hi_2, VALUE, i, hdata); + if (result != 0) + { + if (hdata->sort_order == DESCEND) + result = -result; + break; + } + } + } + } + } + + // Use the address as the 3d sort key + // ( FUNCTION only, always ASCEND ) + if (result == 0 && hi_1->obj->get_type () == Histable::FUNCTION) + { + Function *f1 = (Function*) hi_1->obj; + Function *f2 = (Function*) hi_2->obj; + if (f1->get_addr () < f2->get_addr ()) + result = -1; + else if (f1->get_addr () > f2->get_addr ()) + result = 1; + } + + // Use the Histable id (ID of function, line, etc.) as the 4th sort key + // Note that IDs are not guaranteed to be stable, + if (result == 0) + { + if (hi_1->obj->id < hi_2->obj->id) + result = -1; + else if (hi_1->obj->id > hi_2->obj->id) + result = 1; + } + + if (result == 0) + return result; // shouldn't happen in most cases; line allows for breakpoint + if (hdata->rev_sort) + result = -result; + return result; +} + +int +Hist_data::sort_compare_dlayout (const void *a, const void *b, const void *arg) +{ + assert ((a != (const void *) NULL)); + assert ((b != (const void *) NULL)); + HistItem *hi_1 = *((HistItem **) a); + HistItem *hi_2 = *((HistItem **) b); + DataObject * dobj1 = (DataObject *) (hi_1->obj); + DataObject * dobj2 = (DataObject *) (hi_2->obj); + DataObject * parent1 = dobj1->parent; + DataObject * parent2 = dobj2->parent; + + Hist_data *hdata = (Hist_data*) arg; + + // are the two items members of the same object? + if (parent1 == parent2) + { + // yes + if (parent1) + { + // and they have real parents... + if (parent1->get_typename ()) + { // element + // use dobj1/dobj2 offset for sorting + uint64_t off1 = dobj1->get_offset (); + uint64_t off2 = dobj2->get_offset (); + if (off1 < off2) + return -1; + if (off1 > off2) + return 1; + return 0; + } + } + } + else + { // parents differ + if (parent1) + { + if (parent1 == dobj2) + // sorting an object and its parent: parent always first + return 1; + dobj1 = parent1; + } + if (parent2) + { + if (parent2 == dobj1) + return -1; + dobj2 = parent2; + } + } + // Either two unknowns, or two scalars, or two parents + hi_1 = hdata->hi_map->get (dobj1); + hi_2 = hdata->hi_map->get (dobj2); + return sort_compare_all ((const void*) &hi_1, (const void*) &hi_2, hdata); +} + +Hist_data::Hist_data (MetricList *_metrics, Histable::Type _type, + Hist_data::Mode _mode, bool _viewowned) +{ + hist_items = new Vector<HistItem*>; + metrics = _metrics; + nmetrics = metrics->get_items ()->size (); + type = _type; + mode = _mode; + gprof_item = new_hist_item (NULL); + viewowned = _viewowned; + sort_ind = -1; + rev_sort = false; + + Histable *tobj = new Other; + tobj->name = dbe_strdup (NTXT ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")); + minimum = new_hist_item (tobj); + + tobj = new Other; + tobj->name = dbe_strdup (NTXT ("")); + maximum = new_hist_item (tobj); + + tobj = new Other; + tobj->name = dbe_strdup (NTXT ("xxxxxxxxxxxxxxxxxxxxxx")); + maximum_inc = new_hist_item (tobj); + + tobj = new Other; + tobj->name = dbe_strdup (NTXT ("<Total>")); + total = new_hist_item (tobj); + + tobj = new Other; + tobj->name = dbe_strdup (NTXT ("XXXX Threshold XXXX")); + threshold = new_hist_item (tobj); + + hi_map = new HashMap<Histable*, HistItem*>; + callsite_mark = new DefaultMap<Histable*, int>; + hist_metrics = new Metric::HistMetric[metrics->size ()]; + for (long i = 0, sz = metrics->size (); i < sz; i++) + { + Metric::HistMetric *h = hist_metrics + i; + h->init (); + Metric *m = metrics->get (i); + if (0 != (m->get_visbits () & (VAL_DELTA | VAL_RATIO))) + h->indFirstExp = + metrics->get_listorder (m->get_cmd (), + m->get_subtype (), "EXPGRID==1"); + if (m->is_tvisible () && m->get_type () == BaseMetric::HWCNTR + && m->get_dependent_bm ()) + h->indTimeVal = + metrics->get_listorder (m->get_dependent_bm ()->get_cmd (), + m->get_subtype (), m->get_expr_spec ()); + } + status = NO_DATA; +} + +Hist_data::~Hist_data () +{ + delete[] hist_metrics; + if (hist_items) + { + hist_items->destroy (); + delete hist_items; + hist_items = NULL; + } + if (gprof_item) + { + delete gprof_item; + gprof_item = NULL; + } + if (maximum) + { + delete maximum->obj; + delete maximum; + maximum = NULL; + } + if (maximum_inc) + { + delete maximum_inc->obj; + delete maximum_inc; + maximum_inc = NULL; + } + if (minimum) + { + delete minimum->obj; + delete minimum; + minimum = NULL; + } + if (total) + { + delete total->obj; + delete total; + total = NULL; + } + if (threshold) + { + delete threshold->obj; + delete threshold; + threshold = NULL; + } + delete metrics; + delete hi_map; + delete callsite_mark; +} + +void +Hist_data::dump (char *msg, FILE *f) +{ + fprintf (f, " Hist_data dump: %s\n", msg); + fprintf (f, " %d=%d metrics\n", (int) nmetrics, (int) metrics->size ()); + for (int i = 0; i < nmetrics; i++) + { + Metric *m = metrics->get_items ()->fetch (i); + char *s = m->get_expr_spec (); + fprintf (f, " %4d %15s %4d %15s\n", i, m->get_mcmd (0), + m->get_id (), s ? s : "(NULL)"); + } + + fprintf (f, NTXT (" HistItem listing\n")); + int n = hist_items->size (); + for (int j = -1; j < n; j++) + { + HistItem *hi; + if (j < 0) + { + hi = total; + fprintf (f, NTXT (" total")); + } + else + { + hi = hist_items->fetch (j); + fprintf (f, NTXT ("%30s"), hi->obj->get_name ()); + } + for (int i = 0; i < nmetrics; i++) + { + char *stmp = hi->value[i].l; + switch (hi->value[i].tag) + { + case VT_SHORT: fprintf (f, NTXT (" %d"), hi->value[i].s); + break; + case VT_INT: fprintf (f, NTXT (" %d"), hi->value[i].i); + break; + case VT_LLONG: fprintf (f, NTXT (" %12lld"), hi->value[i].ll); + break; + case VT_FLOAT: fprintf (f, NTXT (" %f"), hi->value[i].f); + break; + case VT_DOUBLE: fprintf (f, NTXT (" %12.6lf"), hi->value[i].d); + break; + case VT_HRTIME: fprintf (f, NTXT (" %12llu"), hi->value[i].ull); + break; + case VT_LABEL: fprintf (f, NTXT (" %s"), stmp ? stmp: "(unnamed)"); + break; + case VT_ADDRESS: fprintf (f, NTXT (" %12lld"), hi->value[i].ll); + break; + case VT_OFFSET: fprintf (f, NTXT (" %p"), hi->value[i].p); + break; + case VT_ULLONG: fprintf (f, NTXT (" %12llu"), hi->value[i].ull); + break; + default: fprintf (f, NTXT (" ")); + break; + } + } + fprintf (f, NTXT ("\n")); + } +} + +void +Hist_data::sort (long ind, bool reverse) +{ + if (mode != MODL && ind != -1 && ind == sort_ind && reverse == rev_sort) + // there's no change to the sorting + return; + + if (mode == MODL) + { + sort_type = AUX; + sort_order = ASCEND; + } + else + { + if (ind == -1) + return; + Metric::Type mtype = metrics->get_items ()->fetch (ind)->get_type (); + sort_type = mtype == Metric::ONAME ? ALPHA : VALUE; + sort_order = (mtype == Metric::ONAME || mtype == Metric::ADDRESS) ? + ASCEND : DESCEND; + sort_ind = ind; + rev_sort = reverse; + } + + if (mode == Hist_data::LAYOUT || mode == Hist_data::DETAIL) + hist_items->sort ((CompareFunc) sort_compare_dlayout, this); + else + hist_items->sort ((CompareFunc) sort_compare_all, this); + + // ensure that <Total> comes first/last + char *tname = NTXT ("<Total>"); + for (int i = 0; i < hist_items->size (); ++i) + { + HistItem *hi = hist_items->fetch (i); + char *name = hi->obj->get_name (); + if (name != NULL && streq (name, tname)) + { + int idx0 = rev_sort ? hist_items->size () - 1 : 0; + if (i != idx0) + { + hist_items->remove (i); + hist_items->insert (idx0, hi); + } + break; + } + } +} + +void +Hist_data::resort (MetricList *mlist) +{ + if (mlist->get_type () != metrics->get_type ()) + if (metrics->get_type () == MET_CALL) + // wrong type of list -- internal error + abort (); + + // get the new sort order + int ind = mlist->get_sort_ref_index (); + bool reverse = mlist->get_sort_rev (); + sort (ind, reverse); +} + +void +Hist_data::compute_minmax () +{ + HistItem *hi; + int index; + + for (int mind = 0; mind < nmetrics; mind++) + { + Metric *mtr = metrics->get_items ()->fetch (mind); + if (mtr->get_subtype () == Metric::STATIC) + continue; + if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ()) + continue; + ValueTag vtype = mtr->get_vtype2 (); + + switch (vtype) + { + case VT_INT: + minimum->value[mind].tag = VT_INT; + minimum->value[mind].i = 0; + maximum->value[mind].tag = VT_INT; + maximum->value[mind].i = 0; + maximum_inc->value[mind].tag = VT_INT; + maximum_inc->value[mind].i = 0; + + Vec_loop (HistItem *, hist_items, index, hi) + { + if (metrics->get_type () == MET_SRCDIS + && callsite_mark->get (hi->obj)) + { + if (hi->value[mind].i > maximum_inc->value[mind].i) + maximum_inc->value[mind].i = hi->value[mind].i; + // ignore ones that has inclusive time for src/dis view + } + else if (hi->value[mind].i > maximum->value[mind].i) + maximum->value[mind].i = hi->value[mind].i; + if (hi->value[mind].i < minimum->value[mind].i) + minimum->value[mind].i = hi->value[mind].i; + } + break; + case VT_DOUBLE: + minimum->value[mind].tag = VT_DOUBLE; + minimum->value[mind].d = 0.0; + maximum->value[mind].tag = VT_DOUBLE; + maximum->value[mind].d = 0.0; + maximum_inc->value[mind].tag = VT_DOUBLE; + maximum_inc->value[mind].d = 0.0; + Vec_loop (HistItem*, hist_items, index, hi) + { + if (metrics->get_type () == MET_SRCDIS && callsite_mark->get (hi->obj)) + { + if (hi->value[mind].d > maximum_inc->value[mind].d) + { + maximum_inc->value[mind].d = hi->value[mind].d; + maximum_inc->value[mind].sign = hi->value[mind].sign; + } + // ignore ones that has inclusive time for src/dis view + } + else + { + if (hi->value[mind].d > maximum->value[mind].d) + { + maximum->value[mind].d = hi->value[mind].d; + maximum->value[mind].sign = hi->value[mind].sign; + } + if (hi->value[mind].d < minimum->value[mind].d) + { + minimum->value[mind].d = hi->value[mind].d; + minimum->value[mind].sign = hi->value[mind].sign; + } + } + } + break; + case VT_LLONG: + case VT_ULLONG: + case VT_ADDRESS: + minimum->value[mind].tag = vtype; + minimum->value[mind].ll = 0; + maximum->value[mind].tag = vtype; + maximum->value[mind].ll = 0; + maximum_inc->value[mind].tag = vtype; + maximum_inc->value[mind].ll = 0; + Vec_loop (HistItem*, hist_items, index, hi) + { + if (metrics->get_type () == MET_SRCDIS && callsite_mark->get (hi->obj)) + { + if (hi->value[mind].ll > maximum_inc->value[mind].ll) + { + maximum_inc->value[mind].ll = hi->value[mind].ll; + maximum_inc->value[mind].sign = hi->value[mind].sign; + } + // ignore ones that has inclusive time for src/dis view + } + else + { + if (hi->value[mind].ll > maximum->value[mind].ll) + { + maximum->value[mind].ll = hi->value[mind].ll; + maximum->value[mind].sign = hi->value[mind].sign; + } + if (hi->value[mind].ll < minimum->value[mind].ll) + { + minimum->value[mind].ll = hi->value[mind].ll; + minimum->value[mind].sign = hi->value[mind].sign; + } + } + } + break; + default: + break; + } + } +} + +Hist_data::HistItem * +Hist_data::new_hist_item (Histable *obj) +{ + long sz = get_metric_list ()->size (); + HistItem *hi = new HistItem (sz); + hi->obj = obj; + + // We precalculate all metrics as integer values + // and convert them to appropriate types later. + for (long i = 0; i < sz; i++) + { + hi->value[i].tag = VT_INT; + hi->value[i].i = 0; + } + return hi; +} + +Hist_data::HistItem * +Hist_data::new_hist_item (Histable *obj, int itype, TValue *value) +{ + long sz = get_metric_list ()->size (); + HistItem *hi = new HistItem (sz); + hi->obj = obj; + hi->type = itype; + if (value) + for (long i = 0; i < sz; i++) + hi->value[i] = value[i]; + + return hi; +} + +Hist_data::HistItem * +Hist_data::find_hist_item (Histable *obj) +{ + if (obj == NULL) + return NULL; + return hi_map->get (obj); +} + +Hist_data::HistItem * +Hist_data::append_hist_item (Histable *obj) +{ + if (obj == NULL) + return NULL; + HistItem *hi = hi_map->get (obj); + if (hi == NULL) + { + hi = new_hist_item (obj); + hist_items->append (hi); + hi_map->put (obj, hi); + } + if (status == NO_DATA) + status = SUCCESS; + return hi; +} + +void +Hist_data::append_hist_item (HistItem *hi) +{ + hist_items->append (hi); +} + +bool +Hist_data::above_threshold (HistItem* hi) +{ + bool mark = false; + int index; + Metric *mitem; + + Vec_loop (Metric*, metrics->get_items (), index, mitem) + { + if (mitem->get_subtype () == Metric::STATIC) + continue; + switch (hi->value[index].tag) + { + case VT_DOUBLE: + if (hi->value[index].d > threshold->value[index].d) + mark = true; + break; + case VT_INT: + if (hi->value[index].i > threshold->value[index].i) + mark = true; + break; + case VT_LLONG: + if (hi->value[index].ll > threshold->value[index].ll) + mark = true; + break; + case VT_ULLONG: + if (hi->value[index].ull > threshold->value[index].ull) + mark = true; + break; + // ignoring the following cases (why?) + case VT_SHORT: + case VT_FLOAT: + case VT_HRTIME: + case VT_LABEL: + case VT_ADDRESS: + case VT_OFFSET: + break; + } + } + return mark; +} + +void +Hist_data::set_threshold (double proportion) +{ + int index; + Metric *mitem; + Vec_loop (Metric*, metrics->get_items (), index, mitem) + { + TValue *thresh = &threshold->value[index]; + TValue *mtotal = &total->value[index]; + thresh->tag = mitem->get_vtype (); + + if (mitem->get_subtype () == Metric::STATIC) + continue; + switch (thresh->tag) + { + case VT_INT: + thresh->i = (int) (proportion * (double) mtotal->i); + break; + case VT_DOUBLE: + thresh->d = proportion * mtotal->d; + break; + case VT_LLONG: + case VT_ULLONG: + thresh->ull = (unsigned long long) (proportion * (double) mtotal->ll); + break; + case VT_SHORT: + case VT_FLOAT: + case VT_HRTIME: + case VT_LABEL: + case VT_ADDRESS: + case VT_OFFSET: + break; + } + } +} + +double +Hist_data::get_percentage (double value, int mindex) +{ + double total_value; + if (value == 0.0) + return 0.0; + + // Get the total value of this sample set. + // The value must be greater than 0. + total_value = total->value[mindex].to_double (); + + // Find out what percentage of the total value this item is. + // Make sure we don't divide by zero. + if (total_value == 0.0) + return 0.0; + return value / total_value; +} + +int +Hist_data::print_label (FILE *out_file, Metric::HistMetric *hist_metric, + int space) +{ + int name_offset = 0; + StringBuilder sb, sb1, sb2, sb3; + if (space > 0) + { + char *fmt = NTXT ("%*s"); + sb.appendf (fmt, space, NTXT ("")); + sb1.appendf (fmt, space, NTXT ("")); + sb2.appendf (fmt, space, NTXT ("")); + sb3.appendf (fmt, space, NTXT ("")); + } + for (int i = 0; i < nmetrics; i++) + { + Metric *m = metrics->get (i); + Metric::HistMetric *hm = &hist_metric[i]; + int len = hm->width; + char *fmt = NTXT ("%-*s"); + if ((i > 0) && (m->get_type () == Metric::ONAME)) + { + name_offset = sb1.length (); + fmt = NTXT (" %-*s"); + len--; + } + sb.appendf (fmt, len, m->legend ? m->legend : NTXT ("")); + sb1.appendf (fmt, len, hm->legend1); + sb2.appendf (fmt, len, hm->legend2); + sb3.appendf (fmt, len, hm->legend3); + } + sb.trim (); + if (sb.length () != 0) + { + sb.toFileLn (out_file); + } + sb1.toFileLn (out_file); + sb2.toFileLn (out_file); + sb3.toFileLn (out_file); + return name_offset; +} + +void +Hist_data::print_content (FILE *out_file, Metric::HistMetric *hist_metric, int limit) +{ + StringBuilder sb; + int cnt = VecSize (hist_items); + if (cnt > limit && limit > 0) + cnt = limit; + for (int i = 0; i < cnt; i++) + { + sb.setLength (0); + print_row (&sb, i, hist_metric, NTXT (" ")); + sb.toFileLn (out_file); + } +} + +static void +append_str (StringBuilder *sb, char *s, size_t len, int vis_bits) +{ + if ((vis_bits & VAL_RATIO) != 0) + { + if (*s != 'N') // Nan + sb->appendf (NTXT ("x ")); + else + sb->appendf (NTXT (" ")); + sb->appendf (NTXT ("%*s"), (int) (len - 2), s); + } + else + sb->appendf (NTXT ("%*s"), (int) len, s); +} + +void +Hist_data::print_row (StringBuilder *sb, int row, Metric::HistMetric *hmp, char *mark) +{ + TValue res; + char buf[256]; + // Print only a list of user's metrics. ( nmetrics <= mlist->size() ) + for (long i = 0; i < nmetrics; i++) + { + // Print only a list of user's metrics. + Metric *m = metrics->get (i); + if (!m->is_any_visible ()) + continue; + Metric::HistMetric *hm = hmp + i; + int len = sb->length (); + if (m->is_tvisible ()) + { + TValue *v = get_value (&res, hist_metrics[i].indTimeVal, row); + char *s = v->to_str (buf, sizeof (buf)); + append_str (sb, s, hm->maxtime_width, m->get_visbits ()); + } + if (m->is_visible ()) + { + TValue *v = get_value (&res, i, row); + char *s = v->to_str (buf, sizeof (buf)); + if (m->get_type () == BaseMetric::ONAME) + { + sb->append (mark); + if (i + 1 == nmetrics) + sb->appendf (NTXT ("%s"), s); + else + sb->appendf (NTXT ("%-*s "), (int) hm->maxvalue_width, s); + continue; + } + else + { + if (len != sb->length ()) + sb->append (' '); + append_str (sb, s, hm->maxvalue_width, m->get_visbits ()); + } + } + if (m->is_pvisible ()) + { + if (len != sb->length ()) + sb->append (' '); + long met_ind = i; + if (m->is_tvisible () && !m->is_visible ()) + met_ind = hist_metrics[i].indTimeVal; + TValue *v = get_real_value (&res, met_ind, row); + double percent = get_percentage (v->to_double (), met_ind); + if (percent == 0.0) + // adjust to change format from xx.yy% + sb->append (NTXT (" 0. ")); + else + // adjust format below to change format from xx.yy% + sb->appendf (NTXT ("%6.2f"), (100.0 * percent)); + } + len = sb->length () - len; + if (hm->width > len && i + 1 != nmetrics) + sb->appendf (NTXT ("%*s"), (int) (hm->width - len), NTXT (" ")); + } +} + +TValue * +Hist_data::get_real_value (TValue *res, int met_index, int row) +{ + HistItem *hi = hist_items->get (row); + Metric *m = metrics->get (met_index); + if (m->get_type () == BaseMetric::ONAME) + { + res->l = dbe_strdup (hi->obj->get_name ()); + res->tag = VT_LABEL; + return res; + } + return hi->value + met_index; +} + +TValue * +Hist_data::get_value (TValue *res, int met_index, int row) +{ + HistItem *hi = hist_items->get (row); + Metric *m = metrics->get (met_index); + if ((m->get_visbits () & (VAL_DELTA | VAL_RATIO)) != 0) + { + int ind = hist_metrics[met_index].indFirstExp; + if ((m->get_visbits () & VAL_DELTA) != 0) + res->make_delta (hi->value + met_index, hi->value + ind); + else + res->make_ratio (hi->value + met_index, hi->value + ind); + return res; + } + return get_real_value (res, met_index, row); +} + +TValue * +Hist_data::get_value (TValue *res, int met_index, HistItem *hi) +{ + Metric *m = metrics->get (met_index); + if ((m->get_visbits () & (VAL_DELTA | VAL_RATIO)) != 0) + { + int ind = hist_metrics[met_index].indFirstExp; + if ((m->get_visbits () & VAL_DELTA) != 0) + res->make_delta (hi->value + met_index, hi->value + ind); + else + res->make_ratio (hi->value + met_index, hi->value + ind); + return res; + } + if (m->get_type () == BaseMetric::ONAME) + { + res->l = dbe_strdup (hi->obj->get_name ()); + res->tag = VT_LABEL; + return res; + } + return hi->value + met_index; +} + +Metric::HistMetric * +Hist_data::get_histmetrics () +{ + // find the width for each column. + for (long i = 0, sz = metrics->size (); i < sz; i++) + { + Metric *m = metrics->get (i); + Metric::HistMetric *hm = hist_metrics + i; + if (m->is_value_visible ()) + { + TValue res; + for (long i1 = 0, sz1 = VecSize(hist_items); i1 < sz1; i1++) + { + TValue *v = get_value (&res, i, i1); + long len = v->get_len (); + if (hm->maxvalue_width < len) + hm->maxvalue_width = len; + } + if ((m->get_visbits () & VAL_RATIO) != 0) + hm->maxvalue_width += 2; // "x " + } + } + + for (long i = 0, sz = metrics->size (); i < sz; i++) + { + Metric *m = metrics->get (i); + Metric::HistMetric *hm = hist_metrics + i; + if (m->is_time_visible ()) + // take a value from depended metric + hm->maxtime_width = hist_metrics[hm->indTimeVal].maxvalue_width; + m->legend_width (hm, 2); + } + return hist_metrics; +} + +void +Hist_data::update_total (Hist_data::HistItem *new_total) +{ + for (long i = 0, sz = metrics->size (); i < sz; i++) + total->value[i] = new_total->value[i]; +} + +void +Hist_data::update_max (Metric::HistMetric *hm_tmp) +{ + Metric::HistMetric *hms = get_histmetrics (); + for (int i = 0; i < nmetrics; i++) + { + Metric::HistMetric *hm = hms + i; + Metric::HistMetric *hm1 = hm_tmp + i; + if (hm1->maxtime_width < hm->maxtime_width) + hm1->maxtime_width = hm->maxtime_width; + if (hm1->maxvalue_width < hm->maxvalue_width) + hm1->maxvalue_width = hm->maxvalue_width; + } +} + +void +Hist_data::update_legend_width (Metric::HistMetric *hm_tmp) +{ + for (int i = 0; i < nmetrics; i++) + { + Metric *m = metrics->get (i); + m->legend_width (hm_tmp + i, 2); + } +} + +void +Metric::HistMetric::update_max (Metric::HistMetric *hm) +{ + if (maxtime_width < hm->maxtime_width) + maxtime_width = hm->maxtime_width; + if (maxvalue_width < hm->maxvalue_width) + maxvalue_width = hm->maxvalue_width; +} + +void +Metric::HistMetric::init () +{ + width = 0; + maxvalue_width = 0; + maxtime_width = 0; + legend1[0] = 0; + legend2[0] = 0; + legend3[0] = 0; + indFirstExp = -1; + indTimeVal = -1; +} + +size_t +Hist_data::value_maxlen (int mindex) +{ + size_t maxlen = maximum->value[mindex].get_len (); + size_t minlen = minimum->value[mindex].get_len (); + // minlen can be bigger than maxlen only for negative value + return minlen > maxlen ? minlen : maxlen; +} + +size_t +Hist_data::time_len (TValue *value, int clock) +{ + TValue tm_value; + tm_value.tag = VT_DOUBLE; + tm_value.sign = value->sign; + tm_value.d = 1.e-6 * value->ll / clock; + return tm_value.get_len (); +} + +size_t +Hist_data::time_maxlen (int mindex, int clock) +{ + size_t maxlen = time_len (&(maximum->value[mindex]), clock); + size_t minlen = time_len (&(minimum->value[mindex]), clock); + // minlen can be bigger than maxlen only for negative value + return minlen > maxlen ? minlen : maxlen; +} + +size_t +Hist_data::name_len (HistItem *item) +{ + char *name = item->obj->get_name (); + return strlen (name); +} + +size_t +Hist_data::name_maxlen () +{ + size_t res = 0; + for (long i = 0; i < size (); i++) + { + HistItem *hi = fetch (i); + size_t len = name_len (hi); + if (res < len) + res = len; + } + return res; +} + +// Returns vector of object ids for the vector of selections +// returns NULL if no valid selections +Vector<uint64_t> * +Hist_data::get_object_indices (Vector<int> *selections) +{ + // if no selections, return NULL + if (selections == NULL || selections->size () == 0) + return NULL; + + Vector<uint64_t> *indices = new Vector<uint64_t>; + for (long i = 0, sz = selections->size (); i < sz; i++) + { + int sel = selections->get (i); + HistItem *hi = hist_items->get (sel); + if (hi == NULL || hi->obj == NULL) + continue; + Vector<Histable*> *v = hi->obj->get_comparable_objs (); + for (long i1 = 0, sz1 = v ? v->size () : 0; i1 < sz1; i1++) + { + Histable *h1 = v->get (i1); + if (h1 && (indices->find_r (h1->id) < 0)) + indices->append (h1->id); + } + if (indices->find_r (hi->obj->id) < 0) + indices->append (hi->obj->id); + } + return indices; +} + +DbeInstr::DbeInstr (uint64_t _id, int _flags, Function *_func, uint64_t _addr) +{ + id = _id; + flags = _flags; + addr = _addr; + func = _func; + img_offset = addr + func->img_offset; + lineno = -1; + size = 0; + current_name_format = NA; + isUsed = false; + inlinedInd = -1; +} + +int +DbeInstr::pc_cmp (DbeInstr *instr2) +{ + int result = 0; + if (instr2 == NULL) + return -1; + + // All PC's with the Line flag go to the + // end of the list. See Module::init_index() + if (flags & PCLineFlag) + { + if (instr2->flags & PCLineFlag) + { + if (addr < instr2->addr) + result = -1; + else if (addr > instr2->addr) + result = 1; + else + result = 0; + } + else + result = 1; + } + else if (instr2->flags & PCLineFlag) + result = -1; + else if (func == instr2->func) + { + if (size == 0) + { + if (addr < instr2->addr) + result = -1; + else if (addr == instr2->addr) + result = 0; + else if (addr >= instr2->addr + instr2->size) + result = 1; + else + result = 0; + } + else if (instr2->size == 0) + { + if (addr > instr2->addr) + result = 1; + else if (addr + size <= instr2->addr) + result = -1; + else + result = 0; + } + else if (addr < instr2->addr) + result = -1; + else if (addr > instr2->addr) + result = 1; + else + result = 0; + + if (result == 0) + { + if (flags & PCTrgtFlag) + { + if (!(instr2->flags & PCTrgtFlag)) + result = -1; + } + else if (instr2->flags & PCTrgtFlag) + result = 1; + } + } + else + result = func->func_cmp (instr2->func); + return result; +} + +char * +DbeInstr::get_name (NameFormat nfmt) +{ + if (name && (nfmt == current_name_format || nfmt == Histable::NA)) + return name; + + free (name); + name = NULL; + current_name_format = nfmt; + char *fname = func->get_name (nfmt); + + if (func->flags & FUNC_FLAG_NO_OFFSET) + name = dbe_strdup (fname); + else if (addr == (uint64_t) - 1 + && func != dbeSession->get_JUnknown_Function ()) + // We use three heuristics above to recognize this special case. + // Once the original problem with bci == -1 is fixed, we don't + // need it any more. + name = dbe_sprintf (GTXT ("<Function %s: HotSpot-compiled leaf instructions>"), + fname); + else if (addr == (uint64_t) - 3) + name = dbe_sprintf (GTXT ("%s <Java native method>"), fname); + else + { + char buf[64], *typetag = NTXT (""), *alloc_typetag = NULL; + StringBuilder sb; + sb.append (fname); + if (func != dbeSession->get_JUnknown_Function ()) + { + if (addr <= 0xFFFFFFFFU) + snprintf (buf, sizeof (buf), " + 0x%08X", (unsigned int) addr); + else + snprintf (buf, sizeof (buf), " + 0x%016llX", + (unsigned long long) addr); + } + else + { + char *subname; + switch ((long int) addr) + { + case -1: + subname = GTXT ("agent error"); + break; + case -2: + subname = GTXT ("GC active"); + break; + case -3: + subname = GTXT ("unknown non-Java frame"); + break; + case -4: + subname = GTXT ("unwalkable non-Java frame"); + break; + case -5: + subname = GTXT ("unknown Java frame"); + break; + case -6: + subname = GTXT ("unwalkable Java frame"); + break; + case -7: + subname = GTXT ("unknown thread state"); + break; + case -8: + subname = GTXT ("thread in exit"); + break; + case -9: + subname = GTXT ("deopt in process ticks"); + break; + case -10: + subname = GTXT ("safepoint synchronizing ticks"); + break; + default: + subname = GTXT ("unexpected error"); + break; + } + snprintf (buf, sizeof (buf), "<%s (%d)>", subname, (int) addr); + } + sb.append (buf); + if (flags & PCTrgtFlag) + // annotate synthetic instruction + sb.append ('*'); // special distinguishing marker + + DbeLine *dbeline = mapPCtoLine (NULL); + char *str = NULL; + if (dbeline && dbeline->lineno > 0) + str = strrchr (dbeline->get_name (nfmt), ','); + if (str) + sb.append (str); + if (strlen (typetag) > 0) + { // include padding for alignment + do + { + sb.append (' '); + } + while (sb.length () < 40); + sb.append (typetag); + delete alloc_typetag; + } + if (inlinedInd >= 0) + add_inlined_info (&sb); + name = sb.toString (); + } + return name; +} + +DbeLine* +DbeInstr::mapPCtoLine (SourceFile *sf) +{ + if (inlinedInd == -1) + { + inlinedInd = -2; + for (int i = 0; i < func->inlinedSubrCnt; i++) + { + InlinedSubr *p = func->inlinedSubr + i; + if (p->level == 0) + { + if (addr < p->low_pc) + break; + if (p->contains (addr)) + { + inlinedInd = i; + break; + } + } + } + } + if (inlinedInd >= 0) + { + DbeLine *dl = func->inlinedSubr[inlinedInd].dbeLine; + return dl->sourceFile->find_dbeline (func, dl->lineno); + } + return func->mapPCtoLine (addr, sf); +} + +void +DbeInstr::add_inlined_info (StringBuilder *sb) +{ + do + { + sb->append (' '); + } + while (sb->length () < 40); + sb->append (NTXT ("<-- ")); + + InlinedSubr *last = NULL; + for (int i = inlinedInd; i < func->inlinedSubrCnt; i++) + { + InlinedSubr *p = func->inlinedSubr + i; + if (p->level == 0 && i > inlinedInd) + break; + if (!p->contains (addr)) + continue; + if (last) + { + if (last->fname) + { + sb->append (last->fname); + sb->append (' '); + } + DbeLine *dl = p->dbeLine; + sb->appendf (NTXT ("%s:%lld <-- "), get_basename (dl->sourceFile->get_name ()), (long long) dl->lineno); + } + last = p; + } + if (last) + { + if (last->fname) + { + sb->append (last->fname); + sb->append (' '); + } + } + DbeLine *dl = func->mapPCtoLine (addr, NULL); + sb->appendf ("%s:%lld ", get_basename (dl->sourceFile->get_name ()), + (long long) dl->lineno); +} + +char * +DbeInstr::get_descriptor () +{ + char *typetag = NTXT (""); + if ((flags & PCTrgtFlag) == 0) // not synthetic instruction + { // use memop descriptor, if available + Module *mod = func->module; + if (mod->hwcprof && mod->infoList) + { + long i; + inst_info_t *info = NULL; + Vec_loop (inst_info_t*, mod->infoList, i, info) + { + if (info->offset == func->img_offset + addr) break; + } + if (info) + { + long t; + datatype_t *dtype = NULL; + Vec_loop (datatype_t*, mod->datatypes, t, dtype) + { + if (dtype->datatype_id == info->memop->datatype_id) + break; + } + if (dtype && dtype->dobj) + typetag = dtype->dobj->get_name (); + } + } + } + return dbe_strdup (typetag); +} + +int64_t +DbeInstr::get_size () +{ + // Function *func = (Function*)dbeSession->get_hobj( pc ); + // Module *mod = func ? func->module : NULL; + // return mod ? mod->instrSize( func->img_offset + addr ) : 0; + return size; +} + +uint64_t +DbeInstr::get_addr () +{ + return func->get_addr () + addr; +} + +Histable * +DbeInstr::convertto (Type type, Histable *obj) +{ + Histable *res = NULL; + SourceFile *source = (SourceFile*) obj; + switch (type) + { + case INSTR: + res = this; + break; + case LINE: + res = mapPCtoLine (source); + break; + case SOURCEFILE: + res = mapPCtoLine (source); + if (res) + res = ((DbeLine*) res)->sourceFile; + break; + case FUNCTION: + res = func; + break; + default: + assert (0); + } + return res; +} + +char * +DbeEA::get_name (NameFormat) +{ + if (name == NULL) + // generate one + name = dbe_strdup (dbeSession->localized_SP_UNKNOWN_NAME); + return name; +} + +Histable * +DbeEA::convertto (Type type, Histable *obj) +{ + Histable *res = NULL; + assert (obj == NULL); + switch (type) + { + case EADDR: + res = this; + break; + case DOBJECT: + res = dobj; + break; + default: + assert (0); + } + return res; +} + +DbeLine::DbeLine (Function *_func, SourceFile *sf, int _lineno) +{ + func = _func; + lineno = _lineno; + sourceFile = sf; + id = sf->id + _lineno; + offset = 0; + size = 0; + flags = 0; + include = NULL; + dbeline_func_next = NULL; + dbeline_base = this; + current_name_format = Histable::NA; +} + +DbeLine::~DbeLine () +{ + delete dbeline_func_next; +} + +int +DbeLine::line_cmp (DbeLine *dbl) +{ + return lineno - dbl->lineno; +} + +void +DbeLine::init_Offset (uint64_t p_offset) +{ + if (offset == 0) + offset = p_offset; + if (dbeline_base && dbeline_base->offset == 0) + dbeline_base->offset = p_offset; +} + +char * +DbeLine::get_name (NameFormat nfmt) +{ + char *srcname = NULL, *basename, *fname; + + if (func == NULL) + { + if (name) + return name; + srcname = sourceFile->get_name (); + basename = get_basename (srcname); + name = dbe_sprintf (GTXT ("line %u in \"%s\""), lineno, basename); + return name; + } + + if (name && (nfmt == current_name_format || nfmt == Histable::NA)) + return name; + + current_name_format = nfmt; + free (name); + fname = func->get_name (nfmt); + if (func->flags & (FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET)) + { + name = dbe_strdup (fname); + return name; + } + + if (sourceFile) + srcname = sourceFile->get_name (); + if (!srcname || strlen (srcname) == 0) + srcname = func->getDefSrcName (); + basename = get_basename (srcname); + + if (lineno != 0) + { + if (sourceFile == func->getDefSrc ()) + name = dbe_sprintf (GTXT ("%s, line %u in \"%s\""), fname, lineno, + basename); + else + name = dbe_sprintf (GTXT ("%s, line %u in alternate source context \"%s\""), + fname, lineno, basename); + } + else if (sourceFile == NULL || (sourceFile->flags & SOURCE_FLAG_UNKNOWN) != 0) + name = dbe_sprintf (GTXT ("<Function: %s, instructions without line numbers>"), + fname); + else + name = dbe_sprintf (GTXT ("<Function: %s, instructions from source file %s>"), + fname, basename); + return name; +} + +int64_t +DbeLine::get_size () +{ + return size; +} + +uint64_t +DbeLine::get_addr () +{ + if (func == NULL && dbeline_func_next == NULL) + return (uint64_t) 0; + Function *f = func ? func : dbeline_func_next->func; + return f->get_addr () + offset; +} + +Histable * +DbeLine::convertto (Type type, Histable *obj) +{ + Histable *res = NULL; + switch (type) + { + case INSTR: + { + Function *f = (Function *) convertto (FUNCTION, NULL); + if (f) + res = f->find_dbeinstr (0, offset); + break; + } + case LINE: + res = dbeline_base; + break; + case FUNCTION: + if (func) + { + res = func; + break; + } + else + { + int not_found = 1; + for (DbeLine *dl = dbeline_base; dl; dl = dl->dbeline_func_next) + { + Function *f = dl->func; + not_found = (obj == NULL // XXXX pass dbeview as Histable* + || ((DbeView*) obj)->get_path_tree ()->get_func_nodeidx (f) == 0); + if (f && f->def_source == sourceFile && (!not_found)) + { + res = f; + break; + } + } + if (res == NULL && dbeline_func_next) + { + for (DbeLine *dl = dbeline_base; dl; dl = dl->dbeline_func_next) + { + Function *f = dl->func; + if (f && f->def_source == sourceFile) + { + res = f; + break; + } + } + } + if (res == NULL && dbeline_func_next) + res = dbeline_func_next->func; + } + break; + case SOURCEFILE: + res = (include) ? include : sourceFile; + break; + default: + assert (0); + } + return res; +} + +CStack_data::CStack_data (MetricList *_metrics) +{ + metrics = _metrics; + total = new_cstack_item (); + cstack_items = new Vector<CStack_item*>; +} + +CStack_data::CStack_item::CStack_item (long n) +{ + stack = NULL; + count = 0; + val = 0; + value = new TValue[n]; + memset (value, 0, sizeof (TValue) * n); +} + +CStack_data::CStack_item::~CStack_item () +{ + delete stack; + delete[] value; +} + +CStack_data::CStack_item * +CStack_data::new_cstack_item () +{ + int nmetrics = metrics->get_items ()->size (); + CStack_item *item = new CStack_item (nmetrics); + + // We precalculate all metrics as integer values + // and convert them to appropriate types later. + for (int i = 0; i < nmetrics; i++) + item->value[i].tag = metrics->get_items ()->fetch (i)->get_vtype (); + return item; +} + +HistableFile::HistableFile () +{ + dbeFile = NULL; + isUsed = false; +} + +Histable::Histable () +{ + name = NULL; + id = 0; + comparable_objs = NULL; + phaseCompareIdx = -1; +} + +Histable::~Histable () +{ + delete_comparable_objs (); + free (name); +} + +void +Histable::delete_comparable_objs () +{ + if (comparable_objs) + { + Vector<Histable*> *v = comparable_objs; + for (int i = 0; i < v->size (); i++) + { + Histable *h = v->fetch (i); + if (h) + { + h->comparable_objs = NULL; + h->phaseCompareIdx = phaseCompareIdx; + } + } + delete v; + } +} + +void +Histable::update_comparable_objs () +{ + if (phaseCompareIdx != ExpGroup::phaseCompareIdx) + { + phaseCompareIdx = ExpGroup::phaseCompareIdx; + delete_comparable_objs (); + } +} + +Vector<Histable*> * +Histable::get_comparable_objs () +{ + return comparable_objs; +} + +Histable * +Histable::get_compare_obj () +{ + Vector<Histable*> *v = get_comparable_objs (); + for (long i = 0, sz = VecSize (v); i < sz; i++) + { + Histable *h = v->get (i); + if (h) + return h; + } + return this; +} + +#define CASE_S(x) case x: return (char *) #x + +char * +Histable::type_to_string () +{ + switch (get_type ()) + { + CASE_S (INSTR); + CASE_S (LINE); + CASE_S (FUNCTION); + CASE_S (MODULE); + CASE_S (LOADOBJECT); + CASE_S (EADDR); + CASE_S (MEMOBJ); + CASE_S (INDEXOBJ); + CASE_S (PAGE); + CASE_S (DOBJECT); + CASE_S (SOURCEFILE); + CASE_S (EXPERIMENT); + CASE_S (OTHER); + default: + break; + } + return NTXT ("ERROR"); +} + +void +Histable::dump_comparable_objs () +{ + Dprintf (DEBUG_COMPARISON, + "# Histable::dump_comparable_objs type=%s(%d) 0x%lx id=%lld %s\n", + type_to_string (), get_type (), (unsigned long) this, (long long) id, + STR (get_name ())); + for (int i = 0, sz = comparable_objs ? comparable_objs->size () : 0; i < sz; i++) + { + Histable *h = comparable_objs->fetch (i); + Dprintf (DEBUG_COMPARISON, " %d type=%s(%d) 0x%lx id=%lld %s\n", i, + h ? h->type_to_string () : "", h ? h->get_type () : -1, + (unsigned long) h, (long long) (h ? h->id : 0), + h ? STR (h->get_name ()) : NTXT ("")); + } +} + +char * +Histable::dump () +{ + StringBuilder sb; + sb.appendf (sizeof (long) == 32 + ? " 0x%08lx : type=%s(%d) id=%lld %s" + : " 0x%016lx : type=%s(%d) id=%lld %s", + (unsigned long) this, type_to_string (), get_type (), + (long long) id, STR (get_name ())); + switch (get_type ()) + { + case INSTR: + { + DbeInstr *o = (DbeInstr *) this; + sb.appendf (sizeof (long) == 32 + ? " func=0x%08lx lineno=%lld" + : " func=0x%016lx lineno=%lld", + (unsigned long) o->func, (long long) o->lineno); + break; + } + case LINE: + { + DbeLine *o = (DbeLine *) this; + sb.appendf (sizeof (long) == 32 + ? " func=0x%08lx sourceFile=0x%08lx lineno=%lld" + : " func=0x%016lx sourceFile=0x%016lx lineno=%lld", + (unsigned long) o->func, (unsigned long) o->sourceFile, + (long long) o->lineno); + break; + } + default: + break; + } + return sb.toString (); +} diff --git a/gprofng/src/Hist_data.h b/gprofng/src/Hist_data.h new file mode 100644 index 0000000..c5f7281 --- /dev/null +++ b/gprofng/src/Hist_data.h @@ -0,0 +1,292 @@ +/* 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. */ + +#ifndef _HIST_DATA_H +#define _HIST_DATA_H + +// A Hist_data object is used to obtain data used for constructing +// a histogram display. + +#include <sys/types.h> + +#include <vec.h> +#include <Map.h> +#include <HashMap.h> + +#include "dbe_structs.h" +#include "Histable.h" +#include "DerivedMetrics.h" + +class DbeLine; +class MetricList; + +class Hist_data +{ +public: + friend class DbeView; + friend class er_print_histogram; + friend class PathTree; + friend class DataSpace; + friend class MemorySpace; + friend class IOActivity; + friend class HeapActivity; + + // HistItem contains all the data about a single histogram item. + struct HistItem + { + HistItem (long n); + ~HistItem (); + Histable *obj; // info on the object + int type; // annotated src/dis: type + TValue *value; // numeric values + long size; + }; + + enum Hist_status + { + SUCCESS = 0, + NO_DATA + }; + + enum Mode + { + ALL, + CALLERS, + CALLEES, + SELF, + MODL, + LAYOUT, + DETAIL + }; + + enum Sort_order + { + ASCEND, + DESCEND + }; + + enum Sort_type + { + ALPHA, + VALUE, + AUX + }; + + Hist_data (MetricList *, Histable::Type, Mode, bool _viewowned = false); + + virtual ~Hist_data (); + void dump (char *msg, FILE *f); + + Hist_status + get_status (void) + { + return status; + } + + // Return the view ownership flag + bool + isViewOwned (void) + { + return viewowned; + } + + // Return the total number of items + long size (void); + + // Append a new HistItem for the specified Histable + HistItem *append_hist_item (Histable *obj); + void append_hist_item (HistItem *hi); + TValue *get_real_value (TValue *res, int met_index, int row); + TValue *get_value (TValue *res, int met_index, int row); + TValue *get_value (TValue *res, int met_index, HistItem *hi); + void print_row (StringBuilder *sb, int row, Metric::HistMetric *hist_metric, char *mark); + void print_content (FILE *out_file, Metric::HistMetric *hist_metric, int limit); + int print_label (FILE *out_file, Metric::HistMetric *hist_metric, int space); + void update_total (Hist_data::HistItem *new_total); + void update_max (Metric::HistMetric *hm_tmp); + void update_legend_width (Metric::HistMetric *hm_tmp); + + // Find an existing HistItem + HistItem *find_hist_item (Histable *obj); + + // sort the data + void sort (long ind, bool reverse); + + // resort the data, if metric sort or direction has changed + void resort (MetricList *mlist); + + // compute minima and maxima + void compute_minmax (void); + + // fetch() takes a hist item index and returns a ptr to the item + HistItem *fetch (long index); + + HistItem * + get_maximums (void) + { + return maximum; + } + + HistItem * + get_maximums_inc (void) + { + return maximum_inc; + } + + HistItem * + get_minimums (void) + { + return minimum; + } + + HistItem * + get_totals (void) + { + return total; + } + + Vector<HistItem*> * + get_hist_items (void) + { + return hist_items; + } + + void + set_status (Hist_status st) + { + status = st; + } + + MetricList * + get_metric_list (void) + { + return metrics; + } + + Map<Histable*, int> * + get_callsite_mark () + { + return callsite_mark; + } + + Metric::HistMetric *get_histmetrics (); + void set_threshold (double proportion); + bool above_threshold (HistItem *hi); + double get_percentage (double value, int mindex); + size_t value_maxlen (int mindex); // Return the drawing length + size_t time_len (TValue *value, int clock); + size_t time_maxlen (int mindex, int clock); + size_t name_len (HistItem *item); + size_t name_maxlen (); + HistItem *new_hist_item (Histable *obj, int itype, TValue *value); + HistItem *update_hist_item (HistItem *hi, TValue *value); + Vector<uint64_t> *get_object_indices (Vector<int> *selections); + +private: + + Metric::HistMetric *hist_metrics; + Vector<HistItem*> *hist_items; // Actual histogram values + HashMap<Histable*, HistItem*>*hi_map; // map: Histable -> HistItem + Map<Histable*, int>*callsite_mark; + Hist_status status; + int nmetrics; // number of metrics + MetricList *metrics; + Histable::Type type; + Sort_order sort_order; + Sort_type sort_type; + int sort_ind; + bool rev_sort; // true if sort is reversed + + Mode mode; + HistItem *gprof_item; // used for gprof-style info + Histable *spontaneous; + + // Private state variables + HistItem *maximum; + HistItem *minimum; + HistItem *maximum_inc; + HistItem *total; + HistItem *threshold; + + // Perform the sort operation with this function + static int sort_compare_all (const void *a, const void *b, const void *arg); + static int sort_compare_dlayout (const void *a, const void *b, const void *arg); + static int sort_compare (HistItem *hi_1, HistItem *hi_2, Sort_type stype, + long ind, Hist_data *hdata); + + // Allocate a new structure of dynamic size + HistItem *new_hist_item (Histable *obj); + + // Flag indicating whether or not the Hist_data structure + // is owned by a DbeView, which has responsibility for + // deleting it, or not, in which case the last user deletes it. + // XXX this is very ugly, and arises from the inconsistent handling + // XXX of the Hist_data structure in various bits of code. + bool viewowned; +}; + +// This structure is destined to merge with Hist_data. +// We currently use it to present callstack data such as +// leak and allocation lists. + +class DbeInstr; + +struct CStack_data +{ + + struct CStack_item + { + CStack_item (long n); + ~CStack_item (); + long count; + int64_t val; + Vector<DbeInstr*> *stack; + TValue *value; // numeric values + }; + + Vector<CStack_item*> *cstack_items; + CStack_item *total; + + CStack_item *new_cstack_item (); + CStack_data (MetricList *); + + long + size () + { + return cstack_items->size (); + } + + CStack_item * + fetch (long i) + { + return cstack_items->fetch (i); + } + + ~CStack_data () + { + cstack_items->destroy (); + delete cstack_items; + delete total; + } + + MetricList *metrics; +}; + +#endif /* _HIST_DATA_H */ diff --git a/gprofng/src/Histable.h b/gprofng/src/Histable.h new file mode 100644 index 0000000..c4cf854 --- /dev/null +++ b/gprofng/src/Histable.h @@ -0,0 +1,333 @@ +/* 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. */ + +#ifndef _HISTABLE_H +#define _HISTABLE_H + +// +// The Histable class hierarchy is used to build up a representation of +// the codeobjects (functions, modules, loadObjects, etc.) that make up the +// text address space of a program. The hierarchy is as follows: +// +// Histable (public) +// LoadObject (public) +// Module (public) +// Function (public) +// +// Dataobjects are objects from the data address space of a program. +// The reason for calling the base class "Histable" is because these +// objects are all valid objects for computing histograms on. + +// A Histable object represents an object in the program text or data. + +#include "dbe_structs.h" +#include "Emsg.h" +#include "Expression.h" + +class DataObject; +class Function; +class SourceFile; +class DbeFile; +class DbeLine; +template <class ITEM> class Vector; + +class Histable +{ + friend class Hist_data; +public: + + enum Type + { + INSTR, LINE, FUNCTION, MODULE, LOADOBJECT, + EADDR, MEMOBJ, INDEXOBJ, PAGE, DOBJECT, + SOURCEFILE, IOACTFILE, IOACTVFD, IOCALLSTACK, + HEAPCALLSTACK, EXPERIMENT, OTHER + }; + + // NameFormat for functions and function based objects + + enum NameFormat + { + NA, LONG, SHORT, MANGLED, SONAME = 0x10 + }; + + static NameFormat + make_fmt (int fnfmt, bool sofmt = false) + { + return (NameFormat) (sofmt ? fnfmt | SONAME : fnfmt); + } + + static int + fname_fmt (NameFormat fmt) + { + return (fmt & ~SONAME); + } + + static bool + soname_fmt (NameFormat fmt) + { + return (fmt & SONAME); + } + + Histable (); + char *dump (); + + virtual ~Histable (); + + virtual char * + get_name (NameFormat = NA) + { + return name; // Return the name of the object + } + + virtual void + set_name (char * _name) + { + name = _name; + } + + virtual void set_name_from_context (Expression::Context *) { } + virtual Type get_type () = 0; + + virtual int64_t + get_size () + { + return 0; + } + + virtual uint64_t + get_addr () + { + return 0ULL; + } + + virtual Vector<Histable*> *get_comparable_objs (); + Histable *get_compare_obj (); + + virtual Histable * + convertto (Type, Histable* = NULL) + { + return this; + } + + Vector<Histable*> *comparable_objs; + int64_t id; // A unique id of this object, within its specific Type + +protected: + char *name; // Object name + int phaseCompareIdx; + void update_comparable_objs (); + void dump_comparable_objs (); + char *type_to_string (); + void delete_comparable_objs (); +}; + +typedef Histable::Type Histable_type; + +// An Other object represents some random histable object +class Other : public Histable +{ +public: + + virtual Type + get_type () + { + return OTHER; + } + + uint64_t value64; + uint32_t tag; +}; + +// DbeInstr represents an instruction. +// +// Used by Analyzer for: Disassembly, PCs, Timeline, and Event tabs. +// +class DbeInstr : public Histable +{ +public: + DbeInstr (uint64_t _id, int _flags, Function *_func, uint64_t _addr); + + virtual Type + get_type () + { + return INSTR; + } + + virtual char *get_name (NameFormat = NA); + virtual int64_t get_size (); + virtual uint64_t get_addr (); + virtual Histable *convertto (Type type, Histable *obj = NULL); + DbeLine *mapPCtoLine (SourceFile *sf); + void add_inlined_info (StringBuilder *sb); + char *get_descriptor (); + int pc_cmp (DbeInstr *instr2); + + uint64_t addr; + uint64_t img_offset; // file offset of the image + int flags; + Function *func; + int lineno; + int inlinedInd; + int64_t size; + bool isUsed; + +private: + NameFormat current_name_format; +}; + +class DbeEA : public Histable +{ +public: + + DbeEA (DataObject *_dobj, Vaddr _eaddr) + { + dobj = _dobj; + eaddr = _eaddr; + }; + + virtual Type + get_type () + { + return EADDR; + }; + + virtual int64_t + get_size () + { + return 1; + }; + + virtual uint64_t + get_addr () + { + return eaddr; + }; + + virtual char *get_name (NameFormat = NA); + virtual Histable *convertto (Type type, Histable *obj = NULL); + + DataObject *dobj; + Vaddr eaddr; +}; + +// DbeLine represents a line in a source file. +// +// For each top-level DbeLine instance, there are three DbeLine subtypes: +// +// A The top-level DbeLine is associated with a sourceFile & lineno, but +// not any particular function. This form of DbeLine is used +// to represent Analyzer Source tab lines. +// +// B Function-specific lines, those associated with a function in addition +// to the the sourceFile & lineno, are stored in a linked list. +// (see "dbeline_func_next"). +// This subtype is used to differentiate a line found in #included source +// that is referenced by multiple functions. +// It is used in the Analyzer Lines tab. +// +// C Function-specific "lines" that don't have line number info are referenced +// from each linked-list element's "dbeline_func_pseudo" field. +// This subtype is needed when a binary doesn't identify line numbers. +// It is used in the Analyzer Lines tab. +// +// When the user switches views between tabs, a selected object in the old +// tab must be translated to an approprate object in the new tab. +// When switching to the Source Tab, the top-level DbeLine (dbeline_base) +// should be used. +// When switching to the Lines Tab, a function-specific dbeline_func_* +// should be used. +// + +class DbeLine : public Histable +{ +public: + + enum Flag + { + OMPPRAGMA = 1 + }; + + DbeLine (Function *_func, SourceFile *sf, int _lineno); + virtual ~DbeLine (); + virtual char *get_name (NameFormat = NA); + virtual int64_t get_size (); + virtual uint64_t get_addr (); + virtual Histable *convertto (Type type, Histable *obj = NULL); + + void init_Offset (uint64_t p_offset); + int line_cmp (DbeLine *dbl); + + virtual Type + get_type () + { + return LINE; + } + + void + set_flag (Flag flag) + { + flags |= flag; + } + + bool + is_set (Flag flag) + { + return (flags & flag) != 0; + } + + Function *func; // note: will be NULL in the base (head) dbeline + int lineno; + int64_t size; + SourceFile *sourceFile; // Default source file + SourceFile *include; // included source file or NULL + + DbeLine *dbeline_base; + // Head of list, a dbeline associated with sourceFile & lineno, but not func: + // dbeline_base->lineno: non-zero + // dbeline_base->sourceFile: non-null + // dbeline_base->func: NULL + // dbeline_base->dbeline_base: this + // dbeline_base->dbeline_func_next: first func-specific dbeline + + DbeLine *dbeline_func_next; + // If non-null, pointer to a function-specific dbeline where: + // dbeline_func_next->lineno: same as dbeline_base->lineno + // dbeline_func_next->sourceFile: same as dbeline_base->sourceFile + // dbeline_func_next->func: pointer to unique function + // dbeline_func_next->dbeline_base: head of the linked list. + // dbeline_func_next->dbeline_func_next: next function-specific dbeline. + +private: + int current_name_format; + int64_t offset; + int flags; +}; + +class HistableFile : public Histable, public DbeMessages +{ +public: + HistableFile (); + + bool isUsed; + DbeFile *dbeFile; +}; + +#endif /* _HISTABLE_H */ diff --git a/gprofng/src/IOActivity.cc b/gprofng/src/IOActivity.cc new file mode 100644 index 0000000..401cab5 --- /dev/null +++ b/gprofng/src/IOActivity.cc @@ -0,0 +1,825 @@ +/* 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 "DbeSession.h" +#include "FileData.h" +#include "StringBuilder.h" +#include "i18n.h" +#include "util.h" +#include "IOActivity.h" +#include "MetricList.h" +#include "Application.h" +#include "Experiment.h" +#include "DbeView.h" +#include "Exp_Layout.h" +#include "i18n.h" + +IOActivity::IOActivity (DbeView *_dbev) +{ + dbev = _dbev; + fDataHash = NULL; + fDataTotal = NULL; + fDataObjs = NULL; + fDataObjsFile = NULL; + hasFile = false; + fDataObjsVfd = NULL; + hasVfd = false; + fDataObjsCallStack = NULL; + hasCallStack = false; + fDataCalStkMap = NULL; + fDataVfdMap = NULL; + hist_data_file_all = NULL; + hist_data_vfd_all = NULL; + hist_data_callstack_all = NULL; +} + +void +IOActivity::reset () +{ + int numExps = dbeSession->nexps (); + FileData *fData = NULL; + DefaultMap<int64_t, FileData*>* fDataMap; + for (int k = 0; k < numExps; k++) + { + Experiment *exp = dbeSession->get_exp (k); + fDataMap = exp->getFDataMap (); + if (fDataMap == NULL) + continue; + + fDataObjs = fDataMap->values (); + if (fDataObjs == NULL) + continue; + int numFiles = fDataObjs->size (); + for (int j = 0; j < numFiles; j++) + { + fData = fDataObjs->fetch (j); + fData->init (); + } + } + + delete fDataHash; + fDataHash = NULL; + delete fDataTotal; + fDataTotal = NULL; + + delete fDataObjsFile; + fDataObjsFile = NULL; + hasFile = false; + + delete fDataObjsVfd; + fDataObjsVfd = NULL; + hasVfd = false; + + delete fDataObjsCallStack; + fDataObjsCallStack = NULL; + hasCallStack = false; + + delete fDataObjs; + fDataObjs = NULL; + delete fDataCalStkMap; + fDataCalStkMap = NULL; + delete fDataVfdMap; + fDataVfdMap = NULL; + + // These three pointers are deleted by DbeView + // They are named iofile_data, iovfd_data, and iocs_data + hist_data_file_all = NULL; + hist_data_vfd_all = NULL; + hist_data_callstack_all = NULL; +} + +void +IOActivity::createHistItemTotals (Hist_data *hist_data, MetricList *mlist, + Histable::Type hType, bool empty) +{ + int mIndex; + Metric *mtr; + Hist_data::HistItem *hi; + FileData *fData = NULL; + + if (fDataTotal == NULL) + { + fDataTotal = new FileData (TOTAL_FILENAME); + fDataTotal->setHistType (hType); + fDataTotal->setVirtualFd (VIRTUAL_FD_TOTAL); + fDataTotal->id = 0; + } + + fData = new FileData (fDataTotal); + fData->setHistType (hType); + hi = hist_data->append_hist_item (fData); + Vec_loop (Metric *, mlist->get_items (), mIndex, mtr) + { + if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ()) + continue; + + Metric::Type mtype = mtr->get_type (); + ValueTag vType = mtr->get_vtype (); + hist_data->total->value[mIndex].tag = vType; + hi->value[mIndex].tag = vType; + double prec = (double) NANOSEC; + switch (mtype) + { + case BaseMetric::IO_READ_BYTES: + if (!empty) + { + hist_data->total->value[mIndex].ll = fDataTotal->getReadBytes (); + hi->value[mIndex].ll = fDataTotal->getReadBytes (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::IO_READ_CNT: + if (!empty) + { + hist_data->total->value[mIndex].ll = fDataTotal->getReadCnt (); + hi->value[mIndex].ll = fDataTotal->getReadCnt (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::IO_READ_TIME: + if (!empty) + { + hist_data->total->value[mIndex].d = + (double) fDataTotal->getReadTime () / prec; + hi->value[mIndex].d = hist_data->total->value[mIndex].d; + } + else + { + hist_data->total->value[mIndex].d = 0.0; + hi->value[mIndex].d = 0.0; + } + break; + case BaseMetric::IO_WRITE_BYTES: + if (!empty) + { + hist_data->total->value[mIndex].ll = fDataTotal->getWriteBytes (); + hi->value[mIndex].ll = fDataTotal->getWriteBytes (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::IO_WRITE_CNT: + if (!empty) + { + hist_data->total->value[mIndex].ll = fDataTotal->getWriteCnt (); + hi->value[mIndex].ll = fDataTotal->getWriteCnt (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::IO_WRITE_TIME: + if (!empty) + { + hist_data->total->value[mIndex].d = + (double) fDataTotal->getWriteTime () / prec; + hi->value[mIndex].d = hist_data->total->value[mIndex].d; + } + else + { + hist_data->total->value[mIndex].d = 0.0; + hi->value[mIndex].d = 0.0; + } + break; + case BaseMetric::IO_OTHER_CNT: + if (!empty) + { + hist_data->total->value[mIndex].ll = fDataTotal->getOtherCnt (); + hi->value[mIndex].ll = fDataTotal->getOtherCnt (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::IO_OTHER_TIME: + if (!empty) + { + hist_data->total->value[mIndex].d = + (double) fDataTotal->getOtherTime () / prec; + hi->value[mIndex].d = hist_data->total->value[mIndex].d; + } + else + { + hist_data->total->value[mIndex].d = 0.0; + hi->value[mIndex].d = 0.0; + } + break; + case BaseMetric::IO_ERROR_CNT: + if (!empty) + { + hist_data->total->value[mIndex].ll = fDataTotal->getErrorCnt (); + hi->value[mIndex].ll = fDataTotal->getErrorCnt (); + } + else + { + hist_data->total->value[mIndex].ll = 0; + hi->value[mIndex].ll = 0; + } + break; + case BaseMetric::IO_ERROR_TIME: + if (!empty) + { + hist_data->total->value[mIndex].d = (double) fDataTotal->getErrorTime () / prec; + hi->value[mIndex].d = hist_data->total->value[mIndex].d; + } + else + { + hist_data->total->value[mIndex].d = 0.0; + hi->value[mIndex].d = 0.0; + } + break; + default: + break; + } + } +} + +void +IOActivity::computeHistTotals (Hist_data *hist_data, MetricList *mlist) +{ + int mIndex; + Metric *mtr; + Vec_loop (Metric *, mlist->get_items (), mIndex, mtr) + { + if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ()) + continue; + + Metric::Type mtype = mtr->get_type (); + ValueTag vType = mtr->get_vtype (); + hist_data->total->value[mIndex].tag = vType; + double prec = (double) NANOSEC; + switch (mtype) + { + case BaseMetric::IO_READ_BYTES: + hist_data->total->value[mIndex].ll = fDataTotal->getReadBytes (); + break; + case BaseMetric::IO_READ_CNT: + hist_data->total->value[mIndex].ll = fDataTotal->getReadCnt (); + break; + case BaseMetric::IO_READ_TIME: + hist_data->total->value[mIndex].d = + (double) fDataTotal->getReadTime () / prec; + break; + case BaseMetric::IO_WRITE_BYTES: + hist_data->total->value[mIndex].ll = fDataTotal->getWriteBytes (); + break; + case BaseMetric::IO_WRITE_CNT: + hist_data->total->value[mIndex].ll = fDataTotal->getWriteCnt (); + break; + case BaseMetric::IO_WRITE_TIME: + hist_data->total->value[mIndex].d = + (double) fDataTotal->getWriteTime () / prec; + break; + case BaseMetric::IO_OTHER_CNT: + hist_data->total->value[mIndex].ll = fDataTotal->getOtherCnt (); + break; + case BaseMetric::IO_OTHER_TIME: + hist_data->total->value[mIndex].d = + (double) fDataTotal->getOtherTime () / prec; + break; + case BaseMetric::IO_ERROR_CNT: + hist_data->total->value[mIndex].ll = fDataTotal->getErrorCnt (); + break; + case BaseMetric::IO_ERROR_TIME: + hist_data->total->value[mIndex].d = + (double) fDataTotal->getErrorTime () / prec; + break; + default: + break; + } + } +} + +void +IOActivity::computeHistData (Hist_data *hist_data, MetricList *mlist, + Hist_data::Mode mode, Histable *selObj) +{ + + Hist_data::HistItem *hi = NULL; + int numObjs = fDataObjs->size (); + int numMetrics = mlist->get_items ()->size (); + + for (int i = 0; i < numObjs; i++) + { + FileData *fData = fDataObjs->fetch (i); + if (mode == Hist_data::ALL) + hi = hist_data->append_hist_item (fData); + else if (mode == Hist_data::SELF) + { + if (fData->id == selObj->id) + hi = hist_data->append_hist_item (fData); + else + continue; + } + + for (int mIndex = 0; mIndex < numMetrics; mIndex++) + { + Metric *mtr = mlist->get_items ()->fetch (mIndex); + if (!mtr->is_visible () && !mtr->is_tvisible () + && !mtr->is_pvisible ()) + continue; + + Metric::Type mtype = mtr->get_type (); + ValueTag vType = mtr->get_vtype (); + hi->value[mIndex].tag = vType; + + double prec = (double) NANOSEC; + switch (mtype) + { + case BaseMetric::IO_READ_BYTES: + hi->value[mIndex].ll = fData->getReadBytes (); + break; + case BaseMetric::IO_READ_CNT: + hi->value[mIndex].ll = fData->getReadCnt (); + break; + case BaseMetric::IO_READ_TIME: + hi->value[mIndex].d = (double) fData->getReadTime () / prec; + break; + case BaseMetric::IO_WRITE_BYTES: + hi->value[mIndex].ll = fData->getWriteBytes (); + break; + case BaseMetric::IO_WRITE_CNT: + hi->value[mIndex].ll = fData->getWriteCnt (); + break; + case BaseMetric::IO_WRITE_TIME: + hi->value[mIndex].d = (double) fData->getWriteTime () / prec; + break; + case BaseMetric::IO_OTHER_CNT: + hi->value[mIndex].ll = fData->getOtherCnt (); + break; + case BaseMetric::IO_OTHER_TIME: + hi->value[mIndex].d = (double) fData->getOtherTime () / prec; + break; + case BaseMetric::IO_ERROR_CNT: + hi->value[mIndex].ll = fData->getErrorCnt (); + break; + case BaseMetric::IO_ERROR_TIME: + hi->value[mIndex].d = (double) fData->getErrorTime () / prec; + break; + default: + break; + } + } + } +} + +Hist_data * +IOActivity::compute_metrics (MetricList *mlist, Histable::Type type, + Hist_data::Mode mode, Histable *selObj) +{ + + // it's already there, just return it + if (mode == Hist_data::ALL) + { + if (type == Histable::IOACTFILE && hist_data_file_all) + return hist_data_file_all; + else if (type == Histable::IOACTVFD && hist_data_vfd_all) + return hist_data_vfd_all; + else if (type == Histable::IOCALLSTACK && hist_data_callstack_all) + return hist_data_callstack_all; + } + + bool has_data = false; + Hist_data *hist_data = NULL; + VMode viewMode = dbev->get_view_mode (); + + switch (type) + { + case Histable::IOACTVFD: + if (!hasVfd) + computeData (type); + + // computeData() creates fDataObjsVfd + // fDataObjsVfd contains the list of vfd objects + if (fDataObjsVfd != NULL) + { + // fDataObjs is used in other methods + fDataObjs = fDataObjsVfd; + has_data = true; + } + else + has_data = false; + + if (has_data && mode == Hist_data::ALL && hist_data_vfd_all == NULL) + { + hist_data_vfd_all = new Hist_data (mlist, type, mode, true); + hist_data = hist_data_vfd_all; + } + else if (has_data) + hist_data = new Hist_data (mlist, type, mode, false); + else + { + hist_data = new Hist_data (mlist, type, mode, false); + createHistItemTotals (hist_data, mlist, type, true); + return hist_data; + } + break; + case Histable::IOACTFILE: + if (!hasFile) + computeData (type); + + // computeData() creates fDataObjsFile + // fDataObjsFile contains the list of file objects + if (fDataObjsFile != NULL) + { + fDataObjs = fDataObjsFile; + has_data = true; + } + else + has_data = false; + + if (has_data && mode == Hist_data::ALL && hist_data_file_all == NULL) + { + hist_data_file_all = new Hist_data (mlist, type, mode, true); + hist_data = hist_data_file_all; + } + else if (has_data) + hist_data = new Hist_data (mlist, type, mode, false); + else + { + hist_data = new Hist_data (mlist, type, mode, false); + createHistItemTotals (hist_data, mlist, type, true); + return hist_data; + } + break; + case Histable::IOCALLSTACK: + if (!hasCallStack) + computeCallStack (type, viewMode); + + // computeCallStack() creates fDataObjsCallStack + // fDataObjsCallStack contains the list of call stack objects + if (fDataObjsCallStack != NULL) + { + fDataObjs = fDataObjsCallStack; + has_data = true; + } + else + has_data = false; + + if (has_data && (mode == Hist_data::ALL) && (hist_data_callstack_all == NULL)) + { + hist_data_callstack_all = new Hist_data (mlist, type, mode, true); + hist_data = hist_data_callstack_all; + } + else if (has_data) + hist_data = new Hist_data (mlist, type, mode, false); + else + { + hist_data = new Hist_data (mlist, type, mode, false); + createHistItemTotals (hist_data, mlist, type, true); + return hist_data; + } + break; + default: + fprintf (stderr, + "IOActivity cannot process data due to wrong Histable (type=%d) \n", + type); + abort (); + } + + if (mode == Hist_data::ALL || (mode == Hist_data::SELF && selObj->id == 0)) + createHistItemTotals (hist_data, mlist, type, false); + else + computeHistTotals (hist_data, mlist); + computeHistData (hist_data, mlist, mode, selObj); + + // Determine by which metric to sort if any + bool rev_sort = mlist->get_sort_rev (); + int sort_ind = -1; + int nmetrics = mlist->get_items ()->size (); + for (int mind = 0; mind < nmetrics; mind++) + if (mlist->get_sort_ref_index () == mind) + sort_ind = mind; + + hist_data->sort (sort_ind, rev_sort); + hist_data->compute_minmax (); + return hist_data; +} + +void +IOActivity::computeData (Histable::Type type) +{ + bool has_iodata = false; + reset (); + int64_t histableId = 0; // It is used by fDataAggr only + // fData uses vfd for histable id + + fDataHash = new HashMap<char*, FileData*>; + FileData *fData = NULL; + FileData *fDataAggr = NULL; + + fDataTotal = new FileData (TOTAL_FILENAME); + fDataTotal->setHistType (type); + fDataTotal->setVirtualFd (VIRTUAL_FD_TOTAL); + fDataTotal->id = histableId++; + + FileData *fDataStdin = new FileData (STDIN_FILENAME); + fDataStdin->setFileDes (STDIN_FD); + fDataStdin->setHistType (type); + fDataStdin->setFsType ("N/A"); + fDataStdin->id = histableId++; + + FileData *fDataStdout = new FileData (STDOUT_FILENAME); + fDataStdout->setFileDes (STDOUT_FD); + fDataStdout->setHistType (type); + fDataStdout->setFsType ("N/A"); + fDataStdout->id = histableId++; + + FileData *fDataStderr = new FileData (STDERR_FILENAME); + fDataStderr->setFileDes (STDERR_FD); + fDataStderr->setHistType (type); + fDataStderr->setFsType ("N/A"); + fDataStderr->id = histableId++; + + FileData *fDataOtherIO = new FileData (OTHERIO_FILENAME); + fDataOtherIO->setFileDes (OTHERIO_FD); + fDataOtherIO->setHistType (type); + fDataOtherIO->setFsType ("N/A"); + fDataOtherIO->id = histableId++; + + DefaultMap<int64_t, FileData*>* fDataMap; + fDataObjsFile = NULL; + fDataObjsVfd = NULL; + + // get the list of io events from DbeView + int numExps = dbeSession->nexps (); + + for (int k = 0; k < numExps; k++) + { + DataView *ioPkts = dbev->get_filtered_events (k, DATA_IOTRACE); + if (ioPkts == NULL || ioPkts->getSize () <= 0) + continue; + Experiment *exp = dbeSession->get_exp (k); + fDataMap = exp->getFDataMap (); + if (fDataMap == NULL) + continue; + delete fDataVfdMap; + fDataVfdMap = new DefaultMap<long, FileData*>; + + long sz = ioPkts->getSize (); + for (long i = 0; i < sz; ++i) + { + hrtime_t event_duration = ioPkts->getLongValue (PROP_EVT_TIME, i); + int64_t nByte = ioPkts->getLongValue (PROP_IONBYTE, i); + IOTrace_type ioType = (IOTrace_type) ioPkts->getIntValue (PROP_IOTYPE, i); + int64_t vFd = ioPkts->getLongValue (PROP_IOVFD, i); + if (vFd >= 0) + { + fData = fDataMap->get (vFd); + if (fData == NULL) + continue; + } + else + continue; + + if (fDataVfdMap->get (vFd) == NULL) + fDataVfdMap->put (vFd, fData); + + switch (ioType) + { + case READ_TRACE: + fData->addReadEvent (event_duration, nByte); + // Set the Histable id for IOVFD + fData->id = fData->getVirtualFd (); + fDataTotal->addReadEvent (event_duration, nByte); + fDataTotal->setReadStat (event_duration, nByte); + break; + case WRITE_TRACE: + fData->addWriteEvent (event_duration, nByte); + // Set the Histable id for IOVFD + fData->id = fData->getVirtualFd (); + fDataTotal->addWriteEvent (event_duration, nByte); + fDataTotal->setWriteStat (event_duration, nByte); + break; + case OPEN_TRACE: + fData->addOtherEvent (event_duration); + // Set the Histable id for IOVFD + fData->id = fData->getVirtualFd (); + fDataTotal->addOtherEvent (event_duration); + break; + case CLOSE_TRACE: + case OTHERIO_TRACE: + fData->addOtherEvent (event_duration); + // Set the Histable id for IOVFD + fData->id = fData->getVirtualFd (); + fDataTotal->addOtherEvent (event_duration); + break; + case READ_TRACE_ERROR: + case WRITE_TRACE_ERROR: + case OPEN_TRACE_ERROR: + case CLOSE_TRACE_ERROR: + case OTHERIO_TRACE_ERROR: + fData->addErrorEvent (event_duration); + // Set the Histable id for IOVFD + fData->id = fData->getVirtualFd (); + fDataTotal->addErrorEvent (event_duration); + break; + + case IOTRACETYPE_LAST: + break; + } + + if (type == Histable::IOACTFILE) + { + fDataAggr = fDataHash->get (fData->getFileName ()); + if (fDataAggr == NULL) + { + bool setInfo = false; + if (vFd == VIRTUAL_FD_STDIN) + fDataAggr = fDataStdin; + else if (vFd == VIRTUAL_FD_STDOUT) + fDataAggr = fDataStdout; + else if (vFd == VIRTUAL_FD_STDERR) + fDataAggr = fDataStderr; + else if (vFd == VIRTUAL_FD_OTHERIO) + fDataAggr = fDataOtherIO; + else + { + fDataAggr = new FileData (fData->getFileName ()); + setInfo = true; + } + fDataHash->put (fData->getFileName (), fDataAggr); + + if (setInfo) + { + fDataAggr->setFsType (fData->getFsType ()); + fDataAggr->setHistType (type); + // Set the Histable id for aggregated file name + fDataAggr->id = histableId; + fDataAggr->setVirtualFd (histableId); + histableId++; + } + } + + fDataAggr->setFileDesList (fData->getFileDes ()); + fDataAggr->setVirtualFds (fData->getVirtualFd ()); + switch (ioType) + { + case READ_TRACE: + fDataAggr->addReadEvent (event_duration, nByte); + break; + case WRITE_TRACE: + fDataAggr->addWriteEvent (event_duration, nByte); + break; + case OPEN_TRACE: + fDataAggr->addOtherEvent (event_duration); + break; + case CLOSE_TRACE: + case OTHERIO_TRACE: + fDataAggr->addOtherEvent (event_duration); + break; + case READ_TRACE_ERROR: + case WRITE_TRACE_ERROR: + case OPEN_TRACE_ERROR: + case CLOSE_TRACE_ERROR: + case OTHERIO_TRACE_ERROR: + fDataAggr->addErrorEvent (event_duration); + break; + case IOTRACETYPE_LAST: + break; + } + } + has_iodata = true; + } + if (sz > 0) + { + if (fDataObjsVfd == NULL) + fDataObjsVfd = new Vector<FileData*>; + fDataObjsVfd->addAll (fDataVfdMap->values ()); + hasVfd = true; + } + } + if (has_iodata && type == Histable::IOACTFILE) + { + fDataObjsFile = fDataHash->values ()->copy (); + hasFile = true; + } +} + +void +IOActivity::computeCallStack (Histable::Type type, VMode viewMode) +{ + bool has_data = false; + int64_t stackIndex = 0; + FileData *fData = NULL; + delete fDataCalStkMap; + fDataCalStkMap = new DefaultMap<void*, FileData*>; + delete fDataTotal; + fDataTotal = new FileData (TOTAL_FILENAME); + fDataTotal->setHistType (type); + + // There is no call stack for total, use the index for id + fDataTotal->id = stackIndex++; + + // get the list of io events from DbeView + int numExps = dbeSession->nexps (); + for (int k = 0; k < numExps; k++) + { + DataView *ioPkts = dbev->get_filtered_events (k, DATA_IOTRACE); + if (ioPkts == NULL || ioPkts->getSize () <= 0) + continue; + long sz = ioPkts->getSize (); + for (long i = 0; i < sz; ++i) + { + hrtime_t event_duration = ioPkts->getLongValue (PROP_EVT_TIME, i); + int64_t nByte = ioPkts->getLongValue (PROP_IONBYTE, i); + void *stackId = getStack (viewMode, ioPkts, i); + IOTrace_type ioType = + (IOTrace_type) ioPkts->getIntValue (PROP_IOTYPE, i); + int64_t vFd = ioPkts->getLongValue (PROP_IOVFD, i); + + if (stackId != NULL && vFd > 0) + { + fData = fDataCalStkMap->get (stackId); + if (fData == NULL) + { + char *stkName = dbe_sprintf (GTXT ("Stack 0x%llx"), + (unsigned long long) stackId); + fData = new FileData (stkName); + fDataCalStkMap->put (stackId, fData); + fData->id = (int64_t) stackId; + fData->setVirtualFd (stackIndex); + stackIndex++; + fData->setHistType (type); + } + } + else + continue; + + switch (ioType) + { + case READ_TRACE: + fData->addReadEvent (event_duration, nByte); + fDataTotal->addReadEvent (event_duration, nByte); + fDataTotal->setReadStat (event_duration, nByte); + break; + case WRITE_TRACE: + fData->addWriteEvent (event_duration, nByte); + fDataTotal->addWriteEvent (event_duration, nByte); + fDataTotal->setWriteStat (event_duration, nByte); + break; + case OPEN_TRACE: + fData->addOtherEvent (event_duration); + fDataTotal->addOtherEvent (event_duration); + break; + case CLOSE_TRACE: + case OTHERIO_TRACE: + fData->addOtherEvent (event_duration); + fDataTotal->addOtherEvent (event_duration); + break; + case READ_TRACE_ERROR: + case WRITE_TRACE_ERROR: + case OPEN_TRACE_ERROR: + fData->addErrorEvent (event_duration); + fDataTotal->addErrorEvent (event_duration); + break; + case CLOSE_TRACE_ERROR: + case OTHERIO_TRACE_ERROR: + fData->addErrorEvent (event_duration); + fDataTotal->addErrorEvent (event_duration); + break; + case IOTRACETYPE_LAST: + break; + } + has_data = true; + } + } + if (has_data) + { + fDataObjsCallStack = fDataCalStkMap->values ()->copy (); + hasCallStack = true; + } +} diff --git a/gprofng/src/IOActivity.h b/gprofng/src/IOActivity.h new file mode 100644 index 0000000..35c4517 --- /dev/null +++ b/gprofng/src/IOActivity.h @@ -0,0 +1,86 @@ +/* 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. */ + +#ifndef _IOACTIVITY_H +#define _IOACTIVITY_H + + +#include <stdio.h> + +#include "vec.h" +#include "Histable.h" +#include "Hist_data.h" +#include "Metric.h" +#include "FileData.h" +#include "DefaultMap.h" +#include "dbe_types.h" + +class Experiment; +class Expression; +class DataView; +class DbeView; + +class IOActivity +{ +public: + + IOActivity (DbeView *_dbev); + void reset (void); + Hist_data *compute_metrics (MetricList *, Histable::Type, Hist_data::Mode, + Histable*); + + ~IOActivity () + { + this->reset (); + } + +private: + void computeData (Histable::Type); + void computeCallStack (Histable::Type, VMode viewMode); + void createHistItemTotals (Hist_data *, MetricList *, Histable::Type, bool); + void computeHistTotals (Hist_data *, MetricList *); + void computeHistData (Hist_data *, MetricList *, Hist_data::Mode, Histable *); + + Vector<FileData*> *fDataObjs; + Vector<FileData*> *fDataObjsFile; + Vector<FileData*> *fDataObjsVfd; + Vector<FileData*> *fDataObjsCallStack; + bool hasFile; + bool hasVfd; + bool hasCallStack; + HashMap<char*, FileData*> *fDataHash; + FileData *fDataTotal; + + // list of FileData objects using the stack id as the key + DefaultMap<void*, FileData*> *fDataCalStkMap; + + // list of FileData objects using the VFD as the key + DefaultMap<long, FileData*> *fDataVfdMap; + + // the cached data for mode=Hist_Data::ALL + Hist_data *hist_data_file_all; + Hist_data *hist_data_vfd_all; + Hist_data *hist_data_callstack_all; + Hist_data *hist_data_callstack; + + DbeView *dbev; +}; + +#endif /* _IOACTIVITY_H */ diff --git a/gprofng/src/IndexMap2D.h b/gprofng/src/IndexMap2D.h new file mode 100644 index 0000000..0f68a3a --- /dev/null +++ b/gprofng/src/IndexMap2D.h @@ -0,0 +1,119 @@ +/* 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. */ + +/* + * Index Map2D implementation. + * + * Index Map2D is dynamic two dimensional array + */ + +#ifndef _DBE_INDEXMAP2D_H +#define _DBE_INDEXMAP2D_H + +#include <assert.h> +#include <vec.h> +#include <Map2D.h> + +template <typename Key1_t, typename Key2_t, typename Value_t> +class IndexMap2D : public Map2D<Key1_t, Key2_t, Value_t> +{ +public: + + IndexMap2D (); + ~IndexMap2D (); + + void put (Key1_t key1, Key2_t key2, Value_t val); + Value_t get (Key1_t key1, Key2_t key2); + Value_t get (Key1_t key1, Key2_t key2, + typename Map2D<Key1_t, Key2_t, Value_t>::Relation rel); + Value_t remove (Key1_t key1, Key2_t key2); + +private: + + Vector<Vector<Value_t>*> *map1; +}; + +template <typename Key1_t, typename Key2_t, typename Value_t> +IndexMap2D<Key1_t, Key2_t, Value_t>::IndexMap2D () +{ + map1 = new Vector<Vector<Value_t>*>; +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +IndexMap2D<Key1_t, Key2_t, Value_t>::~IndexMap2D () +{ + map1->destroy (); + delete map1; +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +void +IndexMap2D<Key1_t, Key2_t, Value_t>::put (Key1_t key1, Key2_t key2, Value_t val) +{ + if (key1 < 0 || key2 < 0) + return; + Vector<Value_t> *map2 = NULL; + if (key1 < map1->size ()) + map2 = map1->fetch ((int) key1); + if (map2 == NULL) + { + map2 = new Vector<Value_t>; + map1->store ((int) key1, map2); + } + map2->store ((int) key2, val); +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +Value_t +IndexMap2D<Key1_t, Key2_t, Value_t>::get (Key1_t key1, Key2_t key2) +{ + if (key1 < 0 || key1 >= map1->size () || key2 < 0) + return (Value_t) 0; + Vector<Value_t> *map2 = map1->fetch ((int) key1); + if (map2 == NULL || key2 >= map2->size ()) + return (Value_t) 0; + return map2->fetch ((int) key2); +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +Value_t +IndexMap2D<Key1_t, Key2_t, Value_t>::get (Key1_t key1, Key2_t key2, + typename Map2D<Key1_t, Key2_t, Value_t>::Relation rel) +{ + if (rel != Map2D<Key1_t, Key2_t, Value_t>::REL_EQEQ) + return (Value_t) 0; + return get (key1, key2); +} + +template <typename Key1_t, typename Key2_t, typename Value_t> +Value_t +IndexMap2D<Key1_t, Key2_t, Value_t>::remove (Key1_t key1, Key2_t key2) +{ + if (key1 < 0 || key1 >= map1->size () || key2 < 0) + return (Value_t) 0; + Vector<Value_t> *map2 = map1->fetch ((int) key1); + if (map2 == NULL || key2 >= map2->size ()) + return (Value_t) 0; + Value_t res = map2->fetch ((int) key2); + map2->store ((int) key2, (Value_t) 0); + return res; +} + +#endif diff --git a/gprofng/src/IndexObject.cc b/gprofng/src/IndexObject.cc new file mode 100644 index 0000000..a7c8a37 --- /dev/null +++ b/gprofng/src/IndexObject.cc @@ -0,0 +1,554 @@ +/* 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 "util.h" +#include "DbeSession.h" +#include "DbeView.h" +#include "IndexObject.h" +#include "StringBuilder.h" + +IndexObject::IndexObject (int _indextype, uint64_t _index) +{ + indextype = _indextype; + obj = NULL; + id = _index; + name = NULL; + nameIsFinal = false; +} + +IndexObject::IndexObject (int _indextype, Histable *_obj) +{ + indextype = _indextype; + obj = _obj; + id = obj ? obj->id : (uint64_t) - 1; + name = NULL; + nameIsFinal = false; +} + +void +IndexObject::set_name (char * other_name) +{ + if (name == NULL) + { + name = other_name; + nameIsFinal = true; + } +} + +static uint64_t +extractExpgrid (uint64_t id) +{ + return (id >> IndexObject::INDXOBJ_EXPGRID_SHIFT) + & IndexObject::INDXOBJ_EXPGRID_MASK; +} + +static uint64_t +extractExpid (uint64_t id) +{ + return (id >> IndexObject::INDXOBJ_EXPID_SHIFT) + & IndexObject::INDXOBJ_EXPID_MASK; +} + +static uint64_t +extractPayload (uint64_t id) +{ + return (id >> IndexObject::INDXOBJ_PAYLOAD_SHIFT) + & IndexObject::INDXOBJ_PAYLOAD_MASK; +} + +static void +printCompareLabel (StringBuilder *sb, uint64_t grpId); + +static bool +printThread (StringBuilder *sbIn, Expression::Context * ctx, uint64_t id) +{ + uint64_t proc = extractExpid (id); + uint64_t thrid = extractPayload (id); + bool isFinal = true; + bool hasJava = false; + bool javaThread = false; + if (ctx) + { + if (ctx->dview && ctx->dview->getProp (PROP_JTHREAD)) + { + hasJava = true; + uint64_t tstamp = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId); + JThread *jthread = ctx->exp->map_pckt_to_Jthread (thrid, tstamp); + if (jthread != JTHREAD_NONE && jthread != JTHREAD_DEFAULT) + { + sbIn->appendf (GTXT ("Process %llu, Thread %llu, JThread %llu \'%s\', Group \'%s\', Parent \'%s\'"), + (unsigned long long) proc, + (unsigned long long) thrid, + (unsigned long long) jthread->jthr_id, + get_str(jthread->name, ""), + get_str(jthread->group_name, ""), + get_str(jthread->parent_name, "")); + javaThread = true; + } + } + } + if (!javaThread) + { + sbIn->appendf (GTXT ("Process %llu, Thread %llu"), + (unsigned long long) proc, (unsigned long long) thrid); + if (hasJava) + // sometimes threads start as native and later become Java; keep checking + isFinal = false; + } + if (ctx && ctx->dbev && ctx->dbev->comparingExperiments ()) + { + Vector <Histable *> *v = ctx->exp->get_comparable_objs (); + int st = 0; + for (long i = 0, sz = VecSize (v); i < sz; i++) + { + Experiment *exp = (Experiment *) v->get (i); + if (exp) + { + if (st == 0) + { + st = 1; + continue; + } + sbIn->appendf (GTXT (" [ Group %llu Process %llu ]"), + (unsigned long long) exp->groupId - 1, + (unsigned long long) exp->getUserExpId ()); + } + } + } + return isFinal; +} + +static bool +printProcess (StringBuilder *sbIn, Expression::Context * ctx, uint64_t id) +{ + uint64_t proc = id; + if (ctx && ctx->exp) + { + int st = 0; + if (ctx->dbev && ctx->dbev->comparingExperiments ()) + { + Vector <Histable *> *v = ctx->exp->get_comparable_objs (); + for (long i = 0, sz = VecSize (v); i < sz; i++) + { + Experiment *exp = (Experiment *) v->get (i); + if (exp) + { + if (st == 0) + { + st = 1; + sbIn->appendf (GTXT ("%s, Process %3llu, PID %llu"), + get_str (exp->utargname, GTXT ("(unknown)")), + (unsigned long long) proc, + (unsigned long long) exp->getPID ()); + continue; + } + sbIn->appendf (GTXT (" [ Group %llu, Process %llu, PID %llu ]"), + (unsigned long long) exp->groupId - 1, + (unsigned long long) exp->getUserExpId (), + (unsigned long long) exp->getPID ()); + } + } + } + if (st == 0) + sbIn->appendf (GTXT ("%s, Process %3llu, PID %llu"), + get_str (ctx->exp->utargname, GTXT ("(unknown)")), + (unsigned long long) proc, + (unsigned long long) ctx->exp->getPID ()); + } + else + sbIn->appendf (GTXT ("Process %3llu"), (unsigned long long) proc); + return true; //name is final +} + +static bool +printExperiment (StringBuilder *sbIn, Expression::Context * ctx, uint64_t id) +{ + uint64_t grpId = extractExpgrid (id); + uint64_t expid = extractExpid (id); + if (ctx && ctx->dbev->comparingExperiments ()) + printCompareLabel (sbIn, grpId); + if (ctx) + { + Experiment *hasFounder = ctx->exp->founder_exp; + int pid = ctx->exp->getPID (); + uint64_t founderExpid; + if (hasFounder) + founderExpid = hasFounder->getUserExpId (); + else + founderExpid = expid; + sbIn->appendf (GTXT ("Base Experiment %llu, Process %llu, PID %llu, %s"), + (unsigned long long) founderExpid, + (unsigned long long) expid, + (unsigned long long) pid, + get_str (ctx->exp->utargname, GTXT ("(unknown)"))); + } + else + sbIn->appendf (GTXT ("Process %llu"), (unsigned long long) expid); + return true; // name is final +} + +void +IndexObject::set_name_from_context (Expression::Context * ctx) +{ + if (name != NULL) + if (nameIsFinal && strstr (name, GTXT ("(unknown)")) == NULL) + return; + if (ctx == NULL || ctx->dview == NULL || ctx->dbev == NULL) + return; + StringBuilder sb; + switch (indextype) + { + case INDEX_THREADS: + nameIsFinal = printThread (&sb, ctx, id); + break; + case INDEX_PROCESSES: + nameIsFinal = printProcess (&sb, ctx, id); + break; + case INDEX_EXPERIMENTS: + nameIsFinal = printExperiment (&sb, ctx, id); + break; + default: + name = NULL; + return; + } + if (sb.length ()) + name = sb.toString (); +} + +static void +printCompareLabel (StringBuilder *sbIn, uint64_t grpId) +{ + static const char *labels[] = {"", GTXT ("Baseline"), GTXT ("Comparison")}; + static int length; + if (!length) + { + length = strlen (labels[1]); + int length2 = strlen (labels[2]); + if (length < length2) + length = length2; + length += 5; // for open/close brace and grpId number and spaces + } + char *s = NULL; + if (grpId != 0) + { + if (grpId <= 2) + s = dbe_sprintf ("[%s]", labels[grpId]); + else + s = dbe_sprintf ("[%s-%llu]", labels[2], + (unsigned long long) (grpId - 1)); + } + sbIn->appendf ("%-*s", length, get_str (s, "")); + free (s); +} + +char * +IndexObject::get_name (NameFormat fmt) +{ + if (name == NULL) + { + StringBuilder sb; + int64_t upper; + int64_t num1; + int64_t num2; + switch (indextype) + { + case INDEX_THREADS: + printThread (&sb, NULL, id); + break; + + case INDEX_CPUS: + sb.sprintf (GTXT ("CPU %llu"), (unsigned long long) id); + break; + + case INDEX_SAMPLES: + sb.sprintf (GTXT ("Sample %llu"), (unsigned long long) id); + break; + + case INDEX_GCEVENTS: + if (id == 0) + { + sb.sprintf (GTXT ("Not in any GCEvent")); + } + else + { + sb.sprintf (GTXT ("GCEvent %llu"), (unsigned long long) id); + } + break; + + case INDEX_SECONDS: + sb.sprintf (GTXT ("Second of execution %llu"), (unsigned long long) id); + break; + + case INDEX_PROCESSES: + printProcess (&sb, NULL, id); + break; + + case INDEX_EXPERIMENTS: + printExperiment (&sb, NULL, id); + break; + case INDEX_BYTES: + upper = id; + if (id == -1) + { + break; + } + if (id % 2 == 1 && id > 1) + { + upper = id - 1; + if (upper >= 1099511627776) + { + num1 = upper / 1099511627776; + sb.sprintf (GTXT (">= %3llu TB"), (unsigned long long) num1); + } + else + { + // XXXX do nothing, this should not happen + } + } + else + { + if (upper >= 1099511627776) + { + num1 = upper / 1099511627776; + num2 = num1 / 4; + if (num2) + { + sb.sprintf (GTXT ("%3lluTB < n <= %3lluTB"), (unsigned long long) num2, (unsigned long long) num1); + } + else + { + sb.sprintf (GTXT ("256GB < n <= %3lluTB"), (unsigned long long) num1); + } + } + else if (upper >= 1073741824) + { + num1 = upper / 1073741824; + num2 = num1 / 4; + if (num2) + { + sb.sprintf (GTXT ("%3lluGB < n <= %3lluGB"), (unsigned long long) num2, (unsigned long long) num1); + } + else + { + sb.sprintf (GTXT ("256MB < n <= %3lluGB"), (unsigned long long) num1); + } + } + else if (upper >= 1048576) + { + num1 = upper / 1048576; + num2 = num1 / 4; + if (num2) + { + sb.sprintf (GTXT ("%3lluMB < n <= %3lluMB"), (unsigned long long) num2, (unsigned long long) num1); + } + else + { + sb.sprintf (GTXT ("256KB < n <= %3lluMB"), (unsigned long long) num1); + } + } + else if (upper >= 1024) + { + num1 = upper / 1024; + num2 = num1 / 4; + if (num2) + { + sb.sprintf (GTXT ("%3lluKB < n <= %3lluKB"), (unsigned long long) num2, (unsigned long long) num1); + } + else + { + sb.sprintf (GTXT (" 256 < n <= %3lluKB"), (unsigned long long) num1); + } + } + else if (upper > 0) + { + num1 = upper; + num2 = num1 / 4; + if (num1 == 1) + { + sb.sprintf (GTXT (" 1 Byte")); + } + else + { + sb.sprintf (GTXT ("%5llu < n <= %5llu Bytes"), (unsigned long long) num2, (unsigned long long) num1); + } + } + else if (upper == 0) + { + sb.sprintf (GTXT (" 0 Bytes")); + } + else + { + sb.sprintf (GTXT ("<No Data>")); + } + } + break; + case INDEX_DURATION: + if (id == -1) + { + break; + } + + if (id > 10000000000000) + { + sb.sprintf (GTXT ("n > 10000s")); + } + else if (id > 1000000000000) + { + sb.sprintf (GTXT ("1000s < n <= 10000s")); + } + else if (id > 100000000000) + { + sb.sprintf (GTXT (" 100s < n <= 1000s")); + } + else if (id > 10000000000) + { + sb.sprintf (GTXT (" 10s < n <= 100s")); + } + else if (id > 1000000000) + { + sb.sprintf (GTXT (" 1s < n <= 10s")); + } + else if (id > 100000000) + { + sb.sprintf (GTXT ("100ms < n <= 1s")); + } + else if (id > 10000000) + { + sb.sprintf (GTXT (" 10ms < n <= 100ms")); + } + else if (id > 1000000) + { + sb.sprintf (GTXT (" 1ms < n <= 10ms")); + } + else if (id > 100000) + { + sb.sprintf (GTXT ("100us < n <= 1ms")); + } + else if (id > 10000) + { + sb.sprintf (GTXT (" 10us < n <= 100us")); + } + else if (id > 1000) + { + sb.sprintf (GTXT (" 1us < n <= 10us")); + } + else if (id > 0) + { + sb.sprintf (GTXT (" 0s < n <= 1us")); + } + else if (id == 0) + { + sb.sprintf (GTXT (" 0s")); + } + else + { + sb.sprintf (GTXT ("<No Data>")); + } + break; + + // Custom index objects + default: + if (obj) + sb.sprintf (GTXT ("%s from %s"), + dbeSession->getIndexSpaceDescr (indextype), obj->get_name (fmt)); + else + { + IndexObjType_t *indexObj = dbeSession->getIndexSpace (indextype); + if (indexObj->memObj) + { + if (strcasecmp (indexObj->name, NTXT ("Memory_page_size")) == 0) + { + if (id == 0) + sb.append (GTXT ("<Unknown>")); + else + sb.sprintf (NTXT ("%s 0x%16.16llx (%llu)"), indexObj->name, + (unsigned long long) id, (unsigned long long) id); + } + else if (strcasecmp (indexObj->name, NTXT ("Memory_in_home_lgrp")) == 0) + { + if (id == 0 || id == 1) + sb.sprintf (NTXT ("%s: %s"), indexObj->name, + id == 1 ? GTXT ("True") : GTXT ("False")); + else + sb.sprintf (NTXT ("%s %s (0x%llx"), indexObj->name, + GTXT ("<Unknown>"), (unsigned long long) id); + } + else if (strcasecmp (indexObj->name, NTXT ("Memory_lgrp")) == 0) + { + if (id == 0) + sb.append (GTXT ("<Unknown>")); + else + sb.sprintf (NTXT ("%s %llu"), indexObj->name, (unsigned long long) id); + } + else + sb.sprintf (NTXT ("%s 0x%16.16llx"), indexObj->name, (unsigned long long) id); + } + else + sb.sprintf ("%s 0x%16.16llx (%llu)", indexObj->name, + (unsigned long long) id, (unsigned long long) id); + } + } + name = sb.toString (); + nameIsFinal = true; + } + return name; +} + +bool +IndexObject::requires_string_sort () +{ + if (indextype == INDEX_PROCESSES || indextype >= INDEX_LAST) + return true; + return false; +} + +Histable * +IndexObject::convertto (Histable_type type, Histable *ext) +{ + if (type == INDEXOBJ) + return this; + if (obj) + return obj->convertto (type, ext); + return NULL; +} + +IndexObjType_t::IndexObjType_t () +{ + type = 0; + name = NULL; + i18n_name = NULL; + index_expr_str = NULL; + index_expr = NULL; + mnemonic = 0; + short_description = NULL; + long_description = NULL; + memObj = NULL; +} + +IndexObjType_t::~IndexObjType_t () +{ + free (name); + free (i18n_name); + free (index_expr_str); + delete index_expr; + free (short_description); + free (long_description); +} diff --git a/gprofng/src/IndexObject.h b/gprofng/src/IndexObject.h new file mode 100644 index 0000000..23f21d3 --- /dev/null +++ b/gprofng/src/IndexObject.h @@ -0,0 +1,111 @@ +/* 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. */ + +#ifndef _INDEXOBJECT_H +#define _INDEXOBJECT_H + +#include "Histable.h" +#include "Expression.h" + +class IndexObject : public Histable +{ +public: + IndexObject (int _indextype, uint64_t _index); + IndexObject (int _indextype, Histable *_obj); + bool requires_string_sort (); // name column should be sorted using name text + + virtual Histable_type + get_type () + { + return INDEXOBJ; + } + + virtual char *get_name (NameFormat = NA); + virtual void set_name (char*); + virtual void set_name_from_context (Expression::Context *); + virtual Histable *convertto (Histable_type, Histable* = NULL); + + virtual uint64_t + get_addr () + { + return id; + } + + uint64_t + get_index () + { + return id; + } + + Histable * + get_obj () + { + return obj; + } + + // for use in index object definitions + static const uint64_t INDXOBJ_EXPGRID_SHIFT = 60; + static const uint64_t INDXOBJ_EXPID_SHIFT = 32; + static const uint64_t INDXOBJ_PAYLOAD_SHIFT = 0; + static const uint64_t INDXOBJ_EXPGRID_MASK = + ((1LLU << (64 - INDXOBJ_EXPGRID_SHIFT)) - 1); + static const uint64_t INDXOBJ_EXPID_MASK = + ((1LLU << (INDXOBJ_EXPGRID_SHIFT - INDXOBJ_EXPID_SHIFT)) - 1); + static const uint64_t INDXOBJ_PAYLOAD_MASK = + ((1LLU << (INDXOBJ_EXPID_SHIFT - INDXOBJ_PAYLOAD_SHIFT)) - 1); + +private: + + int indextype; + Histable *obj; + bool nameIsFinal; +}; + +typedef enum IndexObjTypes +{ + INDEX_THREADS = 0, + INDEX_CPUS, + INDEX_SAMPLES, + INDEX_GCEVENTS, + INDEX_SECONDS, + INDEX_PROCESSES, + INDEX_EXPERIMENTS, + INDEX_BYTES, + INDEX_DURATION, + INDEX_LAST // never used; marks the count of precompiled items +} IndexObjTypes_t; + +class IndexObjType_t +{ +public: + IndexObjType_t (); + ~IndexObjType_t (); + int type; + char *name; // used as input + char *i18n_name; // used for output + char *index_expr_str; + Expression *index_expr; + char mnemonic; + char *short_description; + char *long_description; + MemObjType_t *memObj; +}; + +#endif /* _INDEXOBJECT_H */ diff --git a/gprofng/src/IntervalMap.h b/gprofng/src/IntervalMap.h new file mode 100644 index 0000000..8c20474 --- /dev/null +++ b/gprofng/src/IntervalMap.h @@ -0,0 +1,194 @@ +/* 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. */ + +/* + * Interval Map implementation. + * + * Interval Map makes the following assumptions: + * - if duplicate keys, the last one will be stored + * - <TBC> + */ +#ifndef _DBE_INTERVALMAP_H +#define _DBE_INTERVALMAP_H + +#include <assert.h> +#include <vec.h> +#include <Map.h> + +template <typename Key_t, typename Value_t> +class IntervalMap : public Map<Key_t, Value_t> +{ +public: + + IntervalMap (); + ~IntervalMap (); + void put (Key_t key, Value_t val); + Value_t get (Key_t key); + Value_t get (Key_t key, typename Map<Key_t, Value_t>::Relation rel); + Value_t remove (Key_t key); + +private: + + struct Entry + { + Key_t key; + Value_t val; + }; + + static const int CHUNK_SIZE; + + int entries; + int nchunks; + Entry **chunks; + Vector<Entry*> *index; +}; + +template <typename Key_t, typename Value_t> +const int IntervalMap<Key_t, Value_t>::CHUNK_SIZE = 16384; + +template <typename Key_t, typename Value_t> +IntervalMap<Key_t, Value_t>::IntervalMap () +{ + entries = 0; + nchunks = 0; + chunks = NULL; + index = new Vector<Entry*>; +} + +template <typename Key_t, typename Value_t> +IntervalMap<Key_t, Value_t>::~IntervalMap () +{ + for (int i = 0; i < nchunks; i++) + delete[] chunks[i]; + delete[] chunks; + delete index; +} + +template <typename Key_t, typename Value_t> +void +IntervalMap<Key_t, Value_t>::put (Key_t key, Value_t val) +{ + int lo = 0; + int hi = entries - 1; + while (lo <= hi) + { + int md = (lo + hi) / 2; + Entry *entry = index->fetch (md); + int cmp = entry->key < key ? -1 : entry->key > key ? 1 : 0; + if (cmp < 0) + lo = md + 1; + else if (cmp > 0) + hi = md - 1; + else + { + entry->val = val; + return; + } + } + + if (entries >= nchunks * CHUNK_SIZE) + { + nchunks++; + // Reallocate Entry chunk array + Entry **new_chunks = new Entry*[nchunks]; + for (int i = 0; i < nchunks - 1; i++) + new_chunks[i] = chunks[i]; + delete chunks; + chunks = new_chunks; + + // Allocate new chunk for entries. + chunks[nchunks - 1] = new Entry[CHUNK_SIZE]; + } + Entry *entry = &chunks[entries / CHUNK_SIZE][entries % CHUNK_SIZE]; + entry->key = key; + entry->val = val; + index->insert (lo, entry); + entries++; +} + +template <typename Key_t, typename Value_t> +Value_t +IntervalMap<Key_t, Value_t>::get (Key_t key) +{ + return get (key, Map<Key_t, Value_t>::REL_EQ); +} + +template <typename Key_t, typename Value_t> +Value_t +IntervalMap<Key_t, Value_t>::get (Key_t key, typename Map<Key_t, Value_t>::Relation rel) +{ + int lo = 0; + int hi = entries - 1; + while (lo <= hi) + { + int md = (lo + hi) / 2; + Entry *entry = index->fetch (md); + int cmp = entry->key < key ? -1 : entry->key > key ? 1 : 0; + switch (rel) + { + case Map<Key_t, Value_t>::REL_LT: + if (cmp < 0) + lo = md + 1; + else + hi = md - 1; + break; + case Map<Key_t, Value_t>::REL_GT: + if (cmp <= 0) + lo = md + 1; + else + hi = md - 1; + break; + case Map<Key_t, Value_t>::REL_LE: + case Map<Key_t, Value_t>::REL_GE: + case Map<Key_t, Value_t>::REL_EQ: + if (cmp < 0) + lo = md + 1; + else if (cmp > 0) + hi = md - 1; + else + return entry->val; + break; + } + } + switch (rel) + { + case Map<Key_t, Value_t>::REL_LT: + case Map<Key_t, Value_t>::REL_LE: + return hi >= 0 ? index->fetch (hi)->val : (Value_t) 0; + case Map<Key_t, Value_t>::REL_GT: + case Map<Key_t, Value_t>::REL_GE: + return lo < entries ? index->fetch (lo)->val : (Value_t) 0; + case Map<Key_t, Value_t>::REL_EQ: + break; + } + return (Value_t) 0; +} + +template <typename Key_t, typename Value_t> +Value_t +IntervalMap<Key_t, Value_t>::remove (Key_t) +{ + // Not implemented + if (1) + assert (0); + return (Value_t) 0; +} + +#endif diff --git a/gprofng/src/LoadObject.cc b/gprofng/src/LoadObject.cc new file mode 100644 index 0000000..d9ce3e8 --- /dev/null +++ b/gprofng/src/LoadObject.cc @@ -0,0 +1,1242 @@ +/* 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 <errno.h> + +#include "util.h" +#include "StringBuilder.h" +#include "Application.h" +#include "DbeSession.h" +#include "Experiment.h" +#include "Exp_Layout.h" +#include "DataObject.h" +#include "Elf.h" +#include "Function.h" +#include "Module.h" +#include "ClassFile.h" +#include "Stabs.h" +#include "LoadObject.h" +#include "dbe_types.h" +#include "DbeFile.h" +#include "ExpGroup.h" + +enum +{ + LO_InstHTableSize = 4096, + HTableSize = 1024 +}; + +LoadObject * +LoadObject::create_item (const char *nm, int64_t chksum) +{ + LoadObject *lo = new LoadObject (nm); + lo->checksum = chksum; + dbeSession->append (lo); + return lo; +} + +LoadObject * +LoadObject::create_item (const char *nm, const char *_runTimePath, DbeFile *df) +{ + LoadObject *lo = new LoadObject (nm); + lo->runTimePath = dbe_strdup (_runTimePath); + lo->dbeFile->orig_location = dbe_strdup (_runTimePath); + if (df) + { + if ((df->filetype & DbeFile::F_JAR_FILE) != 0) + { + if (lo->dbeFile->find_in_jar_file (nm, df->get_jar_file ())) + { + lo->dbeFile->inArchive = df->inArchive; + lo->dbeFile->container = df; + } + } + else + { + lo->dbeFile->set_location (df->get_location ()); + lo->dbeFile->sbuf = df->sbuf; + lo->dbeFile->inArchive = df->inArchive; + } + } + dbeSession->append (lo); + return lo; +} + +LoadObject::LoadObject (const char *loname) +{ + flags = 0; + size = 0; + type = SEG_UNKNOWN; + isReadStabs = false; + need_swap_endian = false; + instHTable = new DbeInstr*[LO_InstHTableSize]; + for (int i = 0; i < LO_InstHTableSize; i++) + instHTable[i] = NULL; + + functions = new Vector<Function*>; + funcHTable = new Function*[HTableSize]; + for (int i = 0; i < HTableSize; i++) + funcHTable[i] = NULL; + + seg_modules = new Vector<Module*>; + modules = new HashMap<char*, Module*>; + platform = Unknown; + noname = dbeSession->createUnknownModule (this); + modules->put (noname->get_name (), noname); + pathname = NULL; + arch_name = NULL; + runTimePath = NULL; + objStabs = NULL; + firstExp = NULL; + seg_modules_map = NULL; + comp_funcs = NULL; + warnq = new Emsgqueue (NTXT ("lo_warnq")); + commentq = new Emsgqueue (NTXT ("lo_commentq")); + elf_lo = NULL; + elf_inited = false; + checksum = 0; + isUsed = false; + h_function = NULL; + h_instr = NULL; + + char *nm = (char *) loname; + if (strncmp (nm, NTXT ("./"), 2) == 0) + nm += 2; + set_name (nm); + dbeFile = new DbeFile (nm); + dbeFile->filetype |= DbeFile::F_LOADOBJ | DbeFile::F_FILE; +} + +LoadObject::~LoadObject () +{ + delete seg_modules_map; + delete functions; + delete[] instHTable; + delete[] funcHTable; + delete seg_modules; + delete modules; + delete elf_lo; + free (pathname); + free (arch_name); + free (runTimePath); + delete objStabs; + delete warnq; + delete commentq; + delete h_instr; +} + +Elf * +LoadObject::get_elf () +{ + if (elf_lo == NULL) + { + if (dbeFile->get_need_refind ()) + elf_inited = false; + if (elf_inited) + return NULL; + elf_inited = true; + char *fnm = dbeFile->get_location (); + if (fnm == NULL) + { + append_msg (CMSG_ERROR, GTXT ("Cannot find file: `%s'"), + dbeFile->get_name ()); + return NULL; + } + Elf::Elf_status st = Elf::ELF_ERR_CANT_OPEN_FILE; + elf_lo = Elf::elf_begin (fnm, &st); + if (elf_lo == NULL) + switch (st) + { + case Elf::ELF_ERR_CANT_OPEN_FILE: + append_msg (CMSG_ERROR, GTXT ("Cannot open ELF file `%s'"), fnm); + break; + case Elf::ELF_ERR_BAD_ELF_FORMAT: + default: + append_msg (CMSG_ERROR, GTXT ("Cannot read ELF header of `%s'"), + fnm); + break; + } + } + return elf_lo; +} + +Stabs * +LoadObject::openDebugInfo (char *fname, Stabs::Stab_status *stp) +{ + if (objStabs == NULL) + { + if (fname == NULL) + return NULL; + objStabs = new Stabs (fname, get_pathname ()); + Stabs::Stab_status st = objStabs->get_status (); + if ((st == Stabs::DBGD_ERR_NONE) && (checksum != 0)) + { + Elf *elf = get_elf (); + if (elf && (checksum != elf->elf_checksum ())) + { + char *buf = dbe_sprintf (GTXT ("*** Note: '%s' has an unexpected checksum value; perhaps it was rebuilt. File ignored"), + fname); + commentq->append (new Emsg (CMSG_ERROR, buf)); + delete buf; + st = Stabs::DBGD_ERR_CHK_SUM; + } + } + if (stp) + *stp = st; + if (st != Stabs::DBGD_ERR_NONE) + { + delete objStabs; + objStabs = NULL; + } + } + return objStabs; +} + +uint64_t +LoadObject::get_addr () +{ + return MAKE_ADDRESS (seg_idx, 0); +} + +bool +LoadObject::compare (const char *_path, int64_t _checksum) +{ + return _checksum == checksum && dbe_strcmp (_path, get_pathname ()) == 0; +} + +int +LoadObject::compare (const char *_path, const char *_runTimePath, DbeFile *df) +{ + int ret = 0; + if (dbe_strcmp (_path, get_pathname ()) != 0) + return ret; + ret |= CMP_PATH; + if (_runTimePath) + { + if (dbe_strcmp (_runTimePath, runTimePath) != 0) + return ret; + ret |= CMP_RUNTIMEPATH; + } + if (df && dbeFile->compare (df)) + ret |= CMP_CHKSUM; + return ret; +} + +void +LoadObject::set_platform (Platform_t pltf, int wsz) +{ + switch (pltf) + { + case Sparc: + case Sparcv9: + case Sparcv8plus: + platform = (wsz == W64) ? Sparcv9 : Sparc; + break; + case Intel: + case Amd64: + platform = (wsz == W64) ? Amd64 : Intel; + break; + default: + platform = pltf; + break; + } +}; + +void +LoadObject::set_name (char *string) +{ + char *p; + pathname = dbe_strdup (string); + + p = get_basename (pathname); + if (p[0] == '<') + name = dbe_strdup (p); + else // set a short name to "<basename>" + name = dbe_sprintf (NTXT ("<%s>"), p); +} + +void +LoadObject::dump_functions (FILE *out) +{ + int index; + Function *fitem; + char *sname, *mname; + if (platform == Java) + { + JMethod *jmthd; + Vector<JMethod*> *jmethods = (Vector<JMethod*>*)functions; + Vec_loop (JMethod*, jmethods, index, jmthd) + { + fprintf (out, "id %6llu, @0x%llx sz-%lld %s (module = %s)\n", + (unsigned long long) jmthd->id, (long long) jmthd->get_mid (), + (long long) jmthd->size, jmthd->get_name (), + jmthd->module ? jmthd->module->file_name : noname->file_name); + } + } + else + { + Vec_loop (Function*, functions, index, fitem) + { + if (fitem->alias && fitem->alias != fitem) + fprintf (out, "id %6llu, @0x%llx - %s == alias of '%s'\n", + (ull_t) fitem->id, (ull_t) fitem->img_offset, + fitem->get_name (), fitem->alias->get_name ()); + else + { + mname = fitem->module ? fitem->module->file_name : noname->file_name; + sname = fitem->getDefSrcName (); + fprintf (out, + "id %6llu, @0x%llx - 0x%llx [save 0x%llx] o-%lld sz-%lld %s (module = %s)", + (ull_t) fitem->id, (ull_t) fitem->img_offset, + (ull_t) (fitem->img_offset + fitem->size), + (ull_t) fitem->save_addr, (ull_t) fitem->img_offset, + (ll_t) fitem->size, fitem->get_name (), mname); + if (sname && !streq (sname, mname)) + fprintf (out, " (Source = %s)", sname); + fprintf (out, "\n"); + } + } + } +} + +int +LoadObject::get_index (Function *func) +{ + Function *fp; + uint64_t offset; + int x; + int left = 0; + int right = functions->size () - 1; + offset = func->img_offset; + while (left <= right) + { + x = (left + right) / 2; + fp = functions->fetch (x); + + if (left == right) + { + if (offset >= fp->img_offset + fp->size) + return -1; + if (offset >= fp->img_offset) + return x; + return -1; + } + if (offset < fp->img_offset) + right = x - 1; + else if (offset >= fp->img_offset + fp->size) + left = x + 1; + else + return x; + } + return -1; +} + +char * +LoadObject::get_alias (Function *func) +{ + Function *fp, *alias; + int index, nsize; + static char buf[1024]; + if (func->img_offset == 0 || func->alias == NULL) + return NULL; + int fid = get_index (func); + if (fid == -1) + return NULL; + + nsize = functions->size (); + alias = func->alias; + for (index = fid; index < nsize; index++) + { + fp = functions->fetch (index); + if (fp->alias != alias) + { + fid = index; + break; + } + } + + *buf = '\0'; + for (index--; index >= 0; index--) + { + fp = functions->fetch (index); + if (fp->alias != alias) + break; + if (fp != alias) + { + size_t len = strlen (buf); + if (*buf != '\0') + { + snprintf (buf + len, sizeof (buf) - len, NTXT (", ")); + len = strlen (buf); + } + snprintf (buf + len, sizeof (buf) - len, "%s", fp->get_name ()); + } + } + return buf; +} + +DbeInstr* +LoadObject::find_dbeinstr (uint64_t file_off) +{ + int hash = (((int) file_off) >> 2) & (LO_InstHTableSize - 1); + DbeInstr *instr = instHTable[hash]; + if (instr && instr->img_offset == file_off) + return instr; + Function *fp = find_function (file_off); + if (fp == NULL) + fp = dbeSession->get_Unknown_Function (); + uint64_t func_off = file_off - fp->img_offset; + instr = fp->find_dbeinstr (0, func_off); + instHTable[hash] = instr; + return instr; +} + +Function * +LoadObject::find_function (uint64_t foff) +{ + // Look up in the hash table + int hash = (((int) foff) >> 6) & (HTableSize - 1); + Function *func = funcHTable[hash]; + if (func && foff >= func->img_offset && foff < func->img_offset + func->size) + return func->alias ? func->alias : func; + + // Use binary search + func = NULL; + int left = 0; + int right = functions->size () - 1; + while (left <= right) + { + int x = (left + right) / 2; + Function *fp = functions->fetch (x); + assert (fp != NULL); + + if (foff < fp->img_offset) + right = x - 1; + else if (foff >= fp->img_offset + fp->size) + left = x + 1; + else + { + func = fp; + break; + } + } + + // Plug the hole with a static function + char *func_name = NULL; + Size low_bound = 0, high_bound = 0; + if (func == NULL) + { + int last = functions->size () - 1; + uint64_t usize = (uint64_t) size; + if (foff >= usize) + { + // Cannot map to this LoadObject. Probably LoadObject was changed. + if (last >= 0 && functions->fetch (last)->img_offset == usize) + { + // Function is already created + func = functions->fetch (last); + if (func->size < 0 || (uint64_t) func->size < foff - usize) + func->size = foff - usize; + } + else + { + low_bound = size; + high_bound = foff; + func_name = dbe_sprintf (GTXT ("<static>@0x%llx (%s) -- no functions found"), + low_bound, name); + } + } + else if (last < 0) + { + low_bound = 0; + high_bound = size; + func_name = dbe_sprintf (GTXT ("<static>@0x%llx (%s) -- no functions found"), + low_bound, name); + } + else if (foff < functions->fetch (0)->img_offset) + { + low_bound = 0; + high_bound = functions->fetch (0)->img_offset; + } + else + { + Function *fp = functions->fetch (last); + if (foff >= fp->img_offset + fp->size) + { + low_bound = fp->img_offset + fp->size; + high_bound = size; + } + else + { + fp = functions->fetch (left); + if (foff >= fp->img_offset + fp->size) + { + low_bound = fp->img_offset + fp->size; + high_bound = functions->fetch (left + 1)->img_offset; + } + else + { + Function *fp1 = functions->fetch (left - 1); + low_bound = fp1->img_offset + fp1->size; + high_bound = fp->img_offset; + } + } + } + } + + if (func == NULL) + { + func = dbeSession->createFunction (); + func->size = (unsigned) (high_bound - low_bound); + func->module = noname; + func->img_fname = get_pathname (); + func->img_offset = (off_t) low_bound; + noname->functions->append (func); // unordered + if (func_name == NULL) + func_name = dbe_sprintf (GTXT ("<static>@0x%llx (%s)"), low_bound, + name); + func->set_name (func_name); + free (func_name); + + // now insert the function + functions->insert (left, func); + } + + // Update the hash table + funcHTable[hash] = func; + return func->alias ? func->alias : func; +} + +static void +fixFuncAlias (Vector<Function*> *SymLst) +{ + int ind, i, k; + int64_t len, bestLen, maxSize; + Function *sym, *bestAlias; + + // XXXX it is a clone of Stabs::fixSymtabAlias() + ind = SymLst->size () - 1; + for (i = 0; i < ind; i++) + { + bestAlias = SymLst->fetch (i); + if (bestAlias->img_offset == 0) // Ignore this bad symbol + continue; + sym = SymLst->fetch (i + 1); + if (bestAlias->img_offset != sym->img_offset) + { + if (bestAlias->size == 0 + || sym->img_offset < bestAlias->img_offset + bestAlias->size) + bestAlias->size = (int) (sym->img_offset - bestAlias->img_offset); + continue; + } + + // Find a "best" alias + bestLen = strlen (bestAlias->get_name ()); + maxSize = bestAlias->size; + for (k = i + 1; k <= ind; k++) + { + sym = SymLst->fetch (k); + if (bestAlias->img_offset != sym->img_offset) + { // no more aliases + if ((maxSize == 0) || + (sym->img_offset < bestAlias->img_offset + maxSize)) + maxSize = sym->img_offset - bestAlias->img_offset; + break; + } + if (maxSize < sym->size) + maxSize = sym->size; + len = strlen (sym->get_name ()); + if (len < bestLen) + { + bestAlias = sym; + bestLen = len; + } + } + for (; i < k; i++) + { + sym = SymLst->fetch (i); + sym->alias = bestAlias; + sym->size = maxSize; + } + i--; + } +} + +void +LoadObject::post_process_functions () +{ + if (flags & SEG_FLAG_DYNAMIC || platform == Java) + return; + + char *msg = GTXT ("Processing Load Object Data"); + if (dbeSession->is_interactive ()) + theApplication->set_progress (1, msg); + + // First sort the functions + functions->sort (func_compare); + fixFuncAlias (functions); + + Module *mitem; + int index; + Vec_loop (Module*, seg_modules, index, mitem) + { + mitem->functions->sort (func_compare); + } + + // Find any derived functions, and set their derivedNode + Function *fitem; + Vec_loop (Function*, functions, index, fitem) + { + if (dbeSession->is_interactive () && index % 5000 == 0) + { + int percent = (int) (100.0 * index / functions->size ()); + theApplication->set_progress (percent, (percent != 0) ? NULL : msg); + } + fitem->findDerivedFunctions (); + } + + // 4987698: get the alias name for MAIN_ + fitem = find_function (NTXT ("MAIN_")); + if (fitem) + fitem->module->read_stabs (); + fitem = find_function (NTXT ("@plt")); + if (fitem) + fitem->flags |= FUNC_FLAG_PLT; + if (dbeSession->is_interactive ()) + theApplication->set_progress (0, NTXT ("")); +} + +int +LoadObject::func_compare (const void *p1, const void *p2) +{ + Function *f1 = *(Function **) p1; + Function *f2 = *(Function **) p2; + if (f1->img_offset != f2->img_offset) + return f1->img_offset > f2->img_offset ? 1 : -1; + + // annotated source not available for weak symbols. + if ((f1->module->flags & MOD_FLAG_UNKNOWN) != 0) + { + if ((f2->module->flags & MOD_FLAG_UNKNOWN) == 0) + return -1; + } + else if ((f2->module->flags & MOD_FLAG_UNKNOWN) != 0) + return 1; + return strcoll (f1->get_name (), f2->get_name ()); +} + +Function * +LoadObject::find_function (char *fname) +{ + Function *fitem; + int index; + Vec_loop (Function*, functions, index, fitem) + { + if (strcmp (fitem->get_name (), fname) == 0) + return fitem; + } + return (Function *) NULL; +} + +Function * +LoadObject::find_function (char *fname, unsigned int chksum) +{ + Function *fitem; + int index; + Vec_loop (Function*, functions, index, fitem) + { + if (fitem->chksum == chksum && strcmp (fitem->get_name (), fname) == 0) + return fitem; + } + return (Function *) NULL; +} + +Module * +LoadObject::find_module (char *mname) +{ + for (int i = 0, sz = seg_modules ? seg_modules->size () : 0; i < sz; i++) + { + Module *module = seg_modules->fetch (i); + if (strcmp (module->get_name (), mname) == 0) + return module; + } + return (Module *) NULL; +} + +LoadObject::Arch_status +LoadObject::sync_read_stabs () +{ + Arch_status st = ARCHIVE_SUCCESS; + if (!isReadStabs) + { + aquireLock (); + if (!isReadStabs) + { + st = read_stabs (); + post_process_functions (); + isReadStabs = true; + } + releaseLock (); + } + return st; +} + +LoadObject::Arch_status +LoadObject::read_stabs () +{ + if ((dbeFile->filetype & DbeFile::F_FICTION) != 0) + return ARCHIVE_SUCCESS; + Arch_status stabs_status = ARCHIVE_ERR_OPEN; + if (platform == Java) + { + Module *cf = NULL; + for (int i = 0, sz = seg_modules ? seg_modules->size () : 0; i < sz; i++) + { + Module *mod = seg_modules->fetch (i); + if (mod->dbeFile + && (mod->dbeFile->filetype & DbeFile::F_JAVACLASS) != 0) + { + cf = mod; + break; + } + } + if (cf) + { + int status = cf->readFile (); + switch (status) + { + case Module::AE_OK: + stabs_status = ARCHIVE_SUCCESS; + break; + case Module::AE_NOSTABS: + stabs_status = ARCHIVE_NO_STABS; + break; + case Module::AE_NOTREAD: + default: + stabs_status = ARCHIVE_ERR_OPEN; + break; + } + } + } + else if (strchr (pathname, '`')) + return ARCHIVE_SUCCESS; + else + { + Arch_status st = ARCHIVE_WRONG_ARCH; + Elf *elf = get_elf (); + if (elf == NULL) + { + if (read_archive () == 0) + st = ARCHIVE_SUCCESS; + else + { + char *msg = dbe_sprintf (GTXT ("*** Warning: Can't open file: %s"), + dbeFile->get_name ()); + warnq->append (new Emsg (CMSG_ERROR, msg)); + delete msg; + } + } + else if (checksum != 0 && checksum != elf->elf_checksum ()) + { + if (read_archive () == 0) + st = ARCHIVE_SUCCESS; + else + { + char *msg = dbe_sprintf ( + GTXT ("*** Note: '%s' has an unexpected checksum value; perhaps it was rebuilt. File ignored"), + dbeFile->get_location ()); + commentq->append (new Emsg (CMSG_ERROR, msg)); + delete msg; + } + } + if (st == ARCHIVE_SUCCESS) // An old archive is used + return st; + + Stabs::Stab_status status = Stabs::DBGD_ERR_CANT_OPEN_FILE; + char *location = dbeFile->get_location (true); + if (location == NULL) + return ARCHIVE_ERR_OPEN; + + if (openDebugInfo (location, &status)) + { + status = objStabs->read_archive (this); + isRelocatable = objStabs->is_relocatable (); + size = objStabs->get_textsz (); + platform = objStabs->get_platform (); + wsize = objStabs->get_class (); + } + + switch (status) + { + case Stabs::DBGD_ERR_NONE: + stabs_status = ARCHIVE_SUCCESS; + break; + case Stabs::DBGD_ERR_CANT_OPEN_FILE: + stabs_status = ARCHIVE_ERR_OPEN; + break; + case Stabs::DBGD_ERR_BAD_ELF_LIB: + case Stabs::DBGD_ERR_BAD_ELF_FORMAT: + stabs_status = ARCHIVE_BAD_STABS; + break; + case Stabs::DBGD_ERR_NO_STABS: + stabs_status = ARCHIVE_NO_STABS; + break; + case Stabs::DBGD_ERR_NO_DWARF: + stabs_status = ARCHIVE_NO_DWARF; + break; + default: + stabs_status = ARCHIVE_BAD_STABS; + break; + } + } + return stabs_status; +} + +#define ARCH_STRLEN(s) ((strlen(s) + 4) & ~0x3 ) + +static int +offsetCmp (const void *a, const void *b) +{ + uint32_t o1 = ((inst_info_t *) a)->offset; + uint32_t o2 = ((inst_info_t *) b)->offset; + return o1 == o2 ? 0 : (o1 < o2 ? -1 : 1); +} + +int +LoadObject::read_archive () +{ + if (arch_name == NULL) + return 1; + Module *mod = NULL; + Function *func = NULL; + char *buf; + Data_window *dwin = new Data_window (arch_name); + if (dwin->not_opened ()) + { + delete dwin; + buf = dbe_sprintf (GTXT ("*** Warning: Error opening file for reading: %s: %s"), + arch_name, strerror (errno)); + warnq->append (new Emsg (CMSG_ERROR, buf)); + delete buf; + return 1; + } + dwin->need_swap_endian = need_swap_endian; + + // Prevent reading earlier archive files, which didn't support versioning. + int64_t offset = 0; + ARCH_common *cpkt = (ARCH_common*) dwin->bind (offset, sizeof (ARCH_common)); + uint16_t v16; + if (cpkt) + { + v16 = (uint16_t) cpkt->type; + if (dwin->decode (v16) != ARCH_SEGMENT) + cpkt = NULL; + } + if (cpkt == NULL) + { + buf = dbe_sprintf (GTXT ("archive file malformed %s"), arch_name); + warnq->append (new Emsg (CMSG_WARN, buf)); + delete buf; + return 1; + } + + char *msg = NULL; + unsigned long long pointer_invalid = 0; + for (int64_t last_offset = -5000;;) + { + cpkt = (ARCH_common*) dwin->bind (offset, sizeof (ARCH_common)); + if (cpkt == NULL) + break; + v16 = (uint16_t) cpkt->size; + uint32_t cpktsize = dwin->decode (v16); + cpkt = (ARCH_common*) dwin->bind (offset, cpktsize); + if ((cpkt == NULL) || (cpktsize == 0)) + { + buf = dbe_sprintf (GTXT ("archive file malformed %s"), arch_name); + warnq->append (new Emsg (CMSG_WARN, buf)); + delete buf; + break; + } + + // Update the progress bar + if (dbeSession->is_interactive () && ((offset - last_offset) >= 5000)) + { + last_offset = offset; + int percent = (int) (100.0 * offset / dwin->get_fsize ()); + if (msg == NULL) + msg = dbe_sprintf (GTXT ("Reading Load Object Data: %s"), name); + theApplication->set_progress (percent, (percent != 0) ? NULL : msg); + } + char *ptr = (char *) cpkt; + v16 = (uint16_t) cpkt->type; + switch (dwin->decode (v16)) + { + case ARCH_SEGMENT: + { + ARCH_segment *aseg = (ARCH_segment*) cpkt; + if (dwin->decode (aseg->version) != ARCH_VERSION) + { + buf = dbe_sprintf (GTXT ("Archive file version mismatch for %s"), arch_name); + warnq->append (new Emsg (CMSG_ERROR, buf)); + delete buf; + if (dbeSession->is_interactive ()) + theApplication->set_progress (0, ""); + return 1; + } + if (size == 0) + size = dwin->decode (aseg->textsz); + Platform_t pltf = (Platform_t) dwin->decode (aseg->platform); + if (pltf != Unknown) + { + platform = pltf; // override if known + wsize = (platform == Sparcv9 || platform == Amd64) ? W64 : W32; + } + break; + } + case ARCH_MSG: + { + ARCH_message *amsg = (ARCH_message*) cpkt; + buf = status_str ((Arch_status) dwin->decode (amsg->errcode)); + commentq->append (new Emsg (CMSG_ARCHIVE, buf)); + free (buf); + break; + } + case ARCH_INF: + { + ARCH_info *ainf = (ARCH_info*) cpkt; + Emsg *m = new Emsg (CMSG_ARCHIVE, (char*) (ainf + 1)); + commentq->append (m); + break; + } + case ARCH_MODULE: + { + ARCH_module *amod = (ARCH_module*) cpkt; + char *str = ((char*) amod) + sizeof (ARCH_module); + if (streq (str, SP_UNKNOWN_NAME) && + streq (str + ARCH_STRLEN (str), SP_UNKNOWN_NAME)) + { + mod = noname; + break; + } + mod = dbeSession->createModule (this, str); + mod->lang_code = (Sp_lang_code) dwin->decode (amod->lang_code); + mod->fragmented = dwin->decode (amod->fragmented); + str += ARCH_STRLEN (str); + mod->set_file_name (dbe_strdup (str)); + modules->put (get_basename (str), mod); + break; + } + case ARCH_FUNCTION: + { + if (mod == NULL) + break; + ARCH_function *afnc = (ARCH_function*) cpkt; + func = dbeSession->createFunction (); + func->img_offset = dwin->decode (afnc->offset); + func->size = dwin->decode (afnc->size); + func->save_addr = dwin->decode (afnc->save_addr) + - dwin->decode (afnc->offset); + func->module = mod; + func->set_name (((char*) afnc) + sizeof (ARCH_function)); + mod->functions->append (func); + functions->append (func); + break; + } + case ARCH_LDINSTR: + if (mod == NULL) + break; + Dprintf (DEBUG_LOADOBJ, "LDINSTR list for %s\n", mod->get_name ()); + if (mod->infoList == NULL) + mod->infoList = new Vector<inst_info_t*>; + for (memop_info_t *mp = (memop_info_t*) (ptr + sizeof (ARCH_aninfo)); + (char*) mp < ptr + cpktsize; mp++) + { + memop_info_t *memop = new memop_info_t; + memop->offset = dwin->decode (mp->offset); + memop->id = dwin->decode (mp->id); + memop->signature = dwin->decode (mp->signature); + memop->datatype_id = dwin->decode (mp->datatype_id); + mod->ldMemops.append (memop); + + inst_info_t *instop = new inst_info_t; + instop->type = CPF_INSTR_TYPE_LD; + instop->offset = memop->offset; + instop->memop = memop; + mod->infoList->incorporate (instop, offsetCmp); + Dprintf (DEBUG_LOADOBJ, + "ld: offset=0x%04x id=0x%08x sig=0x%08x dtid=0x%08x\n", + memop->offset, memop->id, memop->signature, + memop->datatype_id); + } + Dprintf (DEBUG_LOADOBJ, "LDINSTR list of %lld for %s\n", + (long long) mod->ldMemops.size (), mod->get_name ()); + break; + case ARCH_STINSTR: + if (mod == NULL) + break; + Dprintf (DEBUG_LOADOBJ, NTXT ("STINSTR list for %s\n"), mod->get_name ()); + if (mod->infoList == NULL) + mod->infoList = new Vector<inst_info_t*>; + for (memop_info_t *mp = (memop_info_t*) (ptr + sizeof (ARCH_aninfo)); + ((char *) mp) < ptr + cpktsize; mp++) + { + memop_info_t *memop = new memop_info_t; + memop->offset = dwin->decode (mp->offset); + memop->id = dwin->decode (mp->id); + memop->signature = dwin->decode (mp->signature); + memop->datatype_id = dwin->decode (mp->datatype_id); + mod->stMemops.append (memop); + + inst_info_t *instop = new inst_info_t; + instop->type = CPF_INSTR_TYPE_ST; + instop->offset = memop->offset; + instop->memop = memop; + mod->infoList->incorporate (instop, offsetCmp); + Dprintf (DEBUG_LOADOBJ, + "st: offset=0x%04x id=0x%08x sig=0x%08x dtid=0x%08x\n", + memop->offset, memop->id, memop->signature, + memop->datatype_id); + } + Dprintf (DEBUG_LOADOBJ, "STINSTR list of %lld for %s\n", + (long long) mod->stMemops.size (), mod->get_name ()); + break; + case ARCH_PREFETCH: + if (mod == NULL) + break; + Dprintf (DEBUG_LOADOBJ, "PFINSTR list for %s\n", mod->get_name ()); + if (mod->infoList == NULL) + mod->infoList = new Vector<inst_info_t*>; + for (memop_info_t *mp = (memop_info_t*) (ptr + sizeof (ARCH_aninfo)); + ((char*) mp) < ptr + cpkt->size; mp++) + { + memop_info_t *memop = new memop_info_t; + memop->offset = dwin->decode (mp->offset); + memop->id = dwin->decode (mp->id); + memop->signature = dwin->decode (mp->signature); + memop->datatype_id = dwin->decode (mp->datatype_id); + mod->pfMemops.append (memop); + + inst_info_t *instop = new inst_info_t; + instop->type = CPF_INSTR_TYPE_PREFETCH; + instop->offset = memop->offset; + instop->memop = memop; + mod->infoList->incorporate (instop, offsetCmp); + Dprintf (DEBUG_LOADOBJ, + "pf: offset=0x%04x id=0x%08x sig=0x%08x dtid=0x%08x\n", + memop->offset, memop->id, memop->signature, + memop->datatype_id); + } + Dprintf (DEBUG_LOADOBJ, "PFINSTR list of %lld for %s\n", + (long long) mod->pfMemops.size (), mod->get_name ()); + break; + case ARCH_BRTARGET: + if (mod == NULL) + break; + for (target_info_t *tp = (target_info_t*) (ptr + sizeof (ARCH_aninfo)); + ((char*) tp) < ptr + cpkt->size; tp++) + { + target_info_t *bTarget = new target_info_t; + bTarget->offset = dwin->decode (tp->offset); + mod->bTargets.append (bTarget); + } + Dprintf (DEBUG_LOADOBJ, "BRTARGET list of %lld for %s\n", + (long long) mod->infoList->size (), mod->get_name ()); + break; + default: + /* Check if the prointer is valid - should be even. */ + pointer_invalid = (unsigned long long) (offset + cpktsize) & 1; + break; // ignore unknown packets + } + if (pointer_invalid) + break; + offset += cpktsize; + } + delete msg; + delete dwin; + + if (dbeSession->is_interactive ()) + theApplication->set_progress (0, NTXT ("")); + return 0; +} + +char * +LoadObject::status_str (Arch_status rv, char */*arg*/) +{ + switch (rv) + { + case ARCHIVE_SUCCESS: + case ARCHIVE_EXIST: + return NULL; + case ARCHIVE_BAD_STABS: + return dbe_sprintf (GTXT ("Error: unable to read symbol table of %s"), + name); + case ARCHIVE_ERR_SEG: + return dbe_sprintf (GTXT ("Error: unable to read load object file %s"), + pathname); + case ARCHIVE_ERR_OPEN: + return dbe_sprintf (GTXT ("Error: unable to open file %s"), + pathname); + case ARCHIVE_ERR_MAP: + return dbe_sprintf (GTXT ("Error: unable to map file %s"), + pathname); + case ARCHIVE_WARN_CHECKSUM: + return dbe_sprintf (GTXT ("Note: checksum differs from that recorded in experiment for %s"), + name); + case ARCHIVE_WARN_MTIME: + return dbe_sprintf (GTXT ("Warning: last-modified time differs from that recorded in experiment for %s"), + name); + case ARCHIVE_WARN_HOST: + return dbe_sprintf (GTXT ("Try running er_archive -F on the experiment, on the host where it was recorded")); + case ARCHIVE_ERR_VERSION: + return dbe_sprintf (GTXT ("Error: Wrong version of archive for %s"), + pathname); + case ARCHIVE_NO_STABS: + return dbe_sprintf (GTXT ("Note: no stabs or dwarf information in %s"), + name); + case ARCHIVE_WRONG_ARCH: +#if ARCH(SPARC) + return dbe_sprintf (GTXT ("Error: file %s is built for Intel, and can't be read on SPARC"), + name); +#else + return dbe_sprintf (GTXT ("Error: file %s is built for SPARC, and can't be read on Intel"), + name); +#endif + case ARCHIVE_NO_LIBDWARF: + return dbe_strdup (GTXT ("Warning: no libdwarf found to read DWARF symbol tables")); + case ARCHIVE_NO_DWARF: + return dbe_sprintf (GTXT ("Note: no DWARF symbol table in %s"), name); + default: + return dbe_sprintf (GTXT ("Warning: unexpected archive error %d"), + (int) rv); + } +} + +uint32_t +LoadObject::get_checksum () +{ + char *errmsg = NULL; + uint32_t crcval = get_cksum (pathname, &errmsg); + if (0 == crcval && errmsg) + { + warnq->append (new Emsg (CMSG_ERROR, errmsg)); + free (errmsg); + } + return crcval; +} + +static char* +get_module_map_key (Module *mod) +{ + return mod->lang_code == Sp_lang_java ? mod->get_name () : mod->file_name; +} + +Module * +LoadObject::get_comparable_Module (Module *mod) +{ + if (mod->loadobject == this) + return mod; + if (get_module_map_key (mod) == NULL) + return NULL; + if (seg_modules_map == NULL) + { + seg_modules_map = new HashMap<char*, Module*>; + for (int i = 0; i < seg_modules->size (); i++) + { + Module *m = seg_modules->fetch (i); + char *key = get_module_map_key (m); + if (key) + { + seg_modules_map->put (m->file_name, m); + char *bname = get_basename (key); + if (bname != key) + seg_modules_map->put (bname, m); + } + } + } + + char *key = get_module_map_key (mod); + Module *cmpMod = seg_modules_map->get (key); + if (cmpMod && cmpMod->comparable_objs == NULL) + return cmpMod; + char *bname = get_basename (key); + if (bname != key) + { + cmpMod = seg_modules_map->get (bname); + if (cmpMod && cmpMod->comparable_objs == NULL) + return cmpMod; + } + return NULL; +} + +Vector<Histable*> * +LoadObject::get_comparable_objs () +{ + update_comparable_objs (); + if (comparable_objs || dbeSession->expGroups->size () <= 1) + return comparable_objs; + comparable_objs = new Vector<Histable*>(dbeSession->expGroups->size ()); + for (int i = 0, sz = dbeSession->expGroups->size (); i < sz; i++) + { + ExpGroup *gr = dbeSession->expGroups->fetch (i); + Histable *h = gr->get_comparable_loadObject (this); + comparable_objs->append (h); + if (h) + h->comparable_objs = comparable_objs; + } + dump_comparable_objs (); + return comparable_objs; +} + +void +LoadObject::append_module (Module *mod) +{ + seg_modules->append (mod); + if (seg_modules_map == NULL) + seg_modules_map = new HashMap<char*, Module*>; + char *key = get_module_map_key (mod); + if (key) + { + seg_modules_map->put (key, mod); + char *bname = get_basename (key); + if (bname != key) + seg_modules_map->put (bname, mod); + } +} + +// LIBRARY_VISIBILITY +Function * +LoadObject::get_hide_function () +{ + if (h_function == NULL) + h_function = dbeSession->create_hide_function (this); + return h_function; +} + +DbeInstr * +LoadObject::get_hide_instr (DbeInstr *instr) +{ + if (h_instr == NULL) + { + Function *hf = get_hide_function (); + h_instr = hf->create_hide_instr (instr); + } + return h_instr; +} diff --git a/gprofng/src/LoadObject.h b/gprofng/src/LoadObject.h new file mode 100644 index 0000000..0c997e3 --- /dev/null +++ b/gprofng/src/LoadObject.h @@ -0,0 +1,210 @@ +/* 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. */ + +#ifndef _LOADOBJECT_H +#define _LOADOBJECT_H + +// A Segment object represents a segment of the program text. + +#include "Histable.h" +#include "Stabs.h" +#include "DbeLock.h" + +#define JAVA_COMPILED_METHODS "JAVA_COMPILED_METHODS" +#define DYNFUNC_SEGMENT "DYNAMIC_FUNCTIONS" +#define SEG_FLAG_DYNAMIC 0x01 +#define SEG_FLAG_JVM 0x02 +#define SEG_FLAG_OMP 0x04 +#define SEG_FLAG_EXE 0x08 +#define SEG_FLAG_REORDER 0x10 + +/* Hash name for all comparable executions */ +#define COMP_EXE_NAME "<COMP_EXE_NAME>" + +class Emsg; +class Elf; +class Experiment; +class Function; +class Module; +template <typename Key_t, typename Value_t> class HashMap; +template <typename Key_t, typename Value_t> class Map; +template <class ITEM> class Vector; + +enum +{ + CMP_PATH = 1, + CMP_RUNTIMEPATH = 2, + CMP_CHKSUM = 4 +}; + +class LoadObject : public HistableFile, public DbeLock +{ +public: + + // The various segments types. + enum seg_type + { + SEG_TEXT, + SEG_DATA, + SEG_BSS, + SEG_HEAP, + SEG_STACK, + SEG_DEVICE, + SEG_UNKNOWN + }; + + // These codes are stored in *.archive files + enum Arch_status + { + ARCHIVE_SUCCESS, + ARCHIVE_EXIST, + ARCHIVE_BAD_STABS, + ARCHIVE_ERR_SEG, + ARCHIVE_ERR_OPEN, + ARCHIVE_ERR_MAP, + ARCHIVE_WARN_MTIME, + ARCHIVE_WARN_HOST, + ARCHIVE_ERR_VERSION, + ARCHIVE_NO_STABS, + ARCHIVE_WRONG_ARCH, + ARCHIVE_NO_LIBDWARF, + ARCHIVE_NO_DWARF, + ARCHIVE_WARN_CHECKSUM + }; + + LoadObject (const char *loname); + + static LoadObject *create_item (const char *nm, int64_t chksum); + static LoadObject *create_item (const char *nm, const char *_runTimePath, DbeFile *df); + + virtual ~LoadObject (); + virtual void set_name (char *string); + virtual uint64_t get_addr (); + virtual Vector<Histable*> *get_comparable_objs (); + + virtual Histable_type + get_type () + { + return LOADOBJECT; + }; + + virtual int64_t + get_size () + { + return size; + } + + char * + get_pathname () + { + return pathname; + } + + void + set_archname (char *aname) + { + free (arch_name); + arch_name = aname; + } + + bool + is_relocatable () + { + return isRelocatable; + } + + bool compare (const char *nm, int64_t _checksum); + int compare (const char *_path, const char *_runTimePath, DbeFile *df); + void set_platform (Platform_t pltf, int wsz); + void dump_functions (FILE *); + int get_index (Function *func); + char *get_alias (Function *func); + DbeInstr *find_dbeinstr (uint64_t file_off); + Function *find_function (uint64_t offset); + Function *find_function (char *fname); + Function *find_function (char *fname, unsigned int chksum); + Module *find_module (char *mname); + Module *get_comparable_Module (Module *mod); + void append_module (Module *mod); + Elf *get_elf (); + Stabs *openDebugInfo (char *fname, Stabs::Stab_status *stp = NULL); + Arch_status read_stabs (); + Arch_status sync_read_stabs (); + void post_process_functions (); + char *status_str (Arch_status rv, char *arg = NULL); + Function *get_hide_function (); + DbeInstr *get_hide_instr (DbeInstr *instr); + uint32_t get_checksum (); + + Emsg * + fetch_warnings (void) // fetch the queue of warning messages + { + return warnq->fetch (); + } + + Emsg * + fetch_comments (void) // fetch the queue of comment messages + { + return commentq->fetch (); + } + + unsigned int flags; // SEG_FLAG_* + bool isReadStabs; + bool need_swap_endian; + int seg_idx; // for compatibility (ADDRESS) + seg_type type; + int64_t size; // size of loadobject in bytes + int64_t max_size; // Maximum size of loadobject in bytes + int64_t min_size; // Min size of loadobject in bytes. + Vector<Function*> *functions; // Ordered list of functions + Vector<Module*> *seg_modules; // list of modules + HashMap<char*, Module*> *modules; + Module *noname; // Module pointer to unknown name + Platform_t platform; // Sparc, Sparcv9, Intel + WSize_t wsize; // word size: 32,64 + Stabs *objStabs; + HashMap<char*, Function*> *comp_funcs; // list of comparable functions + Experiment *firstExp; + char *runTimePath; + time_t mtime; // file timestamp (in seconds) + int64_t checksum; // file checksum + +private: + Elf *elf_lo; + bool elf_inited; + DbeInstr **instHTable; // hash table for DbeInstr + char *pathname; // User name of object file + ino64_t inode; // inode number of segment file + bool isRelocatable; // is relocatable .o + char *arch_name; // .archive name + Emsgqueue *warnq; + Emsgqueue *commentq; + Function **funcHTable; // hash table for functions + Function *h_function; // hide pseudo function + DbeInstr *h_instr; // hide pseudo instr + HashMap<char*, Module*> *seg_modules_map; // to find a comparable module + + static int func_compare (const void *p1, const void *p2); + int read_archive (); + void init_datatypes (); + void update_datatypes (Module*, Vaddr, uint32_t datatype_id); +}; + +#endif /* _LOADOBJECT_H */ diff --git a/gprofng/src/MachineModel.cc b/gprofng/src/MachineModel.cc new file mode 100644 index 0000000..15f493a --- /dev/null +++ b/gprofng/src/MachineModel.cc @@ -0,0 +1,317 @@ +/* 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 <string.h> +#include <unistd.h> +#include <dirent.h> + +#include "DbeSession.h" +#include "Command.h" +#include "Application.h" +#include "MemorySpace.h" +#include "i18n.h" + +#define MAXARGS 20 + +static const char *LIBNAME = "../lib/analyzer/lib/machinemodels"; + +char * +DbeSession::find_mach_model (char *name) +{ + // Read current working directory to see if it's there + if (name[0] == '/') + { + // Absolute path given + char *path = dbe_sprintf (NTXT ("%s.ermm"), name); + if (access (path, R_OK | F_OK) == 0) + return path; + free (path); + // Don't try anywhere else + return NULL; + } + + char *path = dbe_sprintf (NTXT ("./%s.ermm"), name); + if (access (path, R_OK | F_OK) == 0) + return path; + free (path); + + + // Read the user's home directory to see if it's there + char *home = getenv (NTXT ("HOME")); + if (home != NULL) + { + path = dbe_sprintf (NTXT ("%s/%s.ermm"), home, name); + if (access (path, R_OK | F_OK) == 0) + return path; + free (path); + } + if (strchr (name, (int) '/') != NULL) + // name has a slash; don't look in system installation directory + return NULL; + + // Read system installation directory to see if it's there + path = dbe_sprintf ("%s/%s/%s.ermm", theApplication->get_run_dir (), + LIBNAME, name); + if (access (path, R_OK | F_OK) == 0) + return path; + free (path); + return NULL; +} + +// Handle the definitions from a machinemodel file +// Return value is NULL if it was OK, or an error message if not +char * +DbeSession::load_mach_model (char *_name) +{ + CmdType cmd_type; + int arg_count, cparam; + char *cmd, *end_cmd; + char *arglist[MAXARGS]; + char *ret = NULL; + char *path = NULL; + FILE *fptr = NULL; + + // Does name have .ermm suffix? If so, strip it away + char *name = dbe_strdup (_name); + size_t len = strlen (name); + if (len > 5 && strcmp (name + len - 5, ".ermm") == 0) + name[len - 5] = 0; + + if ((mach_model_loaded != NULL) && (strcmp (name, mach_model_loaded) == 0)) + { + ret = dbe_sprintf (GTXT ("Machine model %s is already loaded\n"), name); + free (name); + return ret; + } + else if (mach_model_loaded == NULL && len == 0) + { + ret = dbe_sprintf (GTXT ("No Machine model is loaded\n")); + free (name); + return ret; + } + + if (len != 0) + { + // zero-length just means unload any previously loaded model; only look if non-zero + path = find_mach_model (name); + if (path == NULL) + { + ret = dbe_sprintf (GTXT ("Machine model %s not found\n"), name); + free (name); + return ret; + } + fptr = fopen (path, NTXT ("r")); + if (fptr == NULL) + { + ret = dbe_sprintf (GTXT ("Open of Machine model %s, file %s failed\n"), name, path); + free (path); + free (name); + return ret; + } + } + + // We are now committed to make the new machine model the loaded one; + // Delete any MemoryObjects from any previously loaded machinemodel + if (dbeSession->mach_model_loaded != NULL) + { + Vector <char *> *oldobjs = MemorySpace::getMachineModelMemObjs + (dbeSession->mach_model_loaded); + for (int i = 0; i < oldobjs->size (); i++) + MemorySpace::mobj_delete (oldobjs->fetch (i)); + delete oldobjs; + free (mach_model_loaded); + } + if (len == 0) + { + mach_model_loaded = NULL; + free (name); + // and there's no "loading" to do; just return + return NULL; + } + else + mach_model_loaded = name; + + int line_no = 0; + end_cmd = NULL; + + while (!feof (fptr)) + { + char *script = read_line (fptr); + if (script == NULL) + continue; + + line_no++; + strtok (script, NTXT ("\n")); + + // extract the command + cmd = strtok (script, NTXT (" \t")); + if (cmd == NULL || *cmd == '#' || *cmd == '\n') + { + free (script); + continue; + } + + char *remainder = strtok (NULL, NTXT ("\n")); + // now extract the arguments + int nargs = 0; + for (;;) + { + if (nargs >= MAXARGS) + { + ret = dbe_sprintf (GTXT ("Warning: more than %d arguments to %s command, line %d\n"), + MAXARGS, cmd, line_no); + continue; + } + + char *nextarg = strtok (remainder, NTXT ("\n")); + + if (nextarg == NULL || *nextarg == '#') + // either the end of the line, or a comment indicator + break; + arglist[nargs++] = parse_qstring (nextarg, &end_cmd); + remainder = end_cmd; + if (remainder == NULL) + break; + + // skip any blanks or tabs to get to next argument + while ((*remainder == ' ') || (*remainder == '\t')) + remainder++; + } + + cmd_type = Command::get_command (cmd, arg_count, cparam); + // check for extra arguments + if (cmd_type != UNKNOWN_CMD && cmd_type != INDXOBJDEF && nargs > arg_count) + ret = dbe_sprintf (GTXT ("Warning: extra arguments to %s command, line %d\n"), + cmd, line_no); + if (nargs < arg_count) + { + ret = dbe_sprintf (GTXT ("Error: missing arguments to %s command, line %d\n"), + cmd, line_no); + + // ignore this command + free (script); + continue; + } + + switch (cmd_type) + { + case INDXOBJDEF: + { + char *err = dbeSession->indxobj_define (arglist[0], NULL, + arglist[1], (nargs >= 3) ? PTXT (arglist[2]) : NULL, + (nargs >= 4) ? PTXT (arglist[3]) : NULL); + if (err != NULL) + ret = dbe_sprintf (GTXT (" %s: line %d `%s %s %s'\n"), + err, line_no, cmd, arglist[0], arglist[1]); + break; + } + case COMMENT: + // ignore the line + break; + default: + { + // unexpected command in a machinemodel file + ret = dbe_sprintf (GTXT ("Unexpected command in machinemodel file %s on line %d: `%.64s'\n"), + path, line_no, cmd); + break; + } + } + free (script); + } + fclose (fptr); + return ret; +} + +Vector<char*> * +DbeSession::list_mach_models () +{ + Vector<char*> *model_names = new Vector<char*>(); + + // Open the current directory to read the entries there + DIR *dir = opendir (NTXT (".")); + if (dir != NULL) + { + struct dirent *entry = NULL; + while ((entry = readdir (dir)) != NULL) + { + size_t len = strlen (entry->d_name); + if (len < 5 || strcmp (entry->d_name + len - 5, ".ermm") != 0) + continue; + char *model = dbe_strdup (entry->d_name); + + // remove the .ermm suffix + model[len - 5] = 0; + + // add to vector + model_names->append (dbe_strdup (model)); + } + closedir (dir); + } + + // Read the user's home directory to list the models there + char *home = getenv ("HOME"); + if (home != NULL) + { + dir = opendir (home); + if (dir != NULL) + { + struct dirent *entry = NULL; + while ((entry = readdir (dir)) != NULL) + { + size_t len = strlen (entry->d_name); + if (len < 5 || strcmp (entry->d_name + len - 5, ".ermm") != 0) + continue; + char *model = dbe_strdup (entry->d_name); + + // remove the .ermm suffix + model[len - 5] = 0; + + // add to vector + model_names->append (dbe_strdup (model)); + } + closedir (dir); + } + } + + // Read system installation directory to list the models there + char *sysdir = dbe_sprintf ("%s/%s", theApplication->get_run_dir (), LIBNAME); + + dir = opendir (sysdir); + if (dir != NULL) + { + struct dirent *entry = NULL; + while ((entry = readdir (dir)) != NULL) + { + size_t len = strlen (entry->d_name); + if (len < 5 || strcmp (entry->d_name + len - 5, ".ermm") != 0) + continue; + char *model = dbe_strdup (entry->d_name); + + // remove the .ermm suffix + model[len - 5] = 0; + + // add to vector + model_names->append (dbe_strdup (model)); + } + closedir (dir); + } + return model_names; +} diff --git a/gprofng/src/Makefile.am b/gprofng/src/Makefile.am new file mode 100644 index 0000000..b874b5b --- /dev/null +++ b/gprofng/src/Makefile.am @@ -0,0 +1,202 @@ +## Process this file with automake to generate Makefile.in +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file 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 of the License, 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +AUTOMAKE_OPTIONS = foreign +ACLOCAL_AMFLAGS = -I . -I .. -I ../.. + +CCSOURCES = \ + Application.cc \ + BaseMetric.cc \ + BaseMetricTreeNode.cc \ + CallStack.cc \ + CatchOutOfMemory.cc \ + ClassFile.cc \ + Command.cc \ + CompCom.cc \ + DataObject.cc \ + DataSpace.cc \ + Data_window.cc \ + DataStream.cc \ + DbeApplication.cc \ + DbeFile.cc \ + DbeJarFile.cc \ + DbeLock.cc \ + DbeSession.cc \ + DbeThread.cc \ + DbeView.cc \ + DerivedMetrics.cc \ + Disasm.cc \ + Dwarf.cc \ + DwarfLib.cc \ + Elf.cc \ + Emsg.cc \ + Experiment.cc \ + Exp_Layout.cc \ + ExpGroup.cc \ + Expression.cc \ + FileData.cc \ + Filter.cc \ + FilterSet.cc \ + Function.cc \ + HeapMap.cc \ + HeapData.cc \ + HeapActivity.cc \ + Hist_data.cc \ + IndexObject.cc \ + IOActivity.cc \ + LoadObject.cc \ + MachineModel.cc \ + MemObject.cc \ + MemorySpace.cc \ + Metric.cc \ + MetricList.cc \ + Module.cc \ + Ovw_data.cc \ + PRBTree.cc \ + PathTree.cc \ + PreviewExp.cc \ + Print.cc \ + SAXParserFactory.cc \ + Sample.cc \ + Settings.cc \ + SourceFile.cc \ + Stabs.cc \ + Stats_data.cc \ + StringBuilder.cc \ + Table.cc \ + QLParser.tab.cc \ + dbe_collctrl.cc \ + i18n.cc \ + parse.cc \ + UserLabel.cc \ + util.cc \ + Dbe.cc \ + $(NULL) + +CSOURCES = \ + dbe_hwcdrv.c \ + dbe_hwcfuncs.c \ + dbe_hwctable.c \ + dbe_memmgr.c \ + gethrtime.c \ + $(NULL) + +LIBGPROFNG = libgprofng.la + +AM_CPPFLAGS = $(GPROFNG_CPPFLAGS) -DLOCALEDIR=\"@localedir@\" -I.. -I$(srcdir) \ + -I$(srcdir)/../common \ + -I$(srcdir)/../../include -I$(srcdir)/../../opcodes \ + -I../../bfd -I$(srcdir)/../../bfd +AM_CFLAGS = $(GPROFNG_CFLAGS) $(PTHREAD_CFLAGS) -Wno-switch \ + -Wno-format-truncation +AM_CXXFLAGS = $(AM_CFLAGS) + +man_MANS = gprofng.1 \ + gp-archive.1 \ + gp-collect-app.1 \ + gp-display-src.1 \ + gp-display-text.1 + +MAINTAINERCLEANFILES = $(man_MANS) + +EXTRA_DIST = $(man_MANS) + + +lib_LTLIBRARIES = $(LIBGPROFNG) +libgprofng_la_SOURCES = $(CCSOURCES) $(CSOURCES) +libgprofng_la_LDFLAGS = -version-info 0:0:0 +libgprofng_la_LIBADD = $(top_builddir)/../opcodes/libopcodes.la \ + $(top_builddir)/../bfd/libbfd.la \ + $(GPROFNG_LIBADD) \ + $(PTHREAD_LIBS) -ldl + +dbedir = $(prefix)/etc +dbe_DATA = $(srcdir)/gprofng.rc + + +bin_PROGRAMS = gp-archive gp-collect-app gprofng gp-display-text gp-display-src + +gp_archive_SOURCES = gp-archive.cc ArchiveExp.cc +gp_archive_LDADD = $(LIBGPROFNG) + +gp_collect_app_SOURCES = gp-collect-app.cc checks.cc envsets.cc count.cc +gp_collect_app_LDADD = $(LIBGPROFNG) + +gprofng_SOURCES = gprofng.cc +gprofng_LDADD = $(LIBGPROFNG) + +gp_display_src_SOURCES = gp-display-src.cc +gp_display_src_LDADD = $(LIBGPROFNG) + +gp_display_text_SOURCES = gp-display-text.cc ipc.cc ipcio.cc +gp_display_text_LDADD = $(LIBGPROFNG) + + +if BUILD_MAN + +# The man pages depend on the version number and on a help2man include file. +common_mandeps = $(top_srcdir)/../bfd/version.m4 + +# Use -o so that the `missing' program can infer the output file. +# Embolden subcommand names in the output, and include a SEE ALSO. +# Arrange to regenerate the output if we have help2man, but leave the +# disted output there otherwise. +# Some extra annoying complexity is in place so that people without +# help2man dno't accidentally overwrite the manpage. + +INFO_PAGE = "gprofng" +MANUAL = "User Commands" +TEXT_GPROFNG = "the driver for the gprofng tool suite" +TEXT_GP_ARCHIVE = "archive gprofng experiment data" +TEXT_GP_COLLECT_APP = "collect performance data for the target application" +TEXT_GP_DISPLAY_SRC = "display the source code, optionally interleaved with the disassembly of the target object" +TEXT_GP_DISPLAY_TEXT = "display the performance data in plain text format" + +HELP2MAN_OPT = --libtool --no-info --info-page=$(INFO_PAGE) --manual=$(MANUAL) +H2M_FILTER = | sed 's/\.TP/\.TP\n.B/' | sed 's/Commands:/\.SH COMMANDS/' \ + | sed 's/See also:/\.SH SEE ALSO/' | sed 's/Documentation:/.SH DOCUMENTATION/' \ + | sed 's/Limitations:/.SH LIMITATIONS/' + +gprofng.1: $(srcdir)/gprofng.cc $(common_mandeps) | ./gprofng$(EXEEXT) + $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ + --name=$(TEXT_GPROFNG) ./gprofng$(EXEEXT) $(H2M_FILTER) > $@ + +gp-archive.1: $(srcdir)/gp-archive.cc $(common_mandeps) | ./gp-archive$(EXEEXT) + $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ + --name=$(TEXT_GP_ARCHIVE) ./gp-archive$(EXEEXT) $(H2M_FILTER) > $@ + +gp-collect-app.1: $(srcdir)/gp-collect-app.cc $(common_mandeps) | ./gp-collect-app$(EXEEXT) + $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ + --name=$(TEXT_GP_COLLECT_APP) ./gp-collect-app$(EXEEXT) $(H2M_FILTER) > $@ + +gp-display-src.1: $(srcdir)/gp-display-src.cc $(srcdir)/Command.cc \ + $(common_mandeps) | ./gp-display-src$(EXEEXT) + $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ + --name=$(TEXT_GP_DISPLAY_SRC) ./gp-display-src$(EXEEXT) $(H2M_FILTER) > $@ + +gp-display-text.1: $(srcdir)/gp-display-text.cc $(srcdir)/Command.cc \ + $(common_mandeps) | ./gp-display-text$(EXEEXT) + $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ + --name=$(TEXT_GP_DISPLAY_TEXT) ./gp-display-text$(EXEEXT) $(H2M_FILTER) > $@ + +endif + +# Distribution involves building the binaries to generate the manpage, +# so ensure that the necessary libraries are built at dist time. +dist-hook: $(LIBGPROFNG) + diff --git a/gprofng/src/Makefile.in b/gprofng/src/Makefile.in new file mode 100644 index 0000000..f21671d --- /dev/null +++ b/gprofng/src/Makefile.in @@ -0,0 +1,1171 @@ +# Makefile.in generated by automake 1.15.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2017 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file 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 of the License, 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = gp-archive$(EXEEXT) gp-collect-app$(EXEEXT) \ + gprofng$(EXEEXT) gp-display-text$(EXEEXT) \ + gp-display-src$(EXEEXT) +subdir = src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/../libtool.m4 \ + $(top_srcdir)/../ltoptions.m4 $(top_srcdir)/../ltsugar.m4 \ + $(top_srcdir)/../ltversion.m4 $(top_srcdir)/../lt~obsolete.m4 \ + $(top_srcdir)/acinclude.m4 $(top_srcdir)/../config/warnings.m4 \ + $(top_srcdir)/../config/enable.m4 \ + $(top_srcdir)/../config/ax_pthread.m4 \ + $(top_srcdir)/config/bison.m4 $(top_srcdir)/../bfd/version.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ + "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(dbedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libgprofng_la_DEPENDENCIES = $(top_builddir)/../opcodes/libopcodes.la \ + $(top_builddir)/../bfd/libbfd.la $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am__objects_1 = Application.lo BaseMetric.lo BaseMetricTreeNode.lo \ + CallStack.lo CatchOutOfMemory.lo ClassFile.lo Command.lo \ + CompCom.lo DataObject.lo DataSpace.lo Data_window.lo \ + DataStream.lo DbeApplication.lo DbeFile.lo DbeJarFile.lo \ + DbeLock.lo DbeSession.lo DbeThread.lo DbeView.lo \ + DerivedMetrics.lo Disasm.lo Dwarf.lo DwarfLib.lo Elf.lo \ + Emsg.lo Experiment.lo Exp_Layout.lo ExpGroup.lo Expression.lo \ + FileData.lo Filter.lo FilterSet.lo Function.lo HeapMap.lo \ + HeapData.lo HeapActivity.lo Hist_data.lo IndexObject.lo \ + IOActivity.lo LoadObject.lo MachineModel.lo MemObject.lo \ + MemorySpace.lo Metric.lo MetricList.lo Module.lo Ovw_data.lo \ + PRBTree.lo PathTree.lo PreviewExp.lo Print.lo \ + SAXParserFactory.lo Sample.lo Settings.lo SourceFile.lo \ + Stabs.lo Stats_data.lo StringBuilder.lo Table.lo \ + QLParser.tab.lo dbe_collctrl.lo i18n.lo parse.lo UserLabel.lo \ + util.lo Dbe.lo +am__objects_2 = dbe_hwcdrv.lo dbe_hwcfuncs.lo dbe_hwctable.lo \ + dbe_memmgr.lo gethrtime.lo +am_libgprofng_la_OBJECTS = $(am__objects_1) $(am__objects_2) +libgprofng_la_OBJECTS = $(am_libgprofng_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libgprofng_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(libgprofng_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +PROGRAMS = $(bin_PROGRAMS) +am_gp_archive_OBJECTS = gp-archive.$(OBJEXT) ArchiveExp.$(OBJEXT) +gp_archive_OBJECTS = $(am_gp_archive_OBJECTS) +gp_archive_DEPENDENCIES = $(LIBGPROFNG) +am_gp_collect_app_OBJECTS = gp-collect-app.$(OBJEXT) checks.$(OBJEXT) \ + envsets.$(OBJEXT) count.$(OBJEXT) +gp_collect_app_OBJECTS = $(am_gp_collect_app_OBJECTS) +gp_collect_app_DEPENDENCIES = $(LIBGPROFNG) +am_gp_display_src_OBJECTS = gp-display-src.$(OBJEXT) +gp_display_src_OBJECTS = $(am_gp_display_src_OBJECTS) +gp_display_src_DEPENDENCIES = $(LIBGPROFNG) +am_gp_display_text_OBJECTS = gp-display-text.$(OBJEXT) ipc.$(OBJEXT) \ + ipcio.$(OBJEXT) +gp_display_text_OBJECTS = $(am_gp_display_text_OBJECTS) +gp_display_text_DEPENDENCIES = $(LIBGPROFNG) +am_gprofng_OBJECTS = gprofng.$(OBJEXT) +gprofng_OBJECTS = $(am_gprofng_OBJECTS) +gprofng_DEPENDENCIES = $(LIBGPROFNG) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/../depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(libgprofng_la_SOURCES) $(gp_archive_SOURCES) \ + $(gp_collect_app_SOURCES) $(gp_display_src_SOURCES) \ + $(gp_display_text_SOURCES) $(gprofng_SOURCES) +DIST_SOURCES = $(libgprofng_la_SOURCES) $(gp_archive_SOURCES) \ + $(gp_collect_app_SOURCES) $(gp_display_src_SOURCES) \ + $(gp_display_text_SOURCES) $(gprofng_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +man1dir = $(mandir)/man1 +NROFF = nroff +MANS = $(man_MANS) +DATA = $(dbe_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/../depcomp \ + $(top_srcdir)/../mkinstalldirs +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_SUBDIRS = @BUILD_SUBDIRS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXPECT = @EXPECT@ +FGREP = @FGREP@ +GPROFNG_CFLAGS = @GPROFNG_CFLAGS@ +GPROFNG_CPPFLAGS = @GPROFNG_CPPFLAGS@ +GPROFNG_LIBADD = @GPROFNG_LIBADD@ +GPROFNG_LIBDIR = @GPROFNG_LIBDIR@ +GREP = @GREP@ +HELP2MAN = @HELP2MAN@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JAVA = @JAVA@ +JAVAC = @JAVAC@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LD_NO_AS_NEEDED = @LD_NO_AS_NEEDED@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WERROR = @WERROR@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +gprofng_cflags = @gprofng_cflags@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +jdk_inc = @jdk_inc@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = foreign +ACLOCAL_AMFLAGS = -I . -I .. -I ../.. +CCSOURCES = \ + Application.cc \ + BaseMetric.cc \ + BaseMetricTreeNode.cc \ + CallStack.cc \ + CatchOutOfMemory.cc \ + ClassFile.cc \ + Command.cc \ + CompCom.cc \ + DataObject.cc \ + DataSpace.cc \ + Data_window.cc \ + DataStream.cc \ + DbeApplication.cc \ + DbeFile.cc \ + DbeJarFile.cc \ + DbeLock.cc \ + DbeSession.cc \ + DbeThread.cc \ + DbeView.cc \ + DerivedMetrics.cc \ + Disasm.cc \ + Dwarf.cc \ + DwarfLib.cc \ + Elf.cc \ + Emsg.cc \ + Experiment.cc \ + Exp_Layout.cc \ + ExpGroup.cc \ + Expression.cc \ + FileData.cc \ + Filter.cc \ + FilterSet.cc \ + Function.cc \ + HeapMap.cc \ + HeapData.cc \ + HeapActivity.cc \ + Hist_data.cc \ + IndexObject.cc \ + IOActivity.cc \ + LoadObject.cc \ + MachineModel.cc \ + MemObject.cc \ + MemorySpace.cc \ + Metric.cc \ + MetricList.cc \ + Module.cc \ + Ovw_data.cc \ + PRBTree.cc \ + PathTree.cc \ + PreviewExp.cc \ + Print.cc \ + SAXParserFactory.cc \ + Sample.cc \ + Settings.cc \ + SourceFile.cc \ + Stabs.cc \ + Stats_data.cc \ + StringBuilder.cc \ + Table.cc \ + QLParser.tab.cc \ + dbe_collctrl.cc \ + i18n.cc \ + parse.cc \ + UserLabel.cc \ + util.cc \ + Dbe.cc \ + $(NULL) + +CSOURCES = \ + dbe_hwcdrv.c \ + dbe_hwcfuncs.c \ + dbe_hwctable.c \ + dbe_memmgr.c \ + gethrtime.c \ + $(NULL) + +LIBGPROFNG = libgprofng.la +AM_CPPFLAGS = $(GPROFNG_CPPFLAGS) -DLOCALEDIR=\"@localedir@\" -I.. -I$(srcdir) \ + -I$(srcdir)/../common \ + -I$(srcdir)/../../include -I$(srcdir)/../../opcodes \ + -I../../bfd -I$(srcdir)/../../bfd + +AM_CFLAGS = $(GPROFNG_CFLAGS) $(PTHREAD_CFLAGS) -Wno-switch \ + -Wno-format-truncation + +AM_CXXFLAGS = $(AM_CFLAGS) +man_MANS = gprofng.1 \ + gp-archive.1 \ + gp-collect-app.1 \ + gp-display-src.1 \ + gp-display-text.1 + +MAINTAINERCLEANFILES = $(man_MANS) +EXTRA_DIST = $(man_MANS) +lib_LTLIBRARIES = $(LIBGPROFNG) +libgprofng_la_SOURCES = $(CCSOURCES) $(CSOURCES) +libgprofng_la_LDFLAGS = -version-info 0:0:0 +libgprofng_la_LIBADD = $(top_builddir)/../opcodes/libopcodes.la \ + $(top_builddir)/../bfd/libbfd.la \ + $(GPROFNG_LIBADD) \ + $(PTHREAD_LIBS) -ldl + +dbedir = $(prefix)/etc +dbe_DATA = $(srcdir)/gprofng.rc +gp_archive_SOURCES = gp-archive.cc ArchiveExp.cc +gp_archive_LDADD = $(LIBGPROFNG) +gp_collect_app_SOURCES = gp-collect-app.cc checks.cc envsets.cc count.cc +gp_collect_app_LDADD = $(LIBGPROFNG) +gprofng_SOURCES = gprofng.cc +gprofng_LDADD = $(LIBGPROFNG) +gp_display_src_SOURCES = gp-display-src.cc +gp_display_src_LDADD = $(LIBGPROFNG) +gp_display_text_SOURCES = gp-display-text.cc ipc.cc ipcio.cc +gp_display_text_LDADD = $(LIBGPROFNG) + +# The man pages depend on the version number and on a help2man include file. +@BUILD_MAN_TRUE@common_mandeps = $(top_srcdir)/../bfd/version.m4 + +# Use -o so that the `missing' program can infer the output file. +# Embolden subcommand names in the output, and include a SEE ALSO. +# Arrange to regenerate the output if we have help2man, but leave the +# disted output there otherwise. +# Some extra annoying complexity is in place so that people without +# help2man dno't accidentally overwrite the manpage. +@BUILD_MAN_TRUE@INFO_PAGE = "gprofng" +@BUILD_MAN_TRUE@MANUAL = "User Commands" +@BUILD_MAN_TRUE@TEXT_GPROFNG = "the driver for the gprofng tool suite" +@BUILD_MAN_TRUE@TEXT_GP_ARCHIVE = "archive gprofng experiment data" +@BUILD_MAN_TRUE@TEXT_GP_COLLECT_APP = "collect performance data for the target application" +@BUILD_MAN_TRUE@TEXT_GP_DISPLAY_SRC = "display the source code, optionally interleaved with the disassembly of the target object" +@BUILD_MAN_TRUE@TEXT_GP_DISPLAY_TEXT = "display the performance data in plain text format" +@BUILD_MAN_TRUE@HELP2MAN_OPT = --libtool --no-info --info-page=$(INFO_PAGE) --manual=$(MANUAL) +@BUILD_MAN_TRUE@H2M_FILTER = | sed 's/\.TP/\.TP\n.B/' | sed 's/Commands:/\.SH COMMANDS/' \ +@BUILD_MAN_TRUE@ | sed 's/See also:/\.SH SEE ALSO/' | sed 's/Documentation:/.SH DOCUMENTATION/' \ +@BUILD_MAN_TRUE@ | sed 's/Limitations:/.SH LIMITATIONS/' + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .cc .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libgprofng.la: $(libgprofng_la_OBJECTS) $(libgprofng_la_DEPENDENCIES) $(EXTRA_libgprofng_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libgprofng_la_LINK) -rpath $(libdir) $(libgprofng_la_OBJECTS) $(libgprofng_la_LIBADD) $(LIBS) +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +gp-archive$(EXEEXT): $(gp_archive_OBJECTS) $(gp_archive_DEPENDENCIES) $(EXTRA_gp_archive_DEPENDENCIES) + @rm -f gp-archive$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(gp_archive_OBJECTS) $(gp_archive_LDADD) $(LIBS) + +gp-collect-app$(EXEEXT): $(gp_collect_app_OBJECTS) $(gp_collect_app_DEPENDENCIES) $(EXTRA_gp_collect_app_DEPENDENCIES) + @rm -f gp-collect-app$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(gp_collect_app_OBJECTS) $(gp_collect_app_LDADD) $(LIBS) + +gp-display-src$(EXEEXT): $(gp_display_src_OBJECTS) $(gp_display_src_DEPENDENCIES) $(EXTRA_gp_display_src_DEPENDENCIES) + @rm -f gp-display-src$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(gp_display_src_OBJECTS) $(gp_display_src_LDADD) $(LIBS) + +gp-display-text$(EXEEXT): $(gp_display_text_OBJECTS) $(gp_display_text_DEPENDENCIES) $(EXTRA_gp_display_text_DEPENDENCIES) + @rm -f gp-display-text$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(gp_display_text_OBJECTS) $(gp_display_text_LDADD) $(LIBS) + +gprofng$(EXEEXT): $(gprofng_OBJECTS) $(gprofng_DEPENDENCIES) $(EXTRA_gprofng_DEPENDENCIES) + @rm -f gprofng$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(gprofng_OBJECTS) $(gprofng_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Application.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ArchiveExp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BaseMetric.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BaseMetricTreeNode.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CallStack.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CatchOutOfMemory.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ClassFile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Command.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CompCom.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DataObject.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DataSpace.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DataStream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Data_window.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Dbe.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DbeApplication.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DbeFile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DbeJarFile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DbeLock.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DbeSession.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DbeThread.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DbeView.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DerivedMetrics.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Disasm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Dwarf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DwarfLib.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Elf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Emsg.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ExpGroup.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Exp_Layout.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Experiment.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Expression.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FileData.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Filter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FilterSet.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Function.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HeapActivity.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HeapData.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HeapMap.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Hist_data.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IOActivity.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IndexObject.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LoadObject.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MachineModel.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MemObject.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MemorySpace.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Metric.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetricList.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Module.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Ovw_data.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PRBTree.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PathTree.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PreviewExp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Print.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/QLParser.tab.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SAXParserFactory.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Sample.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Settings.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SourceFile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Stabs.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Stats_data.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/StringBuilder.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Table.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UserLabel.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/checks.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/count.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbe_collctrl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbe_hwcdrv.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbe_hwcfuncs.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbe_hwctable.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbe_memmgr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/envsets.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gethrtime.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gp-archive.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gp-collect-app.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gp-display-src.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gp-display-text.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gprofng.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i18n.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipcio.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-man1: $(man_MANS) + @$(NORMAL_INSTALL) + @list1=''; \ + list2='$(man_MANS)'; \ + test -n "$(man1dir)" \ + && test -n "`echo $$list1$$list2`" \ + || exit 0; \ + echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ + { for i in $$list1; do echo "$$i"; done; \ + if test -n "$$list2"; then \ + for i in $$list2; do echo "$$i"; done \ + | sed -n '/\.1[a-z]*$$/p'; \ + fi; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ + done; } + +uninstall-man1: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man1dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.1[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) +install-dbeDATA: $(dbe_DATA) + @$(NORMAL_INSTALL) + @list='$(dbe_DATA)'; test -n "$(dbedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(dbedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(dbedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dbedir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(dbedir)" || exit $$?; \ + done + +uninstall-dbeDATA: + @$(NORMAL_UNINSTALL) + @list='$(dbe_DATA)'; test -n "$(dbedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(dbedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(MANS) $(DATA) +install-binPROGRAMS: install-libLTLIBRARIES + +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(dbedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-dbeDATA install-man + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man1 + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-dbeDATA \ + uninstall-libLTLIBRARIES uninstall-man + +uninstall-man: uninstall-man1 + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ + clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool cscopelist-am ctags ctags-am dist-hook distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-binPROGRAMS install-data \ + install-data-am install-dbeDATA install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-libLTLIBRARIES \ + install-man install-man1 install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-binPROGRAMS uninstall-dbeDATA \ + uninstall-libLTLIBRARIES uninstall-man uninstall-man1 + +.PRECIOUS: Makefile + + +@BUILD_MAN_TRUE@gprofng.1: $(srcdir)/gprofng.cc $(common_mandeps) | ./gprofng$(EXEEXT) +@BUILD_MAN_TRUE@ $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ +@BUILD_MAN_TRUE@ --name=$(TEXT_GPROFNG) ./gprofng$(EXEEXT) $(H2M_FILTER) > $@ + +@BUILD_MAN_TRUE@gp-archive.1: $(srcdir)/gp-archive.cc $(common_mandeps) | ./gp-archive$(EXEEXT) +@BUILD_MAN_TRUE@ $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ +@BUILD_MAN_TRUE@ --name=$(TEXT_GP_ARCHIVE) ./gp-archive$(EXEEXT) $(H2M_FILTER) > $@ + +@BUILD_MAN_TRUE@gp-collect-app.1: $(srcdir)/gp-collect-app.cc $(common_mandeps) | ./gp-collect-app$(EXEEXT) +@BUILD_MAN_TRUE@ $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ +@BUILD_MAN_TRUE@ --name=$(TEXT_GP_COLLECT_APP) ./gp-collect-app$(EXEEXT) $(H2M_FILTER) > $@ + +@BUILD_MAN_TRUE@gp-display-src.1: $(srcdir)/gp-display-src.cc $(srcdir)/Command.cc \ +@BUILD_MAN_TRUE@ $(common_mandeps) | ./gp-display-src$(EXEEXT) +@BUILD_MAN_TRUE@ $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ +@BUILD_MAN_TRUE@ --name=$(TEXT_GP_DISPLAY_SRC) ./gp-display-src$(EXEEXT) $(H2M_FILTER) > $@ + +@BUILD_MAN_TRUE@gp-display-text.1: $(srcdir)/gp-display-text.cc $(srcdir)/Command.cc \ +@BUILD_MAN_TRUE@ $(common_mandeps) | ./gp-display-text$(EXEEXT) +@BUILD_MAN_TRUE@ $(AM_V_GEN)_BUILDING_MANPAGE=1 $(HELP2MAN) $(HELP2MAN_OPT) \ +@BUILD_MAN_TRUE@ --name=$(TEXT_GP_DISPLAY_TEXT) ./gp-display-text$(EXEEXT) $(H2M_FILTER) > $@ + +# Distribution involves building the binaries to generate the manpage, +# so ensure that the necessary libraries are built at dist time. +dist-hook: $(LIBGPROFNG) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gprofng/src/Map.h b/gprofng/src/Map.h new file mode 100644 index 0000000..530dcfc --- /dev/null +++ b/gprofng/src/Map.h @@ -0,0 +1,61 @@ +/* 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. */ + +#ifndef _DBE_MAP_H +#define _DBE_MAP_H + +#include "vec.h" + +template <typename Key_t, typename Value_t> +class Map +{ +public: + + enum Relation + { + REL_LT, + REL_LE, + REL_EQ, + REL_GE, + REL_GT + }; + + virtual ~Map () { }; + virtual void put (Key_t key, Value_t val) = 0; + virtual Value_t get (Key_t key) = 0; + virtual Value_t get (Key_t key, Relation rel) = 0; + virtual Value_t remove (Key_t key) = 0; + + virtual Vector<Key_t> * + keySet () + { + return NULL; + } + + virtual Vector<Value_t> * + values () + { + return NULL; + } +}; + +#define destroy_map(t, p) if (p) { Vector<t> *v = p->values (); Destroy (v); delete p; } + +#endif diff --git a/gprofng/src/Map2D.h b/gprofng/src/Map2D.h new file mode 100644 index 0000000..41b2be2 --- /dev/null +++ b/gprofng/src/Map2D.h @@ -0,0 +1,53 @@ +/* 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. */ + +#ifndef _DBE_MAP2D_H +#define _DBE_MAP2D_H + +template <typename Key1_t, typename Key2_t, typename Value_t> +class Map2D +{ +public: + + enum MapType + { + Default, + Interval + }; + + // Relation for the first key is always EQUAL + enum Relation + { + REL_EQLT, + REL_EQLE, + REL_EQEQ, + REL_EQGE, + REL_EQGT + }; + + virtual ~Map2D () { }; + virtual void put (Key1_t key1, Key2_t key2, Value_t val) = 0; + virtual Value_t get (Key1_t key1, Key2_t key2) = 0; + virtual Value_t get (Key1_t key1, Key2_t key2, Relation rel) = 0; + virtual Value_t remove (Key1_t key1, Key2_t key2) = 0; + +}; + +#endif diff --git a/gprofng/src/MemObject.cc b/gprofng/src/MemObject.cc new file mode 100644 index 0000000..d207a99 --- /dev/null +++ b/gprofng/src/MemObject.cc @@ -0,0 +1,44 @@ +/* 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 "Exp_Layout.h" +#include "MemObject.h" +#include "DataSpace.h" +#include "ABS.h" +#include "Dbe.h" +#include "i18n.h" + +MemObj::MemObj (uint64_t _index, char *_name) +{ + id = _index; + name = _name; +} + +MemObj::~MemObj () +{ + free (name); +} + +Histable * +MemObj::convertto (Histable_type type, Histable*) +{ + return type == MEMOBJ ? this : NULL; +} diff --git a/gprofng/src/MemObject.h b/gprofng/src/MemObject.h new file mode 100644 index 0000000..6bc459f --- /dev/null +++ b/gprofng/src/MemObject.h @@ -0,0 +1,62 @@ +/* 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. */ + +#ifndef _MEMOBJECT_H +#define _MEMOBJECT_H + +#include "Histable.h" +#include "util.h" + +class MemObj : public Histable +{ +public: + friend class MemorySpace; + + MemObj (uint64_t _index, char *_name); + ~MemObj (); + + virtual Histable *convertto (Histable_type, Histable* = NULL); + + virtual Histable_type + get_type () + { + return MEMOBJ; + } + + virtual char * + get_name (NameFormat = NA) + { + return dbe_strdup (name); + } + + virtual uint64_t + get_addr () + { + return id; + } + + uint64_t + get_index () + { + return id; + } +}; + +#endif /* _MEMOBJECT_H */ diff --git a/gprofng/src/MemorySpace.cc b/gprofng/src/MemorySpace.cc new file mode 100644 index 0000000..3f7c78d --- /dev/null +++ b/gprofng/src/MemorySpace.cc @@ -0,0 +1,452 @@ +/* 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 "util.h" +#include "DbeSession.h" +#include "Application.h" +#include "Experiment.h" +#include "Exp_Layout.h" +#include "MetricList.h" +#include "MemObject.h" +#include "PathTree.h" +#include "DbeView.h" +#include "Metric.h" +#include "MemorySpace.h" +#include "Table.h" +#include "IndexObject.h" + +MemObjType_t::MemObjType_t () +{ + type = -1; + name = NULL; + index_expr = NULL; + machmodel = NULL; + mnemonic = 0; + short_description = NULL; + long_description = NULL; +} + +MemObjType_t::~MemObjType_t () +{ + free (name); + free (index_expr); + free (machmodel); + free (short_description); + free (long_description); +} + +MemorySpace::MemorySpace (DbeView *_dbev, int _mstype) +{ + char *mname; + dbev = _dbev; + phaseIdx = -1; + + // set up the MemoryObject information + objs = new HashMap<uint64_t, MemObj*>; + mstype = _mstype; + msindex_exp = NULL; + msname = NULL; + msindex_exp_str = NULL; + + // find the memory space in the table + MemObjType_t *mot = findMemSpaceByIndex (mstype); + if (mot) + { + msname = dbe_strdup (mot->name); + if (mot->index_expr != NULL) + { + msindex_exp_str = dbe_strdup (mot->index_expr); + msindex_exp = dbeSession->ql_parse (msindex_exp_str); + if (msindex_exp == NULL) + // this was checked when the definition was created + abort (); + } + } + + // create the Total and Unknown objects + mname = dbe_strdup (NTXT ("<Total>")); + total_memobj = createMemObject ((uint64_t) - 2, mname); + mname = dbe_strdup (GTXT ("<Unknown>")); + unk_memobj = createMemObject ((uint64_t) - 1, mname); + hist_data_all = NULL; + selected_mo_index = (uint64_t) - 3; + sel_ind = -1; +} + +MemorySpace::~MemorySpace () +{ + reset (); + delete objs; + free (msname); + free (msindex_exp); + free (msindex_exp_str); +} + +void +MemorySpace::reset () +{ + if (hist_data_all != NULL) + { + delete hist_data_all; + hist_data_all = NULL; + } + // do not clear the selected object's index + // selected_mo_index = (uint64_t)-3; + + // destroy any existing objects, but keep the vector + // Now that we have a hashmap, which has its own vector, + // safe to delete and reallocate + delete objs; + objs = new HashMap<uint64_t, MemObj*>; +} + +// find a memory object by its memory-object index +int +MemorySpace::findMemObject (uint64_t indx) +{ + int index; + Hist_data::HistItem *hi; + if (indx == (uint64_t) - 3) + return -1; + + Vec_loop (Hist_data::HistItem *, hist_data_all->hist_items, index, hi) + { + if (((uint64_t) ((MemObj *) hi->obj)->id) == indx) + return index; + } + // object does not exist; filter change eliminated it, for example + return -1; +} + +// find the object referenced in the packet +MemObj * +MemorySpace::lookupMemObject (Experiment *exp, DataView *packets, long i) +{ + uint64_t idx; + uint64_t va = (uint64_t) packets->getLongValue (PROP_VADDR, i); + if (va == ABS_UNSUPPORTED) + // return NULL, to ignore the record + return NULL; + if (va < ABS_CODE_RANGE) + // The va is not valid, rather, it's an error code + // return the <Unknown> object + return unk_memobj; + + Expression::Context ctx (dbev, exp, packets, i); + idx = msindex_exp->eval (&ctx); + if (idx == (uint64_t) - 1) + return unk_memobj; + + // do a binary search for the memory object + MemObj *res = objs->get (idx); + if (res == NULL) + { + res = createMemObject (idx, NULL); + objs->put (idx, res); + } + else + return res; + + // recompute range + if (idx < idx_min) + idx_min = idx; + if (idx > idx_max) + idx_max = idx; + return res; +} + +MemObj * +MemorySpace::createMemObject (uint64_t index, char *moname) +{ + MemObj *res; + char *name; + if (moname != NULL) + { + res = new MemObj (index, moname); + return res; + } + + // Custom memory objects + // The memory_page_size is defined in the machine model file such + // as ./machinemodels/t4.ermm. + // Most users prefer to look at the hexadecimal version of virtual + // addresses. Display only the hexadecimal version of virtual addresses + // for all machine model views with an exception of virtual page size. + if (dbe_strcmp (msname, NTXT ("Memory_page_size")) == 0) + name = dbe_sprintf (NTXT ("%s 0x%16.16llx (%llu)"), msname, + (long long) index, (unsigned long long) index); + else if (dbe_strcmp (msname, NTXT ("Memory_in_home_lgrp")) == 0) + name = dbe_sprintf (NTXT ("%s: %s"), msname, + index == 1 ? GTXT ("True") : index == 0 ? GTXT ("False") + : GTXT ("<Unknown>")); + else if (dbe_strcmp (msname, NTXT ("Memory_lgrp")) == 0) + name = dbe_sprintf (NTXT ("%s %llu"), msname, (unsigned long long) index); + else + name = dbe_sprintf (NTXT ("%s 0x%16.16llx"), msname, (long long) index); + + res = new MemObj (index, name); + return res; +} + + +static Vector<MemObjType_t*> dyn_memobj_vec; +static Vector<MemObjType_t*> *dyn_memobj = &dyn_memobj_vec; +static Vector<int> *ordlist; + +// Static function to get a vector of custom memory object definitions + +Vector<void*> * +MemorySpace::getMemObjects () +{ + MemObjType_t *mot; + int ii; + int size = dyn_memobj->size (); + Vector<int> *indx = new Vector<int>(size); + Vector<char*> *name = new Vector<char*>(size); + Vector<char> *mnemonic = new Vector<char>(size); + Vector<char*> *formula = new Vector<char*>(size); + Vector<char*> *machmodel = new Vector<char*>(size); + Vector<int> *order = new Vector<int>(size); + Vector<char*> *sdesc = new Vector<char*>(size); + Vector<char*> *ldesc = new Vector<char*>(size); + + if (size > 0) + { + Vec_loop (MemObjType_t *, dyn_memobj, ii, mot) + { + indx->store (ii, mot->type); + order->store (ii, ii); + name->store (ii, dbe_strdup (mot->name)); + formula->store (ii, dbe_strdup (mot->index_expr)); + mnemonic->store (ii, mot->mnemonic); + sdesc->store (ii, mot->short_description == NULL ? NULL + : dbe_strdup (mot->short_description)); + ldesc->store (ii, mot->long_description == NULL ? NULL + : dbe_strdup (mot->long_description)); + if (mot->machmodel == NULL) + machmodel->store (ii, NULL); + else + machmodel->store (ii, dbe_strdup (mot->machmodel)); + } + } + Vector<void*> *res = new Vector<void*>(8); + res->store (0, indx); + res->store (1, name); + res->store (2, mnemonic); + res->store (3, formula); + res->store (4, machmodel); + res->store (5, order); + res->store (6, sdesc); + res->store (7, ldesc); + return (res); +} + +// Static function to set order of memory object tabs +void +MemorySpace::set_MemTabOrder (Vector<int> *orders) +{ + int size = orders->size (); + ordlist = new Vector<int>(size); + for (int i = 0; i < size; i++) + ordlist->store (i, orders->fetch (i)); +} + +// Static function to define a new memory object type +char * +MemorySpace::mobj_define (char *mname, char *mindex_exp, char *_machmodel, + char *short_description, char *long_description) +{ + MemObjType_t *mot; + + if (mname == NULL) + return dbe_strdup (GTXT ("No memory object name has been specified.")); + if (isalpha ((int) (mname[0])) == 0) + return dbe_sprintf (GTXT ("Memory Object type name %s does not begin with an alphabetic character"), + mname); + char *p = mname; + while (*p != 0) + { + if (isalnum ((int) (*p)) == 0 && *p != '_') + return dbe_sprintf (GTXT ("Memory Object type name %s contains a non-alphanumeric character"), + mname); + p++; + } + + mot = findMemSpaceByName (mname); + if (mot != NULL) + { + if (strcmp (mot->index_expr, mindex_exp) == 0) + // It's a redefinition, but the new definition is the same + return NULL; + return dbe_sprintf (GTXT ("Memory/Index Object type name %s is already defined"), + mname); + } + + // make sure the name is not in use + if (dbeSession->findIndexSpaceByName (mname) >= 0) + return dbe_sprintf (GTXT ("Memory/Index Object type name %s is already defined"), + mname); + + if (mindex_exp == NULL || *mindex_exp == 0) + return dbe_strdup (GTXT ("No index-expr has been specified.")); + + // verify that the index expression parses correctly + Expression *e = dbeSession->ql_parse (mindex_exp); + if (e == NULL) + return dbe_sprintf (GTXT ("Memory Object index expression is invalid: %s"), + mindex_exp); + delete e; + + // It's OK, create the new table entry + char *s = dbeSession->indxobj_define (mname, NULL, mindex_exp, + short_description, long_description); + if (s) + return s; + IndexObjType_t *indObj = dbeSession->findIndexSpace (mname); + + mot = new MemObjType_t; + mot->type = indObj->type; + indObj->memObj = mot; + mot->name = dbe_strdup (mname); + mot->index_expr = dbe_strdup (mindex_exp); + mot->mnemonic = MemorySpace::pickMnemonic (mname); + mot->machmodel = dbe_strdup (_machmodel); + mot->short_description = dbe_strdup (short_description); + mot->long_description = dbe_strdup (long_description); + + // add it to the list + dyn_memobj->append (mot); + + // tell the session + if (dbeSession != NULL) + dbeSession->mobj_define (mot); + return NULL; +} + +// Static function to delete a new memory object type + +char * +MemorySpace::mobj_delete (char *mname) +{ + if (mname == NULL) + return dbe_strdup (GTXT ("No memory object name has been specified.\n")); + + // search the dynamic types + for (long idx = 0, sz = VecSize (dyn_memobj); idx < sz; idx++) + { + MemObjType_t *mt = dyn_memobj->get (idx); + if (strcasecmp (mt->name, mname) == 0) + { + // delete it from the vector + mt = dyn_memobj->remove (idx); + delete mt; + dbeSession->removeIndexSpaceByName (mname); + return NULL; + } + } + return dbe_sprintf (GTXT ("Memory object `%s' is not defined.\n"), mname); +} + +// Static function to get a list of memory object names from a machine model + +Vector <char*> * +MemorySpace::getMachineModelMemObjs (char *mname) +{ + Vector <char *> *ret = new Vector <char *> (); + if (mname == NULL) + return ret; + + // search the memory objects + int idx; + MemObjType_t *mt; + Vec_loop (MemObjType_t*, dyn_memobj, idx, mt) + { + if (mt->machmodel != NULL && strcmp (mt->machmodel, mname) == 0) + { + char *n = dbe_strdup (mt->name); + ret->append (n); + } + } + return ret; +} + +char +MemorySpace::pickMnemonic (char *name) +{ + return name[0]; +} + +void +MemorySpace::get_filter_keywords (Vector <void*> *res) +{ + Vector <char*> *kwCategory = (Vector<char*>*) res->fetch (0); + Vector <char*> *kwCategoryI18N = (Vector<char*>*) res->fetch (1); + Vector <char*> *kwDataType = (Vector<char*>*) res->fetch (2); + Vector <char*> *kwKeyword = (Vector<char*>*) res->fetch (3); + Vector <char*> *kwFormula = (Vector<char*>*) res->fetch (4); + Vector <char*> *kwDescription = (Vector<char*>*) res->fetch (5); + Vector <void*> *kwEnumDescs = (Vector<void*>*) res->fetch (6); + + char *vtypeNames[] = VTYPE_TYPE_NAMES; + for (int i = 0, sz = dyn_memobj ? dyn_memobj->size () : 0; i < sz; i++) + { + MemObjType_t *obj = dyn_memobj->fetch (i); + kwCategory->append (dbe_strdup (NTXT ("FK_MEMOBJ"))); + kwCategoryI18N->append (dbe_strdup (GTXT ("Memory Object Definitions"))); + kwDataType->append (dbe_strdup (vtypeNames[TYPE_INT64])); + kwKeyword->append (dbe_strdup (obj->name)); + kwFormula->append (dbe_strdup (obj->index_expr)); + kwDescription->append (NULL); + kwEnumDescs->append (NULL); + } +} + +MemObjType_t * +MemorySpace::findMemSpaceByName (const char *mname) +{ + int idx; + MemObjType_t *mt; + + // search the dynamic types + Vec_loop (MemObjType_t*, dyn_memobj, idx, mt) + { + if (strcasecmp (mt->name, mname) == 0) + return mt; + } + return NULL; +} + +MemObjType_t * +MemorySpace::findMemSpaceByIndex (int index) +{ + int idx; + MemObjType_t *mt; + + // search the dynamic types + Vec_loop (MemObjType_t*, dyn_memobj, idx, mt) + { + if (mt->type == index) + return mt; + } + return NULL; +} diff --git a/gprofng/src/MemorySpace.h b/gprofng/src/MemorySpace.h new file mode 100644 index 0000000..7d02e5e --- /dev/null +++ b/gprofng/src/MemorySpace.h @@ -0,0 +1,113 @@ +/* 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. */ + +#ifndef _MEMORYSPACE_H +#define _MEMORYSPACE_H + +#include <stdio.h> + +#include "dbe_structs.h" +#include "vec.h" +#include "Exp_Layout.h" +#include "Histable.h" +#include "Hist_data.h" +#include "Metric.h" +#include "HashMap.h" + +class Experiment; +class Expression; +class DataView; +class DbeView; +class MemObj; + +class MemObjType_t +{ +public: + MemObjType_t (); + ~MemObjType_t (); + int type; + char *name; + char *index_expr; + char *machmodel; + char mnemonic; + char *short_description; + char *long_description; +}; + +class MemorySpace +{ +public: + + MemorySpace (DbeView *_dbev, int subtype); + ~MemorySpace (); + + void reset (void); + + int + getMemObjType (void) + { + return mstype; + } + + char * + getMemObjTypeName (void) + { + return msname; + } + + Expression * + getMemObjDef (void) + { + return msindex_exp; + } + + // static members, used to define or fetch the various MemorySpaces + static void get_filter_keywords (Vector <void*> *res); + static Vector<void*> *getMemObjects (void); + static void set_MemTabOrder (Vector<int> *); + static char *mobj_define (char *, char *, char *, char *, char *); + static char *mobj_delete (char *); + static MemObjType_t *findMemSpaceByName (const char *mname); + static MemObjType_t *findMemSpaceByIndex (int index); + static char pickMnemonic (char *name); + static Vector<char *> *getMachineModelMemObjs (char *); + +private: + HashMap<uint64_t, MemObj*> *objs; + int findMemObject (uint64_t indx); + MemObj *lookupMemObject (Experiment *exp, DataView*, long); + MemObj *createMemObject (uint64_t, char *moname); + + int mstype; // type of this memory space + char *msname; // name of this memory space + Expression *msindex_exp; // index-expression for this memory space + char *msindex_exp_str; // string for index-expression + Hist_data *hist_data_all; // the cached data for mode=Hist_Data::ALL + uint64_t selected_mo_index; // which page, cacheline, etc. + int sel_ind; // index of selected object in list + DbeView *dbev; + int phaseIdx; + uint64_t idx_min; + uint64_t idx_max; + MemObj *unk_memobj; + MemObj *total_memobj; +}; + +#endif /* _MEMORYSPACE_H */ diff --git a/gprofng/src/Metric.cc b/gprofng/src/Metric.cc new file mode 100644 index 0000000..3b026ff --- /dev/null +++ b/gprofng/src/Metric.cc @@ -0,0 +1,1660 @@ +/* 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 <stdio.h> +#include <strings.h> +#include <limits.h> +#include <sys/param.h> + +#include "util.h" +#include "DbeSession.h" +#include "Experiment.h" +#include "Expression.h" +#include "Metric.h" + +Metric::Metric (BaseMetric *item, SubType st) : BaseMetric (*item) +{ + name = NULL; + abbr = NULL; + abbr_unit = NULL; + baseMetric = item; + set_subtype (st); + visbits = VAL_NA; + if (item->get_type () == DERIVED) + visbits = VAL_VALUE; +} + +Metric::Metric (const Metric& item) : BaseMetric (item) +{ + baseMetric = item.baseMetric; + subtype = item.subtype; + name = dbe_strdup (item.name); + abbr = dbe_strdup (item.abbr); + abbr_unit = dbe_strdup (item.abbr_unit); + visbits = item.visbits; +} + +Metric::~Metric () +{ + free (name); + free (abbr); + free (abbr_unit); +} + +// Note that BaseMetric::get_vtype() has the base value type. +// Here, we get the value type for the displayed metric, +// which may be different if comparison is used. + +ValueTag +Metric::get_vtype2 () +{ + ValueTag vtype = get_vtype (); + if (visbits & VAL_DELTA) + { + switch (vtype) + { + case VT_ULLONG: return VT_LLONG; + default: return vtype; + } + } + if (visbits & VAL_RATIO) + { + switch (vtype) + { + case VT_INT: + case VT_LLONG: + case VT_ULLONG: + case VT_FLOAT: + case VT_DOUBLE: return VT_DOUBLE; + default: return vtype; + } + } + return vtype; +} + +void +Metric::set_subtype (SubType st) +{ + subtype = st; + free (name); + free (abbr); + free (abbr_unit); + name = NULL; + abbr = NULL; + abbr_unit = NULL; + + switch (get_type ()) + { + case CP_LMS_USER: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive User CPU Time")); + abbr = dbe_strdup (GTXT ("Excl. User CPU")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive User CPU Time")); + abbr = dbe_strdup (GTXT ("Incl. User CPU")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed User CPU Time")); + abbr = dbe_strdup (GTXT ("Attr. User CPU")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_USER metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + abort (); + } + break; + + case CP_LMS_WAIT_CPU: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Wait CPU Time")); + abbr = dbe_strdup (GTXT ("Excl. Wait CPU")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Wait CPU Time")); + abbr = dbe_strdup (GTXT ("Incl. Wait CPU")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Wait CPU Time")); + abbr = dbe_strdup (GTXT ("Attr. Wait CPU")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_WAIT_CPU metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_LMS_USER_LOCK: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive User Lock Time")); + abbr = dbe_strdup (GTXT ("Excl. User Lock")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive User Lock Time")); + abbr = dbe_strdup (GTXT ("Incl. User Lock")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed User Lock Time")); + abbr = dbe_strdup (GTXT ("Attr. User Lock")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_USER_LOCK metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_LMS_SYSTEM: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive System CPU Time")); + abbr = dbe_strdup (GTXT ("Excl. Sys. CPU")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive System CPU Time")); + abbr = dbe_strdup (GTXT ("Incl. Sys. CPU")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed System CPU Time")); + abbr = dbe_strdup (GTXT ("Attr. Sys. CPU")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_SYSTEM metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case SYNC_WAIT_TIME: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Sync Wait Time")); + abbr = dbe_strdup (GTXT ("Excl. Sync Wait")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Sync Wait Time")); + abbr = dbe_strdup (GTXT ("Incl. Sync Wait")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Sync Wait Time")); + abbr = dbe_strdup (GTXT ("Attr. Sync Wait")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected LWT metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_LMS_TFAULT: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Text Page Fault Time")); + abbr = dbe_strdup (GTXT ("Excl. Text Fault")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Text Page Fault Time")); + abbr = dbe_strdup (GTXT ("Incl. Text Fault")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Text Page Fault Time")); + abbr = dbe_strdup (GTXT ("Attr. Text Fault")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_TFAULT metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_LMS_DFAULT: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Data Page Fault Time")); + abbr = dbe_strdup (GTXT ("Excl. Data Fault")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Data Page Fault Time")); + abbr = dbe_strdup (GTXT ("Incl. Data Fault")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Data Page Fault Time")); + abbr = dbe_strdup (GTXT ("Attr. Data Fault")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_DFAULT metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_KERNEL_CPU: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Kernel CPU Time")); + abbr = dbe_strdup (GTXT ("Excl. Kernel CPU")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Kernel CPU Time")); + abbr = dbe_strdup (GTXT ("Incl. Kernel CPU")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Kernel CPU Time")); + abbr = dbe_strdup (GTXT ("Attr. Kernel CPU")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_KERNEL_CPU metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case HWCNTR: + { + char *sstr, *estr1, *estr2; + if (get_hw_ctr () == NULL) + abort (); + sstr = get_username (); + if (st == EXCLUSIVE) + { + estr1 = GTXT ("Exclusive "); + estr2 = GTXT ("Excl. "); + } + else if (st == INCLUSIVE) + { + estr1 = GTXT ("Inclusive "); + estr2 = GTXT ("Incl. "); + } + else if (st == ATTRIBUTED) + { + estr1 = GTXT ("Attributed "); + estr2 = GTXT ("Attr. "); + } + else if (st == DATASPACE) + { + estr1 = GTXT ("Data-derived "); + estr2 = GTXT ("Data. "); + } + else + { + estr1 = dbe_sprintf (GTXT ("Unexpected hwc %s metric subtype %d"), + get_aux (), st); + estr2 = dbe_strdup (NTXT ("??")); + } + name = dbe_sprintf (NTXT ("%s%s"), estr1, sstr); + abbr = dbe_sprintf (NTXT ("%s%s"), estr2, sstr); + break; + } + + case DERIVED: + { + switch (st) + { + case EXCLUSIVE: + name = dbe_sprintf (GTXT ("Exclusive %s"), get_username ()); + abbr = dbe_sprintf (GTXT ("Excl. %s"), get_cmd ()); + break; + case INCLUSIVE: + name = dbe_sprintf (GTXT ("Inclusive %s"), get_username ()); + abbr = dbe_sprintf (GTXT ("Incl. %s"), get_cmd ()); + break; + case ATTRIBUTED: + name = dbe_sprintf (GTXT ("Attributed %s"), get_username ()); + abbr = dbe_sprintf (GTXT ("Attr. %s"), get_cmd ()); + break; + default: + name = dbe_sprintf (GTXT ("Unexpected derived %s metric subtype %d"), + get_username (), st); + abbr = dbe_strdup (NTXT ("??")); + break; + } + break; + } + + case OMP_MASTER_THREAD: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Master Thread Time")); + abbr = dbe_strdup (GTXT ("Excl. Master Thread")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Master Thread Time")); + abbr = dbe_strdup (GTXT ("Incl. Master Thread")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Master Thread Time")); + abbr = dbe_strdup (GTXT ("Attr. Master Thread")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected Master Thread metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_TOTAL: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Total Thread Time")); + abbr = dbe_strdup (GTXT ("Excl. Total Thread")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Total Thread Time")); + abbr = dbe_strdup (GTXT ("Incl. Total Thread")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Total Thread Time")); + abbr = dbe_strdup (GTXT ("Attr. Total Thread")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected TOTAL metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case SYNC_WAIT_COUNT: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Sync Wait Count")); + abbr = dbe_strdup (GTXT ("Excl. Sync Wait Count")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Sync Wait Count")); + abbr = dbe_strdup (GTXT ("Incl. Sync Wait Count")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Sync Wait Count")); + abbr = dbe_strdup (GTXT ("Attr. Sync Wait Count")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected LWCNT metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_TOTAL_CPU: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Total CPU Time")); + abbr = dbe_strdup (GTXT ("Excl. Total CPU")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Total CPU Time")); + abbr = dbe_strdup (GTXT ("Incl. Total CPU")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Total CPU Time")); + abbr = dbe_strdup (GTXT ("Attr. Total CPU")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected TOTAL_CPU metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case CP_LMS_TRAP: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Trap CPU Time")); + abbr = dbe_strdup (GTXT ("Excl. Trap CPU")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Trap CPU Time")); + abbr = dbe_strdup (GTXT ("Incl. Trap CPU")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Trap CPU Time")); + abbr = dbe_strdup (GTXT ("Attr. Trap CPU")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_TRAP metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_LMS_KFAULT: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Kernel Page Fault Time")); + abbr = dbe_strdup (GTXT ("Excl. Kernel Page Fault")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Kernel Page Fault Time")); + abbr = dbe_strdup (GTXT ("Incl. Kernel Page Fault")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Kernel Page Fault Time")); + abbr = dbe_strdup (GTXT ("Attr. Kernel Page Fault")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_KFAULT metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_LMS_SLEEP: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Sleep Time")); + abbr = dbe_strdup (GTXT ("Excl. Sleep")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Sleep Time")); + abbr = dbe_strdup (GTXT ("Incl. Sleep")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Sleep Time")); + abbr = dbe_strdup (GTXT ("Attr. Sleep")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_SLEEP metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case CP_LMS_STOPPED: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Stopped Time")); + abbr = dbe_strdup (GTXT ("Excl. Stopped")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Stopped Time")); + abbr = dbe_strdup (GTXT ("Incl. Stopped")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Stopped Time")); + abbr = dbe_strdup (GTXT ("Attr. Stopped")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected CP_LMS_STOPPED metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case HEAP_ALLOC_BYTES: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Bytes Allocated")); + abbr = dbe_strdup (GTXT ("Excl. Bytes Allocated")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Bytes Allocated")); + abbr = dbe_strdup (GTXT ("Incl. Bytes Allocated")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Bytes Allocated")); + abbr = dbe_strdup (GTXT ("Attr. Bytes Allocated")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected BYTES_MALLOCD metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case HEAP_ALLOC_CNT: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Allocations")); + abbr = dbe_strdup (GTXT ("Excl. Allocations")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Allocations")); + abbr = dbe_strdup (GTXT ("Incl. Allocations")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Allocations")); + abbr = dbe_strdup (GTXT ("Attr. Allocations")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected MALLOCS metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case HEAP_LEAK_BYTES: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Bytes Leaked")); + abbr = dbe_strdup (GTXT ("Excl. Bytes Leaked")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Bytes Leaked")); + abbr = dbe_strdup (GTXT ("Incl. Bytes Leaked")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Bytes Leaked")); + abbr = dbe_strdup (GTXT ("Attr. Bytes Leaked")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected BYTES_LEAKED metric subtype %d"), + st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case HEAP_LEAK_CNT: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Leaks")); + abbr = dbe_strdup (GTXT ("Excl. Leaks")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Leaks")); + abbr = dbe_strdup (GTXT ("Incl. Leaks")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Leaks")); + abbr = dbe_strdup (GTXT ("Attr. Leaks")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected LEAKS metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_READ_BYTES: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Read Bytes")); + abbr = dbe_strdup (GTXT ("Excl. Read Bytes")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Read Bytes")); + abbr = dbe_strdup (GTXT ("Incl. Read Bytes")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Read Bytes")); + abbr = dbe_strdup (GTXT ("Attr. Read Bytes")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected READ_BYTES metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_WRITE_BYTES: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Write Bytes")); + abbr = dbe_strdup (GTXT ("Excl. Write Bytes")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Write Bytes")); + abbr = dbe_strdup (GTXT ("Incl. Write Bytes")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Write Bytes")); + abbr = dbe_strdup (GTXT ("Attr. Write Bytes")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected WRITE_BYTES metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_READ_CNT: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Read Count")); + abbr = dbe_strdup (GTXT ("Excl. Read Count")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Read Count")); + abbr = dbe_strdup (GTXT ("Incl. Read Count")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Read Count")); + abbr = dbe_strdup (GTXT ("Attr. Read Count")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected READCNT metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_WRITE_CNT: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Write Count")); + abbr = dbe_strdup (GTXT ("Excl. Write Count")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Write Count")); + abbr = dbe_strdup (GTXT ("Incl. Write Count")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Write Count")); + abbr = dbe_strdup (GTXT ("Attr. Write Count")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected WRITECNT metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_OTHER_CNT: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Other I/O Count")); + abbr = dbe_strdup (GTXT ("Excl. Other I/O Count")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Other I/O Count")); + abbr = dbe_strdup (GTXT ("Incl. Other I/O Count")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Other I/O Count")); + abbr = dbe_strdup (GTXT ("Attr. Other I/O Count")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OTHERIOCNT metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_ERROR_CNT: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive I/O Error Count")); + abbr = dbe_strdup (GTXT ("Excl. I/O Error Count")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive I/O Error Count")); + abbr = dbe_strdup (GTXT ("Incl. I/O Error Count")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed I/O Error Count")); + abbr = dbe_strdup (GTXT ("Attr. I/O Error Count")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected IOERRORCNT metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_READ_TIME: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Read Time")); + abbr = dbe_strdup (GTXT ("Excl. Read Time")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Read Time")); + abbr = dbe_strdup (GTXT ("Incl. Read Time")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Read Time")); + abbr = dbe_strdup (GTXT ("Attr. Read Time")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected READ_TIME metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_WRITE_TIME: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Write Time")); + abbr = dbe_strdup (GTXT ("Excl. Write Time")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Write Time")); + abbr = dbe_strdup (GTXT ("Incl. Write Time")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Write Time")); + abbr = dbe_strdup (GTXT ("Attr. Write Time")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected WRITE_TIME metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_OTHER_TIME: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Other I/O Time")); + abbr = dbe_strdup (GTXT ("Excl. Other I/O Time")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Other I/O Time")); + abbr = dbe_strdup (GTXT ("Incl. Other I/O Time")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Other I/O Time")); + abbr = dbe_strdup (GTXT ("Attr. Other I/O Time")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OTHERIO_TIME metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case IO_ERROR_TIME: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive I/O Error Time")); + abbr = dbe_strdup (GTXT ("Excl. I/O Error Time")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive I/O Error Time")); + abbr = dbe_strdup (GTXT ("Incl. I/O Error Time")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed I/O Error Time")); + abbr = dbe_strdup (GTXT ("Attr. I/O Error Time")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected IOERROR_TIME metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + + case SIZES: + name = dbe_strdup (GTXT ("Size")); + abbr = dbe_strdup (GTXT ("Size")); + abbr_unit = dbe_strdup (GTXT ("bytes")); + break; + + case ADDRESS: + name = dbe_strdup (GTXT ("PC Address")); + abbr = dbe_strdup (GTXT ("PC Addr.")); + break; + + case ONAME: + name = dbe_strdup (GTXT ("Name")); + abbr = dbe_strdup (GTXT ("Name")); + break; + + case OMP_NONE: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Non-OpenMP Time")); + abbr = dbe_strdup (GTXT ("Excl. Non-OMP")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Non-OpenMP Time")); + abbr = dbe_strdup (GTXT ("Incl. Non-OMP")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Non-OpenMP Time")); + abbr = dbe_strdup (GTXT ("Attr. Non-OMP")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected Non-OpenMP metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_OVHD: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Overhead Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP ovhd.")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Overhead Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP ovhd.")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Overhead Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP ovhd.")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Overhead metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_WORK: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Work Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP Work")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Work Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP Work")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Work Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP Work")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Work metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_IBAR: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Implicit Barrier Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP i-barr.")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Implicit Barrier Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP i-barr.")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Implicit Barrier Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP i-barr.")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Implicit Barrier metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_EBAR: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Explicit Barrier Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP e-barr.")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Explicit Barrier Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP e-barr.")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Explicit Barrier Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP e-barr.")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Explicit Barrier metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_WAIT: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Wait Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP Wait")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Wait Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP Wait")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Wait Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP Wait")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Wait metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_SERL: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Serial Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP serl")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Serial Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP serl")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Serial Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP serl")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Slave Idle metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_RDUC: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Reduction Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP rduc")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Reduction Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP rduc")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Reduction Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP rduc")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Reduction metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_LKWT: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Lock Wait Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP lkwt")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Lock Wait Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP lkwt")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Lock Wait Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP lkwt")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Lock Wait metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_CTWT: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Critical Section Wait Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP ctwt")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Critical Section Wait Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP ctwt")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Critical Section Wait Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP ctwt")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Critical Section Wait metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_ODWT: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Ordered Section Wait Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP odwt")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Ordered Section Wait Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP odwt")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Ordered Section Wait Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP odwt")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Ordered Section Wait metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_MSTR: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Master Serial Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP ser.")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Master Serial Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP ser.")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Master Serial Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP ser.")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Master Serial metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_SNGL: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Single Region Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP sngl")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Single Region Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP sngl")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Single Region Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP sngl")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Single Region metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case OMP_ORDD: + abbr_unit = dbe_strdup (GTXT ("sec.")); + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive OpenMP Ordered Region Time")); + abbr = dbe_strdup (GTXT ("Excl. OMP ordd")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive OpenMP Ordered Region Time")); + abbr = dbe_strdup (GTXT ("Incl. OMP ordd")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed OpenMP Ordered Region Time")); + abbr = dbe_strdup (GTXT ("Attr. OMP ordd")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected OpenMP Ordered Region metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case RACCESS: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Race Accesses")); + abbr = dbe_strdup (GTXT ("Excl. Race Accesses")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Race Accesses")); + abbr = dbe_strdup (GTXT ("Incl. Race Accesses")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Race Accesses")); + abbr = dbe_strdup (GTXT ("Attr. Race Accesses")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected Race Access metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + case DEADLOCKS: + if (st == EXCLUSIVE) + { + name = dbe_strdup (GTXT ("Exclusive Deadlocks")); + abbr = dbe_strdup (GTXT ("Excl. Deadlocks")); + } + else if (st == INCLUSIVE) + { + name = dbe_strdup (GTXT ("Inclusive Deadlocks")); + abbr = dbe_strdup (GTXT ("Incl. Deadlocks")); + } + else if (st == ATTRIBUTED) + { + name = dbe_strdup (GTXT ("Attributed Deadlocks")); + abbr = dbe_strdup (GTXT ("Attr. Deadlocks")); + } + else + { + name = dbe_sprintf (GTXT ("Unexpected Deadlocks metric subtype %d"), st); + abbr = dbe_strdup (NTXT ("??")); + } + break; + default: + abort (); + } +} //Metric::set_subtype + +static bool +is_width_ok (int lines, size_t width, size_t *tlen, int last) +{ + size_t len = 0; + for (int i = 0; i <= last; i++) + { + if (len != 0) + len++; + if (len + tlen[i] > width) + { + if (--lines == 0) + return false; + len = 0; + } + len += tlen[i]; + } + return true; +} + +void +Metric::legend_width (HistMetric *hitem, int gap) +{ + size_t tlen[MAX_LEN]; + char *tok[MAX_LEN], buf[MAX_LEN], unit[MAX_LEN]; + hitem->width = hitem->maxtime_width; + if (hitem->maxvalue_width > 0) + { + if (hitem->width > 0) + hitem->width++; + hitem->width += hitem->maxvalue_width; + } + if (is_pvisible ()) + { + if (hitem->width > 0) + { + hitem->width++; + } + hitem->width += 6; // adjust to change format from xx.yy% + } + snprintf (buf, sizeof (buf), "%s", get_abbr ()); + size_t max_len = hitem->width; + if (legend) + { + size_t legend_len = strlen (legend); + if (max_len < legend_len) + max_len = legend_len; + } + tok[0] = buf; + int last = 0; + for (int i = 0;; i++) + { + if (buf[i] == ' ') + { + buf[i] = '\0'; + while (buf[i + 1] == ' ') + i++; + tlen[last] = strlen (tok[last]); + if (max_len < tlen[last]) + max_len = tlen[last]; + last++; + tok[last] = buf + i + 1; + } + else if (buf[i] == '\0') + { + tlen[last] = strlen (tok[last]); + if (max_len < tlen[last]) + max_len = tlen[last]; + if (tlen[last] == 0 && last > 0) + last--; + break; + } + } + + *unit = '\0'; // get the extra unit tokens + int max_lines = 3; + if (is_tvisible ()) + { + char *s = GTXT ("sec."); + if ((get_visbits () & VAL_DELTA) != 0) + s = GTXT ("delta"); + else if ((get_visbits () & VAL_RATIO) != 0) + s = GTXT ("ratio"); + long len = strlen (s); + if (hitem->maxtime_width < len) + { + hitem->width += len - hitem->maxtime_width; + hitem->maxtime_width = len; + } + snprintf (unit, sizeof (unit), "%*s", (int) hitem->maxtime_width, s); + } + if (is_visible ()) + { + char *s = NTXT (""); + if (!is_tvisible ()) + { + if ((get_visbits () & VAL_DELTA) != 0) + s = GTXT ("delta"); + else if ((get_visbits () & VAL_RATIO) != 0) + s = GTXT ("ratio"); + else if ((get_value_styles () & VAL_TIMEVAL) != 0 && !is_time_val ()) + s = GTXT ("sec."); + } + long len = strlen (s); + if (hitem->maxvalue_width < len) + { + hitem->width += len - hitem->maxvalue_width; + hitem->maxvalue_width = len; + } + if (*unit) + { + max_lines = 2; + len = strlen (unit); + snprintf (unit + len, sizeof (unit) - len, " %*s", + (int) hitem->maxvalue_width, s); + } + else + snprintf (unit, sizeof (unit), "%*s", (int) hitem->maxvalue_width, s); + } + if (is_pvisible ()) + { + max_lines = 2; + if (*unit) + { + size_t len = strlen (unit); + snprintf (unit + len, sizeof (unit) - len, GTXT (" %%")); + } + else + snprintf (unit, sizeof (unit), GTXT (" %%")); + } + for (size_t i = strlen (unit); i > 0;) + { // remove trailing spaces + i--; + if (unit[i] != ' ') + break; + unit[i] = 0; + } + + if (*unit) + { + last++; + tlen[last] = strlen (unit); + tok[last] = unit; + if (max_len < tlen[last]) + max_len = tlen[last]; + if (max_lines == 3 && *unit == ' ') + { + char *str = unit; + while (*str == ' ') + str++; + tlen[last] = strlen (str); + tok[last] = str; + } + } + + int last1 = max_lines == 3 ? last : last - 1; + while (!is_width_ok (max_lines, max_len, tlen, last1)) + max_len++; + hitem->width = max_len + gap; + + char *legends[3]; + legends[0] = hitem->legend1; + legends[1] = hitem->legend2; + legends[2] = hitem->legend3; + for (int i = 0, ind = 0; i < 3; i++) + { + char *str = legends[i]; + *str = 0; + for (; ind <= last; ind++) + { + if (*unit && (ind == last)) + { + // Try to put 'unit' in 'legend3' + if (i != 2) + { + tok[last] = unit; // restore a formated 'unit' + break; + } + } + size_t len = strlen (str); + if (len != 0) + { + if (len + 1 + tlen[ind] > max_len) + break; + snprintf (str + len, MAX_LEN - len, NTXT (" %s"), tok[ind]); + } + else + { + if (len + tlen[ind] > max_len) + break; + snprintf (str + len, MAX_LEN - len, NTXT ("%s"), tok[ind]); + } + } + } +} + +int +Metric::get_real_visbits () +{ + int v = visbits; + if (!is_time_val () && (visbits & (VAL_TIMEVAL | VAL_VALUE)) != 0) + { + v &= ~(VAL_TIMEVAL | VAL_VALUE); + v |= (get_value_styles () & (VAL_TIMEVAL | VAL_VALUE)); + } + return v; +} + +char * +Metric::get_vis_string (int vis) +{ + char *vis_str; + if (subtype == STATIC) + vis_str = NTXT (""); + else + { + int v; + if (is_time_val ()) + v = vis & (VAL_TIMEVAL | VAL_VALUE | VAL_PERCENT); + else + { + v = vis & VAL_PERCENT; + if ((vis & (VAL_TIMEVAL | VAL_VALUE)) != 0) + v |= (get_value_styles () & (VAL_TIMEVAL | VAL_VALUE)); + } + switch (v) + { + case VAL_TIMEVAL: + vis_str = NTXT ("."); + break; + case VAL_VALUE: + vis_str = NTXT ("+"); + break; + case VAL_TIMEVAL | VAL_VALUE: + vis_str = NTXT (".+"); + break; + case VAL_PERCENT: + vis_str = NTXT ("%"); + break; + case VAL_TIMEVAL | VAL_PERCENT: + vis_str = NTXT (".%"); + break; + case VAL_VALUE | VAL_PERCENT: + vis_str = NTXT ("+%"); + break; + case VAL_TIMEVAL | VAL_VALUE | VAL_PERCENT: + vis_str = NTXT (".+%"); + break; + default: + vis_str = NTXT ("!"); + break; + } + } + return vis_str; +} + +char * +Metric::get_vis_str () +{ + char *vis_str = NULL; + if (visbits == -1) + { + // unitialized, return all possible with a trailing - + if (subtype == STATIC) + vis_str = NTXT (".-"); + else if (is_time_val ()) + vis_str = NTXT (".+%-"); + else + vis_str = NTXT (".%-"); + } + else + vis_str = get_vis_string (get_real_visbits ()); + return vis_str; +} + +void +Metric::set_dmetrics_visbits (int dmetrics_visbits) +{ + visbits = VAL_NA; // clear global state + + // process the "show" bits + int _visbits = dmetrics_visbits & ~VAL_HIDE_ALL; + assert (_visbits != -1); + if (_visbits == 0) + return; // done. (Ignore VAL_HIDE_ALL since there's nothing to hide.) + if (get_subtype () == STATIC) + // percent, time value not applicable + visbits = VAL_VALUE; + else + { + // now or in the bits, but manage . and + according to the is_time_val setting + if (is_time_val () == 0) + { + if ((_visbits & VAL_VALUE) || (_visbits & VAL_TIMEVAL)) + visbits |= VAL_VALUE; + } + else + visbits |= (_visbits & (VAL_VALUE | VAL_TIMEVAL)); + visbits |= (_visbits & (VAL_PERCENT | VAL_RATIO | VAL_DELTA)); + } + // process the "hide" bit + if (dmetrics_visbits & VAL_HIDE_ALL) + visbits |= VAL_HIDE_ALL; +} + +void +Metric::set_vvisible (bool set) +{ + if (set) + { + visbits |= VAL_VALUE; + visbits &= ~VAL_HIDE_ALL; + } + else + visbits &= ~VAL_VALUE; +} + +void +Metric::set_tvisible (bool set) +{ + if (set) + { + visbits |= VAL_TIMEVAL; + visbits &= ~VAL_HIDE_ALL; + } + else + visbits &= ~VAL_TIMEVAL; +} + +void +Metric::set_pvisible (bool set) +{ + if (set) + { + visbits |= VAL_PERCENT; + visbits &= ~VAL_HIDE_ALL; + } + else + visbits &= ~VAL_PERCENT; +} + +char * +Metric::get_mcmd (bool allPossible) +{ + char *sc = NTXT (""); // subtype == STATIC + char *hide; + char *vis = get_vis_string (allPossible ? get_value_styles () + : get_real_visbits ()); + if (subtype == INCLUSIVE) + sc = NTXT ("i"); + else if (subtype == EXCLUSIVE) + sc = NTXT ("e"); + else if (subtype == ATTRIBUTED) + sc = NTXT ("a"); + else if (subtype == DATASPACE) + sc = NTXT ("d"); + if (allPossible) + hide = NTXT (""); + else if (visbits == VAL_NA || (visbits & VAL_HIDE_ALL) != 0) + hide = NTXT ("!"); + else + hide = NTXT (""); + + char *mcmd = get_cmd (); + return dbe_sprintf (GTXT ("%s%s%s%s"), sc, hide, vis, mcmd); +} + +char * +Metric::dump () +{ + int len = 4; + BaseMetric *bm = (BaseMetric *) this; + char *s = bm->dump (); + char *msg = dbe_sprintf ("%s\n%*c subtype=%d time_val=%d vis=%d tvis=%d" + " pvis=%d\n%*c abbr='%s' cmd='%s' name='%s'\n", + STR (s), len, ' ', get_subtype (), is_time_val (), + is_visible (), is_tvisible (), is_pvisible (), + len, ' ', STR (get_abbr ()), STR (get_cmd ()), + STR (get_name ())); + free (s); + return msg; +} + diff --git a/gprofng/src/Metric.h b/gprofng/src/Metric.h new file mode 100644 index 0000000..a2b3778 --- /dev/null +++ b/gprofng/src/Metric.h @@ -0,0 +1,188 @@ +/* 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. */ + +#ifndef _METRIC_H +#define _METRIC_H + +#include "dbe_structs.h" +#include "vec.h" +#include "enums.h" +#include "BaseMetric.h" + +#define MAX_LEN 1024 + +class Expression; + +// The metric class defines the metrics that are available. The metrics are +// registered when the experiment's log file is read. +class Metric : public BaseMetric +{ +public: + + typedef struct HistMetricS + { + int width; + int maxvalue_width; + int maxtime_width; + char legend1[MAX_LEN]; + char legend2[MAX_LEN]; + char legend3[MAX_LEN]; + int indFirstExp; // only for -compare=[delta|ratio] + int indTimeVal; // only for HWC time-converted metrics + void update_max (struct HistMetricS *hm); + void init (); + } HistMetric; + + Metric (const Metric& item); // copy constructor + Metric (BaseMetric *item, SubType st); + Metric (char *_name, SubType st); // for derived metrics + virtual ~Metric (); + + char *get_mcmd (bool); // e.user, a.total, etc. NOTI18N + int get_real_visbits (); // methods for managing visibility + ValueTag get_vtype2 (); // takes comparison visbits into account + void set_dmetrics_visbits (int _dmetrics_visbits); + + // fetch various fields from a Metric + SubType + get_subtype () + { + return subtype; + } + + char * + get_name () + { + return name; + } + + char * + get_abbr () + { + return abbr; + } + + char * + get_abbr_unit () + { + return abbr_unit; + } + + BaseMetric * + get_base_metric () + { + return baseMetric; + } + + int + get_visbits () + { + return visbits; + } + + void + set_raw_visbits (int _visbits) + { + visbits = _visbits; + } + + void + clear_all_visbits () + { + visbits = VAL_NA; + } + + void + enable_all_visbits () + { + visbits = get_value_styles (); + } + + +#define VAL_IS_HIDDEN(n) ((n) == -1 || (n) == VAL_NA || ((n) & VAL_HIDE_ALL) != 0) + + bool + is_any_visible () + { + return !VAL_IS_HIDDEN (visbits) + && (visbits & (VAL_VALUE | VAL_TIMEVAL | VAL_PERCENT)); + } + + bool + is_value_visible () + { + return (visbits & VAL_VALUE) != 0 + || (!is_time_val () && (visbits & VAL_TIMEVAL) != 0); + } + + bool + is_time_visible () + { + return is_time_val () && (visbits & VAL_TIMEVAL) != 0; + } + + bool + is_visible () + { + return !VAL_IS_HIDDEN (visbits) && is_value_visible (); + } + + bool + is_tvisible () + { + return !VAL_IS_HIDDEN (visbits) && is_time_visible (); + } + + bool + is_pvisible () + { + return !VAL_IS_HIDDEN (visbits) && (visbits & VAL_PERCENT) != 0; + } + + bool + is_time_val () + { + int v = VAL_TIMEVAL | VAL_VALUE; + return (get_value_styles () & v) == v; + } + + // per-bit handling of visbits + // Note: Forces VAL_HIDE_ALL to zero. Use only on temporary Metric objects. + void set_vvisible (bool set); + void set_tvisible (bool set); + void set_pvisible (bool set); + + void set_subtype (SubType st); + void legend_width (HistMetric *hitem, int gap); + char *get_vis_str (); + char *get_vis_string (int vis); + char *dump (); + + +private: + BaseMetric *baseMetric; + SubType subtype; // specific variant for this Metric + char *name; + char *abbr; + char *abbr_unit; + int visbits; // ValueType, e.g. VAL_VALUE|VAL_TIMEVAL +}; + +#endif /* _METRIC_H */ diff --git a/gprofng/src/MetricList.cc b/gprofng/src/MetricList.cc new file mode 100644 index 0000000..7596524 --- /dev/null +++ b/gprofng/src/MetricList.cc @@ -0,0 +1,1075 @@ +/* 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 "util.h" +#include "Command.h" +#include "DbeSession.h" +#include "MetricList.h" +#include "StringBuilder.h" + +// Build a metric reference list +MetricList::MetricList (Vector<BaseMetric*> *base_metrics, MetricType _mtype) +{ + mtype = _mtype; + items = new Vector<Metric*>; + sort_ref_index = 0; + sort_reverse = false; + + Metric *mitem; + // loop over the base_metrics, and add in all the appropriate subtypes + for (long i = 0, sz = base_metrics ? base_metrics->size () : 0; i < sz; i++) + { + BaseMetric *mtr = base_metrics->get (i); + if (mtr->is_internal ()) + continue; + switch (mtype) + { + case MET_DATA: + if ((mtr->get_flavors () & BaseMetric::DATASPACE) != 0) + { + mitem = new Metric (mtr, BaseMetric::DATASPACE); + items->append (mitem); + } + break; + + case MET_INDX: + { + if ((mtr->get_flavors () & BaseMetric::INCLUSIVE) != 0 + || (mtr->get_flavors () & BaseMetric::EXCLUSIVE) != 0) + { + int index2; + Metric *item2 = NULL; + bool found = false; + Vec_loop (Metric*, items, index2, item2) + { + if (item2->get_subtype () == BaseMetric::EXCLUSIVE + && dbe_strcmp (item2->get_cmd (), mtr->get_cmd ()) == 0) + { + found = true; + break; + } + } + if (found == false) + { + mitem = new Metric (mtr, BaseMetric::EXCLUSIVE); + items->append (mitem); + } + } + } + break; + + case MET_CALL: + case MET_CALL_AGR: + if ((mtr->get_flavors () & BaseMetric::ATTRIBUTED) != 0) + { + mitem = new Metric (mtr, BaseMetric::ATTRIBUTED); + items->append (mitem); + } + // now fall through to add exclusive and inclusive + + case MET_NORMAL: + case MET_COMMON: + if (mtr->get_flavors () & BaseMetric::EXCLUSIVE) + { + mitem = new Metric (mtr, BaseMetric::EXCLUSIVE); + items->append (mitem); + } + if (mtr->get_flavors () & BaseMetric::INCLUSIVE) + { + mitem = new Metric (mtr, BaseMetric::INCLUSIVE); + items->append (mitem); + } + break; + case MET_SRCDIS: + if (mtr->get_flavors () & BaseMetric::INCLUSIVE) + { + mitem = new Metric (mtr, BaseMetric::INCLUSIVE); + items->append (mitem); + } + break; + case MET_IO: + { + if (mtr->get_packet_type () == DATA_IOTRACE + && ((mtr->get_flavors () & BaseMetric::INCLUSIVE) != 0 + || (mtr->get_flavors () & BaseMetric::EXCLUSIVE) != 0)) + { + int index2; + Metric *item2 = NULL; + bool found = false; + Vec_loop (Metric*, items, index2, item2) + { + if (item2->get_subtype () == BaseMetric::EXCLUSIVE + && dbe_strcmp (item2->get_cmd (), mtr->get_cmd ()) == 0) + { + found = true; + break; + } + } + if (found == false) + { + mitem = new Metric (mtr, BaseMetric::EXCLUSIVE); + items->append (mitem); + } + } + } + break; + case MET_HEAP: + { + if (mtr->get_packet_type () == DATA_HEAP + && ((mtr->get_flavors () & BaseMetric::INCLUSIVE) != 0 + || (mtr->get_flavors () & BaseMetric::EXCLUSIVE) != 0)) + { + int index2; + Metric *item2 = NULL; + bool found = false; + Vec_loop (Metric*, items, index2, item2) + { + if ((item2->get_subtype () == BaseMetric::EXCLUSIVE) && + (dbe_strcmp (item2->get_cmd (), mtr->get_cmd ()) == 0)) + { + found = true; + break; + } + } + if (found == false) + { + mitem = new Metric (mtr, BaseMetric::EXCLUSIVE); + items->append (mitem); + } + } + } + break; + } + + // add the static + if (mtr->get_flavors () & BaseMetric::STATIC) + { + switch (mtype) + { + case MET_NORMAL: + case MET_COMMON: + case MET_CALL: + case MET_CALL_AGR: + case MET_SRCDIS: + mitem = new Metric (mtr, BaseMetric::STATIC); + items->append (mitem); + break; + default: + if (mtr->get_type () == BaseMetric::ONAME) + { + mitem = new Metric (mtr, BaseMetric::STATIC); + items->append (mitem); + } + break; + } + } + } + // set all metrics visible + for (long i = 0, sz = items ? items->size () : 0; i < sz; i++) + items->get (i)->enable_all_visbits (); +} + +// Constructor for an empty list -- items will be added one at a time +MetricList::MetricList (MetricType _mtype) +{ + mtype = _mtype; + items = new Vector<Metric*>; + sort_ref_index = 0; + sort_reverse = false; +} + +MetricList::~MetricList () +{ + Destroy (items); +} + +// Duplicate a metric list +MetricList::MetricList (MetricList *old) +{ + mtype = old->mtype; + + // get an empty vector + items = new Vector<Metric*>; + Metric *item; + Metric *nitem; + int index; + sort_ref_index = old->get_sort_ref_index (); + sort_reverse = old->get_sort_rev (); + Vec_loop (Metric*, old->items, index, item) + { + nitem = new Metric (*item); + items->append (nitem); + } +} + +// set_metrics: +// Sets the particular metric list, according to the metric spec +// If fromRcFile, updates dbeSession->get_reg_metrics_tree() with new defaults. +char * +MetricList::set_metrics (const char *mspec, bool fromRcFile, + DerivedMetrics * /* derived_metrics */) +{ + BaseMetric::SubType subtypes[10]; + int nsubtypes; + int dmetrics_vis; // literal translation of metrics/dmetrics %.+ + bool parseOK = false; + char *errbuf; + Vector<Metric*> *old_items = items; + items = new Vector<Metric*>; + Vector<BaseMetric*> *base_items = dbeSession->get_base_reg_metrics (); + + // and copy the input specification + char *buf = dbe_strdup (mspec); + + // append metric items from parsing the string + for (char *mcmd = strtok (buf, NTXT (":")); mcmd != NULL; + mcmd = strtok (NULL, NTXT (":"))) + { + // parse the single metric_spec, based on the type of list being constructed, into: + // a vector of SubTypes (any of [iead] or STATIC) + // a integer mask for the visibility bits + // and the string name of the base metric + // it might be "all", "any", or "hwc" or it should match a metric in the list + // it might also be "bit", meaning any bit-computed metric + char *mname = parse_metric_spec (mcmd, subtypes, &nsubtypes, + &dmetrics_vis, &parseOK); + if (!parseOK) + { + // error parsing the metric specification + // not from an rc file, it's an error + if (!fromRcFile) + { + delete base_items; + items->destroy (); + delete items; + items = old_items; + free (buf); + return mname; + } + continue; + } + + // loop over subtypes requested + // set the visibility of and sort order according to the vis bits, + // and the order of encounter in the processing + int ret = add_matching_dmetrics (base_items, mname, subtypes, nsubtypes, + dmetrics_vis, fromRcFile); + if (ret != 0 && !fromRcFile) + { + if (ret == 1) + errbuf = dbe_sprintf (GTXT ("No data recorded to support metric specification: %s\n"), + mcmd); + else + errbuf = dbe_sprintf (GTXT ("Metric specification for `%s' has appeared before in %s"), + mcmd, mspec); + delete base_items; + items->destroy (); + delete items; + items = old_items; + free (buf); + return errbuf; + } + } // we've processed the entire spec + + // update metric defaults + if (fromRcFile) + { + for (long i = 0, sz = items->size (); i < sz; i++) + { + Metric *m = items->get (i); + int visbits = m->get_visbits (); + BaseMetric::SubType subtype = m->get_subtype (); + BaseMetric *reg_bm = m->get_base_metric (); + reg_bm->set_default_visbits (subtype, visbits); + BaseMetricTreeNode *mtree = dbeSession->get_reg_metrics_tree (); + BaseMetricTreeNode *bmtnode = mtree->register_metric (m); + BaseMetric *tree_bm = bmtnode->get_BaseMetric (); + tree_bm->set_default_visbits (subtype, visbits); + } + } + + // ensure that name is present, remove hidden metrics + nsubtypes = 1; + for (long i = items->size () - 1; i >= 0; i--) + { + Metric *m = items->fetch (i); + if (!m->is_any_visible ()) + { + delete m; + items->remove (i); + continue; + } + if (m->get_type () == BaseMetric::ONAME) + nsubtypes = 0; + } + + // did we get at least one valid match? + if (items->size () == 0 && !fromRcFile) + { + errbuf = dbe_sprintf (GTXT ("No valid metrics specified in `%s'\n"), mspec); + delete base_items; + items->destroy (); + delete items; + items = old_items; + free (buf); + return errbuf; + } + + if (nsubtypes == 1) + { + subtypes[0] = BaseMetric::STATIC; + (void) add_matching_dmetrics (base_items, NTXT ("name"), subtypes, 1, VAL_VALUE, true); + } + + // replace the old list of items, with the new set + if (old_items) + { + old_items->destroy (); + delete old_items; + } + set_fallback_sort (); + free (buf); + delete base_items; + return NULL; +} + +void +MetricList::set_fallback_sort () +{ + // sort by first visible of the appropriate flavor + char *sortcmd = NULL; + switch (mtype) + { + case MET_NORMAL: + case MET_COMMON: + sortcmd = NTXT ("ei.any:name"); + break; + case MET_SRCDIS: + sortcmd = NTXT ("i.any:name"); + break; + case MET_CALL: + case MET_CALL_AGR: + sortcmd = NTXT ("a.any:name"); + break; + case MET_DATA: + sortcmd = NTXT ("d.any:name"); + break; + case MET_INDX: + sortcmd = NTXT ("e.any:name"); + break; + case MET_IO: + sortcmd = NTXT ("e.any:name"); + break; + case MET_HEAP: + sortcmd = NTXT ("e.any:name"); + break; + } + if (NULL != sortcmd) + (void) set_sort (sortcmd, true); +} + +void +MetricList::set_metrics (MetricList *mlist) +{ + // verify that the type is appropriate for the call + if (mtype == MET_NORMAL || mtype == MET_COMMON + || (mlist->mtype != MET_NORMAL && mlist->mtype != MET_COMMON)) + abort (); + + Vector<Metric*> *mlist_items = mlist->get_items (); + items->destroy (); + items->reset (); + + int sort_ind = mlist->get_sort_ref_index (); + for (int i = 0, mlist_sz = mlist_items->size (); i < mlist_sz; i++) + { + Metric *mtr = mlist_items->fetch (i); + if (!mtr->is_any_visible ()) + continue; + + // Add a new Metric with probably a new sub_type to this->items: + // for MET_CALL and MET_CALL_AGR the matching entry to an e. or i. is itself + // for MET_DATA, the matching entry to an e. or i. is the d. metric + // for MET_INDX, the matching entry to an e. or i. is the e. metric + // for MET_IO, the matching entry to an e. or i. is the e. metric + // for MET_HEAP, the matching entry to an e. or i. is the e. metric + // Save static entries (SIZES and ADDRESS) only for MET_NORMAL, MET_CALL, MET_CALL_AGR, MET_SRCDIS + switch (mtr->get_type ()) + { + case BaseMetric::SIZES: + case BaseMetric::ADDRESS: + switch (mtype) + { + case MET_NORMAL: + case MET_COMMON: + case MET_CALL: + case MET_CALL_AGR: + case MET_SRCDIS: + break; + default: + continue; + } + break; + default: + break; + } + + BaseMetric::SubType st = mtr->get_subtype (); + if (st != BaseMetric::STATIC) + { + if (mtype == MET_CALL || mtype == MET_CALL_AGR) + { + if ((mtr->get_flavors () & BaseMetric::ATTRIBUTED) == 0) + continue; + st = BaseMetric::ATTRIBUTED; + } + else if (mtype == MET_DATA) + { + if ((mtr->get_flavors () & BaseMetric::DATASPACE) == 0) + continue; + st = BaseMetric::DATASPACE; + } + else if (mtype == MET_INDX) + { + if ((mtr->get_flavors () & BaseMetric::EXCLUSIVE) == 0) + continue; + st = BaseMetric::EXCLUSIVE; + } + else if (mtype == MET_IO) + { + if (mtr->get_packet_type () != DATA_IOTRACE || + (mtr->get_flavors () & BaseMetric::EXCLUSIVE) == 0) + continue; + st = BaseMetric::EXCLUSIVE; + } + else if (mtype == MET_HEAP) + { + if (mtr->get_packet_type () != DATA_HEAP || + (mtr->get_flavors () & BaseMetric::EXCLUSIVE) == 0) + continue; + st = BaseMetric::EXCLUSIVE; + } + else if (mtype == MET_SRCDIS) + { + if ((mtr->get_flavors () & BaseMetric::INCLUSIVE) == 0) + continue; + st = BaseMetric::INCLUSIVE; + } + } + + bool found = false; + for (int i1 = 0, items_sz = items->size (); i1 < items_sz; i1++) + { + Metric *m1 = items->fetch (i1); + if (mtr->get_id () == m1->get_id () && st == m1->get_subtype ()) + { + if (sort_ind == i) + sort_ind = i1; + found = true; + break; + } + } + if (found) + continue; + Metric *m = new Metric (*mtr); + m->set_subtype (st); + m->set_raw_visbits (mtr->get_visbits ()); + if (sort_ind == i) + sort_ind = items->size (); + items->append (m); + } + if (sort_ind >= items->size ()) + sort_ind = 0; + if (mtype == MET_IO) + sort_ind = 0; + if (mtype == MET_HEAP) + sort_ind = 0; + sort_ref_index = sort_ind; + +} + + +// set_sort: +// Sets the sort for the metric list to the first metric +// in mspec that is present; if fromRcFile is false, then +// only one metric may be specified. The requested sort +// metric must be visible, or it won't be in the metric list + +char * +MetricList::set_sort (const char *mspec, bool fromRcFile) +{ + char *mcmd; + BaseMetric::SubType subtypes[10]; + int nsubtypes; + int vis; + bool parseOK = false; + bool reverse = false; + char buf[BUFSIZ]; + char *list = buf; + char *mname; + + // copy the input specification + snprintf (buf, sizeof (buf), NTXT ("%s"), mspec); + char *listp = list; + if (*listp == '-') + { + // reverse sort specified + reverse = true; + listp++; + } + + // search for metric items from parsing the string + while ((mcmd = strtok (listp, NTXT (":"))) != NULL) + { + listp = NULL; // let strtok keep track + + // parse the single metric_spec, based on the type of list being constructed, into: + // a vector of SubTypes (any of [iead] or STATIC) + // a integer mask for the visibility bits + // and the string name of the base metric + mname = parse_metric_spec (mcmd, subtypes, &nsubtypes, &vis, &parseOK); + if (!parseOK) + { + // error parsing the metric specification + // not from an rc file, it's an error + if (!fromRcFile) + return (mname); + continue; + } + if (VAL_IS_HIDDEN (vis)) + continue; + + // loop over subtypes requested to find metric + // add a metric of that subtype, with specified vis.bits + for (int i = 0; i < nsubtypes; i++) + { + // make sure the subtype is acceptable + if ((mtype == MET_CALL || mtype == MET_CALL_AGR) + && subtypes[i] != BaseMetric::ATTRIBUTED + && subtypes[i] != BaseMetric::STATIC) + return dbe_sprintf (GTXT ("Inclusive, Exclusive, or Data metrics cannot be specified for caller-callee sort: %s\n"), + mcmd); + if (mtype == MET_DATA && subtypes[i] != BaseMetric::DATASPACE + && subtypes[i] != BaseMetric::STATIC) + return dbe_sprintf (GTXT ("Inclusive, Exclusive, or Attributed metrics cannot be specified for data-derived sort: %s\n"), + mcmd); + if (mtype == MET_INDX && subtypes[i] != BaseMetric::EXCLUSIVE + && subtypes[i] != BaseMetric::STATIC) + return dbe_sprintf (GTXT ("Inclusive, Data or Attributed metrics cannot be specified for index sort: %s\n"), + mcmd); + if ((mtype == MET_NORMAL || mtype == MET_COMMON + || mtype == MET_SRCDIS) + && (subtypes[i] == BaseMetric::DATASPACE + || subtypes[i] == BaseMetric::ATTRIBUTED)) + return dbe_sprintf (GTXT ("Data or Attributed metrics cannot be specified for sort: %s\n"), mcmd); + if (set_sort_metric (mname, subtypes[i], reverse)) + return NULL; + } + // continue looking at entries + } + + // not found on the list at all + switch (mtype) + { + case MET_NORMAL: + case MET_COMMON: + case MET_SRCDIS: + return dbe_sprintf (GTXT ("Invalid sort specification: %s\n"), mspec); + case MET_CALL: + case MET_CALL_AGR: + return dbe_sprintf (GTXT ("Invalid caller-callee sort specification: %s\n"), + mspec); + case MET_DATA: + return dbe_sprintf (GTXT ("Invalid data-derived sort specification: %s\n"), + mspec); + case MET_INDX: + return dbe_sprintf (GTXT ("Invalid index sort specification: %s\n"), + mspec); + case MET_IO: + return dbe_sprintf (GTXT ("Invalid I/O sort specification: %s\n"), mspec); + case MET_HEAP: + return dbe_sprintf (GTXT ("Invalid heap sort specification: %s\n"), + mspec); + } + return NULL; +} + +// set_sort to the metric with the given visible index + +void +MetricList::set_sort (int visindex, bool reverse) +{ + Metric *mitem; + if (visindex < items->size ()) + { + mitem = items->fetch (visindex); + if (mitem->is_any_visible ()) + { + sort_ref_index = visindex; + sort_reverse = reverse; + return; + } + } + set_fallback_sort (); +} + +bool +MetricList::set_sort_metric (char *mname, BaseMetric::SubType mst, bool reverse) +{ + bool any = false, hwc = false, bit = false; + + // check keywords 'any', 'all', 'bit' and 'hwc' + if (!strcasecmp (mname, Command::ANY_CMD)) + any = true; + else if (!strcasecmp (mname, Command::ALL_CMD)) + any = true; + else if (!strcasecmp (mname, Command::HWC_CMD)) + hwc = true; + else if (!strcasecmp (mname, Command::BIT_CMD)) + bit = true; + + for (int i = 0, items_sz = items->size (); i < items_sz; i++) + { + Metric *m = items->fetch (i); + if (mst == m->get_subtype () + && (any || (hwc && m->get_type () == BaseMetric::HWCNTR) + || (bit && m->get_cmd () + && strncmp (Command::BIT_CMD, m->get_cmd (), + strlen (Command::BIT_CMD)) == 0) + || dbe_strcmp (mname, m->get_cmd ()) == 0)) + { + sort_ref_index = i; + sort_reverse = reverse; + return true; + } + } + return false; +} + +// Print to a file of a list of metrics from a supplied vector +// Debug flag = 1, prints the short name and address of the list +// Debug flag = 2, prints the details of the list +void +MetricList::print_metric_list (FILE *dis_file, char *leader, int debug) +{ + Metric *item; + int index; + char fmt_name[64]; + fprintf (dis_file, NTXT ("%s"), leader); + if (items == NULL) + { + fprintf (dis_file, GTXT ("NULL metric list can not be printed; aborting")); + abort (); + } + + if (items->size () == 0) + { + fprintf (dis_file, GTXT ("metric list is empty; aborting\n")); + abort (); + } + + // if debugging, print list address and string, and sort name + if (debug != 0) + { + char *s = get_metrics (); + fprintf (dis_file, "\tmetriclist at 0x%lx: %s, %lld metrics; sort by %s\n", + (unsigned long) this, s, (long long) items->size (), + get_sort_name ()); + free (s); + if (debug == 1) + return; + } + + // Find the longest metric name & command + size_t max_len = 0; + size_t max_len2 = 0; + + Vec_loop (Metric*, items, index, item) + { + // get the name + char *mn = item->get_name (); + size_t len = strlen (mn); + if (max_len < len) + max_len = len; + + mn = item->get_mcmd (true); + len = strlen (mn); + if (max_len2 < len) + max_len2 = len; + free (mn); + + } + if (debug == 2) + snprintf (fmt_name, sizeof (fmt_name), "%%%ds: %%-%ds", (int) max_len, + (int) max_len2); + else + snprintf (fmt_name, sizeof (fmt_name), "%%%ds: %%s", (int) max_len); + + Vec_loop (Metric*, items, index, item) + { + char *mcmd = item->get_mcmd (true); + fprintf (dis_file, fmt_name, item->get_name (), mcmd); + free (mcmd); + if (debug == 2) + fprintf (dis_file, "\t[st %2d, VT %d, vis = %4s, T=%d, sort = %c]", + item->get_subtype (), item->get_vtype (), + item->get_vis_str (), item->is_time_val (), + sort_ref_index == index ? 'Y' : 'N'); + fputc ('\n', dis_file); + } + + fputc ('\n', dis_file); + fflush (dis_file); +} + +// Return a string formatted from a vector of metrics +// string is in the form suitable for a "metrics <string>" command +char * +MetricList::get_metrics () +{ + Metric *item; + int index; + StringBuilder sb; + Vec_loop (Metric*, items, index, item) + { + if (sb.length () != 0) + sb.append (':'); + char *mcmd = item->get_mcmd (false); + sb.append (mcmd); + free (mcmd); + } + return sb.toString (); +} + +int +MetricList::get_listorder (Metric *mtr) +{ + for (int i = 0, items_sz = items->size (); i < items_sz; i++) + { + Metric *m = items->fetch (i); + if (m->get_subtype () == mtr->get_subtype () + && m->get_id () == mtr->get_id ()) + return i; + } + return -1; +} + +int +MetricList::get_listorder (char *cmd, BaseMetric::SubType st, const char *expr) +{ + for (long i = 0, items_sz = items->size (); i < items_sz; i++) + { + Metric *m = items->fetch (i); + if (m->get_subtype () == st && dbe_strcmp (m->get_cmd (), cmd) == 0 + && dbe_strcmp (m->get_expr_spec (), expr) == 0) + return (int) i; + } + return -1; +} + +Metric * +MetricList::find_metric_by_name (char *cmd) +{ + for (long i = 0, items_sz = items->size (); i < items_sz; i++) + { + Metric *m = items->fetch (i); + if (dbe_strcmp (m->get_cmd (), cmd) == 0) + return m; + } + return NULL; +} + +// find a metric by name and subtype +Metric * +MetricList::find_metric (char *cmd, BaseMetric::SubType st) +{ + int i = get_listorder (cmd, st); + if (i < 0) + return NULL; + return items->fetch (i); +} + +// Get the sort metric from a list; forces sort by first if not set +Metric * +MetricList::get_sort_metric () +{ + int i = get_sort_ref_index (); + return i >= 0 ? items->fetch (i) : NULL; +} + +char * +MetricList::get_sort_name () +{ + Metric *item = get_sort_metric (); + if (item == NULL) + return dbe_strdup (NTXT ("")); + char *n = item->get_name (); + return sort_reverse ? dbe_sprintf ("-%s", n) : dbe_strdup (n); +} + +char * +MetricList::get_sort_cmd () +{ + char *buf; + Metric *item = get_sort_metric (); + if (item == NULL) + return dbe_strdup (NTXT ("")); + char *n = item->get_mcmd (false); + if (sort_reverse) + { + buf = dbe_sprintf (NTXT ("-%s"), n); + free (n); + } + else + buf = n; + return buf; +} + +Metric * +MetricList::append (BaseMetric *bm, BaseMetric::SubType st, int visbits) +{ + for (long i = 0, sz = items->size (); i < sz; i++) + { + Metric *m = items->get (i); + if (m->get_id () == bm->get_id () && m->get_subtype () == st) + return NULL; + } + Metric *met = new Metric (bm, st); + met->set_dmetrics_visbits (visbits); + items->append (met); + return met; +} + +int +MetricList::add_matching_dmetrics (Vector<BaseMetric*> *base_items, + char *mcmd, BaseMetric::SubType *_subtypes, + int nsubtypes, int dmetrics_visbits, + bool fromRcFile) +{ + bool any = false, hwc = false, bit = false; + int got_metric = 1; + + // check keywords 'any', 'all', 'bit', and 'hwc' + if (!strcasecmp (mcmd, Command::ANY_CMD)) + any = true; + else if (!strcasecmp (mcmd, Command::ALL_CMD)) + any = true; + else if (!strcasecmp (mcmd, Command::HWC_CMD)) + hwc = true; + else if (!strcasecmp (mcmd, Command::BIT_CMD)) + bit = true; + + BaseMetric::SubType *subtypes = _subtypes; + BaseMetric::SubType all_subtypes[2] = + { BaseMetric::EXCLUSIVE, BaseMetric::INCLUSIVE }; + + if (nsubtypes == 0 || (nsubtypes == 1 && subtypes[0] == BaseMetric::STATIC)) + { + // user did not specify ei; treat as wildcard and supply both. + subtypes = all_subtypes; + nsubtypes = 2; + } + + // scan the metrics to find all matches + for (int i = 0, base_sz = base_items->size (); i < base_sz; i++) + { + BaseMetric *item = base_items->fetch (i); + if (!(any || (hwc && item->get_type () == BaseMetric::HWCNTR) + || (bit && item->get_cmd () + && strncmp (item->get_cmd (), Command::BIT_CMD, + strlen (Command::BIT_CMD)) == 0) + || dbe_strcmp (item->get_cmd (), mcmd) == 0)) + continue; + if (item->is_internal ()) + continue; + if (item->get_flavors () & BaseMetric::STATIC) + { + got_metric = 0; + int vis = item->get_type () != BaseMetric::ONAME ? + dmetrics_visbits : VAL_VALUE; + if (append (item, BaseMetric::STATIC, vis) == NULL && !fromRcFile) + return 2; + continue; + } + + // special case for omp metrics: make visible only if + // omp data has been collected + if (!dbeSession->is_omp_available () + && (strcasecmp (mcmd, "ompwork") == 0 + || strcasecmp (mcmd, "ompwait") == 0)) + continue; + + for (int j = 0; j < nsubtypes; j++) + { + if (append (item, subtypes[j], dmetrics_visbits) == NULL + && !fromRcFile) + return 2; + } + got_metric = 0; + if (!(any || hwc || bit)) + break; + } + return got_metric; +} + +// parse a single metric specification, to give: +// a vector of subtypes, and a count of the number of them +// an integer visibility +// return the string for the metric name + +char * +MetricList::parse_metric_spec (char *mcmd, BaseMetric::SubType *subtypes, + int *nsubtypes, int *dmetrics_visb, bool *isOK) +{ + size_t len_vtype; + int index; + int vis; + bool got_e, got_i, got_a, got_d; + char *str = mcmd; + char *str2; + + *isOK = true; + + // For dynamic metrics, each keyword is of the form <flavor><visibility><metric-name> + // For static metrics, each keyword is of the form [<visibility>]<metric-name> + // <flavor> can be either "i" for inclusive or "e" for exclusive + // <visibility> can be any combination of "." (to show the metric as a time), + // "%" (to show it as a percentage), "+" (to show it as a count), and "!" (turn off the metric) + + // find subtype + index = 0; + size_t len_subtype = strspn (str, NTXT ("eiad")); + str2 = str + len_subtype; + + // find vis + if (len_subtype == 0) + { + // only a . or ! is possible if no subtypes + len_vtype = strspn (str2, NTXT (".!")); + vis = VAL_VALUE; + } + else + { + len_vtype = strspn (str2, NTXT (".+%!")); + vis = VAL_NA; + } + + // if no visibility bits, there can't be a subtype + if (len_vtype == 0) + len_subtype = 0; + + if (len_subtype == 0) + { + // must be a static metric + subtypes[index++] = BaseMetric::STATIC; + vis = VAL_VALUE; + } + else + { + // figure out which subtypes are specified + got_e = got_i = got_a = got_d = false; + for (size_t i = 0; i < len_subtype; i++) + { + str += len_subtype; + if (mcmd[i] == 'e') + { // exclusive + if (mtype == MET_DATA) + { + *isOK = false; + return dbe_sprintf (GTXT ("Invalid metric specification: %s inapplicable for data metrics\n"), + mcmd); + } + if (!got_e) + { + got_e = true; + subtypes[index++] = BaseMetric::EXCLUSIVE; + } + } + else if (mcmd[i] == 'i') + { // inclusive + if (mtype == MET_DATA) + { + *isOK = false; + return dbe_sprintf (GTXT ("Invalid metric specification: %s inapplicable for data metrics\n"), + mcmd); + } + if (mtype == MET_INDX) + { + *isOK = false; + return dbe_sprintf (GTXT ("Invalid metric specification: %s inapplicable for index metrics\n"), + mcmd); + } + if (!got_i) + { + got_i = true; + subtypes[index++] = BaseMetric::INCLUSIVE; + } + } + else if (mcmd[i] == 'a') + { // attributed + if (mtype != MET_CALL && mtype != MET_CALL_AGR) + { + *isOK = false; + return dbe_sprintf (GTXT ("Invalid metric specification: %s applicable for caller-callee metrics only\n"), + mcmd); + } + if (!got_a) + { + got_a = true; + subtypes[index++] = BaseMetric::ATTRIBUTED; + } + } + else if (mcmd[i] == 'd') + { // data-space + if (mtype != MET_DATA) + { + *isOK = false; + return dbe_sprintf (GTXT ("Invalid metric specification: %s applicable for data-derived metrics only\n"), + mcmd); + } + if (!got_d) + { + got_d = true; + subtypes[index++] = BaseMetric::DATASPACE; + } + } + } + } + *nsubtypes = index; + + // now determine the visiblity bits + if (len_vtype > 0) + { + for (size_t i = 0; i < len_vtype; i++) + { + if (str2[i] == '+') + vis = (vis | VAL_VALUE); + else if (str2[i] == '.') + vis = (vis | VAL_TIMEVAL); + else if (str2[i] == '%') + vis = (vis | VAL_PERCENT); + else if (str2[i] == '!') + vis = (vis | VAL_HIDE_ALL); + } + } + *dmetrics_visb = vis; + return mcmd + len_subtype + len_vtype; +} diff --git a/gprofng/src/MetricList.h b/gprofng/src/MetricList.h new file mode 100644 index 0000000..b8486c1 --- /dev/null +++ b/gprofng/src/MetricList.h @@ -0,0 +1,163 @@ +/* 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. */ + +#ifndef _METRICLIST_H +#define _METRICLIST_H + +#include "dbe_structs.h" +#include "vec.h" +#include "enums.h" +#include "Metric.h" +#include "DerivedMetrics.h" +#include <stdio.h> + +// +// The MetricList class is used to manage a list of metrics + +class MetricList +{ +public: + + MetricList (Vector<BaseMetric*> *base_metrics, MetricType type); + MetricList (MetricList *old); + MetricList (MetricType _mtype); + ~MetricList (); + + // Methods concerning a list of metrics + // set metrics -- text, caller-callee, data, and index flavors + // flavor depends on mtype in the list + // returns NULL if OK, or an error string if not + // always returns NULL if fromRcFile is TRUE + char *set_metrics (const char *metric_cmd, bool fromRcFile, DerivedMetrics *derived_metrics); + + // update the caller-callee or dataspace metrics to match normal metrics + // It is assumed that this->mtype is MET_CALL, MET_DATA, or MET_INDX and + // that metrics_list->mtype is MET_NORMAL + void set_metrics (MetricList *metrics_list); + + // produce a string for the metrics from a vector + char *get_metrics (); + + // set the sort metric for a list from a metric string + // returns NULL if OK, or an error string if not + char *set_sort (const char *metric_cmd, bool fromRcFile); + + // set the sort metric for a list from the first visible index + void set_fallback_sort (); + + // set the sort metric for a list from a visible index + void set_sort (int visindex, bool reverse); + + char *get_sort_name (); // get the name of the sort metric from a vector + + bool + get_sort_rev () // get the boolean reverse for the sort metric + { + return sort_reverse; + } + + void + set_sort_rev (bool v) + { + sort_reverse = v; + } + + int + get_sort_ref_index () + { + return sort_ref_index; + } + + void + set_sort_ref_index (int ind) + { + sort_ref_index = ind; + } + + bool set_sort_metric (char *metric_cmd, BaseMetric::SubType mst, bool reverse); + Metric *find_metric (char *cmd, BaseMetric::SubType st); + Metric *find_metric_by_name (char *cmd); + int get_listorder (char *cmd, BaseMetric::SubType st, const char *expr = NULL); + int get_listorder (Metric *mtr); + Metric *get_sort_metric (); // get the sort metric from a vector + char *get_sort_cmd (); // get the command name of the sort metric + + MetricType + get_type () + { + return mtype; + } + + Vector<Metric*> * + get_items () // get the vector of metrics from the list + { + return items; + } + + Metric * + get (long i) + { + return items->get (i); + } + + void + put (long i, Metric *m) + { + items->put (i, m); + } + + void + append (Metric *m) + { + items->append (m); + } + + long + size () + { + return items ? items->size () : 0; + } + + Metric *append (BaseMetric *bm, BaseMetric::SubType st, int visbits); + + // produce a list of all metrics from a vector + void print_metric_list (FILE *dis_file, char *leader, int debug); + + // Add any and all matching metrics to the growing list + // return value is zero for OK, 1 for no match + int add_matching_dmetrics (Vector<BaseMetric*> *base_items, char *cmd, + BaseMetric::SubType *subtypes, int nsubtypes, + int dmetrics_vis, // literal translation of dmetrics +. etc. + bool fromRcFile); + +private: + // parse a metric specification substring, based on type of list + char *parse_metric_spec (char *cmd, BaseMetric::SubType *subtypes, + int *nsubtypes, int *dmetrics_visb, bool *isOK); + + Vector<Metric*> *items; + MetricType mtype; + + // the sort reference index + int sort_ref_index; + bool sort_reverse; +}; + +#endif /* _METRICLIST_H */ diff --git a/gprofng/src/Module.cc b/gprofng/src/Module.cc new file mode 100644 index 0000000..1e61b42 --- /dev/null +++ b/gprofng/src/Module.cc @@ -0,0 +1,1840 @@ +/* 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 <unistd.h> +#include <ar.h> +#include <ctype.h> +#include <sys/param.h> + +#include "util.h" +#include "DbeSession.h" +#include "Experiment.h" +#include "DataObject.h" +#include "Function.h" +#include "DbeView.h" +#include "MetricList.h" +#include "Module.h" +#include "ClassFile.h" +#include "LoadObject.h" +#include "Disasm.h" +#include "CompCom.h" +#include "Dwarf.h" +#include "DbeFile.h" +#include "PathTree.h" +#include "Elf.h" + +Module::Module () +{ + lang_code = Sp_lang_unknown; + flags = 0; + status = AE_NOTREAD; + openSourceFlag = AE_NOTREAD; + hexVisible = false; + disPath = NULL; + stabsPath = NULL; + stabsTmp = NULL; + disName = NULL; + stabsName = NULL; + indexStabsLink = NULL; + file_name = NULL; + functions = new Vector<Function*>; + loadobject = NULL; + dot_o_file = NULL; + main_source = dbeSession->get_Unknown_Source (); + srcContext = main_source; + includes = new Vector<SourceFile*>; + includes->append (main_source); + curr_inc = NULL; + fragmented = 0; + hwcprof = 0; + hdrOffset = 0; + hasDwarf = false; + hasStabs = false; + readStabs = false; + comComs = NULL; + infoList = NULL; + datatypes = NULL; + objStabs = NULL; + disasm = NULL; + comp_flags = NULL; + comp_dir = NULL; + linkerStabName = NULL; + disMTime = (time_t) 0; + stabsMTime = (time_t) 0; + real_timestamp = 0; + curr_timestamp = 0; + src_items = NULL; + dis_items = NULL; + data_items = NULL; + cur_dbev = NULL; + maximum = NULL; + maximum_inc = NULL; + empty = NULL; + inlinedSubr = NULL; +} + +Module::~Module () +{ + removeStabsTmp (); + delete includes; + if (comComs != NULL) + { + comComs->destroy (); + delete comComs; + } + free (comp_flags); + free (comp_dir); + free (linkerStabName); + free (disPath); + free (stabsPath); + free (disName); + free (stabsName); + delete functions; + free (file_name); + if (indexStabsLink) + // Remove a link to the current module + indexStabsLink->indexStabsLink = NULL; + + if (dot_o_file) + { + delete dot_o_file->dbeFile; + delete dot_o_file; + } + delete src_items; + delete dis_items; + delete disasm; + free (inlinedSubr); + if (lang_code != Sp_lang_java) + delete dbeFile; + +} + +Stabs * +Module::openDebugInfo () +{ + setFile (); + objStabs = loadobject->openDebugInfo (disPath); + return objStabs; +} + +void +Module::removeStabsTmp () +{ + // Remove temporary *.o (got from *.a) after reading Stabs + if (stabsTmp != NULL) + { + unlink (stabsTmp); + free (stabsTmp); + stabsTmp = NULL; + } +} + +int64_t +Module::get_size () +{ + Function *fp; + int index; + int64_t result = 0; + Vec_loop (Function*, functions, index, fp) + { + result += fp->size; + } + return result; +} + +bool +Module::is_fortran () +{ + return Stabs::is_fortran (lang_code); +} + +SourceFile * +Module::findSource (const char *fname, bool create) +{ + SourceFile *sf = NULL; + if (loadobject && loadobject->firstExp) + sf = loadobject->firstExp->get_source (fname); + if (sf == NULL) + sf = dbeSession->createSourceFile (fname); + for (int i = 0, sz = includes ? includes->size () : 0; i < sz; i++) + { + SourceFile *sf1 = includes->fetch (i); + if (sf == sf1) + return sf; + } + if (create) + { + if (includes == NULL) + includes = new Vector<SourceFile*>; + includes->append (sf); + return sf; + } + return NULL; +} + +SourceFile * +Module::setIncludeFile (char *includeFile) +{ + curr_inc = NULL; + if (includeFile) + curr_inc = findSource (includeFile, true); + return curr_inc; +} + +char * +Module::anno_str (char *fnm) +{ + char timebuf1[26], timebuf2[26]; + const time_t real_time = (time_t) (unsigned int) real_timestamp; + const time_t curr_time = (time_t) (unsigned int) curr_timestamp; + + switch (status) + { + case AE_OK: + case AE_NOTREAD: + return NULL; + case AE_NOSRC: + return dbe_sprintf (GTXT ("Source file `%s' not readable"), + fnm ? fnm : file_name); + case AE_NOOBJ: + if (lang_code == Sp_lang_java) + { + Emsg *emsg = get_error (); + if (emsg) + { + char *s = dbe_strdup (emsg->get_msg ()); + remove_msg (emsg); + return s; + } + return dbe_sprintf (GTXT ("Object file `%s.class' not readable"), + name); + } + return dbe_sprintf (GTXT ("Object file `%s' not readable"), get_name ()); + case AE_NOLOBJ: + if (lang_code == Sp_lang_java) + return dbe_sprintf (GTXT ("Object file `%s' not readable"), + dbeFile ? dbeFile->get_name () : name); + return dbe_sprintf (GTXT ("Object file `%s' not readable"), loadobject->get_pathname ()); + case AE_NOSTABS: + return dbe_sprintf (GTXT ("Error reading line-number information in object `%s'; source annotation not available"), + stabsPath ? stabsPath : NTXT ("")); + case AE_NOSYMTAB: + return dbe_sprintf (GTXT ("Error reading symbol table in object `%s'; disassembly annotation not available"), + disPath ? disPath : NTXT ("")); + case AE_TIMESRC: + return dbe_sprintf (GTXT ("Warning! Source file `%s' is newer than the experiment data"), + main_source->dbeFile->getResolvedPath ()); + case AE_TIMEDIS: + return dbe_sprintf (GTXT ("Warning! Object file `%s' is newer than the experiment data"), + disName ? disName : NTXT ("")); + case AE_TIMESTABS: + return dbe_sprintf (GTXT ("Warning! Object file `%s' is newer than the experiment data"), + stabsName ? stabsName : NTXT ("")); + case AE_TIMESTABS_DIFF: + snprintf (timebuf1, sizeof (timebuf1), NTXT ("%s"), ctime (&curr_time)); + snprintf (timebuf2, sizeof (timebuf2), NTXT ("%s"), ctime (&real_time)); + timebuf1[24] = timebuf2[24] = '\0'; + return dbe_sprintf (GTXT ("Warning! Object file `%s' is not the same one that was linked into executable.\n" + "\tObject file: `%s'\n\tcompiled on: %s\n" + "\tExecutable contains object file compiled on: %s"), + getResolvedObjectPath (), getResolvedObjectPath (), + timebuf1, timebuf2); + case AE_OTHER: + default: + return dbe_strdup (GTXT ("Annotation computation error")); + } +}//anno_str + +LoadObject * +Module::createLoadObject (const char *lo_name) +{ + LoadObject *lo = new LoadObject (lo_name); + lo->dbeFile->filetype |= DbeFile::F_DOT_O; + return lo; +} + +static bool +tsIsNewer (time_t t1, time_t t2) +{ + return t1 != 0 && t2 != 0 && t1 < t2; +} + +Module::Anno_Errors +Module::checkTimeStamp (bool chkDis) +{ + /* Check the linked and the real object timestamps due to bug #4796329 */ + if (real_timestamp && curr_timestamp && real_timestamp != curr_timestamp) + return AE_TIMESTABS_DIFF; + + time_t srctime = main_source->getMTime (); + for (int index = 0; index < dbeSession->nexps (); index++) + { + time_t mtime = dbeSession->get_exp (index)->get_mtime (); + if (tsIsNewer (mtime, srctime)) + return AE_TIMESRC; + if (tsIsNewer (mtime, stabsMTime)) + return AE_TIMESTABS; + if (chkDis && tsIsNewer (mtime, disMTime)) + return AE_TIMEDIS; + } + return AE_OK; +}//checkTimeStamp + +static size_t +get_ar_size (char *s, size_t len) +{ + size_t sz = 0; + for (size_t i = 0; i < len; i++) + { + if (s[i] < '0' || s[i] > '9') + break; + sz = sz * 10 + (s[i] - '0'); + } + return sz; +} + +static void +dump_hdr_field (char *nm, char *s, size_t len) +{ + Dprintf (DEBUG_READ_AR, NTXT (" %s "), nm); + for (size_t i = 0; i < len; i++) + Dprintf (DEBUG_READ_AR, "%c", isprint (s[i]) ? s[i] : '?'); + Dprintf (DEBUG_READ_AR, NTXT (" ")); + for (size_t i = 0; i < len; i++) + Dprintf (DEBUG_READ_AR, NTXT (" %d"), s[i]); + Dprintf (DEBUG_READ_AR, NTXT (" \n")); +} + +static void +dump_ar_hdr (int lineNum, struct ar_hdr *hdr) +{ + if (DEBUG_READ_AR) + { + Dprintf (DEBUG_READ_AR, NTXT ("Module::read_ar %d\n"), lineNum); + dump_hdr_field (NTXT ("ar_name"), hdr->ar_name, sizeof (hdr->ar_name)); + dump_hdr_field (NTXT ("ar_date"), hdr->ar_date, sizeof (hdr->ar_date)); + dump_hdr_field (NTXT ("ar_uid"), hdr->ar_uid, sizeof (hdr->ar_uid)); + dump_hdr_field (NTXT ("ar_gid"), hdr->ar_gid, sizeof (hdr->ar_gid)); + dump_hdr_field (NTXT ("ar_mode"), hdr->ar_mode, sizeof (hdr->ar_mode)); + dump_hdr_field (NTXT ("ar_size"), hdr->ar_size, sizeof (hdr->ar_size)); + dump_hdr_field (NTXT ("ar_fmag"), hdr->ar_fmag, sizeof (hdr->ar_fmag)); + } +} + +bool +Module::read_ar (int ar, int obj, char *obj_base) +{ + struct ar_hdr hdr; // Archive header + char magic[SARMAG]; // Magic string from archive + Dprintf (DEBUG_READ_AR, "Module::read_ar %d %p %s %s \n", __LINE__, + this, STR (obj_base), STR (get_name ())); + // Check the magic string + if ((read_from_file (ar, magic, SARMAG) != SARMAG) + || strncmp (magic, ARMAG, SARMAG)) + return false; + + // Read and skip the first file in the archive (index file to ld) + if (read_from_file (ar, &hdr, sizeof (hdr)) != sizeof (hdr)) + return false; + DEBUG_CODE dump_ar_hdr (__LINE__, &hdr); + if (lseek (ar, get_ar_size (hdr.ar_size, sizeof (hdr.ar_size)), SEEK_CUR) + == -1) + return false; + + // Read the string file where it keeps long file names (if exist) + if (read_from_file (ar, &hdr, sizeof (hdr)) != sizeof (hdr)) + return false; + DEBUG_CODE dump_ar_hdr (__LINE__, &hdr); + char *longnames = NULL; // Area with names longer than ~13 + size_t longnames_size = 0; + if (!strncmp (hdr.ar_name, NTXT ("//"), 2)) + { + longnames_size = get_ar_size (hdr.ar_size, sizeof (hdr.ar_size)); + longnames = (char *) malloc (longnames_size + 1); + int64_t cnt = read_from_file (ar, longnames, longnames_size); + if (cnt != (int64_t) longnames_size) + { + free (longnames); + return false; + } + longnames[longnames_size] = 0; + } + else + // back out, no long file names + lseek (ar, -(sizeof (hdr)), SEEK_CUR); + + // Search the ar for the object file name + char ar_buf[sizeof (hdr.ar_name) + 1]; + ar_buf[sizeof (hdr.ar_name)] = 0; + while (1) + { + if (read_from_file (ar, &hdr, sizeof (hdr)) != sizeof (hdr)) + break; + DEBUG_CODE dump_ar_hdr (__LINE__, &hdr); + char *ar_name; + if (hdr.ar_name[0] != '/') + { // Name is in the header + for (size_t i = 0; i < sizeof (hdr.ar_name); i++) + { + if (hdr.ar_name[i] == '/') + { + ar_buf[i] = 0; + break; + } + ar_buf[i] = hdr.ar_name[i]; + } + ar_name = ar_buf; + } + else if (hdr.ar_name[1] == ' ') + { // Name is blank + ar_buf[0] = 0; + ar_name = ar_buf; + } + else + { // Name is in the string table + if (longnames == NULL) + break; + size_t offset = get_ar_size (hdr.ar_name + 1, + sizeof (hdr.ar_name) - 1); + if (offset >= longnames_size) + break; + for (size_t i = offset; i < longnames_size; i++) + { + if (longnames[i] == '/') + { + longnames[i] = 0; + break; + } + } + ar_name = longnames + offset; + } + Dprintf (DEBUG_READ_AR, "Module::read_ar %d ar_name=%s\n", __LINE__, + ar_name); + + if (streq (ar_name, obj_base)) + { // create object file + free (longnames); + for (size_t objsize = get_ar_size (hdr.ar_size, sizeof (hdr.ar_size)); + objsize > 0;) + { + char buf[MAXPATHLEN]; + size_t n = objsize < sizeof (buf) ? objsize : sizeof (buf); + int64_t cnt = read_from_file (ar, buf, n); + if (cnt != (int64_t) n) + return false; + cnt = write (obj, buf, n); + if (cnt != (int64_t) n) + return false; + objsize -= n; + } + return true; + } + if (lseek (ar, get_ar_size (hdr.ar_size, sizeof (hdr.ar_size)), + SEEK_CUR) == -1) + break; + } + free (longnames); + return false; +} + +static char * +get_obj_name_from_lib (char *nm) +{ + char *base = strrchr (nm, '('); + if (base) + { + size_t last = strlen (base) - 1; + if (base[last] == ')') + return base; + } + return NULL; +} + +bool +Module::setFile () +{ + if ((loadobject->flags & SEG_FLAG_DYNAMIC) != 0) + return true; + if ((loadobject->dbeFile->filetype & DbeFile::F_FICTION) != 0) + return false; + if ((flags & MOD_FLAG_UNKNOWN) != 0) + return true; + + if (lang_code == Sp_lang_java) + { + if (dbeFile->get_need_refind ()) + { + char *fnm = dbeFile->get_location (); + stabsPath = dbe_strdup (fnm); + stabsName = dbe_strdup (fnm); + disPath = dbe_strdup (fnm); + disName = dbe_strdup (fnm); + stabsMTime = dbeFile->sbuf.st_mtime; + } + return dbeFile->get_location () != NULL; + } + + if (dbeFile == NULL) + { + char *objname = get_obj_name_from_lib (name); + if (objname) + { + // in the format of libpath(obj) + objname = dbe_strdup (objname + 1); + size_t last = strlen (objname) - 1; + objname[last] = '\0'; + } + dbeFile = new DbeFile (objname ? objname : name); + free (objname); + dbeFile->filetype |= DbeFile::F_DOT_O; + } + if (dbeFile->get_need_refind ()) + { + disMTime = (time_t) 0; + stabsMTime = (time_t) 0; + free (disName); + free (stabsName); + disName = NULL; + stabsName = NULL; + + // Find the Executable/Shared-Object file of module + char *path = loadobject->dbeFile->get_location (); + if (path) + { + disPath = strdup (path); + disName = strdup (path); + disMTime = loadobject->dbeFile->sbuf.st_mtime; + } + + char *objname = get_obj_name_from_lib (name); + if (objname) + { + // in the format of libpath(obj) + char *namebuf = dbe_strdup (name); + char *base = namebuf + (objname - name); + *base = '\0'; + base++; + size_t last = strlen (base) - 1; + base[last] = '\0'; + stabsTmp = dbeSession->get_tmp_file_name (base, false); + dbeSession->tmp_files->append (strdup (stabsTmp)); + + DbeFile *dbf = dbeSession->getDbeFile (namebuf, + DbeFile::F_DOT_A_LIB | DbeFile::F_FILE); + path = dbf->get_location (); + int ar = -1, obj = -1; + if (path != NULL) + { + ar = open64 (path, O_RDONLY | O_LARGEFILE); + if (ar != -1) + obj = open64 (stabsTmp, O_CREAT | O_WRONLY | O_LARGEFILE, 0600); + } + if (ar != -1 && obj != -1 && read_ar (ar, obj, base)) + { + dbeFile->set_location (stabsTmp); + dbeFile->check_access (stabsTmp); // init 'sbuf' + dbeFile->sbuf.st_mtime = 0; // Don't check timestamps + dbeFile->container = dbf; + stabsPath = strdup (stabsTmp); + stabsName = strdup (path); + stabsMTime = dbeFile->sbuf.st_mtime; + } + else + { + removeStabsTmp (); + objname = NULL; + } + if (ar != -1) + close (ar); + if (obj != -1) + close (obj); + free (namebuf); + } + if (objname == NULL) + { + path = dbeFile->get_location (); + if (path != NULL) + { + stabsPath = strdup (path); + stabsName = strdup (path); + stabsMTime = hasDwarf ? 0 : dbeFile->sbuf.st_mtime; + } + } + + // First, try to access the symbol table of the module itself + // If failed, access the symbol table of the executable + if (stabsPath == NULL) + { + if (disPath == NULL) + return false; + stabsPath = strdup (disPath); + stabsName = strdup (disName); + stabsMTime = disMTime; + } + else if (disPath == NULL) + { + disPath = strdup (stabsPath); + disName = strdup (stabsName); + disMTime = stabsMTime; + } + } + return stabsPath != NULL; +} + +// openStabs -- open mappings from PCs to source lines +bool +Module::openStabs (bool all) +{ + if ((loadobject->flags & SEG_FLAG_DYNAMIC) != 0 + || (flags & MOD_FLAG_UNKNOWN) != 0) + return true; + if (loadobject->platform == Java) + { + setIncludeFile (NULL); + readFile (); + return ( status == AE_OK); + } + if (readStabs) + return true; + + // Read Stabs info. + int64_t Inode = main_source->getInode (); + char *fname = strrchr (file_name, (int) '/'); + char *mname = strrchr (main_source->get_name (), (int) '/'); + if (fname && mname && !streq (fname, mname)) + { + SourceFile *sf = findSource (file_name, false); + if (sf != NULL) + Inode = sf->getInode (); + } + + comComs = new Vector<ComC*>; + Stabs *stabs = openDebugInfo (); + if (stabs == NULL) + return false; + int st = stabs->read_stabs (Inode, this, comComs, true); + if (!hasDwarf && hasStabs && !streq (stabsPath, disPath)) + { + // Read stabs from .o file + if (dot_o_file == NULL) + { + if (dbeFile->get_location ()) + { + dot_o_file = createLoadObject (dbeFile->get_name ()); + dot_o_file->dbeFile->set_location (dbeFile->get_location ()); + dot_o_file->dbeFile->sbuf = dbeFile->sbuf; + dot_o_file->dbeFile->container = dbeFile->container; + } + } + if (dot_o_file + && dot_o_file->sync_read_stabs () == LoadObject::ARCHIVE_SUCCESS) + { + Stabs *stabs_o = dot_o_file->objStabs; + if (stabs_o) + { + st = stabs_o->read_stabs (Inode, this, + comComs->size () > 0 ? NULL : comComs); + Elf *elf_o = stabs_o->openElf (false); + if (elf_o->dwarf) + stabs->read_dwarf_from_dot_o (this); + } + } + } + if (all) + read_hwcprof_info (); + + readStabs = true; + return st == Stabs::DBGD_ERR_NONE; +} + +char * +Module::get_disasm (uint64_t inst_address, uint64_t end_address, + uint64_t start_address, uint64_t address, int64_t &inst_size) +{ + return disasm->get_disasm (inst_address, end_address, start_address, + address, inst_size); +} + +void +Module::read_stabs (bool all) +{ + if (openSourceFlag == AE_NOTREAD) + { + openSourceFlag = AE_OK; + if (lang_code == Sp_lang_java) + { + char *clpath = file_name; + if (clpath == NULL || strcmp (clpath, "<Unknown>") == 0) + clpath = ClassFile::get_java_file_name (name, false); + main_source = findSource (clpath, true); + main_source->dbeFile->filetype |= DbeFile::F_JAVA_SOURCE; + if (clpath != file_name) + free (clpath); + } + else + main_source = findSource (file_name, true); + if (setFile ()) + openStabs (all); + } +} + +bool +Module::openDisPC () +{ + if (disasm == NULL) + { + if (!(loadobject->flags & SEG_FLAG_DYNAMIC) && loadobject->platform != Java) + { + // Read Stabs & Symbol tables + if (openDebugInfo () == NULL) + return false; + if (!objStabs->read_symbols (functions)) + return false; + } + disasm = new Disasm (loadobject->platform, objStabs); + } + return true; +} + +static SourceFile *cmpSrcContext; // Use only for func_cmp + +static int +func_cmp (const void *a, const void *b) +{ + Function *fp1 = *((Function **) a); + Function *fp2 = *((Function **) b); + return fp1->func_cmp (fp2, cmpSrcContext); +} + +bool +Module::computeMetrics (DbeView *dbev, Function *func, MetricList *metrics, + Histable::Type type, bool src_metric, + bool func_scope, SourceFile *source) +{ + name_idx = metrics->get_listorder (NTXT ("name"), Metric::STATIC); + if (name_idx < 0) + { + metrics->print_metric_list (stderr, + GTXT ("Fatal: no name metric in Module::computeMetrics mlist:\n"), + 1); + abort (); + } + + // Now find the metrics for size and address, if present + size_index = metrics->get_listorder (NTXT ("size"), Metric::STATIC); + addr_index = metrics->get_listorder (NTXT ("address"), Metric::STATIC); + + // free the old cached data for both src and disassembly + // If it's disassembly with visible source metrics, we use both + if (dis_items) + { + delete dis_items; + dis_items = NULL; + } + if (src_items) + { + delete src_items; + src_items = NULL; + } + + // ask the DbeView to generate new data to be cached + if (src_metric || type == Histable::LINE) + { + Histable *obj = (func_scope) ? (Histable*) func : (Histable*)this; + if (lang_code == Sp_lang_java) + obj = func_scope ? (Histable *) func : + (source && source->get_type () == Histable::SOURCEFILE ? + (Histable *) source : (Histable *) this); + src_items = dbev->get_hist_data (metrics, Histable::LINE, 0, + Hist_data::MODL, obj, source); + } + if (type == Histable::INSTR) + dis_items = dbev->get_hist_data (metrics, Histable::INSTR, 0, + Hist_data::MODL, + func_scope ? (Histable*) func : (Histable*) this, + source); + + Hist_data *cur_hist_data; + if (type == Histable::INSTR) + cur_hist_data = dis_items; + else + cur_hist_data = src_items; + + Vector<Metric*> *items = cur_hist_data->get_metric_list ()->get_items (); + long sz = items->size (); + empty = new TValue[sz]; + memset (empty, 0, sizeof (TValue) * sz); + for (long i = 0; i < sz; i++) + empty[i].tag = items->get (i)->get_vtype (); + return true; +} + +// Method to get annotated source or disassembly for the module +// or a function within it +Hist_data * +Module::get_data (DbeView *dbev, MetricList *mlist, Histable::Type type, + TValue *ftotal, SourceFile *srcFile, Function *func, + Vector<int> *marks, int threshold, int vis_bits, + int src_visible, bool hex_vis, bool func_scope, + bool /*src_only*/, Vector<int_pair_t> *marks2d, + Vector<int_pair_t> *marks2d_inc) +{ + cur_dbev = dbev; + srcContext = srcFile ? srcFile : main_source; + read_stabs (); + status = AE_OK; + dbev->warning_msg = NULL; + dbev->error_msg = NULL; + if (type == Histable::LINE) + { + if (!srcContext->readSource ()) + { + status = AE_NOSRC; + dbev->error_msg = anno_str (srcContext->get_name ()); + return NULL; + } + if (!computeMetrics (dbev, func, mlist, type, false, func_scope, srcContext)) + { + status = AE_OTHER; + dbev->error_msg = anno_str (); + return NULL; + } + status = checkTimeStamp (false); + } + else + { // Histable::INSTR + Anno_Errors src_status = AE_OK; + if (!srcContext->readSource ()) + { + src_status = AE_NOSRC; + dbev->error_msg = anno_str (srcContext->get_name ()); + } + if (!setFile ()) + status = AE_NOLOBJ; + else + { + if (!openStabs ()) + src_status = AE_NOSTABS; + if (!openDisPC ()) + status = AE_NOSYMTAB; + } + if (status != AE_OK) + { + dbev->error_msg = anno_str (); + return NULL; + } + if (src_status != AE_OK && func != NULL) + { + if (loadobject->platform == Java && (func->flags & FUNC_FLAG_NATIVE) != 0) + { + append_msg (CMSG_ERROR, + GTXT ("`%s' is a native method; byte code not available\n"), + func->get_name ()); + status = AE_NOOBJ; + dbev->error_msg = anno_str (); + return NULL; + } + func_scope = true; + } + // get the disassembly-line metric data + if (!computeMetrics (dbev, func, mlist, type, + (src_visible & SRC_METRIC) != 0, + func_scope, srcContext)) + { + status = AE_OTHER; + dbev->error_msg = anno_str (); + return NULL; + } + status = checkTimeStamp (true); + } + total = ftotal; + + // initialize line number + init_line (); + + // initialize data -- get duplicate metric list for the line texts + // pick up the metric list from the computed data + MetricList *nmlist = NULL; + if (type == Histable::INSTR) + { + mlist = dis_items->get_metric_list (); + nmlist = new MetricList (mlist); + data_items = new Hist_data (nmlist, Histable::INSTR, Hist_data::MODL); + data_items->set_status (dis_items->get_status ()); + set_dis_data (func, vis_bits, dbev->get_cmpline_visible (), + src_visible, hex_vis, func_scope, + dbev->get_funcline_visible ()); + } + else + { + mlist = src_items->get_metric_list (); + nmlist = new MetricList (mlist); + data_items = new Hist_data (nmlist, Histable::LINE, Hist_data::MODL); + data_items->set_status (src_items->get_status ()); + set_src_data (func_scope ? func : NULL, vis_bits, + dbev->get_cmpline_visible (), + dbev->get_funcline_visible ()); + } + data_items->compute_minmax (); + + Metric *mitem; + int index; + Hist_data::HistItem *max_item; + TValue *value; + Hist_data::HistItem *max_item_inc; + TValue *value_inc; + double dthreshold = threshold / 100.0; + + int sz = data_items->get_metric_list ()->get_items ()->size (); + maximum = new TValue[sz]; + maximum_inc = new TValue[sz]; + memset (maximum, 0, sizeof (TValue) * sz); + memset (maximum_inc, 0, sizeof (TValue) * sz); + max_item = data_items->get_maximums (); + max_item_inc = data_items->get_maximums_inc (); + + Vec_loop (Metric*, data_items->get_metric_list ()->get_items (), index, mitem) + { + maximum_inc[index].tag = maximum[index].tag = mitem->get_vtype (); + + if (mitem->get_subtype () == Metric::STATIC) + continue; + if (!mitem->is_visible () && !mitem->is_tvisible () + && !mitem->is_pvisible ()) + continue; + + value = &max_item->value[index]; + value_inc = &max_item_inc->value[index]; + + double dthresh; + if (mitem->is_zeroThreshold () == true) + dthresh = 0; + else + dthresh = dthreshold; + switch (value->tag) + { + case VT_INT: + maximum[index].i = (int) (dthresh * (double) value->i); + maximum_inc[index].i = (int) (dthresh * (double) value_inc->i); + break; + case VT_DOUBLE: + maximum[index].d = dthresh * value->d; + maximum_inc[index].d = dthresh * value_inc->d; + break; + case VT_LLONG: + maximum[index].ll = (unsigned long long) (dthresh * (double) value->ll); + maximum_inc[index].ll = (unsigned long long) + (dthresh * (double) value_inc->ll); + break; + case VT_ULLONG: + maximum[index].ull = (unsigned long long) + (dthresh * (double) value->ull); + maximum_inc[index].ull = (unsigned long long) + (dthresh * (double) value_inc->ull); + break; + default: + // not needed for non-numerical metrics + break; + } + } + + // mark all high values + for (int index1 = 0; index1 < data_items->size (); index1++) + { + Hist_data::HistItem *hi = data_items->fetch (index1); + int index2; + Vec_loop (Metric*, nmlist->get_items (), index2, mitem) + { + bool mark = false; + if (mitem->get_subtype () == Metric::STATIC) + continue; + if (!mitem->is_visible () && !mitem->is_tvisible () + && !mitem->is_pvisible ()) + continue; + + switch (hi->value[index2].tag) + { + case VT_DOUBLE: + if (nmlist->get_type () == MET_SRCDIS + && data_items->get_callsite_mark ()->get (hi->obj)) + { + if (hi->value[index2].d > maximum_inc[index2].d) + mark = true; + break; + } + if (hi->value[index2].d > maximum[index2].d) + mark = true; + break; + case VT_INT: + if (nmlist->get_type () == MET_SRCDIS + && data_items->get_callsite_mark ()->get (hi->obj)) + { + if (hi->value[index2].i > maximum_inc[index2].i) + mark = true; + break; + } + if (hi->value[index2].i > maximum[index2].i) + mark = true; + break; + case VT_LLONG: + if (nmlist->get_type () == MET_SRCDIS + && data_items->get_callsite_mark ()->get (hi->obj)) + { + if (hi->value[index2].ll > maximum_inc[index2].ll) + mark = true; + break; + } + if (hi->value[index2].ll > maximum[index2].ll) + mark = true; + break; + case VT_ULLONG: + if (nmlist->get_type () == MET_SRCDIS + && data_items->get_callsite_mark ()->get (hi->obj)) + { + if (hi->value[index2].ull > maximum_inc[index2].ull) + mark = true; + break; + } + if (hi->value[index2].ull > maximum[index2].ull) + mark = true; + break; + // ignoring the following cases (why?) + case VT_SHORT: + case VT_FLOAT: + case VT_HRTIME: + case VT_LABEL: + case VT_ADDRESS: + case VT_OFFSET: + break; + } + if (mark) + { + marks->append (index1); + break; + } + } + } + + // mark all high values to marks2d + if (marks2d != NULL && marks2d_inc != NULL) + { + for (int index1 = 0; index1 < data_items->size (); index1++) + { + Hist_data::HistItem *hi = data_items->fetch (index1); + int index2; + Vec_loop (Metric*, nmlist->get_items (), index2, mitem) + { + Metric::SubType subType = mitem->get_subtype (); + if (subType == Metric::STATIC) + continue; + if (!mitem->is_visible () && !mitem->is_tvisible () + && !mitem->is_pvisible ()) + continue; + switch (hi->value[index2].tag) + { + case VT_DOUBLE: + if (nmlist->get_type () == MET_SRCDIS + && data_items->get_callsite_mark ()->get (hi->obj)) + { + if (hi->value[index2].d > maximum_inc[index2].d) + { + int_pair_t pair = {index1, index2}; + marks2d_inc->append (pair); + } + break; + } + if (hi->value[index2].d > maximum[index2].d) + { + int_pair_t pair = {index1, index2}; + marks2d->append (pair); + } + break; + case VT_INT: + if (nmlist->get_type () == MET_SRCDIS + && data_items->get_callsite_mark ()->get (hi->obj)) + { + if (hi->value[index2].i > maximum_inc[index2].i) + { + int_pair_t pair = {index1, index2}; + marks2d_inc->append (pair); + } + break; + } + if (hi->value[index2].i > maximum[index2].i) + { + int_pair_t pair = {index1, index2}; + marks2d->append (pair); + } + break; + case VT_LLONG: + if (nmlist->get_type () == MET_SRCDIS + && data_items->get_callsite_mark ()->get (hi->obj)) + { + if (hi->value[index2].ll > maximum_inc[index2].ll) + { + int_pair_t pair = {index1, index2}; + marks2d_inc->append (pair); + } + break; + } + if (hi->value[index2].ll > maximum[index2].ll) + { + int_pair_t pair = {index1, index2}; + marks2d->append (pair); + } + break; + case VT_ULLONG: + if (nmlist->get_type () == MET_SRCDIS + && data_items->get_callsite_mark ()->get (hi->obj)) + { + if (hi->value[index2].ull > maximum_inc[index2].ull) + { + int_pair_t pair = {index1, index2}; + marks2d_inc->append (pair); + } + break; + } + if (hi->value[index2].ull > maximum[index2].ull) + { + int_pair_t pair = {index1, index2}; + marks2d->append (pair); + } + break; + case VT_SHORT: + case VT_FLOAT: + case VT_HRTIME: + case VT_LABEL: + case VT_ADDRESS: + case VT_OFFSET: + break; + } + } + } + } + + // free memory used by Computing & Printing metrics + delete[] maximum; + delete[] maximum_inc; + delete[] empty; + maximum = NULL; + maximum_inc = NULL; + empty = NULL; + dbev->warning_msg = anno_str (); + return data_items; +} + +Vector<uint64_t> * +Module::getAddrs (Function *func) +{ + uint64_t start_address = func->img_offset; + uint64_t end_address = start_address + func->size; + int64_t inst_size = 0; + + // initialize "disasm" if necessary + if (!openDisPC ()) + return NULL; + + Vector<uint64_t> *addrs = new Vector<uint64_t>; + for (uint64_t inst_address = start_address; inst_address < end_address;) + { + char *s = disasm->get_disasm (inst_address, end_address, start_address, + func->img_offset, inst_size); + free (s); + addrs->append (inst_address - start_address); + inst_address += inst_size; + if (inst_size == 0) + break; + } + return addrs; +} + +void +Module::init_line () +{ + // initialize the compiler commentary data + cindex = 0; + if (comComs != NULL && comComs->size () > 0) + cline = comComs->fetch (cindex)->line; + else + cline = -1; + + sindex = 0; + if (src_items && src_items->size () > 0) + sline = ((DbeLine*) src_items->fetch (0)->obj)->lineno; + else + sline = -1; + + dindex = 0; + mindex = 0; + mline = -1; + if (dis_items && dis_items->size () > 0) + { + daddr = (DbeInstr*) dis_items->fetch (0)->obj; + + // After sorting all HistItems with PCLineFlag appear + // at the end of the list. Find the first one. + for (mindex = dis_items->size () - 1; mindex >= 0; mindex--) + { + Hist_data::HistItem *item = dis_items->fetch (mindex); + if (!(((DbeInstr*) item->obj)->flags & PCLineFlag)) + break; + mline = (unsigned) (((DbeInstr*) item->obj)->addr); + } + mindex++; + } + else + daddr = NULL; +} + +void +Module::set_src_data (Function *func, int vis_bits, int cmpline_visible, + int funcline_visible) +{ + Function *curr_func = NULL; + + // start at the top of the file, and loop over all lines in the file (source context) + for (curline = 1; curline <= srcContext->getLineCount (); curline++) + { + // Before writing the line, see if there's compiler commentary to insert + if (cline == curline) + set_ComCom (vis_bits); + + // Find out if we need to print zero metrics with the line + DbeLine *dbeline = srcContext->find_dbeline (NULL, curline); + Anno_Types type = AT_SRC_ONLY; + if (dbeline->dbeline_func_next) + { + if (func) + for (DbeLine *dl = dbeline->dbeline_func_next; dl; dl = dl->dbeline_func_next) + { + if (dl->func == func) + { + type = AT_SRC; + break; + } + } + else + type = AT_SRC; + } + + if (funcline_visible) + { // show red lines + // is there a function index line to insert? + Function *func_next = NULL; + for (DbeLine *dl = dbeline; dl; dl = dl->dbeline_func_next) + { + Function *f = dl->func; + if (f && f->line_first == curline + && f->getDefSrc () == srcContext) + { + if (lang_code == Sp_lang_java + && (f->flags & FUNC_FLAG_DYNAMIC)) + continue; + if (cur_dbev && cur_dbev->get_path_tree ()->get_func_nodeidx (f)) + { + func_next = f; + break; + } + else if (func_next == NULL) + func_next = f; + } + } + if (func_next && curr_func != func_next) + { + curr_func = func_next; + char *func_name = curr_func->get_name (); + if (is_fortran () && streq (func_name, NTXT ("MAIN_"))) + func_name = curr_func->get_match_name (); + Hist_data::HistItem *item = + src_items->new_hist_item (curr_func, AT_FUNC, empty); + item->value[name_idx].l = dbe_sprintf (GTXT ("<Function: %s>"), + func_name); + data_items->append_hist_item (item); + } + } // end of red line + set_src (type, dbeline); // add the source line + } // end of loop over source lines + + // See if compiler flags are set; if so, append them + if (cmpline_visible && comp_flags) + { + Hist_data::HistItem *item = src_items->new_hist_item (NULL, AT_EMPTY, + empty); + item->value[name_idx].l = strdup (NTXT ("")); + data_items->append_hist_item (item); + item = src_items->new_hist_item (NULL, AT_COM, empty); + item->value[name_idx].l = dbe_sprintf (GTXT ("Compile flags: %s"), + comp_flags); + data_items->append_hist_item (item); + } +} + +void +Module::set_dis_data (Function *func, int vis_bits, int cmpline_visible, + int src_visible, bool hex_vis, bool func_scope, + int funcline_visible) +{ + bool nextFile = false; + + // initialize the source output, if any + curline = (srcContext->getLineCount () > 0) ? 1 : -1; + if (func) + nextFile = srcContext != func->getDefSrc (); + curr_inc = srcContext; + + bool src_code = (src_visible & SRC_CODE); + Anno_Types src_type = (src_visible & SRC_METRIC) ? AT_SRC : AT_SRC_ONLY; + + char *img_fname = func ? func->img_fname : NULL; + + // Build a new Function list + Vector<Function*> *FuncLst = new Vector<Function*>; + if (func_scope) + { + if (func) + FuncLst->append (func); + } + else + { + for (int i = 0, sz = functions ? functions->size () : 0; i < sz; i++) + { + Function *fitem = functions->fetch (i); + if (fitem != fitem->cardinal ()) + continue; + if (img_fname == NULL) + img_fname = fitem->img_fname; + if (fitem->img_fname == NULL || strcmp (fitem->img_fname, img_fname)) + continue; + FuncLst->append (fitem); + } + } + if (FuncLst->size () == 0) + { // no function is good + delete FuncLst; + return; + } + cmpSrcContext = srcContext; + FuncLst->sort (func_cmp); + + disasm->set_hex_visible (hex_vis); + for (int index = 0, sz = FuncLst->size (); index < sz; index++) + { + Function *fitem = FuncLst->fetch (index); + uint64_t start_address, end_address; + int64_t inst_size; + if (fitem->getDefSrc () != srcContext && curline > 0) + { + // now flush the left source line, if available + for (; curline <= srcContext->getLineCount (); curline++) + { + // see if there's a compiler comment line to dump + if (cline == curline) + set_ComCom (vis_bits); + if (src_code) + set_src (src_type, srcContext->find_dbeline (curline)); + } + curline = -1; + } + + curr_inc = NULL; + // disassemble one function + start_address = objStabs ? + objStabs->mapOffsetToAddress (fitem->img_offset) : 0; + end_address = start_address + fitem->size; + inst_size = 0; + + disasm->set_addr_end (end_address); + if ((loadobject->flags & SEG_FLAG_DYNAMIC) + && loadobject->platform != Java) + disasm->set_img_name (img_fname); + + for (uint64_t inst_address = start_address; inst_address < end_address;) + { + uint64_t address = inst_address - start_address; + DbeInstr *instr = fitem->find_dbeinstr (0, address); + DbeLine *dbeline = (DbeLine *) (instr->convertto (Histable::LINE)); + if (instr->lineno == -1 && dbeline && dbeline->lineno > 0) + instr->lineno = dbeline->lineno; + + // now write the unannotated source line, if available + if (curline > 0) + { // source is present + int lineno = curline - 1; + if (instr->lineno != -1) + { + if (dbeline && streq (dbeline->sourceFile->get_name (), + srcContext->get_name ())) + lineno = instr->lineno; + } + else if (curr_inc == NULL && srcContext == fitem->def_source + && fitem->line_first > 0) + lineno = fitem->line_first; + + for (; curline <= lineno; curline++) + { + // see if there's a compiler comment line to dump + if (cline == curline) + set_ComCom (vis_bits); + if (mline == curline) + set_MPSlave (); + if (src_code) + set_src (src_type, srcContext->find_dbeline (curline)); + if (curline >= srcContext->getLineCount ()) + { + curline = -1; + break; + } + } + } + + if (funcline_visible) + { // show red lines + if (!curr_inc || (dbeline && curr_inc != dbeline->sourceFile)) + { + Hist_data::HistItem *item = dis_items->new_hist_item (dbeline, AT_FUNC, empty); + curr_inc = dbeline ? dbeline->sourceFile : srcContext; + char *str; + if (curr_inc != srcContext) + { + char *fileName = curr_inc->dbeFile->getResolvedPath (); + str = dbe_sprintf (GTXT ("<Function: %s, instructions from source file %s>"), + fitem->get_name (), fileName); + } + else + str = dbe_sprintf (GTXT ("<Function: %s>"), + fitem->get_name ()); + item->value[name_idx].l = str; + data_items->append_hist_item (item); + } + } + + char *dis_str = get_disasm (inst_address, end_address, start_address, + fitem->img_offset, inst_size); + if (inst_size == 0) + break; + else if (instr->size == 0) + instr->size = (unsigned int) inst_size; + inst_address += inst_size; + + // stomp out control characters + for (size_t i = 0, len = strlen (dis_str); i < len; i++) + { + if (dis_str[i] == '\t') + dis_str[i] = ' '; + } + + for (int i = 0; i < bTargets.size (); i++) + { + target_info_t *bTarget = bTargets.fetch (i); + if (bTarget->offset == fitem->img_offset + address) + { + // insert a new line for the bTarget + size_t colon = strcspn (dis_str, NTXT (":")); + char *msg = GTXT ("* <branch target>"); + size_t len = colon + strlen (msg); + len = (len < 50) ? (50 - len) : 1; + char *new_dis_str = dbe_sprintf ("%.*s%s%*c <===----<<<", + (int) colon, dis_str, msg, + (int) len, ' '); + DbeInstr *bt = fitem->find_dbeinstr (PCTrgtFlag, address); + bt->lineno = instr->lineno; + bt->size = 0; + set_dis (bt, AT_DIS, nextFile, new_dis_str); + break; + } + } + + // AnalyzerInfo/Datatype annotations + if (infoList != NULL) + { + inst_info_t *info = NULL; + int pinfo; + Vec_loop (inst_info_t*, infoList, pinfo, info) + { + if (info->offset == fitem->img_offset + address) break; + } + if (info != NULL) + { // got a matching memop + char typetag[400]; + typetag[0] = '\0'; + long t; + datatype_t *dtype = NULL; + Vec_loop (datatype_t*, datatypes, t, dtype) + { + if (dtype->datatype_id == info->memop->datatype_id) + break; + } + if (datatypes != NULL) + { + size_t len = strlen (typetag); + if (dtype == NULL || t == datatypes->size ()) + snprintf (typetag + len, sizeof (typetag) - len, "%s", + PTXT (DOBJ_UNSPECIFIED)); + else if (dtype->dobj == NULL) + snprintf (typetag + len, sizeof (typetag) - len, "%s", + PTXT (DOBJ_UNDETERMINED)); + else + snprintf (typetag + len, sizeof (typetag) - len, "%s", + dtype->dobj->get_name ()); + } + if (strlen (typetag) > 1) + { + char *new_dis_str; + new_dis_str = dbe_sprintf ("%-50s %s", dis_str, typetag); + free (dis_str); + dis_str = new_dis_str; + } + } + } + set_dis (instr, AT_DIS, nextFile, dis_str); + } + } + + // now flush the left source line, if available + if (curline > 0) + { // source is present + for (; curline <= srcContext->getLineCount (); curline++) + { + // see if there's a compiler comment line to dump + if (cline == curline) + set_ComCom (vis_bits); + + if (src_code) + set_src (src_type, srcContext->find_dbeline (curline)); + } + } + + // See if compiler flags are set; if so, append them + if (cmpline_visible && comp_flags) + { + Hist_data::HistItem *item = dis_items->new_hist_item (NULL, AT_EMPTY, + empty); + item->value[name_idx].l = dbe_strdup (NTXT ("")); + data_items->append_hist_item (item); + item = dis_items->new_hist_item (NULL, AT_COM, empty); + item->value[name_idx].l = dbe_sprintf (GTXT ("Compile flags: %s"), + comp_flags); + data_items->append_hist_item (item); + } + delete FuncLst; +} + +// set_src -- inserts one or more lines into the growing data list +void +Module::set_src (Anno_Types type, DbeLine *dbeline) +{ + Hist_data::HistItem *item; + + // Flush items that are not represented in source + while (sline >= 0 && sline < curline) + { + item = src_items->fetch (sindex); + if (((DbeLine*) item->obj)->lineno > 0) + set_one (item, AT_QUOTE, item->obj->get_name ()); + + if (++sindex < src_items->size ()) // get next line with metrics + sline = ((DbeLine*) src_items->fetch (sindex)->obj)->lineno; + else + sline = -1; + } + + // write values in the metric fields for the given source line + if (curline == sline) + { // got metrics for this line + item = src_items->fetch (sindex); + if (((DbeLine*) item->obj)->lineno > 0) + set_one (item, AT_SRC, srcContext->getLine (curline)); + + if (++sindex < src_items->size ()) // get next line metric index + sline = ((DbeLine*) src_items->fetch (sindex)->obj)->lineno; + else + sline = -1; + } + else + { + item = data_items->new_hist_item (dbeline, type, empty); + if (size_index != -1) + item->value[size_index].ll = dbeline->get_size (); + if (addr_index != -1) + item->value[addr_index].ll = dbeline->get_addr (); + item->value[name_idx].l = dbe_strdup (srcContext->getLine (curline)); + data_items->append_hist_item (item); + } +} + +void +Module::set_dis (DbeInstr *instr, Anno_Types type, bool nextFile, char *dis_str) +{ + // Flush items that are not represented in disassembly + while (daddr && daddr->pc_cmp (instr) < 0) + { + if (!nextFile) + set_one (dis_items->fetch (dindex), AT_QUOTE, daddr->get_name ()); + if (++dindex < dis_items->size ()) // get next line metric index + daddr = (DbeInstr*) dis_items->fetch (dindex)->obj; + else + daddr = NULL; + } + + // Write values in the metric fields for the given pc index value + if (instr->inlinedInd >= 0) + { + StringBuilder sb; + sb.append (dis_str); + instr->add_inlined_info (&sb); + free (dis_str); + dis_str = sb.toString (); + } + if (daddr && daddr->pc_cmp (instr) == 0) + { + Hist_data::HistItem *item = data_items->new_hist_item (instr, type, + dis_items->fetch (dindex)->value); + item->value[name_idx].tag = VT_LABEL; + item->value[name_idx].l = dis_str; + data_items->append_hist_item (item); + if (dis_items->get_callsite_mark ()->get (dis_items->fetch (dindex)->obj)) + data_items->get_callsite_mark ()->put (item->obj, 1); + + if (++dindex < dis_items->size ()) // get next line metric index + daddr = (DbeInstr*) dis_items->fetch (dindex)->obj; + else + daddr = NULL; + } + else + { + // create a new item for this PC + Hist_data::HistItem *item = dis_items->new_hist_item (instr, type, empty); + if (size_index != -1) + item->value[size_index].ll = instr->size; + if (addr_index != -1) + item->value[addr_index].ll = instr->get_addr (); + item->value[name_idx].tag = VT_LABEL; + item->value[name_idx].l = dis_str; + data_items->append_hist_item (item); + } +} + +void +Module::set_MPSlave () +{ + Hist_data::HistItem *item; + Function *fp; + int index; + + // write the inclusive metrics for slave threads + while (mline == curline) + { + item = dis_items->fetch (mindex); + DbeInstr *instr = (DbeInstr *) item->obj; + Vec_loop (Function*, functions, index, fp) + { + if (fp->derivedNode == instr) + { + set_one (item, AT_QUOTE, (fp->isOutlineFunction) ? + GTXT ("<inclusive metrics for outlined functions>") : + GTXT ("<inclusive metrics for slave threads>")); + break; + } + } + + mindex++; + if (mindex < dis_items->size ()) + mline = (unsigned) ((DbeInstr*) (dis_items->fetch (mindex)->obj))->addr; + else + mline = -1; + } +}//set_MPSlave + +void +Module::set_one (Hist_data::HistItem *org_item, Anno_Types type, + const char *text) +{ + if (org_item == NULL) + return; + Hist_data::HistItem *item = data_items->new_hist_item (org_item->obj, type, + org_item->value); + item->value[name_idx].tag = VT_LABEL; + item->value[name_idx].l = dbe_strdup (text); + data_items->append_hist_item (item); + if (org_item != NULL && src_items != NULL + && src_items->get_callsite_mark ()->get (org_item->obj)) + data_items->get_callsite_mark ()->put (item->obj, 1); +}//set_one + +void +Module::set_ComCom (int vis_bits) +{ + Hist_data::HistItem *item; + Function *func = dbeSession->get_Unknown_Function (); + + if (vis_bits) + { + // precede the compiler commentary with a blank line + item = data_items->new_hist_item (func, AT_EMPTY, empty); + item->value[name_idx].l = dbe_strdup (NTXT ("")); + data_items->append_hist_item (item); + } + while (cline == curline) + { + ComC *comm = comComs->fetch (cindex); + if (comm->visible & vis_bits) + { + // write the compiler commentary + item = data_items->new_hist_item (func, AT_COM, empty); + item->value[name_idx].l = dbe_strdup (comm->com_str); + data_items->append_hist_item (item); + } + if (++cindex < comComs->size ()) + cline = comComs->fetch (cindex)->line; + else + cline = -1; + } +} + +void +Module::dump_dataobjects (FILE *out) +{ + int index; + datatype_t *dtype; + Vec_loop (datatype_t*, datatypes, index, dtype) + { + fprintf (out, NTXT ("[0x%08X,%6lld] %4d %6d %s "), dtype->datatype_id, + dtype->dobj ? dtype->dobj->id : 0LL, + dtype->memop_refs, dtype->event_data, + (dtype->dobj != NULL ? (dtype->dobj->get_name () ? + dtype->dobj->get_name () : "<NULL>") : "<no object>")); +#if DEBUG + Histable* scope = dtype->dobj ? dtype->dobj->get_scope () : NULL; + if (scope != NULL) + { + switch (scope->get_type ()) + { + case Histable::LOADOBJECT: + case Histable::FUNCTION: + fprintf (out, NTXT ("%s"), scope->get_name ()); + break; + case Histable::MODULE: + { + char *filename = get_basename (scope->get_name ()); + fprintf (out, NTXT ("%s"), filename); + break; + } + default: + fprintf (out, NTXT ("\tUnexpected scope %d:%s"), + scope->get_type (), scope->get_name ()); + } + } +#endif + fprintf (out, NTXT ("\n")); + } +} + +void +Module::set_name (char *str) +{ + free (name); + name = str; +} + +void +Module::read_hwcprof_info () +{ + if (hwcprof == 0) + { + hwcprof = 1; + Stabs *stabs = openDebugInfo (); + if (stabs) + stabs->read_hwcprof_info (this); + } +} + +void +Module::reset_datatypes () +{ + for (int i = 0, sz = datatypes ? datatypes->size () : -1; i < sz; i++) + { + datatype_t *t = datatypes->fetch (i); + t->event_data = 0; + } +} + +DataObject * +Module::get_dobj (uint32_t dtype_id) +{ + read_hwcprof_info (); + for (int i = 0, sz = datatypes ? datatypes->size () : -1; i < sz; i++) + { + datatype_t *t = datatypes->fetch (i); + if (t->datatype_id == dtype_id) + { + t->event_data++; + return t->dobj; + } + } + return NULL; +} + +int +Module::readFile () +{ + return AE_OK; +} + +Vector<Histable*> * +Module::get_comparable_objs () +{ + update_comparable_objs (); + if (comparable_objs || dbeSession->expGroups->size () <= 1 || loadobject == NULL) + return comparable_objs; + Vector<Histable*> *comparableLoadObjs = loadobject->get_comparable_objs (); + if (comparableLoadObjs == NULL) + return NULL; + comparable_objs = new Vector<Histable*>(comparableLoadObjs->size ()); + for (int i = 0, sz = comparableLoadObjs->size (); i < sz; i++) + { + Module *mod = NULL; + LoadObject *lo = (LoadObject*) comparableLoadObjs->fetch (i); + if (lo) + { + mod = lo->get_comparable_Module (this); + if (mod) + mod->comparable_objs = comparable_objs; + } + comparable_objs->store (i, mod); + } + dump_comparable_objs (); + return comparable_objs; +} + +JMethod * +Module::find_jmethod (const char *nm, const char *sig) +{ + // Vladimir: Probably we should not use linear search + for (long i = 0, sz = VecSize (functions); i < sz; i++) + { + JMethod *jmthd = (JMethod*) functions->get (i); + char *jmt_name = jmthd->get_name (Histable::SHORT); + if (strcmp (jmt_name, nm) == 0 + && strcmp (jmthd->get_signature (), sig) == 0) + return jmthd; + } + return NULL; +} diff --git a/gprofng/src/Module.h b/gprofng/src/Module.h new file mode 100644 index 0000000..39c4322 --- /dev/null +++ b/gprofng/src/Module.h @@ -0,0 +1,284 @@ +/* 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. */ + +#ifndef _MODULE_H +#define _MODULE_H + +// A Module object represents a .o file that was used to build up a segement. +// Its main function is to compute source and disassembly annotations +// Text reordering and/or function outlining imply that a module may +// not be contiguous. + +#include "Histable.h" +#include "Hist_data.h" + +#define MOD_FLAG_UNKNOWN 0x01 + +class LoadObject; +class MetricList; +class ComC; +class Disasm; +class Hist_data; +class Stabs; +class SourceFile; +class DataObject; +class JMethod; +template <class ITEM> class Vector; + +class InlinedSubr +{ +public: + InlinedSubr (); + DbeLine *dbeLine; + Function *func; + char *fname; + uint64_t low_pc; + uint64_t high_pc; + int level; + + bool + contains (InlinedSubr *p) + { + return low_pc <= p->low_pc && high_pc >= p->high_pc; + } + + bool + contains (uint64_t pc) + { + return low_pc <= pc && high_pc > pc; + } +}; + +class Module : public HistableFile +{ +public: + // Annotated Source or Disassembly + enum Anno_Errors + { + AE_OK, + AE_NOTREAD, + AE_NOSRC, + AE_NOOBJ, + AE_NOLOBJ, + AE_NOSTABS, + AE_NOSYMTAB, + AE_TIMESRC, + AE_TIMEDIS, + AE_TIMESTABS, + AE_TIMESTABS_DIFF, + AE_OTHER + }; + + // The following enums are duplicated in Java + enum Anno_Types + { + AT_LIST = 0, + AT_SRC, + AT_SRC_ONLY, + AT_DIS, + AT_COM, + AT_QUOTE, + AT_FUNC, + AT_EMPTY, + AT_DIS_ONLY + }; + + Module (); + virtual ~Module (); + virtual int64_t get_size (); + virtual void set_name (char *str); + virtual Vector<Histable*> *get_comparable_objs (); + virtual int readFile (); + + virtual Histable_type + get_type () + { + return MODULE; + } + + inline Anno_Errors + get_status () + { + return status; + } + + inline void + set_file_name (char *fnm) + { + free (file_name); + file_name = fnm; + } + + // get error string + char *anno_str (char *fnm = NULL); + + // generate annotated source/disassembly data + Hist_data *get_data (DbeView *dbev, MetricList *mlist, + Histable::Type type, TValue *ftotal, SourceFile *srcFile, + Function *func, Vector<int> *marks, int threshold, + int vis_bits, int src_visible, bool hex_visible, + bool func_scope, bool src_only, + Vector<int_pair_t> *marks2d = NULL, + Vector<int_pair_t> *marks2d_inc = NULL); + + Vector<uint64_t> *getAddrs (Function *func); + SourceFile *setIncludeFile (char *includeFile); + + SourceFile * + getIncludeFile () + { + return curr_inc; + } + + SourceFile * + getMainSrc () + { + return main_source; + } + + char * + getResolvedObjectPath () + { + return stabsPath ? stabsPath : get_name (); + } + + char * + getDebugPath () + { + setFile (); + return stabsPath; + } + + void read_stabs (bool all = true); + void dump_dataobjects (FILE *out); + DataObject *get_dobj (uint32_t dtype_id); + void reset_datatypes (); + void read_hwcprof_info (); + bool is_fortran (); + SourceFile *findSource (const char *fname, bool create); + bool openStabs (bool all = true); + LoadObject *createLoadObject (const char *lo_name); + JMethod *find_jmethod (const char *nm, const char *sig); + + unsigned int flags; // flags used for marking traversals + Sp_lang_code lang_code; // What is source lang. in module + char *file_name; // Full path to actual source file + Vector<Function*> *functions; // Unordered list of functions + LoadObject *loadobject; // Parent loadobject + LoadObject *dot_o_file; // The .o file with debug information + unsigned fragmented; // -xF used when compiling module + int real_timestamp; // Linked timestamp from N_OPT stab + int curr_timestamp; // Current object timestamp from N_OPT stab + char *comp_flags; // compiler flags used to compile module + char *comp_dir; // directory used to compile module + char *linkerStabName; // Name from 'N_UNDF' stab + Stabs *objStabs; // stabs of object file + bool readStabs; + bool hasStabs; + bool hasDwarf; + uint64_t hdrOffset; // offset in .debug_info + unsigned hwcprof; // hwcprof info status + Vector<inst_info_t*> *infoList; // merged list + Vector<memop_info_t*> ldMemops; // load instructions + Vector<memop_info_t*> stMemops; // store instructions + Vector<memop_info_t*> pfMemops; // prefetch instructions + Vector<target_info_t*> bTargets; // branch targets + Vector<datatype_t*> *datatypes; // object type descriptors + Vector<SourceFile*> *includes; + Module *indexStabsLink; // correspondent module for the .o file + InlinedSubr *inlinedSubr; + +protected: + void removeStabsTmp (); // Remove temporary *.o (got from *.a) + + // Check timestamp, warn users if src/dis/stabs later than exp. + Anno_Errors checkTimeStamp (bool chkDis); + + // Set paths for reading Stabs and Symbols + bool read_ar (int ar, int obj, char *obj_base); + bool setFile (); + + // Open appropriate symbol tables, construct set of PC ranges, + // and maps to source lines for each PC + Stabs *openDebugInfo (); + + // Construct PC index table + bool openDisPC (); + + // Compute data--scan data to compute metrics per-src-line/dis-line + bool computeMetrics (DbeView *dbev, Function *func, MetricList *mlist, + Histable::Type type, bool src_metric, + bool func_scope, SourceFile *source); + void init_line (); + void init_index (Hist_data *witems, int &wlindex, int &wmsize, int &wmindex); + + void set_src_data (Function *func, int vis_bits, int cmpline_visible, + int funcline_visible); + void set_dis_data (Function *func, int vis_bits, int cmpline_visible, + int src_visible, bool hex_vis, bool func_scope, + int funcline_visible); + void set_src (Anno_Types type, DbeLine *dbeline); + void set_dis (DbeInstr *instr, Anno_Types type, bool nextFile, char *dis_str); + void set_MPSlave (); + void set_one (Hist_data::HistItem *org_item, Anno_Types type, const char *text); + void set_ComCom (int vis_bits); + + virtual char *get_disasm (uint64_t inst_address, uint64_t end_address, + uint64_t start_address, uint64_t f_offset, + int64_t &inst_size); + + Anno_Errors status; + Anno_Errors openSourceFlag; + bool hexVisible; // show hex code in disasm + time_t disMTime; // Creating time for disassembly + time_t stabsMTime; // Creating time for stabs + SourceFile *main_source; + SourceFile *curr_inc; // pointer to include file or NULL + SourceFile *srcContext; + Vector<ComC*> *comComs; // table of compiler comments + Disasm *disasm; + Hist_data *src_items; + Hist_data *dis_items; + Hist_data *data_items; + DbeView * cur_dbev; + TValue *total; + TValue *maximum; + TValue *maximum_inc; + TValue *empty; + int name_idx; // index of name metric in list for src/dis + int size_index; // index of size metric in list for src/dis + int addr_index; // index of address metric in list for src/dis + + int curline; // line# of next source line to be processed + int cindex, cline; // index and src line of next compiler-comment + int sindex, sline; // index and src line of next item in src_items + int dindex; + DbeInstr *daddr; // pointer to next DbeInstr with metrics + int mindex; // MP index and src line of next metric-value + int mline; // MP line to be processed by source + + char *disPath; // path for disassembly + char *stabsPath; // path for reading stabs + char *stabsTmp; // temporary *.o from *.a + char *disName; // library/path for disassembly + char *stabsName; // library/path for stabs +}; + +#endif /* _MODULE_H */ diff --git a/gprofng/src/Ovw_data.cc b/gprofng/src/Ovw_data.cc new file mode 100644 index 0000000..2cd5718 --- /dev/null +++ b/gprofng/src/Ovw_data.cc @@ -0,0 +1,242 @@ +/* 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 <memory.h> +#include <values.h> +#include <assert.h> +#include "Data_window.h" +#include "Exp_Layout.h" +#include "Table.h" +#include "Ovw_data.h" +#include "Sample.h" +#include "data_pckts.h" +#include "util.h" +#include "i18n.h" + +void +Ovw_data::sum (Ovw_data *data) +{ + Ovw_item data_totals = data->get_totals (); + if (totals == NULL) + { + totals = reset_item (new Ovw_item); + *totals = data_totals; + totals->start.tv_sec = totals->end.tv_sec = -1; + totals->start.tv_nsec = totals->end.tv_nsec = 0; + } + else + { + tsadd (&totals->duration, &data_totals.duration); + tsadd (&totals->tlwp, &data_totals.tlwp); + if (tstodouble (totals->duration) != 0) + totals->nlwp = tstodouble (totals->tlwp) / tstodouble (totals->duration); + + for (int i = 0, size = totals->size; i < size; i++) + tsadd (&totals->values[i].t, &data_totals.values[i].t); + } +} + +Ovw_data::Ovw_item * +Ovw_data::reset_item (Ovw_data::Ovw_item *item) +{ + memset (item, 0, sizeof (*item)); + return item; +} + +Ovw_data::Ovw_item +Ovw_data::get_totals () +{ + // This routine will return the totals values for item in the sample. + // Compute maximums and totals only once, and save the result. + // On subsequent calls, just return the saved result. + // If maximums is NULL, then totals is also NULL + if (totals != NULL) + return *totals; + + timestruc_t zero = {0, 0}; + totals = reset_item (new Ovw_item); + totals->start.tv_sec = MAXINT; // new + totals->start.tv_nsec = MAXINT; // new + totals->start_label = totals->end_label = NTXT ("Total"); + totals->type = VT_HRTIME; + + int nsampsel = 0; + for (int index = 0; index < size (); index++) + { + Ovw_item item = fetch (index); + nsampsel++; + + // Compute totals + for (int i = 0; i < OVW_NUMVALS + 1; i++) + tsadd (&totals->values[i].t, &item.values[i].t); + + int_max (&totals->states, item.states); + tsadd (&totals->total.t, &item.total.t); + int_max (&totals->size, item.size); + tsadd (&totals->duration, &item.duration); + tsadd (&totals->tlwp, &item.tlwp); + totals->number += item.number; + if (tscmp (&totals->start, &item.start) > 0) + totals->start = item.start; + if (tscmp (&totals->end, &item.end) < 0) + totals->end = item.end; + } + + if (totals->start.tv_sec == MAXINT && totals->start.tv_nsec == MAXINT) + totals->start = zero; + totals->nlwp = tstodouble (totals->tlwp) / tstodouble (totals->duration); + + if (nsampsel == 0) + { + totals->size = OVW_NUMVALS + 1; + totals->start.tv_sec = totals->end.tv_sec = -1; + totals->start.tv_nsec = totals->end.tv_nsec = 0; + totals->nlwp = -1; + } + return *totals; +} + +Ovw_data::Ovw_item +Ovw_data::get_labels () +{ + Ovw_item ovw_item; + Value *values; + memset (&ovw_item, 0, sizeof (Ovw_item)); + values = &ovw_item.values[0]; + + char *stateUNames[/*LMS_NUM_STATES*/] = LMS_STATE_USTRINGS; + values[0].l = dbe_strdup (GTXT ("Leftover")); + values[OVW_LMS_USER + 1].l = stateUNames[LMS_USER]; + values[OVW_LMS_SYSTEM + 1].l = stateUNames[LMS_SYSTEM]; + values[OVW_LMS_WAIT_CPU + 1].l = stateUNames[LMS_WAIT_CPU]; + values[OVW_LMS_USER_LOCK + 1].l = stateUNames[LMS_USER_LOCK]; + values[OVW_LMS_TFAULT + 1].l = stateUNames[LMS_TFAULT]; + values[OVW_LMS_DFAULT + 1].l = stateUNames[LMS_DFAULT]; + values[OVW_LMS_KFAULT + 1].l = stateUNames[LMS_KFAULT]; + values[OVW_LMS_SLEEP + 1].l = stateUNames[LMS_SLEEP]; + values[OVW_LMS_STOPPED + 1].l = stateUNames[LMS_STOPPED]; + values[OVW_LMS_TRAP + 1].l = stateUNames[LMS_TRAP]; + + ovw_item.size = OVW_NUMVALS + 1; + ovw_item.states = 0; + ovw_item.type = VT_LABEL; + return ovw_item; +} + +Ovw_data::Ovw_data () +{ + packets = NULL; + ovw_items = new Vector<Ovw_item*>; + totals = NULL; +} + +Ovw_data::Ovw_data (DataView *_packets, hrtime_t exp_start) +{ + packets = _packets; + ovw_items = new Vector<Ovw_item*>; + totals = NULL; + long npackets = packets->getSize (); + for (long index = 0; index < npackets; index++) + { + Ovw_item *ovw_item = new Ovw_item; + memset (ovw_item, 0, sizeof (Ovw_item)); + Sample *sample = (Sample*) packets->getObjValue (PROP_SMPLOBJ, index); + extract_data (ovw_item, sample); + hr2timestruc (&ovw_item->start, sample->get_start_time () - exp_start); + hr2timestruc (&ovw_item->end, sample->get_end_time () - exp_start); + // No need to check for duration, as duration has to be > 0. + // If not, it would have been found out in yyparse. + tssub (&ovw_item->duration, &ovw_item->end, &ovw_item->start); + ovw_item->number = sample->get_number (); + ovw_item->start_label = sample->get_start_label (); + ovw_item->end_label = sample->get_end_label (); + + int size = ovw_item->size; + for (int j = 0; j < size; j++) + tsadd (&ovw_item->tlwp, &ovw_item->values[j].t); + if (tstodouble (ovw_item->duration) != 0) + ovw_item->nlwp = tstodouble (ovw_item->tlwp) / + tstodouble (ovw_item->duration); + ovw_items->append (ovw_item); + } +} + +Ovw_data::~Ovw_data () +{ + ovw_items->destroy (); + delete ovw_items; + delete totals; +} + +void +Ovw_data::extract_data (Ovw_data::Ovw_item *ovw_item, Sample *sample) +{ + // This routine break out the data in "data" into buckets in "ovw_item" + int index; + int states; + timestruc_t sum, rtime; + timestruc_t zero = {0, 0}; + Value *values; + PrUsage *prusage = sample->get_usage (); + if (prusage == NULL) + prusage = new PrUsage; + + values = &ovw_item->values[0]; + hr2timestruc (&values[OVW_LMS_USER + 1].t, prusage->pr_utime); + hr2timestruc (&values[OVW_LMS_SYSTEM + 1].t, prusage->pr_stime); + hr2timestruc (&values[OVW_LMS_WAIT_CPU + 1].t, prusage->pr_wtime); + hr2timestruc (&values[OVW_LMS_USER_LOCK + 1].t, prusage->pr_ltime); + hr2timestruc (&values[OVW_LMS_TFAULT + 1].t, prusage->pr_tftime); + hr2timestruc (&values[OVW_LMS_DFAULT + 1].t, prusage->pr_dftime); + hr2timestruc (&values[OVW_LMS_TRAP + 1].t, prusage->pr_ttime); + hr2timestruc (&values[OVW_LMS_KFAULT + 1].t, prusage->pr_kftime); + hr2timestruc (&values[OVW_LMS_SLEEP + 1].t, prusage->pr_slptime); + hr2timestruc (&values[OVW_LMS_STOPPED + 1].t, prusage->pr_stoptime); + ovw_item->size = OVW_NUMVALS + 1; + + //XXX: Compute values[0] as rtime - sum_of(other_times) + sum = zero; + states = 0; + for (index = 1; index < ovw_item->size; index++) + { + if (values[index].t.tv_sec != 0 || values[index].t.tv_nsec != 0) + states++; + tsadd (&sum, &values[index].t); + } + + // If the sum of all times is greater than rtime then adjust + // rtime to be equal to sum and also adjust the pr_rtime field + hr2timestruc (&rtime, prusage->pr_rtime); + if (tscmp (&sum, &rtime) > 0) + { + ovw_item->total.t = sum; + values[0].t = zero; + } + else + { + ovw_item->total.t = rtime; + tssub (&rtime, &rtime, &sum); + tsadd (&values[0].t, &rtime); + states++; + } + ovw_item->type = VT_HRTIME; + ovw_item->states = states; +} diff --git a/gprofng/src/Ovw_data.h b/gprofng/src/Ovw_data.h new file mode 100644 index 0000000..0c3372c --- /dev/null +++ b/gprofng/src/Ovw_data.h @@ -0,0 +1,102 @@ +/* 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. */ + +#ifndef _OVW_DATA_H +#define _OVW_DATA_H + +// An Ovw_data object is used to supply data for constructing an overview +// display. + +#include "dbe_structs.h" + +class Sample; +class DataView; + +class Ovw_data +{ +public: + + enum OVW_LMS_STORAGE + {// in display order, not LMS_* order + // Note: use same display order of LMS_* in: er.rc, TimelineVariable.java, + // Ovw_data.h, BaseMetricTreeNode.cc and Experiment.cc metric registration + OVW_LMS_USER, + OVW_LMS_SYSTEM, + OVW_LMS_TRAP, + OVW_LMS_USER_LOCK, + OVW_LMS_DFAULT, + OVW_LMS_TFAULT, + OVW_LMS_KFAULT, + OVW_LMS_STOPPED, + OVW_LMS_WAIT_CPU, + OVW_LMS_SLEEP, + OVW_NUMVALS // must be last + }; + + // Ovw_item contains one slice of data + struct Ovw_item + { + Value values [OVW_NUMVALS + 1]; // Value list (value[0] is left over) + int states; // Number of non-zero states + Value total; // Total of all values + int size; // Number of values + timestruc_t start; // Start time of sample + timestruc_t duration; // Duration of sample + timestruc_t end; // End time of sample + timestruc_t tlwp; // Total LWP time + double nlwp; // Average number of LWPs + ValueTag type; // Type of value + int number; // Sample number + char *start_label; // Sample start label + char *end_label; // Sample end label + }; + + Ovw_data (DataView *, hrtime_t exp_start); + Ovw_data (); + ~Ovw_data (); + void sum (Ovw_data *data); + Ovw_item get_totals (); + Ovw_item get_labels (); + + // zero out contents of Ovw_item + static Ovw_item *reset_item (Ovw_item *item); + + int + size () + { + return ovw_items->size (); + } + + Ovw_item + fetch (int index) + { + return *ovw_items->fetch (index); + } + +private: + // Compute the values for "ovw_item" from "sample". + void extract_data (Ovw_item *ovw_item, Sample *sample); + + Vector<Ovw_item*> *ovw_items; + Ovw_item *totals; // Item to cache totals + DataView *packets; +}; + +#endif /* _OVW_DATA_H */ diff --git a/gprofng/src/PRBTree.cc b/gprofng/src/PRBTree.cc new file mode 100644 index 0000000..5046a91 --- /dev/null +++ b/gprofng/src/PRBTree.cc @@ -0,0 +1,480 @@ +/* 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. */ + +// The Persistent Red-Black Tree +// +// Implementation is based on an algorithm described in +// Sarnak, N., Tarjan, R., "Planar point location using +// persistent search trees", in Communications of the ACM, +// 1986, Vol.29, Number 7. +// + +#include "config.h" +#include <memory.h> +#include <string.h> + +#include "vec.h" +#include "PRBTree.h" + +#define ASSERT(x) + +#define IS_BLACK(x) ((x)==NULL || (x)->color == Black) +#define IS_RED(x) ((x)!=NULL && (x)->color == Red) +#define SET_BLACK(x) if(x) x->color = Black +#define SET_RED(x) (x)->color = Red + +#define D_OPPOSITE(x) (((x)==Left) ? Right : Left ) + +PRBTree::LMap::LMap (Key_t _key, void *_item) +{ + key = _key; + item = _item; + color = Red; + parent = NULL; + for (int i = 0; i < NPTRS; i++) + { + dir[i] = None; + chld[i] = NULL; + time[i] = 0; + } +}; + +PRBTree::LMap::LMap (const LMap& lm) +{ + key = lm.key; + item = lm.item; + color = lm.color; + parent = lm.parent; + for (int i = 0; i < NPTRS; i++) + { + dir[i] = None; + chld[i] = NULL; + time[i] = 0; + } +}; + +PRBTree::PRBTree () +{ + roots = new Vector<LMap*>; + root = NULL; + times = new Vector<Time_t>; + rtts = (Time_t) 0; + curts = (Time_t) 0; + mlist = NULL; + vals = NULL; +} + +PRBTree::~PRBTree () +{ + while (mlist) + { + LMap *lm = mlist; + mlist = mlist->next; + delete lm; + } + delete times; + delete roots; + delete vals; +} + +Vector<void *> * +PRBTree::values () +{ + if (vals == NULL) + { + vals = new Vector<void*>; + for (LMap *lm = mlist; lm; lm = lm->next) + vals->append (lm->item); + } + return vals; +} + +PRBTree::LMap * +PRBTree::rb_new_node (Key_t key, void *item) +{ + LMap *lm = new LMap (key, item); + lm->next = mlist; + mlist = lm; + return lm; +} + +PRBTree::LMap * +PRBTree::rb_new_node (LMap *lm) +{ + LMap *lmnew = new LMap (*lm); + lmnew->next = mlist; + mlist = lmnew; + return lmnew; +} + +PRBTree::LMap * +PRBTree::rb_child (LMap *lm, Direction d, Time_t ts) +{ + if (lm == NULL) + return NULL; + for (int i = 0; i < NPTRS; i++) + { + if (lm->time[i] > ts) + continue; + if (lm->dir[i] == d) + return lm->chld[i]; + else if (lm->dir[i] == None) + break; + } + return NULL; +} + +PRBTree::Direction +PRBTree::rb_which_chld (LMap *lm) +{ + LMap *prnt = lm->parent; + if (prnt == NULL) + return None; + for (int i = 0; i < NPTRS; i++) + { + if (prnt->dir[i] == None) + break; + if (prnt->chld[i] == lm) + return (Direction) prnt->dir[i]; + } + return None; +} + +PRBTree::LMap * +PRBTree::rb_neighbor (LMap *lm, Time_t ts) +{ + ASSERT (lm->dir[0] != None); + Direction d = D_OPPOSITE (lm->dir[0]); + LMap *y = NULL; + LMap *next = lm->chld[0]; + while (next) + { + y = next; + next = rb_child (y, d, ts); + } + return y; +} + +PRBTree::LMap * +PRBTree::rb_copy_node (LMap *lm, Direction d) +{ + LMap *nlm = rb_new_node (lm); + rb_fix_chld (lm->parent, nlm, rb_which_chld (lm)); + if (d == None) + { + d = Left; + rb_fix_chld (nlm, rb_child (lm, d, curts), d); + } + + // copy the other child + Direction dd = D_OPPOSITE (d); + rb_fix_chld (nlm, rb_child (lm, dd, curts), dd); + return nlm; +} + +PRBTree::LMap * +PRBTree::rb_fix_chld (LMap *prnt, LMap *lm, Direction d) +{ + + if (prnt == NULL) + { + // fixing root + ASSERT (d == None); + if (rtts == curts) + root = lm; + else + { + roots->append (root); + times->append (rtts); + root = lm; + rtts = curts; + } + if (lm != NULL) + lm->parent = prnt; + return prnt; + } + + // If we already have a d-pointer at time curts, reuse it + for (int i = 0; prnt->time[i] == curts; i++) + { + if (prnt->dir[i] == d) + { + prnt->chld[i] = lm; + if (lm != NULL) + lm->parent = prnt; + return prnt; + } + } + + if (prnt->dir[NPTRS - 1] != None) + prnt = rb_copy_node (prnt, d); + ASSERT (prnt->dir[NPTRS - 1] == None); + + for (int i = NPTRS - 1; i > 0; i--) + { + prnt->dir[i] = prnt->dir[i - 1]; + prnt->chld[i] = prnt->chld[i - 1]; + prnt->time[i] = prnt->time[i - 1]; + } + prnt->dir[0] = d; + prnt->chld[0] = lm; + prnt->time[0] = curts; + if (lm != NULL) + lm->parent = prnt; + return prnt; +} + +PRBTree::LMap * +PRBTree::rb_rotate (LMap *x, Direction d) +{ + Direction dd = D_OPPOSITE (d); + LMap *y = rb_child (x, dd, curts); + x = rb_fix_chld (x, rb_child (y, d, curts), dd); + rb_fix_chld (x->parent, y, rb_which_chld (x)); + rb_fix_chld (y, x, d); + return x; +} + +void +PRBTree::rb_remove_fixup (LMap *x, LMap *prnt, Direction d0) +{ + + while (IS_BLACK (x) && (x != root)) + { + Direction d = (x == NULL) ? d0 : rb_which_chld (x); + Direction dd = D_OPPOSITE (d); + LMap *y = rb_child (prnt, dd, curts); + if (IS_RED (y)) + { + SET_BLACK (y); + SET_RED (prnt); + prnt = rb_rotate (prnt, d); + y = rb_child (prnt, dd, curts); + } + LMap *y_d = rb_child (y, d, curts); + LMap *y_dd = rb_child (y, dd, curts); + if (IS_BLACK (y_d) && IS_BLACK (y_dd)) + { + SET_RED (y); + x = prnt; + prnt = x->parent; + } + else + { + if (IS_BLACK (y_dd)) + { + SET_BLACK (y_d); + SET_RED (y); + y = rb_rotate (y, dd); + prnt = y->parent->parent; + y = rb_child (prnt, dd, curts); + y_dd = rb_child (y, dd, curts); + } + y->color = prnt->color; + SET_BLACK (prnt); + SET_BLACK (y_dd); + prnt = rb_rotate (prnt, d); + break; + } + } + SET_BLACK (x); +} + +PRBTree::LMap * +PRBTree::rb_locate (Key_t key, Time_t ts, bool low) +{ + LMap *lm; + Direction d; + int i, lt, rt; + int tsz = times->size (); + + if (ts >= rtts) + lm = root; + else + { + // exponential search + for (i = 1; i <= tsz; i = i * 2) + if (times->fetch (tsz - i) <= ts) + break; + + if (i <= tsz) + { + lt = tsz - i; + rt = tsz - i / 2 - 1; + } + else + { + lt = 0; + rt = tsz - 1; + } + while (lt <= rt) + { + int md = (lt + rt) / 2; + if (times->fetch (md) <= ts) + lt = md + 1; + else + rt = md - 1; + } + if (rt < 0) + return NULL; + lm = roots->fetch (rt); + } + + LMap *last_lo = NULL; + LMap *last_hi = NULL; + while (lm != NULL) + { + if (key >= lm->key) + { + last_lo = lm; + d = Right; + } + else + { + last_hi = lm; + d = Left; + } + lm = rb_child (lm, d, ts); + } + return low ? last_lo : last_hi; +} + +//==================================================== Public interface + +bool +PRBTree::insert (Key_t key, Time_t ts, void *item) +{ + LMap *lm, *y; + Direction d, dd; + if (ts > curts) + curts = ts; + else if (ts < curts) + return false; // can only update the current tree + + // Insert in the tree in the usual way + y = NULL; + d = None; + for (LMap *next = root; next;) + { + y = next; + if (key == y->key) + { + // copy the node with both children + lm = rb_copy_node (y, None); + // but use the new item + lm->item = item; + return true; + } + d = (key < y->key) ? Left : Right; + next = rb_child (y, d, curts); + } + lm = rb_new_node (key, item); + rb_fix_chld (y, lm, d); + + // Rebalance the tree + while (IS_RED (lm->parent)) + { + d = rb_which_chld (lm->parent); + dd = D_OPPOSITE (d); + + y = rb_child (lm->parent->parent, dd, curts); + if (IS_RED (y)) + { + SET_BLACK (lm->parent); + SET_BLACK (y); + SET_RED (lm->parent->parent); + lm = lm->parent->parent; + } + else + { + if (rb_which_chld (lm) == dd) + { + lm = lm->parent; + lm = rb_rotate (lm, d); + } + SET_BLACK (lm->parent); + SET_RED (lm->parent->parent); + rb_rotate (lm->parent->parent, dd); + } + } + + // Color the root Black + SET_BLACK (root); + return true; +} + +bool +PRBTree::remove (Key_t key, Time_t ts) +{ + LMap *lm, *x, *y, *prnt; + if (ts > curts) + curts = ts; + else if (ts < curts) + return false; // can only update the current tree + + lm = rb_locate (key, curts, true); + if (lm == NULL || lm->key != key) + return false; + + if (rb_child (lm, Left, curts) && rb_child (lm, Right, curts)) + y = rb_neighbor (lm, curts); + else + y = lm; + + x = rb_child (y, Left, curts); + if (x == NULL) + x = rb_child (y, Right, curts); + + if (y != lm) + { + lm = rb_copy_node (lm, None); // copied with children + lm->key = y->key; + lm->item = y->item; + } + + Direction d = rb_which_chld (y); + prnt = rb_fix_chld (y->parent, x, d); + if (IS_BLACK (y)) + rb_remove_fixup (x, prnt, d); + return true; +} + +void * +PRBTree::locate (Key_t key, Time_t ts) +{ + LMap *lm = rb_locate (key, ts, true); + return lm ? lm->item : NULL; +} + +void * +PRBTree::locate_up (Key_t key, Time_t ts) +{ + LMap *lm = rb_locate (key, ts, false); + return lm ? lm->item : NULL; +} + +void * +PRBTree::locate_exact_match (Key_t key, Time_t ts) +{ + LMap *lm = rb_locate (key, ts, true); + if (lm && key == lm->key) + return lm->item; + return NULL; +} diff --git a/gprofng/src/PRBTree.h b/gprofng/src/PRBTree.h new file mode 100644 index 0000000..1a6f07f --- /dev/null +++ b/gprofng/src/PRBTree.h @@ -0,0 +1,106 @@ +/* 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. */ + +// +// The Persistent Red-Black Tree +// + +#ifndef _PRBTREE_H +#define _PRBTREE_H + +#include "dbe_types.h" +template <class ITEM> class Vector; + +// The number of pointers in a node must be set greater than 2. +// The higher the number the faster the search seems to be and +// the more memory the tree takes. +#define NPTRS 5 + +class PRBTree +{ +public: + + typedef Vaddr Key_t; + typedef hrtime_t Time_t; + + PRBTree (); + ~PRBTree (); + + bool insert (Key_t key, Time_t ts, void *item); + bool remove (Key_t key, Time_t ts); + void *locate (Key_t key, Time_t ts); + void *locate_exact_match (Key_t key, Time_t ts); + void *locate_up (Key_t key, Time_t ts); + Vector<void *> *values (); + +private: + + enum Color + { + Red, + Black + }; + + enum Direction + { + None, + Left, + Right + }; + + struct LMap + { + Key_t key; + void *item; + LMap *parent; + LMap *chld[NPTRS]; + Time_t time[NPTRS]; + char dir[NPTRS]; + char color; + LMap *next; + + LMap (Key_t _key, void *_item); + LMap (const LMap& lm); + }; + friend struct LMap; + + LMap *mlist; // The master list of all nodes + Vector<LMap*> *roots; + Vector<Time_t> *times; + Vector<void *> *vals; + LMap *root; + Time_t rtts; // root timestamp + Time_t curts; // last update timestamp + + LMap *rb_locate (Key_t key, Time_t ts, bool low); + LMap *rb_new_node (Key_t key, void *item); + LMap *rb_new_node (LMap *lm); + LMap *rb_copy_node (LMap *lm, Direction d); + LMap *rb_fix_chld (LMap *prnt, LMap *lm, Direction d); + LMap *rb_rotate (LMap *x, Direction d); + void rb_remove_fixup (LMap *x, LMap *prnt, Direction d0); + + static LMap *rb_child (LMap *lm, Direction d, Time_t ts); + static Direction rb_which_chld (LMap *lm); + static LMap *rb_neighbor (LMap *lm, Time_t ts); + +}; + +#endif /* _PRBTREE_H */ diff --git a/gprofng/src/PathTree.cc b/gprofng/src/PathTree.cc new file mode 100644 index 0000000..798e55c --- /dev/null +++ b/gprofng/src/PathTree.cc @@ -0,0 +1,2637 @@ +/* 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 <stdio.h> +#include <stdlib.h> + +#include "util.h" +#include "DefaultMap.h" +#include "CacheMap.h" + +#include "DbeSession.h" +#include "Application.h" +#include "CallStack.h" +#include "Emsg.h" +#include "Experiment.h" +#include "Expression.h" +#include "Function.h" +#include "Histable.h" +#include "IndexObject.h" +#include "MetricList.h" +#include "Module.h" +#include "DbeView.h" +#include "Metric.h" +#include "PathTree.h" +#include "LoadObject.h" +#include "Sample.h" +#include "StringBuilder.h" +#include "Table.h" + +// Define counts, rate for error warnings for statistical profiles +#define MIN_PROF_CNT 100 +#define MAX_PROF_RATE 1000. + +#define NUM_DESCENDANTS(nd) ((nd)->descendants ? (nd)->descendants->size() : 0) +#define IS_LEAF(nd) ((nd)->descendants == NULL) + +#ifdef DEBUG +#define DBG(__func) __func +#else +#define DBG(__func) +#endif + +void +PathTree::construct (DbeView *_dbev, int _indxtype, PathTreeType _pathTreeType) +{ + dbev = _dbev; + indxtype = _indxtype; + pathTreeType = _pathTreeType; + status = 0; + nchunks = 0; + chunks = NULL; + nodes = 1; // don't use node 0 + nslots = 0; + slots = NULL; + root_idx = 0; + root = NULL; + depth = 1; + dnodes = 0; + phaseIdx = -1; + nexps = 0; + total_obj = NULL; + indx_expr = NULL; + statsq = NULL; + warningq = NULL; + cancel_ok = 1; + ptree_internal = NULL; + ftree_internal = NULL; + ftree_needs_update = false; + depth_map = NULL; + init (); +} + +PathTree::~PathTree () +{ + fini (); + for (long i = 0; i < nchunks; i++) + delete[] chunks[i]; + delete[] chunks; +} + +void +PathTree::init () +{ + fn_map = new DefaultMap<Function*, NodeIdx>; + stack_prop = PROP_NONE; + desc_htable_size = 511; + desc_htable_nelem = 0; + descHT = new hash_node_t*[desc_htable_size]; + for (int i = 0; i < desc_htable_size; i++) + descHT[i] = NULL; + pathMap = new CacheMap<uint64_t, NodeIdx>; + statsq = new Emsgqueue (NTXT ("statsq")); + warningq = new Emsgqueue (NTXT ("warningq")); + if (indxtype < 0) + { + Function *ftotal = dbeSession->get_Total_Function (); + if (pathTreeType == PATHTREE_INTERNAL_FUNCTREE) + total_obj = ftotal; + else + total_obj = ftotal->find_dbeinstr (0, 0); + VMode view_mode = dbev->get_view_mode (); + if (view_mode == VMODE_MACHINE) + stack_prop = PROP_MSTACK; + else if (view_mode == VMODE_EXPERT) + stack_prop = PROP_XSTACK; + else if (view_mode == VMODE_USER) + { + stack_prop = PROP_USTACK; + if (dbeSession->is_omp_available () + && pathTreeType == PATHTREE_INTERNAL_OMP) + stack_prop = PROP_XSTACK; + } + } + else + { + total_obj = new IndexObject (indxtype, (uint64_t) - 2); + total_obj->set_name (dbe_strdup (NTXT ("<Total>"))); + char *idxname = dbeSession->getIndexSpaceName (indxtype); + if (streq (idxname, NTXT ("OMP_preg"))) + stack_prop = PROP_CPRID; + else if (streq (idxname, NTXT ("OMP_task"))) + stack_prop = PROP_TSKID; + else + indx_expr = dbeSession->getIndexSpaceExpr (indxtype); + } + root_idx = new_Node (0, total_obj, false); + root = NODE_IDX (root_idx); +} + +void +PathTree::fini () +{ + // For each node free its descendants vector + // and reset the node list of its function + for (long i = 1; i < nodes; i++) + { + Node *node = NODE_IDX (i); + if (node->descendants) + delete node->descendants; + } + nodes = 1; // don't use node 0 + + for (int i = 0; i < nslots; i++) + { + int **tmp = slots[i].mvals; + for (long j = 0; j < nchunks; j++) + delete[] tmp[j]; + delete[] tmp; + } + delete[] slots; + slots = NULL; + nslots = 0; + delete fn_map; + fn_map = NULL; + delete pathMap; + pathMap = NULL; + destroy (depth_map); + depth_map = NULL; + if (indxtype >= 0) + delete total_obj; + + for (int i = 0; i < desc_htable_size; i++) + { + hash_node_t *p = descHT[i]; + while (p) + { + hash_node_t *p1 = p; + p = p->next; + delete p1; + } + } + delete[] descHT; + delete statsq; + delete warningq; + depth = 1; + dnodes = 0; + phaseIdx = -1; + nexps = 0; + status = 0; +} + +PtreePhaseStatus +PathTree::reset () +{ + if (pathTreeType == PATHTREE_INTERNAL_FUNCTREE) + return NORMAL; // never process reset for ftree_internal. + + if (dbeSession->is_omp_available () && dbev->get_view_mode () == VMODE_USER + && pathTreeType == PATHTREE_MAIN && ptree_internal == NULL) + ptree_internal = new PathTree (dbev, indxtype, PATHTREE_INTERNAL_OMP); + + if (phaseIdx != dbev->getPhaseIdx ()) + { + fini (); + init (); + phaseIdx = dbev->getPhaseIdx (); + ftree_needs_update = true; + } + for (; nexps < dbeSession->nexps (); nexps++) + { + ftree_needs_update = true; + if (add_experiment (nexps) == CANCELED) + return CANCELED; + } + + // LIBRARY_VISIBILITY + if (dbev->isNewViewMode ()) + dbev->resetNewViewMode (); + if (dbev->isShowHideChanged ()) + dbev->resetShowHideChanged (); + return NORMAL; +} + +int +PathTree::allocate_slot (int id, ValueTag vtype) +{ + + int i; + int slot_idx = find_slot (id); + if (slot_idx >= 0) + { + DBG (assert (slots[slot_idx].vtype == vtype)); + return slot_idx; + } + slot_idx = nslots++; + + Slot *old_slots = slots; + slots = new Slot[nslots]; + for (i = 0; i < slot_idx; i++) + slots[i] = old_slots[i]; + delete[] old_slots; + + slots[slot_idx].id = id; + slots[slot_idx].vtype = vtype; + int **ip = new int*[nchunks]; + for (i = 0; i < nchunks; i++) + ip[i] = NULL; + slots[slot_idx].mvals = ip; + + return slot_idx; +} + +void +PathTree::allocate_slots (Slot *new_slots, int new_nslots) +{ + // duplicates new_slots + + // if previously had more slots than currently requested, delete the data from those slots. + for (int i = new_nslots; i < nslots; i++) + { + int **tmp = slots[i].mvals; + for (long j = 0; j < nchunks; j++) + delete tmp[j]; + delete tmp; + } + if (new_nslots == 0) + { + nslots = new_nslots; + delete[] slots; + slots = NULL; + return; + } + + Slot *old_slots = slots; + slots = new Slot[new_nslots]; + for (int i = 0; i < new_nslots; i++) + { + slots[i] = new_slots[i]; // pick up id and vtype + if (i < nslots) + slots[i].mvals = old_slots[i].mvals; + else + { + if (nchunks == 0) + slots[i].mvals = NULL; + else + { + int **ip = new int*[nchunks]; + for (long j = 0; j < nchunks; j++) + ip[j] = NULL; + slots[i].mvals = ip; + } + } + } + nslots = new_nslots; + delete old_slots; +} + +int +PathTree::find_slot (int id) +{ + for (int i = 0; i < nslots; i++) + if (slots[i].id == id) + return i; + return -1; +} + +PathTree::NodeIdx +PathTree::new_Node (NodeIdx anc, Histable *instr, bool leaf) +{ + if (nodes >= nchunks * CHUNKSZ) + { + long idx = nchunks++; + + // Reallocate Node chunk array + Node **old_chunks = chunks; + chunks = new Node*[nchunks]; + for (long k = 0; k < idx; k++) + chunks[k] = old_chunks[k]; + delete[] old_chunks; + + // Reallocate metric value chunk arrays. + for (int i = 0; i < nslots; i++) + { + int **mvals = new int*[nchunks]; + for (long k = 0; k < idx; k++) + { + mvals[k] = slots[i].mvals[k]; + } + delete[] slots[i].mvals; + slots[i].mvals = mvals; + slots[i].mvals[idx] = NULL; + } + + // Allocate new chunk for nodes. + // Note that we don't need to allocate new chunks + // for metric values at this point as we rely on + // lazy allocation. + // + allocate_chunk (chunks, idx); + } + NodeIdx node_idx = nodes++; + Node *node = NODE_IDX (node_idx); + node->ancestor = anc; + node->descendants = leaf ? (Vector<NodeIdx>*)NULL : new Vector<NodeIdx>(2); + node->instr = instr; + Function *func = (Function*) (instr->convertto (Histable::FUNCTION)); + node->funclist = fn_map->get (func); + fn_map->put (func, node_idx); + return node_idx; +} + +PathTree::NodeIdx +PathTree::find_path (Experiment *exp, DataView *dview, long recIdx) +{ + if (indx_expr != NULL) + { + Expression::Context ctx (dbev, exp, dview, recIdx); + uint64_t idx = indx_expr->eval (&ctx); + Histable *cur_obj = dbeSession->createIndexObject (indxtype, idx); + cur_obj->set_name_from_context (&ctx); + NodeIdx dsc_idx = find_in_desc_htable (root_idx, cur_obj, true); + depth = 2; + return dsc_idx; + } + + bool showAll = dbev->isShowAll (); + int t_stack_prop = stack_prop; + void *stackId = dview->getObjValue (t_stack_prop, recIdx); + NodeIdx node_idx; + if (stackId != NULL) + { + // pathMap does not work with NULL key + node_idx = pathMap->get ((uint64_t) stackId); + if (node_idx != 0) + return node_idx; + } + Vector<Histable*> *stack = (Vector<Histable*>*)CallStack::getStackPCs (stackId, !showAll); + int stack_size = stack->size (); + if (stack_size == 0) + return root_idx; + + node_idx = root_idx; + int thisdepth = 1; + + for (int i = stack_size - 1; i >= 0; i--) + { + bool leaf = (i == 0); + Histable *cur_addr = stack->fetch (i); + + // bail out of loop if load object API-only is set + // and this is not the top frame + // This is now done in HSTACK if hide is set + + Function *func = (Function*) cur_addr->convertto (Histable::FUNCTION); + if (func != NULL) + { + Module *mod = func->module; + LoadObject *lo = mod->loadobject; + int segx = lo->seg_idx; + if (showAll && dbev->get_lo_expand (segx) == LIBEX_API + && i != stack_size - 1) + leaf = true; + } + + NodeIdx dsc_idx = find_desc_node (node_idx, cur_addr, leaf); + thisdepth++; + node_idx = dsc_idx; + + // LIBEX_API processing might have set leaf to true + if (leaf) + break; + } + if (thisdepth > depth) + depth = thisdepth; + delete stack; + pathMap->put ((uint64_t) stackId, node_idx); + return node_idx; +} + +static int +desc_node_comp (const void *s1, const void *s2, const void *ptree) +{ + PathTree::NodeIdx t1, t2; + t1 = *(PathTree::NodeIdx *)s1; + t2 = *(PathTree::NodeIdx *)s2; + PathTree* Ptree = (PathTree *) ptree; + PathTree::Node *n1 = Ptree->NODE_IDX (t1); + PathTree::Node *n2 = Ptree->NODE_IDX (t2); + Histable *d1 = n1->instr; + Histable *d2 = n2->instr; + if (d1->id < d2->id) + return -1; + else if (d1->id > d2->id) + return +1; + else + return 0; +} + +PathTree::NodeIdx +PathTree::find_in_desc_htable (NodeIdx node_idx, Histable *instr, bool leaf) +{ + unsigned int hash_code = (unsigned int) instr->id % desc_htable_size; + Node *node = NODE_IDX (node_idx); + hash_node_t *p = NULL; + for (p = descHT[hash_code]; p; p = p->next) + { + Node *dsc = NODE_IDX (p->nd); + Histable *dinstr = dsc->instr; + if (dinstr->id == instr->id && leaf == IS_LEAF (dsc)) + return p->nd; + } + // Not found + NodeIdx dsc_idx = new_Node (node_idx, instr, leaf); + node->descendants->append (dsc_idx); + p = new hash_node_t (); + p->nd = dsc_idx; + p->next = descHT[hash_code]; + descHT[hash_code] = p; + desc_htable_nelem++; + + // time to resize + if (desc_htable_nelem == desc_htable_size) + { + int old_htable_size = desc_htable_size; + desc_htable_size = old_htable_size * 2 + 1; + hash_node_t **old_htable = descHT; + descHT = new hash_node_t*[desc_htable_size]; + for (int i = 0; i < desc_htable_size; i++) + descHT[i] = NULL; + + for (int i = 0; i < old_htable_size; i++) + if (old_htable[i] != NULL) + { + hash_node *old_p; + hash_node_t *hash_p = old_htable[i]; + while (hash_p != NULL) + { + hash_node_t *new_p = new hash_node_t (); + new_p->nd = hash_p->nd; + Node *dnode = NODE_IDX (hash_p->nd); + Histable *dnode_instr = dnode->instr; + hash_code = (unsigned int) dnode_instr->id % desc_htable_size; + new_p->next = descHT[hash_code]; + descHT[hash_code] = new_p; + old_p = hash_p; + hash_p = hash_p->next; + delete old_p; + } + } + delete[] old_htable; + } + return dsc_idx; +} + +PathTree::NodeIdx +PathTree::find_desc_node (NodeIdx node_idx, Histable *instr, bool leaf) +{ + // Binary search. All nodes are ordered by Histable::id. + + // We have a special case when two nodes with the same + // id value may co-exist: one representing a leaf node and + // another one representing a call site. + Node *node = NODE_IDX (node_idx); + int left = 0; + int right = NUM_DESCENDANTS (node) - 1; + while (left <= right) + { + int index = (left + right) / 2; + NodeIdx dsc_idx = node->descendants->fetch (index); + Node *dsc = NODE_IDX (dsc_idx); + Histable *dinstr = dsc->instr; + if (instr->id < dinstr->id) + right = index - 1; + else if (instr->id > dinstr->id) + left = index + 1; + else if (leaf == IS_LEAF (dsc)) + return dsc_idx; + else if (leaf) + right = index - 1; + else + left = index + 1; + } + + // None was found. Create one. + NodeIdx dsc_idx = new_Node (node_idx, instr, leaf); + node->descendants->insert (left, dsc_idx); + return dsc_idx; +} + +PtreePhaseStatus +PathTree::process_packets (Experiment *exp, DataView *packets, int data_type) +{ + Expression::Context ctx (dbev, exp); + char *progress_bar_msg = NULL; + int progress_bar_percent = -1; + + Vector<BaseMetric*> *mlist = dbev->get_all_reg_metrics (); + Vector<BaseMetric*> mlist2; + StringBuilder stb; + for (int midx = 0, mlist_sz = mlist->size (); midx < mlist_sz; ++midx) + { + BaseMetric *mtr = mlist->fetch (midx); + if (mtr->get_packet_type () == data_type && + (mtr->get_expr () == NULL || mtr->get_expr ()->passes (&ctx))) + { + Hwcentry *hwc = mtr->get_hw_ctr (); + if (hwc) + { + stb.setLength (0); + // XXX this should be done at metric registration + Collection_params *col_params = exp->get_params (); + for (int i = 0; i < MAX_HWCOUNT; i++) + { + // We may have duplicate counters in col_params, + // check for all (see 5081284). + if (dbe_strcmp (hwc->name, col_params->hw_aux_name[i]) == 0) + { + if (stb.length () != 0) + stb.append (NTXT ("||")); + stb.append (NTXT ("HWCTAG==")); + stb.append (i); + } + } + if (stb.length () == 0) + continue; + stb.append (NTXT ("&& ((HWCINT & ")); + stb.append ((long long) HWCVAL_ERR_FLAG); + stb.append (NTXT (")==0)")); + char *s = stb.toString (); + mtr->set_cond_spec (s); + free (s); + } + ValueTag vtype = mtr->get_vtype (); + switch (vtype) + { + case VT_INT: + case VT_ULLONG: + case VT_LLONG: + break; // nothing to do + default: + vtype = VT_ULLONG; // ym: not sure when this would happen + break; + } + allocate_slot (mtr->get_id (), vtype); + mlist2.append (mtr); + } + } + + Slot **mslots = new Slot*[mlist2.size ()]; + for (int midx = 0, mlist_sz = mlist2.size (); midx < mlist_sz; ++midx) + { + BaseMetric *mtr = mlist2.fetch (midx); + int id = mtr->get_id (); + int slot_ind = find_slot (id); + mslots[midx] = SLOT_IDX (slot_ind); + } + + for (long i = 0, packets_sz = packets->getSize (); i < packets_sz; ++i) + { + if (dbeSession->is_interactive ()) + { + if (NULL == progress_bar_msg) + progress_bar_msg = dbe_sprintf (GTXT ("Processing Experiment: %s"), + get_basename (exp->get_expt_name ())); + int val = (int) (100 * i / packets_sz); + if (val > progress_bar_percent) + { + progress_bar_percent += 10; + if (theApplication->set_progress (val, progress_bar_msg) + && cancel_ok) + { + delete[] mslots; + return CANCELED; + } + } + } + + NodeIdx path_idx = 0; + ctx.put (packets, i); + + for (int midx = 0, mlist_sz = mlist2.size (); midx < mlist_sz; ++midx) + { + BaseMetric *mtr = mlist2.fetch (midx); + if (mtr->get_cond () != NULL && !mtr->get_cond ()->passes (&ctx)) + continue; + + int64_t mval = mtr->get_val ()->eval (&ctx); + if (mval == 0) + continue; + if (path_idx == 0) + path_idx = find_path (exp, packets, i); + NodeIdx node_idx = path_idx; + Slot *mslot = mslots[midx]; + while (node_idx) + { + INCREMENT_METRIC (mslot, node_idx, mval); + node_idx = NODE_IDX (node_idx)->ancestor; + } + } + } + if (dbeSession->is_interactive ()) + free (progress_bar_msg); + delete[] mslots; + if (indx_expr != NULL) + root->descendants->sort ((CompareFunc) desc_node_comp, this); + return NORMAL; +} + +DataView * +PathTree::get_filtered_events (int exp_index, int data_type) +{ + if (indx_expr != NULL) + { + IndexObjType_t *indexObj = dbeSession->getIndexSpace (indxtype); + if (indexObj->memObj && data_type != DATA_HWC) + return NULL; + } + return dbev->get_filtered_events (exp_index, data_type); +} + +PtreePhaseStatus +PathTree::add_experiment (int exp_index) +{ + StringBuilder sb; + char *expt_name; + char *base_name; + Emsg *m; + Experiment *experiment = dbeSession->get_exp (exp_index); + if (experiment->broken != 0) + return NORMAL; + status = 0; + expt_name = experiment->get_expt_name (); + base_name = get_basename (expt_name); + + hrtime_t starttime = gethrtime (); + hrtime_t startvtime = gethrvtime (); + + // Experiment::getEndTime was initially implemented as + // returning exp->last_event. To preserve the semantics + // new Experiment::getLastEvent() is used here. + hrtime_t tot_time = experiment->getLastEvent () - experiment->getStartTime (); + + if (!dbev->isShowAll () && (dbev->isShowHideChanged () + || dbev->isNewViewMode ())) + experiment->resetShowHideStack (); + + // To report experiment index to the user, + // start numeration from 1, not 0 + sb.sprintf (GTXT ("PathTree processing experiment %d (`%s'); duration %lld.%06lld"), + exp_index + 1, base_name, + tot_time / NANOSEC, (tot_time % NANOSEC / 1000)); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + + DataView *prof_packet = get_filtered_events (exp_index, DATA_CLOCK); + if (prof_packet && prof_packet->getSize () > 0) + { + if (process_packets (experiment, prof_packet, DATA_CLOCK) == CANCELED) + return CANCELED; + long clock_cnt = prof_packet->getSize (); + double clock_rate; + if (tot_time != 0) + clock_rate = (double) clock_cnt / (double) tot_time * (double) NANOSEC; + else + clock_rate = (double) 0.; + if (experiment->timelineavail) + sb.sprintf (GTXT (" Processed %ld clock-profile events (%3.2f/sec.)"), + clock_cnt, clock_rate); + else + sb.sprintf (GTXT (" Processed %ld clock-profile events"), clock_cnt); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + + // check for statistical validity + if ((experiment->timelineavail == true) + && !dbev->get_filter_active () && (clock_cnt < MIN_PROF_CNT)) + { + sb.sprintf (GTXT ("WARNING: too few clock-profile events (%ld) in experiment %d (`%s') for statistical validity"), + clock_cnt, exp_index + 1, base_name); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + } + } + + DataView *sync_packet = get_filtered_events (exp_index, DATA_SYNCH); + if (sync_packet && sync_packet->getSize () > 0) + { + if (process_packets (experiment, sync_packet, DATA_SYNCH) == CANCELED) + return CANCELED; + long sync_cnt = sync_packet->getSize (); + sb.sprintf (GTXT (" Processed %ld synctrace events"), sync_cnt); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + } + + DataView *iotrace_packet = get_filtered_events (exp_index, DATA_IOTRACE); + if (iotrace_packet && iotrace_packet->getSize () > 0) + { + if (process_packets (experiment, iotrace_packet, DATA_IOTRACE) == CANCELED) + return CANCELED; + long iotrace_cnt = iotrace_packet->getSize (); + sb.sprintf (GTXT (" Processed %ld IO trace events"), iotrace_cnt); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + } + + DataView *hwc_packet = get_filtered_events (exp_index, DATA_HWC); + if (hwc_packet && hwc_packet->getSize () > 0) + { + if (process_packets (experiment, hwc_packet, DATA_HWC) == CANCELED) + return CANCELED; + long hwc_cnt = hwc_packet->getSize (); + double hwc_rate = (double) hwc_cnt / (double) tot_time * (double) NANOSEC; + if (experiment->timelineavail) + sb.sprintf (GTXT (" Processed %ld hwc-profile events (%3.2f/sec.)"), + hwc_cnt, hwc_rate); + else + sb.sprintf (GTXT (" Processed %ld hwc-profile events"), hwc_cnt); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + + // check for statistical validity + if (experiment->timelineavail && !dbev->get_filter_active () && (hwc_cnt < MIN_PROF_CNT)) + { + sb.sprintf (GTXT ("WARNING: too few HW counter profile events (%ld) in experiment %d (`%s') for statistical validity"), + hwc_cnt, exp_index + 1, base_name); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + } + } + + DataView *heap_packet = get_filtered_events (exp_index, DATA_HEAP); + if (heap_packet && heap_packet->getSize () > 0) + { + if (process_packets (experiment, heap_packet, DATA_HEAP) == CANCELED) + return CANCELED; + long heap_cnt = heap_packet->getSize (); + sb.sprintf (GTXT (" Processed %ld heaptrace events"), heap_cnt); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + } + + DataView *race_packet = get_filtered_events (exp_index, DATA_RACE); + if (race_packet && race_packet->getSize () > 0) + { + if (process_packets (experiment, race_packet, DATA_RACE) == CANCELED) + return CANCELED; + long race_cnt = race_packet->getSize (); + sb.sprintf (GTXT (" Processed %ld race access events"), race_cnt); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + } + + DataView *deadlock_packet = get_filtered_events (exp_index, DATA_DLCK); + if (deadlock_packet && deadlock_packet->getSize () > 0) + { + if (process_packets (experiment, deadlock_packet, DATA_DLCK) == CANCELED) + return CANCELED; + long race_cnt = deadlock_packet->getSize (); + sb.sprintf (GTXT (" Processed %ld race access events"), race_cnt); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + } + + hrtime_t pathtime = gethrtime () - starttime; + hrtime_t pathvtime = gethrvtime () - startvtime; + sb.sprintf (GTXT ("PathTree time = %lld.%06lld CPU-time %lld.%06lld\n"), + pathtime / NANOSEC, (pathtime % NANOSEC) / 1000, + pathvtime / NANOSEC, (pathvtime % NANOSEC) / 1000); + m = new Emsg (CMSG_COMMENT, sb); + statsq->append (m); + return NORMAL; +} + +Hist_data * +PathTree::compute_metrics (MetricList *mlist, Histable::Type type, + Hist_data::Mode mode, Vector<Histable*> *objs, + Histable *context, Vector<Histable*> *sel_objs, + PtreeComputeOption computeOpt) +{ + VMode view_mode = dbev->get_view_mode (); + + // For displaying disassembly correctly in user mode with openmp + if (ptree_internal != NULL && + (view_mode == VMODE_EXPERT || + (view_mode == VMODE_USER && (type == Histable::INSTR + || (dbev->isOmpDisMode () + && type == Histable::FUNCTION + && mode == Hist_data::CALLEES + && computeOpt == COMPUTEOPT_OMP_CALLEE)) + ))) + return ptree_internal->compute_metrics (mlist, type, mode, objs, context, + sel_objs); + + PtreePhaseStatus resetStatus = reset (); + + hist_data = new Hist_data (mlist, type, mode); + int nmetrics = mlist->get_items ()->size (); + int sort_ind = -1; + Hist_data::HistItem *hi; + int index; + + if (status != 0 || resetStatus == CANCELED) + return hist_data; + + hist_data->set_status (Hist_data::SUCCESS); + if (dbeSession->is_interactive () && mode != Hist_data::CALLEES) + theApplication->set_progress (0, GTXT ("Constructing Metrics")); + + xlate = new int[nmetrics]; + for (int mind = 0; mind < nmetrics; mind++) + { + Metric *mtr = mlist->get (mind); + xlate[mind] = find_slot (mtr->get_id ()); + } + + // Compute dynamic metrics + obj_list = new Histable*[depth]; + if ((type == Histable::LINE || type == Histable::INSTR) + && mode == Hist_data::CALLERS) + node_list = new Node*[depth]; + percent = 0; + ndone = 0; + if (mode == Hist_data::MODL) + { + Histable *obj = objs && objs->size () > 0 ? objs->fetch (0) : NULL; + if (obj != NULL) + { + switch (obj->get_type ()) + { + case Histable::FUNCTION: + { + Vector<Function*> *funclist = new Vector<Function*>; + funclist->append ((Function*) obj); + get_metrics (funclist, context); + delete funclist; + break; + } + case Histable::MODULE: + { + Vector<Histable*> *comparableModules = obj->get_comparable_objs (); + if (comparableModules != NULL) + { + Vector<Function*> *functions = new Vector<Function*>; + for (int i = 0; i < comparableModules->size (); i++) + { + Module *mod = (Module*) comparableModules->fetch (i); + if (mod) + { + bool found = false; + for (int i1 = 0; i1 < i; i1++) + { + if (mod == comparableModules->fetch (i1)) + { + found = true; + break; + } + } + if (!found) + functions->addAll (mod->functions); + } + } + get_metrics (functions, context); + delete functions; + } + else + get_metrics (((Module*) obj)->functions, context); + break; + } + case Histable::SOURCEFILE: + get_metrics (((SourceFile *) obj)->get_functions (), context); + break; + default: + DBG (assert (0)); + } + } + } + else if (mode == Hist_data::CALLERS) + { + if (objs && objs->size () > 0) + get_clr_metrics (objs); + } + else if (mode == Hist_data::CALLEES) + { + if (objs && objs->size () > 0) + get_cle_metrics (objs); + else // Special case: get root + get_cle_metrics (NULL); + } + else if (mode == Hist_data::SELF) + { + if (objs->size () == 1) + { + Histable *obj = objs->fetch (0); + if (obj != NULL) + { + if (obj->get_type () == Histable::LINE) + { + Vector<Function*> *funclist = new Vector<Function*>; + for (DbeLine *dl = (DbeLine*) obj->convertto (Histable::LINE); + dl; dl = dl->dbeline_func_next) + if (dl->func) + funclist->append (dl->func); + + get_self_metrics (obj, funclist, sel_objs); + delete funclist; + } + else if (obj->get_type () == Histable::FUNCTION + || obj->get_type () == Histable::INSTR) + { + // Use shortcut for functions and oth. + if (context) + { + Vector<Function*> *funclist = NULL; + if (context->get_type () == Histable::MODULE) + funclist = ((Module*) context)->functions->copy (); + else + { + funclist = new Vector<Function*>; + funclist->append ((Function*) context); + } + get_self_metrics (obj, funclist, sel_objs); + delete funclist; + } + else + get_self_metrics (objs); + } + else + get_self_metrics (objs); + } + } + else + get_self_metrics (objs); + } + else // Hist_data::ALL + get_metrics (root_idx, 0); + + delete[] obj_list; + if ((type == Histable::LINE || type == Histable::INSTR) + && mode == Hist_data::CALLERS) + delete[] node_list; + + // Postprocess; find total + for (long mind = 0, sz = mlist->get_items ()->size (); mind < sz; mind++) + { + Metric *mtr = mlist->get_items ()->get (mind); + Metric::SubType subtype = mtr->get_subtype (); + ValueTag vtype = mtr->get_vtype (); + hist_data->total->value[mind].tag = vtype; + + switch (vtype) + { + // ignoring the following cases (why?) + case VT_SHORT: + case VT_FLOAT: + case VT_HRTIME: + case VT_LABEL: + case VT_ADDRESS: + case VT_OFFSET: + break; + + case VT_INT: + // Calculate total as the sum of all values in hist_data for + // ATTRIBUTED metrics only. For all others, use root node values. + // + if ((mode == Hist_data::CALLERS || mode == Hist_data::CALLEES) + && subtype == Metric::ATTRIBUTED) + { + hist_data->total->value[mind].i = 0; + Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi) + { + hist_data->total->value[mind].i += hi->value[mind].i; + } + if (mode == Hist_data::CALLEES) + hist_data->total->value[mind].i += hist_data->gprof_item->value[mind].i; + } + else if (xlate[mind] != -1) + ASN_METRIC_VAL (hist_data->total->value[mind], slots[xlate[mind]], + root_idx); + break; + + case VT_LLONG: + Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi) + { + hi->value[mind].tag = vtype; + } + + if ((mode == Hist_data::CALLERS || mode == Hist_data::CALLEES) + && subtype == Metric::ATTRIBUTED) + { + hist_data->total->value[mind].ll = 0; + Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi) + { + hist_data->total->value[mind].ll += hi->value[mind].ll; + } + if (mode == Hist_data::CALLEES) + hist_data->total->value[mind].ll += hist_data->gprof_item->value[mind].ll; + } + else if (xlate[mind] != -1) + ASN_METRIC_VAL (hist_data->total->value[mind], slots[xlate[mind]], root_idx); + break; + + case VT_ULLONG: + Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi) + { + hi->value[mind].tag = vtype; + } + if ((mode == Hist_data::CALLERS || mode == Hist_data::CALLEES) + && subtype == Metric::ATTRIBUTED) + { + hist_data->total->value[mind].ull = 0; + Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi) + { + hist_data->total->value[mind].ull += hi->value[mind].ull; + } + if (mode == Hist_data::CALLEES) + hist_data->total->value[mind].ull += hist_data->gprof_item->value[mind].ull; + } + else if (xlate[mind] != -1) + ASN_METRIC_VAL (hist_data->total->value[mind], slots[xlate[mind]], root_idx); + break; + + case VT_DOUBLE: + double prec = mtr->get_precision (); + ValueTag vt = (xlate[mind] != -1) ? slots[xlate[mind]].vtype : VT_INT; + Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi) + { + double val = (vt == VT_LLONG ? hi->value[mind].ll : + (vt == VT_ULLONG ? hi->value[mind].ull + : hi->value[mind].i)); + hi->value[mind].tag = vtype; + hi->value[mind].d = val / prec; + } + + if ((mode == Hist_data::CALLERS || mode == Hist_data::CALLEES) + && subtype == Metric::ATTRIBUTED) + { + hist_data->total->value[mind].d = 0.0; + Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi) + { + hist_data->total->value[mind].d += hi->value[mind].d; + } + if (mode == Hist_data::CALLEES) + hist_data->total->value[mind].d += + (double) (vt == VT_LLONG ? hist_data->gprof_item->value[mind].ll : + (vt == VT_ULLONG ? hist_data->gprof_item->value[mind].ull : + hist_data->gprof_item->value[mind].i)) / prec; + } + else if (xlate[mind] != -1) + { + TValue& total = hist_data->total->value[mind]; + ASN_METRIC_VAL (total, slots[xlate[mind]], root_idx); + double val = (vt == VT_LLONG ? total.ll : + (vt == VT_ULLONG ? total.ll : total.i)); + total.d = val / prec; + } + break; + } + } + delete[] xlate; + + // Determine by which metric to sort if any + bool rev_sort = mlist->get_sort_rev (); + for (long mind = 0, sz = mlist->get_items ()->size (); mind < sz; mind++) + { + Metric *mtr = mlist->get_items ()->get (mind); + if (mlist->get_sort_ref_index () == mind) + sort_ind = mind; + + switch (mtr->get_type ()) + { + case BaseMetric::SIZES: + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + Histable *h = mtr->get_comparable_obj (hi->obj); + hi->value[mind].tag = VT_LLONG; + hi->value[mind].ll = h ? h->get_size () : 0; + } + break; + case BaseMetric::ADDRESS: + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + Histable *h = mtr->get_comparable_obj (hi->obj); + hi->value[mind].tag = VT_ADDRESS; + hi->value[mind].ll = h ? h->get_addr () : 0; + } + break; + case BaseMetric::DERIVED: + { + Definition *def = mtr->get_definition (); + long *map = def->get_map (); + for (long i1 = 0, sz1 = hist_data->hist_items->size (); i1 < sz1; i1++) + { + /* Hist_data::HistItem * */hi = hist_data->hist_items->get (i1); + hi->value[mind].tag = VT_DOUBLE; + hi->value[mind].d = def->eval (map, hi->value); + } + hist_data->total->value[mind].tag = VT_DOUBLE; + hist_data->total->value[mind].d = def->eval (map, hist_data->total->value); + } + break; + default: + break; + } + } + + hist_data->sort (sort_ind, rev_sort); + hist_data->compute_minmax (); + if (dbeSession->is_interactive () && mode != Hist_data::CALLERS) + theApplication->set_progress (0, GTXT ("")); + +#if DEBUG_FTREE + if (ftree_hist_data) + { + bool matches = ftree_debug_match_hist_data (hist_data, ftree_hist_data); + if (!matches) + assert (false); + delete hist_data; + hist_data = ftree_hist_data; // return the debug version + } +#endif + return hist_data; +} + +#if DEBUG_FTREE +bool +PathTree::ftree_debug_match_hist_data (Hist_data *data /* ref */, + Hist_data *data_tmp) +{ + if (data->get_status () != Hist_data::SUCCESS) + { + DBG (assert (false)); + return false; + } + if (data == NULL && data != data_tmp) + { + DBG (assert (false)); + return false; + } + + MetricList *mlist; + mlist = data->get_metric_list (); + MetricList *mlist_tmp; + mlist_tmp = data_tmp->get_metric_list (); + if (mlist->size () != mlist_tmp->size ()) + { + DBG (assert (false)); + return false; + } + + // Get table size: count visible metrics + int nitems = data->size (); + if (data->size () != data_tmp->size ()) + { + DBG (assert (false)); + return false; + } + + for (int i = 0; i < nitems; ++i) + { + Hist_data::HistItem *item = data->fetch (i); + Hist_data::HistItem *item_tmp = data_tmp->fetch (i); + if (item->obj->id != item_tmp->obj->id) + { + DBG (assert (false)); + return false; + } + } + + for (long i = 0, sz = mlist->size (); i < sz; i++) + { + long met_ind = i; + Metric *mitem = mlist->get (i); + Metric *mitem_tmp = mlist_tmp->get (i); + + if (mitem->get_id () != mitem_tmp->get_id ()) + { + DBG (assert (false)); + return false; + } + if (mitem->get_visbits () != mitem_tmp->get_visbits ()) + { + DBG (assert (false)); + return false; + } + if (mitem->get_vtype () != mitem_tmp->get_vtype ()) + { + DBG (assert (false)); + return false; + } + + if (!mitem->is_visible () && !mitem->is_tvisible () + && !mitem->is_pvisible ()) + continue; + // table->append(dbeGetTableDataOneColumn(data, i)); + for (long row = 0, sz_row = data->size (); row < sz_row; row++) + { + Metric *m = mitem; + TValue res; + TValue res_tmp; + TValue *v = data->get_value (&res, met_ind, row); + TValue *v_tmp = data_tmp->get_value (&res_tmp, met_ind, row); + if ((m->get_visbits () & VAL_RATIO) != 0) + { + if (v->tag != VT_LABEL) + { + if (v->to_double () != v_tmp->to_double ()) + { + DBG (assert (false)); + return false; + } + } + continue; + } + switch (m->get_vtype ()) + { + case VT_DOUBLE: + { + double diff = v->d - v_tmp->d; + if (diff < 0) diff = -diff; + if (diff > 0.0001) + { + DBG (assert (false)); + return false; + } + else + DBG (assert (true)); + break; + } + case VT_INT: + if (v->i != v_tmp->i) + { + DBG (assert (false)); + return false; + } + break; + case VT_ULLONG: + case VT_LLONG: + case VT_ADDRESS: + if (v->ll != v_tmp->ll) + { + DBG (assert (false)); + return false; + } + break; + + case VT_LABEL: + if (dbe_strcmp (v->l, v_tmp->l)) + { + DBG (assert (false)); + return false; + } + break; + default: + DBG (assert (false)); + return false; + } + } + } + return true; +} +#endif + +Histable * +PathTree::get_hist_func_obj (Node *node) +{ + LoadObject *lo; + Function *func; + func = (Function*) (node->instr->convertto (Histable::FUNCTION)); + // LIBRARY VISIBILITY + lo = func->module->loadobject; + if (dbev->get_lo_expand (lo->seg_idx) == LIBEX_HIDE) + return lo->get_hide_function (); + return get_compare_obj (func); +} + +Histable * +PathTree::get_hist_obj (Node *node, Histable* context) +{ + LoadObject *lo; + Function *func; + switch (hist_data->type) + { + case Histable::INSTR: + if (hist_data->mode == Hist_data::MODL) + { + if (node->instr->get_type () != Histable::INSTR) + return NULL; + } + else + { + // LIBRARY VISIBILITY + func = (Function*) (node->instr->convertto (Histable::FUNCTION)); + lo = func->module->loadobject; + if (dbev->get_lo_expand (lo->seg_idx) == LIBEX_HIDE) + return lo->get_hide_function (); + } + return node->instr; + + case Histable::LINE: + if (hist_data->mode != Hist_data::MODL) + { + func = (Function*) (node->instr->convertto (Histable::FUNCTION)); + lo = func->module->loadobject; + // LIBRARY VISIBILITY + if (dbev->get_lo_expand (lo->seg_idx) == LIBEX_HIDE) + return lo->get_hide_function (); + } + // For openmp user mode - the stack is already made with dbelines, + // no need to convert it + if (node->instr->get_type () == Histable::LINE) + return node->instr; + return node->instr->convertto (Histable::LINE, context); + + case Histable::FUNCTION: + if (pathTreeType == PATHTREE_INTERNAL_FUNCTREE && node->ancestor != 0) + func = (Function*) node->instr; + else + func = (Function*) (node->instr->convertto (Histable::FUNCTION)); + lo = func->module->loadobject; + // LIBRARY VISIBILITY + if (dbev->get_lo_expand (lo->seg_idx) == LIBEX_HIDE) + return lo->get_hide_function (); + return get_compare_obj (func); + case Histable::MODULE: + func = (Function*) (node->instr->convertto (Histable::FUNCTION)); + return func->module; + case Histable::LOADOBJECT: + func = (Function*) (node->instr->convertto (Histable::FUNCTION)); + return func->module->loadobject; + case Histable::INDEXOBJ: + case Histable::MEMOBJ: + return node->instr; + default: + DBG (assert (0)); + } + return NULL; +} + +Histable * +PathTree::get_compare_obj (Histable *obj) +{ + if (obj && dbev->comparingExperiments ()) + obj = dbev->get_compare_obj (obj); + return obj; +} + +void +PathTree::get_metrics (NodeIdx node_idx, int dpth) +{ + Node *node = NODE_IDX (node_idx); + Histable *cur_obj = get_hist_obj (node); + obj_list[dpth] = cur_obj; + + // Check for recursion (inclusive metrics) + int incl_ok = 1; + for (int i = dpth - 1; i >= 0; i--) + if (cur_obj == obj_list[i]) + { + incl_ok = 0; + break; + } + + // Check for leaf nodes (exclusive metrics) + int excl_ok = 0; + if (IS_LEAF (node) || node == NODE_IDX (root_idx)) + excl_ok = 1; + + // We shouldn't eliminate empty subtrees here because + // we create the list of hist items dynamically and want + // one for each object in the tree. + cur_obj = get_compare_obj (cur_obj); + Hist_data::HistItem *hi = hist_data->append_hist_item (cur_obj); + DBG (assert (hi != NULL)); + + MetricList *mlist = hist_data->get_metric_list (); + for (long ind = 0, sz = mlist->size (); ind < sz; ind++) + { + if (xlate[ind] == -1) + continue; + Metric *mtr = mlist->get (ind); + Metric::SubType subtype = mtr->get_subtype (); + if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx)) + continue; + + switch (subtype) + { + case Metric::INCLUSIVE: + if (incl_ok && hi) + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + break; + case Metric::EXCLUSIVE: + if (excl_ok && hi) + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + break; + // ignoring the following cases (why?) + case Metric::STATIC: + case Metric::ATTRIBUTED: + break; + case Metric::DATASPACE: + if (hi) + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + break; + } + } + + if (dbeSession->is_interactive ()) + { + ndone++; + int new_percent = 95 * ndone / nodes; + if (new_percent > percent) + { + percent = new_percent; + theApplication->set_progress (percent, NULL); + } + } + + // Recursively process all descendants + int index; + int dsize = NUM_DESCENDANTS (node); + for (index = 0; index < dsize; index++) + get_metrics (node->descendants->fetch (index), dpth + 1); +} + +void +PathTree::get_clr_metrics (Vector<Histable*> *objs, NodeIdx node_idx, + int pmatch, int dpth) +{ + Node *node = NODE_IDX (node_idx); + Histable *cur_obj; + if (hist_data->type == Histable::LINE || hist_data->type == Histable::INSTR) + { + cur_obj = get_hist_func_obj (node); + node_list[dpth] = node; + } + else + cur_obj = get_hist_obj (node); + obj_list[dpth] = cur_obj; + + bool match = false; + int nobj = objs->size (); + if (dpth + 1 >= nobj) + { + match = true; + for (int i = 0; i < nobj; ++i) + { + if (objs->fetch (i) != obj_list[dpth - nobj + 1 + i]) + { + match = false; + break; + } + } + } + + Hist_data::HistItem *hi = NULL; + Hist_data::HistItem *hi_adj = NULL; + if (match && dpth >= nobj) + { + if (hist_data->type == Histable::LINE + || hist_data->type == Histable::INSTR) + hi = hist_data->append_hist_item (get_hist_obj (node_list[dpth - nobj])); + else + hi = hist_data->append_hist_item (obj_list[dpth - nobj]); + + if (pmatch >= 0 && pmatch >= nobj) + { + if (hist_data->type == Histable::LINE + || hist_data->type == Histable::INSTR) + hi_adj = hist_data->append_hist_item (get_hist_obj ( + node_list[pmatch - nobj])); + else + hi_adj = hist_data->append_hist_item (obj_list[pmatch - nobj]); + } + } + + if (hi != NULL) + { + MetricList *mlist = hist_data->get_metric_list (); + for (long ind = 0, sz = mlist->size (); ind < sz; ind++) + { + if (xlate[ind] == -1) + continue; + if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx)) + continue; + Metric *mtr = mlist->get (ind); + Metric::SubType subtype = mtr->get_subtype (); + + switch (subtype) + { + case Metric::ATTRIBUTED: + if (hi) + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + if (hi_adj) + SUB_METRIC_VAL (hi_adj->value[ind], slots[xlate[ind]], node_idx); + break; + case Metric::STATIC: + case Metric::EXCLUSIVE: + case Metric::INCLUSIVE: + case Metric::DATASPACE: + break; + } + } + } + + // Recursively process all descendants + int dsize = NUM_DESCENDANTS (node); + for (int index = 0; index < dsize; index++) + get_clr_metrics (objs, node->descendants->fetch (index), + match ? dpth : pmatch, dpth + 1); +} + +void +PathTree::get_clr_metrics (Vector<Histable*> *objs) +{ + get_clr_metrics (objs, root_idx, -1, 0); +} + +void +PathTree::get_cle_metrics (Vector<Histable*> *objs, NodeIdx node_idx, int pcle, + int pmatch, int dpth) +{ + Node *node = NODE_IDX (node_idx); + Histable *cur_obj = get_hist_obj (node); + obj_list[dpth] = cur_obj; + + bool match = false; + int nobj = objs->size (); + if (dpth + 1 >= nobj) + { + match = true; + for (int i = 0; i < nobj; ++i) + if (objs->fetch (i) != obj_list[dpth - nobj + 1 + i]) + { + match = false; + break; + } + } + + Hist_data::HistItem *hi = NULL; + Hist_data::HistItem *hi_adj = NULL; + if (pmatch >= 0 && dpth == pmatch + 1) + hi = hist_data->append_hist_item (cur_obj); + if (match && IS_LEAF (node)) + hi = hist_data->gprof_item; + if (pcle >= 0) + hi_adj = hist_data->append_hist_item (obj_list[pcle]); + + if (hi != NULL) + { + MetricList *mlist = hist_data->get_metric_list (); + for (long ind = 0, sz = mlist->size (); ind < sz; ind++) + { + if (xlate[ind] == -1) + continue; + if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx)) + continue; + Metric *mtr = mlist->get (ind); + Metric::SubType subtype = mtr->get_subtype (); + if (subtype == Metric::ATTRIBUTED) + { + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + if (hi_adj) + SUB_METRIC_VAL (hi_adj->value[ind], slots[xlate[ind]], node_idx); + } + } + } + + // Recursively process all descendants + int dsize = NUM_DESCENDANTS (node); + for (int index = 0; index < dsize; index++) + get_cle_metrics (objs, node->descendants->fetch (index), + pmatch >= 0 && dpth == pmatch + 1 ? dpth : pcle, + match ? dpth : pmatch, dpth + 1); +} + +void +PathTree::get_cle_metrics (Vector<Histable*> *objs, NodeIdx node_idx, int dpth) +{ + Node *node = NODE_IDX (node_idx); + Histable *cur_obj = get_hist_obj (node); + Hist_data::HistItem *hi = NULL; + if (NULL == objs) // Special case: get root + hi = hist_data->append_hist_item (cur_obj); + else + { + if (dpth == objs->size ()) + hi = hist_data->append_hist_item (cur_obj); + else if (cur_obj == objs->fetch (dpth)) + { + // Recursively process all descendants + int dsize = NUM_DESCENDANTS (node); + for (int index = 0; index < dsize; index++) + get_cle_metrics (objs, node->descendants->fetch (index), dpth + 1); + if (dpth == objs->size () - 1 && dsize == 0) + hi = hist_data->gprof_item; + } + } + + if (hi != NULL) + { + MetricList *mlist = hist_data->get_metric_list (); + for (long ind = 0, sz = mlist->size (); ind < sz; ind++) + { + if (xlate[ind] == -1) + continue; + if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx)) + continue; + Metric *mtr = mlist->get (ind); + Metric::SubType subtype = mtr->get_subtype (); + if (subtype == Metric::ATTRIBUTED) + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + } + } +} + +void +PathTree::ftree_reset () +{ + if (pathTreeType == PATHTREE_MAIN && indxtype < 0) + { + reset (); + if (ftree_needs_update) + { + if (ftree_internal == NULL) + { + ftree_internal = new PathTree (dbev, indxtype, + PATHTREE_INTERNAL_FUNCTREE); + if (ftree_internal == NULL) + return; + } + ftree_internal->ftree_build (this); + ftree_needs_update = false; + } + } +} + +void +PathTree::ftree_build (PathTree * mstr) +{ + fini (); + init (); + allocate_slots (mstr->slots, mstr->nslots); + ftree_build (mstr, mstr->root_idx, root_idx); + depth = mstr->depth; + depth_map_build (); +} + +#if DEBUG_FTREE // possibly TBR +void +PathTree::ftree_dump () +{ + hrtime_t starttime, endtime; + int nmetrics = 1; + // int nmetrics = nslots; + for (int kk = 0; kk < nmetrics; kk++) + { + int id = slots[kk].id; + starttime = gethrtime (); + long nodecnt = 0; + for (int ii = 0; ii < depth; ii++) + { + Vector<Vector<void*>*> *tmp = (Vector<Vector<void*>*>*)get_ftree_level + (id, ii); + if (tmp == NULL) + continue; + long sz = tmp->get (0)->size (); + nodecnt += sz; +#if 1 + // fprintf(stderr, "... finished (%ld nodes)\n", sz); +#else + Vector<NodeIdx> *nodeIdxList = (Vector<NodeIdx> *)tmp->get (0); + Vector<NodeIdx> *ancestorNodeIdxList = (Vector<NodeIdx> *)tmp->get (1); + Vector<uint64_t> *idList = (Vector<uint64_t> *)tmp->get (2); + Vector<uint64_t> *vals = (Vector<uint64_t> *)tmp->get (3); + for (int jj = 0; jj < sz; jj++) + fprintf (stderr, " ...%d:%d node=%ld, anc=%ld, id=%llu, val=%llu\n", + sz, jj, nodeIdxList->get (jj), + ancestorNodeIdxList->get (jj), + idList->get (jj), vals->get (jj)); +#endif + destroy (tmp); + } + endtime = gethrtime (); + fprintf (stderr, "====================== %ld nodes time=%llu\n", + nodecnt, (endtime - starttime) / 1000 / 1000); + } +} +#endif + +// ftree: translate mstr Histable::INSTR to Histable::FUNCTION +void +PathTree::ftree_build (PathTree *mstr, NodeIdx mstr_node_idx, + NodeIdx local_node_idx) +{ + // requires: slots, nslots + Node *mstr_node = mstr->NODE_IDX (mstr_node_idx); + int dsize = NUM_DESCENDANTS (mstr_node); + + // Add metrics + for (int i = 0; i < nslots; i++) + { + if (i >= mstr->nslots) + continue; //weird + if (slots[i].vtype != mstr->slots[i].vtype) + continue; //weird + TValue val; + val.ll = 0; + mstr->ASN_METRIC_VAL (val, mstr->slots[i], mstr_node_idx); + int64_t mval; + switch (slots[i].vtype) + { + case VT_ULLONG: + case VT_LLONG: + mval = val.ll; + break; + case VT_INT: + mval = val.i; + break; + default: + mval = 0; + break; + } + if (mval) + { + Slot * mslot = SLOT_IDX (i); + if (mslot) + INCREMENT_METRIC (mslot, local_node_idx, mval); + } + } + + // Recursively process all descendants + for (int index = 0; index < dsize; index++) + { + NodeIdx mstr_desc_node_idx = mstr_node->descendants->fetch (index); + Node *mstr_desc_node = mstr->NODE_IDX (mstr_desc_node_idx); + Function *func = (Function*) mstr_desc_node->instr->convertto (Histable::FUNCTION); + int mstr_desc_dsize = NUM_DESCENDANTS (mstr_desc_node); + bool leaf = (mstr_desc_dsize == 0); + NodeIdx local_desc_node_idx = find_desc_node (local_node_idx, func, leaf); + ftree_build (mstr, mstr_desc_node_idx, local_desc_node_idx); + } +} + +void +PathTree::depth_map_build () +{ + destroy (depth_map); + depth_map = new Vector<Vector<NodeIdx>*>(depth); + if (depth) + { + depth_map->put (depth - 1, 0); // fill vector with nulls + depth_map_build (root_idx, 0); + } +} + +void +PathTree::depth_map_build (NodeIdx node_idx, int dpth) +{ + Node *node = NODE_IDX (node_idx); + + Vector<NodeIdx> *node_idxs = depth_map->get (dpth); + if (node_idxs == NULL) + { + node_idxs = new Vector<NodeIdx>(); + depth_map->store (dpth, node_idxs); + } + node_idxs->append (node_idx); + + // Recursively process all descendants + int dsize = NUM_DESCENDANTS (node); + for (int index = 0; index < dsize; index++) + { + NodeIdx desc_node_idx = node->descendants->fetch (index); + depth_map_build (desc_node_idx, dpth + 1); + } +} + +int +PathTree::get_ftree_depth () +{ // external use only + ftree_reset (); + if (!ftree_internal) + return 0; + return ftree_internal->get_depth (); +} + +Vector<Function*>* +PathTree::get_ftree_funcs () +{ // external use only + ftree_reset (); + if (!ftree_internal) + return NULL; + return ftree_internal->get_funcs (); +} + +Vector<Function*>* +PathTree::get_funcs () +{ + // get unique functions + if (fn_map == NULL) + return NULL; + return fn_map->keySet (); +} + +Vector<void*>* +PathTree::get_ftree_level (BaseMetric *bm, int dpth) +{ // external use only + ftree_reset (); + if (!ftree_internal) + return NULL; + return ftree_internal->get_level (bm, dpth); +} + +Vector<void*>* +PathTree::get_level (BaseMetric *bm, int dpth) +{ + // Nodes at tree depth dpth + if (dpth < 0 || dpth >= depth) + return NULL; + if (depth_map == NULL) + return NULL; + Vector<NodeIdx> *node_idxs = depth_map->get (dpth); + return get_nodes (bm, node_idxs); +} + +Vector<void*>* +PathTree::get_ftree_node_children (BaseMetric *bm, NodeIdx node_idx) +{ // external use only + ftree_reset (); + if (!ftree_internal) + return NULL; + return ftree_internal->get_node_children (bm, node_idx); +} + +Vector<void*>* +PathTree::get_node_children (BaseMetric *bm, NodeIdx node_idx) +{ + // Nodes that are children of node_idx + if (depth_map == NULL) + return NULL; + if (node_idx == 0) // special case for root + return get_nodes (bm, depth_map->get (0)); + if (node_idx < 0 || node_idx >= nodes) + return NULL; + Node *node = NODE_IDX (node_idx); + if (node == NULL) + return NULL; + Vector<NodeIdx> *node_idxs = node->descendants; + return get_nodes (bm, node_idxs); +} + +Vector<void*>* +PathTree::get_nodes (BaseMetric *bm, Vector<NodeIdx> *node_idxs) +{ // used for ftree + // capture info for node_idxs: + // node's idx + // node->ancestor idx + // node->instr->id + // mind metric value // in the future, could instead accept vector of mind + if (node_idxs == NULL) + return NULL; + long sz = node_idxs->size (); + if (sz <= 0) + return NULL; + + bool calculate_metric = false; + ValueTag vtype; + int slot_idx; + double prec; + if (bm != NULL) + { + int mind = bm->get_id (); + slot_idx = find_slot (mind); // may be -1 (CPI and IPC) + prec = bm->get_precision (); + vtype = bm->get_vtype (); + } + else + { + slot_idx = -1; + prec = 1.0; + vtype = VT_INT; + } + + if (slot_idx >= 0) + { + switch (vtype) + { + case VT_ULLONG: + case VT_LLONG: + case VT_INT: + if (slots[slot_idx].vtype == vtype) + calculate_metric = true; + else + DBG (assert (false)); + break; + case VT_DOUBLE: + calculate_metric = true; + break; + default: + break; + } + } + + Vector<void*> *results = new Vector<void*>(4); + if (!calculate_metric) + results->store (3, NULL); + else + { + // Code below cribbed from Dbe.cc:dbeGetTableDataV2Data. + // TBD: possibly create an intermediate HistData and instead call that routine + switch (vtype) + { + case VT_ULLONG: + case VT_LLONG: + { + Vector<long long> *vals = new Vector<long long>(sz); + for (long i = 0; i < sz; i++) + { + NodeIdx node_idx = node_idxs->get (i); + TValue val; + val.ll = 0; + ASN_METRIC_VAL (val, slots[slot_idx], node_idx); + vals->append (val.ll); + } + results->store (3, vals); + break; + } + case VT_DOUBLE: + { + Vector<double> *vals = new Vector<double>(sz); + TValue val; + val.tag = slots[slot_idx].vtype; // required for to_double(); + for (long i = 0; i < sz; i++) + { + NodeIdx node_idx = node_idxs->get (i); + val.ll = 0; + ASN_METRIC_VAL (val, slots[slot_idx], node_idx); + double dval = val.to_double (); + dval /= prec; + vals->append (dval); + } + results->store (3, vals); + break; + } + case VT_INT: + { + Vector<int> *vals = new Vector<int>(sz); + for (long i = 0; i < sz; i++) + { + NodeIdx node_idx = node_idxs->get (i); + TValue val; + val.i = 0; + ASN_METRIC_VAL (val, slots[slot_idx], node_idx); + vals->append (val.i); + } + results->store (3, vals); + break; + } + default: + results->store (3, NULL); + break; + } + } + + Vector<int> *nodeIdxList = new Vector<int>(sz); + Vector<int> *ancestorNodeIdxList = new Vector<int>(sz); + Vector<uint64_t> *idList = new Vector<uint64_t>(sz); + for (long i = 0; i < sz; i++) + { + NodeIdx node_idx = node_idxs->get (i); + Node *node = NODE_IDX (node_idx); + NodeIdx ancestor_idx = node->ancestor; + Histable *func = node->instr; + nodeIdxList->append (node_idx); + ancestorNodeIdxList->append (ancestor_idx); + idList->append (func->id); + } + + results->store (0, nodeIdxList); + results->store (1, ancestorNodeIdxList); + results->store (2, idList); + return results; +} + +void +PathTree::get_cle_metrics (Vector<Histable*> *objs) +{ + if (NULL == objs || objs->fetch (0) == get_hist_obj (NODE_IDX (root_idx))) + // Call Tree optimization + get_cle_metrics (objs, root_idx, 0); + else + // General case + get_cle_metrics (objs, root_idx, -1, -1, 0); +} + +void +PathTree::get_metrics (Vector<Function*> *functions, Histable *context) +{ + Function *fitem; + int excl_ok, incl_ok; + NodeIdx node_idx; + Node *node, *anc; + int index; + + Vec_loop (Function*, functions, index, fitem) + { + node_idx = fn_map->get (fitem); + for (; node_idx; node_idx = node->funclist) + { + node = NODE_IDX (node_idx); + Histable *h_obj = get_hist_obj (node, context); + if (h_obj == NULL) + continue; + + // Check for recursion (inclusive metrics) + incl_ok = 1; + for (anc = NODE_IDX (node->ancestor); anc; + anc = NODE_IDX (anc->ancestor)) + { + if (h_obj == get_hist_obj (anc, context)) + { + incl_ok = 0; + break; + } + } + + // Check for leaf nodes (exclusive metrics) + excl_ok = 0; + if (IS_LEAF (node)) + excl_ok = 1; + + h_obj = get_compare_obj (h_obj); + Hist_data::HistItem *hi = hist_data->append_hist_item (h_obj); + + if (!excl_ok) + hist_data->get_callsite_mark ()->put (h_obj, 1); + MetricList *mlist = hist_data->get_metric_list (); + for (long ind = 0, sz = mlist->size (); ind < sz; ind++) + { + if (xlate[ind] == -1) + continue; + Metric *mtr = mlist->get (ind); + Metric::SubType subtype = mtr->get_subtype (); + if (subtype == Metric::INCLUSIVE && !incl_ok) + continue; + if (subtype == Metric::EXCLUSIVE && !excl_ok) + continue; + if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx)) + continue; + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + } + } + } +} + +void +PathTree::get_self_metrics (Vector<Histable*> *objs, NodeIdx node_idx, + bool seen, int dpth) +{ + Node *node = NODE_IDX (node_idx); + Histable *cur_obj = get_hist_obj (node); + obj_list[dpth] = cur_obj; + + bool match = false; + int nobj = objs->size (); + if (dpth + 1 >= nobj) + { + match = true; + for (int i = 0; i < nobj; ++i) + { + if (objs->fetch (i) != obj_list[dpth - nobj + 1 + i]) + { + match = false; + break; + } + } + } + + if (match) + { + Hist_data::HistItem *hi = hist_data->append_hist_item (cur_obj); + int incl_ok = !seen; + int excl_ok = 0; + if (IS_LEAF (node) || node == NODE_IDX (root_idx)) + excl_ok = 1; + MetricList *mlist = hist_data->get_metric_list (); + for (long ind = 0, sz = mlist->size (); ind < sz; ind++) + { + if (xlate[ind] == -1) + continue; + if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx)) + continue; + Metric *mtr = mlist->get (ind); + Metric::SubType subtype = mtr->get_subtype (); + switch (subtype) + { + case Metric::INCLUSIVE: + if (incl_ok && hi) + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + break; + case Metric::EXCLUSIVE: + case Metric::ATTRIBUTED: + if (excl_ok && hi) + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + break; + case Metric::DATASPACE: + if (hi) + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + break; + // ignoring the following cases (why?) + case Metric::STATIC: + break; + } + } + } + + if (dbeSession->is_interactive ()) + { + ndone++; + int new_percent = 95 * ndone / nodes; + if (new_percent > percent) + { + percent = new_percent; + theApplication->set_progress (percent, NULL); + } + } + + // Recursively process all descendants + int index; + int dsize = NUM_DESCENDANTS (node); + for (index = 0; index < dsize; index++) + get_self_metrics (objs, node->descendants->fetch (index), + seen || match, dpth + 1); +} + +void +PathTree::get_self_metrics (Vector<Histable*> *objs) +{ + get_self_metrics (objs, root_idx, false, 0); +} + +void +PathTree::get_self_metrics (Histable *obj, Vector<Function*> *funclist, + Vector<Histable*>* sel_objs) +{ + int excl_ok, incl_ok; + NodeIdx node_idx; + Node *node, *anc; + + if (obj == NULL) + return; + + SourceFile *src = NULL; + if (obj && obj->get_type () == Histable::LINE) + { + DbeLine *dbeline = (DbeLine*) obj; + src = dbeline->sourceFile; + } + + Hist_data::HistItem *hi = hist_data->append_hist_item (obj); + for (int i = 0, sz = funclist ? funclist->size () : 0; i < sz; i++) + { + Function *fitem = (Function*) get_compare_obj (funclist->fetch (i)); + node_idx = fn_map->get (fitem); + for (; node_idx; node_idx = node->funclist) + { + node = NODE_IDX (node_idx); + if (obj && obj->get_type () == Histable::LINE) + { + Histable *h = get_hist_obj (node, src); + if (h == NULL) + continue; + if (h->convertto (Histable::LINE) != obj->convertto (Histable::LINE)) + continue; + } + else if (get_hist_obj (node, src) != obj) + continue; + + // Check for recursion (inclusive metrics) + incl_ok = 1; + for (anc = NODE_IDX (node->ancestor); anc; + anc = NODE_IDX (anc->ancestor)) + { + if (get_hist_obj (anc, src) == obj) + { + incl_ok = 0; + break; + } + if (sel_objs != NULL) + for (int k = 0; k < sel_objs->size (); k++) + if (sel_objs->fetch (k) == get_hist_obj (anc, src)) + { + incl_ok = 0; + break; + } + } + + // Check for leaf nodes (exclusive metrics) + excl_ok = 0; + if (IS_LEAF (node) || node == NODE_IDX (root_idx)) + excl_ok = 1; + + MetricList *mlist = hist_data->get_metric_list (); + for (long ind = 0, ind_sz = mlist->size (); ind < ind_sz; ind++) + { + if (xlate[ind] == -1) + continue; + Metric *mtr = mlist->get (ind); + Metric::SubType subtype = mtr->get_subtype (); + if (subtype == Metric::INCLUSIVE && !incl_ok) + continue; + if (subtype == Metric::EXCLUSIVE && !excl_ok) + continue; + if (subtype == Metric::ATTRIBUTED && !excl_ok) + continue; + if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx)) + continue; + ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx); + } + } + } +} + +Vector<Histable*> * +PathTree::get_clr_instr (Histable * func) +{ + Vector<Histable*> * instrs = NULL; + if (func->get_type () != Histable::FUNCTION) + return NULL; + NodeIdx node_idx = fn_map->get ((Function*) func); + Node *node = NODE_IDX (node_idx); + if (node == NULL) + return new Vector<Histable*>(); + int instr_num = 0; + for (; node; node = NODE_IDX (node->funclist)) + instr_num++; + instrs = new Vector<Histable*>(instr_num); + node = NODE_IDX (node_idx); + Histable *instr = NODE_IDX (node->ancestor)->instr; + instr_num = 0; + instrs->store (instr_num, instr); + node = NODE_IDX (node->funclist); + for (; node; node = NODE_IDX (node->funclist)) + { + instr = NODE_IDX (node->ancestor)->instr; + instr_num++; + instrs->store (instr_num, instr); + } + return instrs; +} + +Vector<void*> * +PathTree::get_cle_instr (Histable * func, Vector<Histable*>*&instrs) +{ + if (func->get_type () != Histable::FUNCTION) + return NULL; + NodeIdx node_idx = fn_map->get ((Function*) func); + Node *node = NODE_IDX (node_idx); + if (node == NULL) + { + instrs = new Vector<Histable*>(); + return new Vector<void*>(); + } + int instr_num = 0; + for (; node; node = NODE_IDX (node->funclist)) + instr_num++; + instrs = new Vector<Histable*>(instr_num); + Vector<void*> *callee_info = new Vector<void*>(instr_num); + node = NODE_IDX (node_idx); + Histable *instr = node->instr; + instr_num = 0; + instrs->store (instr_num, instr); + int dec_num = 0; + NodeIdx dec_idx = 0; + if (NUM_DESCENDANTS (node) > 0) + { + Vector<Histable*> * callee_instrs = new Vector<Histable*>(node->descendants->size ()); + Vec_loop (NodeIdx, node->descendants, dec_num, dec_idx) + { + Node * dec_node = NODE_IDX (dec_idx); + //XXXX Note: there can be more than one instrs in one leaf function + callee_instrs->store (dec_num, dec_node->instr); + } + callee_info->store (instr_num, callee_instrs); + } + else + callee_info->store (instr_num, NULL); + node = NODE_IDX (node->funclist); + for (; node; node = NODE_IDX (node->funclist)) + { + instr = node->instr; + instr_num++; + instrs->store (instr_num, instr); + if (NUM_DESCENDANTS (node) > 0) + { + Vector<Histable*> * callee_instrs = new Vector<Histable*>(node->descendants->size ()); + Vec_loop (NodeIdx, node->descendants, dec_num, dec_idx) + { + Node * dec_node = NODE_IDX (dec_idx); + //XXXX Note: there can be more than one instrs in one leaf function + callee_instrs->store (dec_num, dec_node->instr); + } + callee_info->store (instr_num, callee_instrs); + } + else + callee_info->store (instr_num, NULL); + } + return callee_info; +} + +// +// +// The following methods are used for debugging purpose only. +// +// +static int maxdepth; +static int maxwidth; + +void +PathTree::print (FILE *fd) +{ + (void) reset (); + fprintf (fd, NTXT ("n = %lld, dn = %lld, MD = %lld\n\n"), + (long long) nodes, (long long) dnodes, (long long) depth); + maxdepth = 0; + maxwidth = 0; + print (fd, root, 0); + fprintf (fd, NTXT ("md = %lld, mw = %lld\n"), + (long long) maxdepth, (long long) maxwidth); +} + +void +PathTree::print (FILE *fd, PathTree::Node *node, int lvl) +{ + const char *t; + char *n; + if (lvl + 1 > maxdepth) + maxdepth = lvl + 1; + for (int i = 0; i < lvl; i++) + fprintf (fd, NTXT ("-")); + Histable *instr = node->instr; + if (instr->get_type () == Histable::LINE) + { + t = "L"; + n = ((DbeLine *) instr)->func->get_name (); + } + else if (instr->get_type () == Histable::INSTR) + { + t = "I"; + n = ((DbeInstr *) instr)->func->get_name (); + } + else + { + t = "O"; + n = instr->get_name (); + } + long long addr = (long long) instr->get_addr (); + fprintf (fd, NTXT ("%s %s (0x%08llx) -- ndesc = %lld\n"), + t, n, addr, (long long) (NUM_DESCENDANTS (node))); + + // Recursively process all descendants + int dsize = NUM_DESCENDANTS (node); + if (dsize > maxwidth) + maxwidth = dsize; + for (int index = 0; index < dsize; index++) + print (fd, NODE_IDX (node->descendants->fetch (index)), lvl + 1); +} + +void +PathTree::printn (FILE *fd) +{ + int n = dbg_nodes (root); + fprintf (fd, GTXT ("Number of nodes: %d, total size: %d\n"), n, (int) (n * sizeof (Node))); +} + +void +PathTree::dumpNodes (FILE *fd, Histable *obj) +{ + const char *t; + char *n; + NodeIdx node_idx = fn_map->get ((Function*) obj); + Node *node = NODE_IDX (node_idx); + if (node == NULL) + { + fprintf (fd, GTXT ("No nodes associated with %s\n"), obj->get_name ()); + return; + } + Histable *instr = node->instr; + for (; node; node = NODE_IDX (node->funclist)) + { + instr = node->instr; + if (instr->get_type () == Histable::LINE) + { + t = "L"; + n = ((DbeLine *) instr)->func->get_name (); + } + else if (instr->get_type () == Histable::INSTR) + { + t = "I"; + n = ((DbeInstr *) instr)->func->get_name (); + } + else + { + t = "O"; + n = instr->get_name (); + } + long long addr = (long long) instr->get_addr (); + if (addr <= 0xFFFFFFFFU) + fprintf (fd, NTXT ("0x%08x -- %s %s\n"), (uint32_t) addr, t, n); + else + fprintf (fd, NTXT ("0x%016llX -- %s %s\n"), addr, t, n); + } +} + +int +PathTree::dbg_nodes (PathTree::Node *node) +{ + int res = 1; + int dsize = NUM_DESCENDANTS (node); + for (int index = 0; index < dsize; index++) + res += dbg_nodes (NODE_IDX (node->descendants->fetch (index))); + return res; +} + +static int mind_g; + +int +leak_alloc_comp (const void *s1, const void *s2) +{ + // See Hist_data::sort_compare() for duplicate code + int result = 0; + CStack_data::CStack_item *t1, *t2; + t1 = *(CStack_data::CStack_item **)s1; + t2 = *(CStack_data::CStack_item **)s2; + + switch (t1->value[mind_g].tag) + { + case VT_INT: + if (t1->value[mind_g].i < t2->value[mind_g].i) + result = -1; + else if (t1->value[mind_g].i > t2->value[mind_g].i) + result = 1; + else + result = 0; + break; + case VT_LLONG: + if (t1->value[mind_g].ll < t2->value[mind_g].ll) + result = -1; + else if (t1->value[mind_g].ll > t2->value[mind_g].ll) + result = 1; + else + result = 0; + break; + case VT_ULLONG: + if (t1->value[mind_g].ull < t2->value[mind_g].ull) + result = -1; + else if (t1->value[mind_g].ull > t2->value[mind_g].ull) + result = 1; + else + result = 0; + break; + // ignoring the following cases (why?) + case VT_SHORT: + case VT_FLOAT: + case VT_DOUBLE: + case VT_HRTIME: + case VT_LABEL: + case VT_ADDRESS: + case VT_OFFSET: + break; + } + // Sort in descending order + return -result; +} + +CStack_data * +PathTree::get_cstack_data (MetricList *mlist) +{ + (void) reset (); + CStack_data *lam = new CStack_data (mlist); + int nmetrics = mlist->get_items ()->size (); + mind_g = -1; + xlate = new int[nmetrics]; + for (int mind = 0; mind < nmetrics; mind++) + { + xlate[mind] = -1; + Metric *mtr = mlist->get_items ()->fetch (mind); + if (mlist->get_sort_ref_index () == mind) + mind_g = mind; + xlate[mind] = find_slot (mtr->get_id ()); + } + + // now fill in the actual data + obj_list = new Histable*[depth]; + get_cstack_list (lam, root_idx, 0); + delete[] obj_list; + + if (mind_g >= 0) + lam->cstack_items->sort (leak_alloc_comp); + delete[] xlate; + return lam; +} + +void +PathTree::get_cstack_list (CStack_data *lam, NodeIdx node_idx, int dpth) +{ + + Node *node = NODE_IDX (node_idx); + obj_list[dpth] = node->instr; + + CStack_data::CStack_item *item = NULL; + if (IS_LEAF (node)) + item = lam->new_cstack_item (); + int nmetrics = lam->metrics->get_items ()->size (); + bool subtree_empty = true; + + for (int mind = 0; mind < nmetrics; mind++) + { + if (xlate[mind] == -1) + continue; + if (IS_MVAL_ZERO (slots[xlate[mind]], node_idx)) + continue; + else + subtree_empty = false; + if (item) + { + ADD_METRIC_VAL (item->value[mind], slots[xlate[mind]], node_idx); + ADD_METRIC_VAL (lam->total->value[mind], slots[xlate[mind]], node_idx); + } + } + + if (subtree_empty) + { + delete item; + return; + } + + if (item) + { + // Finish processing a leaf node + item->stack = new Vector<DbeInstr*>(dpth); + for (int i = 1; i <= dpth; i++) + item->stack->append ((DbeInstr*) obj_list[i]); + lam->cstack_items->append (item); + } + else + { + // Recursively process all descendants + int dsize = NUM_DESCENDANTS (node); + for (int index = 0; index < dsize; index++) + get_cstack_list (lam, node->descendants->fetch (index), dpth + 1); + } +} + +Emsg * +PathTree::fetch_stats () +{ + if (statsq == NULL) + return NULL; + return statsq->fetch (); +} + +void +PathTree::delete_stats () +{ + if (statsq != NULL) + { + delete statsq; + statsq = new Emsgqueue (NTXT ("statsq")); + } +} + +Emsg * +PathTree::fetch_warnings () +{ + if (warningq == NULL) + return NULL; + return warningq->fetch (); +} + +void +PathTree::delete_warnings () +{ + if (warningq != NULL) + { + delete warningq; + warningq = new Emsgqueue (NTXT ("warningq")); + } +} diff --git a/gprofng/src/PathTree.h b/gprofng/src/PathTree.h new file mode 100644 index 0000000..dc3ad1f --- /dev/null +++ b/gprofng/src/PathTree.h @@ -0,0 +1,405 @@ +/* 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. */ + +#ifndef _PATH_TREE_H +#define _PATH_TREE_H + +#include <vec.h> +#include <Map.h> + +#include "dbe_structs.h" +#include "Hist_data.h" +#include "Histable.h" +#include "Metric.h" + +typedef enum +{ + NORMAL = 0, CANCELED +} PtreePhaseStatus; + +class PathTree +{ +public: + + PathTree (DbeView *_dbev, int _indxtype = -1) + { + construct (_dbev, _indxtype, PATHTREE_MAIN); + } + + ~PathTree (); + + static void make_deltas (int vtype, TValue *v1, TValue *v2); + static void make_ratios (int vtype, TValue *v1, TValue *v2); + + typedef enum + { + COMPUTEOPT_NONE = 0, + COMPUTEOPT_OMP_CALLEE + } PtreeComputeOption; + + Hist_data *compute_metrics (MetricList *, Histable::Type, + Hist_data::Mode, Vector<Histable*>*, + Histable*, Vector<Histable*>* sel_objs = NULL, + PtreeComputeOption flag = COMPUTEOPT_NONE); + // Get aggregated callstack data + CStack_data *get_cstack_data (MetricList *); + + Vector<Histable*> *get_clr_instr (Histable *); + Vector<void*> *get_cle_instr (Histable *, Vector<Histable*>*&); + + int + get_status () + { + return status; + } + + int + get_depth () + { + return depth; + } + + int + getStackProp () + { + return stack_prop; + } + + typedef long NodeIdx; + + struct Node + { + inline void + reset () + { + ancestor = 0; + descendants = NULL; + instr = NULL; + funclist = 0; + } + + NodeIdx ancestor; + Vector<NodeIdx> *descendants; + Histable *instr; + NodeIdx funclist; + }; + + static const int CHUNKSZ = 16384; + + inline Node * + NODE_IDX (NodeIdx idx) + { + return idx ? &chunks[idx / CHUNKSZ][idx % CHUNKSZ] : NULL; + } + + // queue for messages (statistics for pathtree processing) + Emsg *fetch_stats (void); // fetch the queue of comment messages + void delete_stats (void); // delete the queue of stats messages + Emsg *fetch_warnings (void); // fetch the queue of warnings messages + void delete_warnings (void); // delete the queue of warnings messages + + NodeIdx + get_func_nodeidx (Function * func) + { + return fn_map == NULL ? (NodeIdx) 0 : fn_map->get (func); + } + + void print (FILE *); + void dumpNodes (FILE *, Histable *); + + // flame charts functions - get values from ftree_internal + int get_ftree_depth (); // Depth of tree + Vector<void*>* get_ftree_level (BaseMetric *bm, int dpth); + Vector<void*>* get_ftree_node_children (BaseMetric *bm, NodeIdx node_idx); + Vector<Function*>* get_ftree_funcs (); + Vector<Function*>* get_funcs (); // Unique functions in tree + +private: + + enum + { + MAX_DESC_HTABLE_SZ = 65535 + }; + + typedef struct hash_node + { + NodeIdx nd; + struct hash_node *next; + } hash_node_t; + + int desc_htable_size; + int desc_htable_nelem; + hash_node_t **descHT; + + struct Slot + { + int id; + ValueTag vtype; + union + { + int **mvals; + int64_t **mvals64; + }; + }; + + typedef enum + { + PATHTREE_MAIN = 0, + PATHTREE_INTERNAL_OMP, + PATHTREE_INTERNAL_FUNCTREE + } PathTreeType; + + DbeView *dbev; + int indxtype; + int stack_prop; + Expression *indx_expr; + Histable *total_obj; + Map<Function*, NodeIdx> *fn_map; + Map<uint64_t, NodeIdx> *pathMap; + Map<uint64_t, uint64_t> *hideMap; + int status; + NodeIdx root_idx; + Node *root; + int depth; + long nodes; + long dnodes; + long nchunks; + Node **chunks; + int nslots; + Slot *slots; + int phaseIdx; + int nexps; + Emsgqueue *statsq; + Emsgqueue *warningq; + Hist_data *hist_data; + int percent; + int ndone; + Histable **obj_list; + Node **node_list; + int *xlate; + bool cancel_ok; + PathTreeType pathTreeType; + PathTree *ptree_internal; + PathTree *ftree_internal; // function-based pathtree + bool ftree_needs_update; + Vector<Vector<NodeIdx>*> *depth_map; // for each depth level, list of nodes + + void init (); + void fini (); + PtreePhaseStatus reset (); + PtreePhaseStatus add_experiment (int); + PtreePhaseStatus process_packets (Experiment*, DataView*, int); + DataView *get_filtered_events (int exp_index, int data_type); + void construct (DbeView *_dbev, int _indxtype, PathTreeType _pathTreeType); + + PathTree (DbeView *_dbev, int _indxtype, PathTreeType _pathTreeType) + { + construct (_dbev, _indxtype, _pathTreeType); + } + + inline int * + allocate_chunk (int **p, NodeIdx idx) + { + int *res = new int[CHUNKSZ]; + for (int i = 0; i < CHUNKSZ; i++) + res[i] = 0; + p[idx] = res; + return res; + }; + + inline int64_t * + allocate_chunk (int64_t **p, NodeIdx idx) + { + int64_t *res = new int64_t[CHUNKSZ]; + for (int i = 0; i < CHUNKSZ; i++) + res[i] = 0; + p[idx] = res; + return res; + }; + + inline Node * + allocate_chunk (Node **p, NodeIdx idx) + { + Node *res = new Node[CHUNKSZ]; + for (int i = 0; i < CHUNKSZ; i++) + res[i].reset (); + p[idx] = res; + return res; + }; + + inline bool + IS_MVAL_ZERO (Slot& slot, NodeIdx idx) + { + if (slot.vtype == VT_LLONG || slot.vtype == VT_ULLONG) + { + int64_t *tmp = slot.mvals64[idx / CHUNKSZ]; + return tmp ? tmp[idx % CHUNKSZ] == 0 : true; + } + else + { + int *tmp = slot.mvals[idx / CHUNKSZ]; + return tmp ? tmp[idx % CHUNKSZ] == 0 : true; + } + } + + inline void + ASN_METRIC_VAL (TValue& v, Slot& slot, NodeIdx idx) + { + if (slot.vtype == VT_LLONG) + { + int64_t *tmp = slot.mvals64[idx / CHUNKSZ]; + if (tmp) + v.ll = tmp[idx % CHUNKSZ]; + } + else if (slot.vtype == VT_ULLONG) + { + uint64_t *tmp = (uint64_t *) slot.mvals64[idx / CHUNKSZ]; + if (tmp) + v.ull = tmp[idx % CHUNKSZ]; + } + else + { + int *tmp = slot.mvals[idx / CHUNKSZ]; + if (tmp) + v.i = tmp[idx % CHUNKSZ]; + } + } + + inline void + ADD_METRIC_VAL (TValue& v, Slot& slot, NodeIdx idx) + { + if (slot.vtype == VT_LLONG) + { + int64_t *tmp = slot.mvals64[idx / CHUNKSZ]; + if (tmp) + v.ll += tmp[idx % CHUNKSZ]; + } + else if (slot.vtype == VT_ULLONG) + { + uint64_t *tmp = (uint64_t *) slot.mvals64[idx / CHUNKSZ]; + if (tmp) + v.ull += tmp[idx % CHUNKSZ]; + } + else + { + int *tmp = slot.mvals[idx / CHUNKSZ]; + if (tmp) v.i += tmp[idx % CHUNKSZ]; + } + } + + inline void + SUB_METRIC_VAL (TValue& v, Slot& slot, NodeIdx idx) + { + if (slot.vtype == VT_LLONG) + { + int64_t *tmp = slot.mvals64[idx / CHUNKSZ]; + if (tmp) + v.ll -= tmp[idx % CHUNKSZ]; + } + else if (slot.vtype == VT_ULLONG) + { + uint64_t *tmp = (uint64_t *) slot.mvals64[idx / CHUNKSZ]; + if (tmp) + v.ull -= tmp[idx % CHUNKSZ]; + } + else + { + int *tmp = slot.mvals[idx / CHUNKSZ]; + if (tmp) + v.i -= tmp[idx % CHUNKSZ]; + } + } + + inline void + INCREMENT_METRIC (Slot *slot, NodeIdx idx, int64_t val) + { + if (slot->vtype == VT_LLONG) + { + int64_t *tmp = slot->mvals64[idx / CHUNKSZ]; + if (tmp == NULL) + tmp = allocate_chunk (slot->mvals64, idx / CHUNKSZ); + tmp[idx % CHUNKSZ] += val; + } + else if (slot->vtype == VT_ULLONG) + { + uint64_t *tmp = (uint64_t *) slot->mvals64[idx / CHUNKSZ]; + if (tmp == NULL) + tmp = (uint64_t *) allocate_chunk (slot->mvals64, idx / CHUNKSZ); + tmp[idx % CHUNKSZ] += val; + } + else + { + int *tmp = slot->mvals[idx / CHUNKSZ]; + if (tmp == NULL) + tmp = allocate_chunk (slot->mvals, idx / CHUNKSZ); + tmp[idx % CHUNKSZ] += (int) val; + } + } + + inline Slot * + SLOT_IDX (int idx) + { + if (idx < 0 || idx >= nslots) + return NULL; + return &slots[idx]; + } + + int allocate_slot (int id, ValueTag vtype); + void allocate_slots (Slot *slots, int nslots); + int find_slot (int); + NodeIdx new_Node (NodeIdx, Histable*, bool); + NodeIdx find_path (Experiment*, DataView*, long); + NodeIdx find_desc_node (NodeIdx, Histable*, bool); + NodeIdx find_in_desc_htable (NodeIdx, Histable*, bool); + Histable *get_hist_obj (Node *, Histable* = NULL); + Histable *get_hist_func_obj (Node *); + Histable *get_compare_obj (Histable *obj); + void get_metrics (NodeIdx, int); + void get_metrics (Vector<Function*> *, Histable *); + void get_clr_metrics (Vector<Histable*>*, NodeIdx, int, int); + void get_clr_metrics (Vector<Histable*>*); + void get_cle_metrics (Vector<Histable*>*, NodeIdx, int, int, int); + void get_cle_metrics (Vector<Histable*>*, NodeIdx, int); + void get_cle_metrics (Vector<Histable*>*); + void get_self_metrics (Vector<Histable*>*, NodeIdx, bool, int); + void get_self_metrics (Vector<Histable*>*); + void get_self_metrics (Histable *, Vector<Function*> *funclist, + Vector<Histable*>* sel_objs = NULL); + void get_cstack_list (CStack_data *, NodeIdx, int); + + // Generate PathTree based on Functions instead of Instructions // Used for flame chart + void ftree_reset (); + void ftree_build (PathTree *mstr); + void ftree_build (PathTree *mstr, NodeIdx mstr_node_idx, NodeIdx local_node_idx); + void depth_map_build (); + void depth_map_build (NodeIdx node_idx, int depth); + Vector<void*>* get_level (BaseMetric *bm, int dpth); + Vector<void*>* get_nodes (BaseMetric *bm, Vector<NodeIdx> *node_idxs); + Vector<void*>* get_node_children (BaseMetric *bm, NodeIdx node_idx); + bool ftree_debug_match_hist_data (Hist_data *data, Hist_data *data_tmp); + void ftree_dump (); + + // Debugging functions + void print (FILE *, PathTree::Node*, int); + void printn (FILE *); + int dbg_nodes (PathTree::Node*); +}; + +#endif /* _PATH_TREE_H */ diff --git a/gprofng/src/PreviewExp.cc b/gprofng/src/PreviewExp.cc new file mode 100644 index 0000000..553afd3 --- /dev/null +++ b/gprofng/src/PreviewExp.cc @@ -0,0 +1,113 @@ +/* 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 <stdio.h> + +#include "PreviewExp.h" +#include "Data_window.h" +#include "DbeSession.h" +#include "Emsg.h" +#include "Print.h" +#include "i18n.h" + +PreviewExp::PreviewExp (): Experiment () { } + +PreviewExp::~PreviewExp () { }//~PreviewExp + +Experiment::Exp_status +PreviewExp::experiment_open (char *path) +{ + // Find experiment directory + if ((status = find_expdir (path)) != SUCCESS) + { + size_t len = strlen (path); + is_group = ((len > 4) && !strcmp (&path[len - 4], NTXT (".erg"))); + return status; + } + else + is_group = 0; + + read_log_file (); + if (status == FAILURE) + return status; + + if (status == INCOMPLETE && resume_ts != MAX_TIME) + // experiment is incomplete and "resumed" (non-paused) + // PreviewExp does not process all the packets, therefore... + // ... last_event does not reflect reality + // ... we don't know the duration or the end. + last_event = ZERO_TIME; // mark last_event as uninitialized + + read_notes_file (); + return status; +} + +Vector<char*> * +PreviewExp::preview_info () +{ + Vector<char*> *info = new Vector<char*>; + if (is_group) + info->append (GTXT ("Experiment Group")); + else + info->append (GTXT ("Experiment")); + info->append (expt_name); + + if (status == FAILURE /* != SUCCESS */) + { + if (is_group) + { + Vector<char*> *grp_list = dbeSession->get_group_or_expt (expt_name); + for (int i = 0, grp_sz = grp_list->size (); i < grp_sz; i++) + { + char *nm = grp_list->fetch (i); + char *str = dbe_sprintf (GTXT ("Exp.#%d"), i + 1); + info->append (str); + info->append (nm); + } + delete grp_list; + } + else + { + info->append (GTXT ("Error message")); + info->append (mqueue_str (errorq, GTXT ("No errors\n"))); + } + return info; + } + info->append (GTXT ("Experiment header")); + info->append (mqueue_str (commentq, GTXT ("Empty header\n"))); + info->append (GTXT ("Error message")); + info->append (mqueue_str (errorq, GTXT ("No errors\n"))); + info->append (GTXT ("Warning message")); + info->append (mqueue_str (warnq, GTXT ("No warnings\n"))); + info->append (GTXT ("Notes")); + info->append (mqueue_str (notesq, GTXT ("\n"))); + return info; +} + +char * +PreviewExp::mqueue_str (Emsgqueue *msgqueue, char *null_str) +{ + char *mesgs = pr_mesgs (msgqueue->fetch (), null_str, ""); + char *last = mesgs + strlen (mesgs) - 1; + if (*last == '\n') + *last = '\0'; + return mesgs; +} diff --git a/gprofng/src/PreviewExp.h b/gprofng/src/PreviewExp.h new file mode 100644 index 0000000..8b7834a --- /dev/null +++ b/gprofng/src/PreviewExp.h @@ -0,0 +1,49 @@ +/* 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. */ + +#ifndef _PREVIEW_EXP_H +#define _PREVIEW_EXP_H + +#include "Experiment.h" +#include "vec.h" + +class PreviewExp : public Experiment +{ +public: + PreviewExp (); + ~PreviewExp (); + + virtual Exp_status experiment_open (char *path); + + Vector<char*> *preview_info (); + + char * + getArgList () + { + return uarglist; + } + +private: + char *mqueue_str (Emsgqueue *msgqueue, char *null_str); + + int is_group; +}; + +#endif /* ! _PREVIEW_EXP_H */ diff --git a/gprofng/src/Print.cc b/gprofng/src/Print.cc new file mode 100644 index 0000000..d6662df --- /dev/null +++ b/gprofng/src/Print.cc @@ -0,0 +1,3485 @@ +/* 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 <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <math.h> +#include <assert.h> +#include <libintl.h> +//#include <libgen.h> +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "util.h" +#include "Dbe.h" +#include "StringBuilder.h" +#include "DbeSession.h" +#include "DbeView.h" +#include "Settings.h" +#include "Print.h" +#include "DbeView.h" +#include "Experiment.h" +#include "MetricList.h" +#include "Module.h" +#include "Function.h" +#include "DataSpace.h" +#include "DataObject.h" +#include "FilterExp.h" +#include "LoadObject.h" +#include "Emsg.h" +#include "Table.h" +#include "DbeFile.h" +#include "CallStack.h" + +int +er_print_common_display::open (Print_params *params) +{ + pr_params = *params; + pr_params.name = dbe_strdup (params->name); + if (params->dest == DEST_PRINTER) + { + tmp_file = dbeSession->get_tmp_file_name (NTXT ("print"), false); + dbeSession->tmp_files->append (strdup (tmp_file)); + out_file = fopen (tmp_file, NTXT ("w")); + } + else if (params->dest == DEST_OPEN_FILE) + out_file = pr_params.openfile; + else + out_file = fopen (pr_params.name, NTXT ("w")); + + if (out_file == NULL) + // Failure + return 1; + return 0; +} + +bool +er_print_common_display::print_output () +{ + char *sys_call; + bool ret = true; + if (pr_params.dest != DEST_OPEN_FILE) + fclose (out_file); + + if (pr_params.dest == DEST_PRINTER) + { + if (streq ((char *) pr_params.name, NTXT (""))) + sys_call = dbe_sprintf ("(/usr/bin/lp -c -n%d %s) 2>/dev/null 1>&2", + pr_params.ncopies, tmp_file); + else + sys_call = dbe_sprintf ("(/usr/bin/lp -c -d%s -n%d %s) 2>/dev/null 1>&2", + pr_params.name, pr_params.ncopies, tmp_file); + if (system (sys_call) != 0) + ret = false; + unlink (tmp_file); + free (sys_call); + } + + return ret; +} + +// Return the report. If the report size is greater than max, return truncated report +// Allocates memory, so the caller should free this memory. + +char * +er_print_common_display::get_output (int maxsize) +{ + off_t max = (off_t) maxsize; + if (out_file != (FILE *) NULL) + { + fclose (out_file); // close tmp_file + out_file = (FILE *) NULL; + } + struct stat sbuf; + int st = stat (tmp_file, &sbuf); + if (st == 0) + { + off_t sz = sbuf.st_size; + if (sz > max) + return dbe_sprintf (GTXT ("Error: report is too long.\n")); + if (sz <= 0) + return dbe_sprintf (GTXT ("Error: empty temporary file: %s\n"), + tmp_file); + max = sz; + } + + FILE *f = fopen (tmp_file, "r"); + if (f == NULL) + return dbe_sprintf (GTXT ("Error: cannot open temporary file: %s\n"), + tmp_file); + char *report = (char *) malloc (max); + if (report) + { + if (1 != fread (report, max - 1, 1, f)) + { + fclose (f); + free (report); + return dbe_sprintf (GTXT ("Error: cannot read temporary file: %s\n"), + tmp_file); + } + report[max - 1] = 0; + } + fclose (f); + return report; +} + +void +er_print_common_display::header_dump (int exp_idx) +{ + if (load && (exp_idx == exp_idx1)) + { + load = false; + print_load_object (out_file); + } + print_header (dbeSession->get_exp (exp_idx), out_file); +} + +char * +pr_load_objects (Vector<LoadObject*> *loadobjects, char *lead) +{ + int size, i; + LoadObject *lo; + Emsg *m; + char *msg; + StringBuilder sb; + char *lo_name; + size = loadobjects->size (); + for (i = 0; i < size; i++) + { + lo = loadobjects->fetch (i); + lo_name = lo->get_name (); + if (lo_name != NULL) + { + size_t len = strlen (lo_name); + if (len > 7 && streq (lo_name + len - 7, NTXT (".class>"))) + continue; + } + + // print the segment name + sb.append (lead); + sb.append (NTXT (" ")); + sb.append (lo->get_name ()); + sb.append (NTXT (" (")); + sb.append (lo->get_pathname ()); + sb.append (NTXT (")\n")); + + // and any warnings + m = lo->fetch_warnings (); + if (m != NULL) + { + msg = pr_mesgs (m, NULL, NTXT (" ")); + sb.append (msg); + free (msg); + } + } + return sb.toString (); +} + +char * +pr_mesgs (Emsg *msg, const char *null_str, const char *lead) +{ + Emsg *m; + StringBuilder sb; + if (msg == NULL) + return dbe_strdup (null_str); + for (m = msg; m; m = m->next) + { + sb.append (lead); + sb.append (m->get_msg ()); + sb.append (NTXT ("\n")); + } + return sb.toString (); +} + +void +print_load_object (FILE *out_file) +{ + Vector<LoadObject*> *loadobjects = dbeSession->get_text_segments (); + char *msg = pr_load_objects (loadobjects, NTXT ("\t")); + fprintf (out_file, GTXT ("Load Object Coverage:\n")); + fprintf (out_file, NTXT ("%s"), msg); + fprintf (out_file, + "----------------------------------------------------------------\n"); + free (msg); + delete loadobjects; +} + +void +print_header (Experiment *exp, FILE *out_file) +{ + fprintf (out_file, GTXT ("Experiment: %s\n"), exp->get_expt_name ()); + char *msg = pr_mesgs (exp->fetch_notes (), NTXT (""), NTXT ("")); + fprintf (out_file, NTXT ("%s"), msg); + free (msg); + + msg = pr_mesgs (exp->fetch_errors (), GTXT ("No errors\n"), NTXT ("")); + fprintf (out_file, NTXT ("%s"), msg); + free (msg); + + msg = pr_mesgs (exp->fetch_warnings (), GTXT ("No warnings\n"), NTXT ("")); + fprintf (out_file, NTXT ("%s"), msg); + free (msg); + + msg = pr_mesgs (exp->fetch_comments (), NTXT (""), NTXT ("")); + fprintf (out_file, NTXT ("%s"), msg); + free (msg); + + msg = pr_mesgs (exp->fetch_pprocq (), NTXT (""), NTXT ("")); + fprintf (out_file, NTXT ("%s"), msg); + free (msg); +} + +void +get_width (Hist_data *data, + MetricList *metrics_list, Metric::HistMetric *hist_metric) +{ + Metric *mitem; + Metric::HistMetric *hitem; + int last_column; + int index; + + // find the last visible column. + last_column = 0; + Vec_loop (Metric*, metrics_list->get_items (), index, mitem) + { + if (mitem->is_visible () || mitem->is_tvisible () || mitem->is_pvisible ()) + last_column = index; + } + + // find the width for each column. + + Vec_loop (Metric*, metrics_list->get_items (), index, mitem) + { + hitem = &hist_metric[index]; + + if (mitem->is_visible ()) + { + if (mitem->get_vtype () == VT_LABEL) + { + if (index == last_column) + hitem->maxvalue_width = 0; + else + hitem->maxvalue_width = data->name_maxlen (); + // truncate names which will be too long + if (hitem->maxvalue_width > MAX_LEN - 3) + hitem->maxvalue_width = MAX_LEN - 3; + } + else if (mitem->get_vtype () == VT_ADDRESS) + { + hitem->maxvalue_width = data->value_maxlen (index); + if (hitem->maxvalue_width < 13) + hitem->maxvalue_width = 13; + } + else + hitem->maxvalue_width = data->value_maxlen (index); + } + else + hitem->maxvalue_width = 0; + + if (mitem->is_tvisible ()) + { + if (mitem->get_visbits () & VAL_RATIO) + hitem->maxtime_width = data->value_maxlen (index); + else + hitem->maxtime_width = data->time_maxlen (index, + dbeSession->get_clock (-1)); + } + else + { + hitem->maxtime_width = 0; + } + } +} + +void +get_format (char **fmt_int, char **fmt_real0, char **fmt_real1, + MetricList *metrics_list, Metric::HistMetric *hist_metric, + int nspace) +{ + Metric *mitem; + Metric::HistMetric *hitem; + int index; + int visible, tvisible, pvisible; + size_t maxlen; + bool prev; + char numstr[MAX_LEN], pstr_int[MAX_LEN], + pstr_real0[MAX_LEN], pstr_real1[MAX_LEN]; + + // find the width for each column. + Vec_loop (Metric*, metrics_list->get_items (), index, mitem) + { + hitem = &hist_metric[index]; + visible = mitem->is_visible (); + tvisible = mitem->is_tvisible (); + pvisible = mitem->is_pvisible (); + *pstr_int = *pstr_real0 = *pstr_real1 = '\0'; + + // Get 'Show Value' format + const char *sign = (mitem->get_visbits () & VAL_DELTA) ? "+" : ""; + if (visible) + { + maxlen = hitem->maxvalue_width; + switch (mitem->get_vtype2 ()) + { + case VT_DOUBLE: + if (mitem->get_visbits () & VAL_RATIO) + { + snprintf (numstr, sizeof (numstr), "x %%#%d.0lf ", + (int) (maxlen - 3)); + snprintf (pstr_real0, sizeof (pstr_real0), numstr, 0.0); + snprintf (pstr_real1, sizeof (pstr_real1), "x%%s%%%d.3lf ", + (int) maxlen); + } + else + { + snprintf (numstr, sizeof (numstr), "%%#%s%d.0lf ", sign, + (int) (maxlen - 3)); + snprintf (pstr_real0, sizeof (pstr_real0), numstr, 0.0); + snprintf (pstr_real1, sizeof (pstr_real1), "%%s%%%s%d.3lf ", + sign, (int) maxlen); + } + break; + case VT_INT: + snprintf (pstr_int, sizeof (pstr_int), "%%%s%dd ", sign, + (int) maxlen); + break; + case VT_LLONG: + snprintf (pstr_int, sizeof (pstr_int), "%%%s%dlld ", sign, + (int) maxlen); + break; + case VT_ULLONG: + snprintf (pstr_int, sizeof (pstr_int), "%%%s%dllu ", sign, + (int) maxlen); + break; + case VT_ADDRESS: + if (maxlen <= 13) + { + snprintf (pstr_int, sizeof (pstr_int), "%%%dd:0x%%08x", 2); + } + else + { + snprintf (pstr_int, sizeof (pstr_int), "%%%dd:0x%%08x", + (int) (maxlen - 13)); + } + break; + case VT_FLOAT: + snprintf (numstr, sizeof (numstr), "%%#%d.0f ", + (int) (maxlen - 3)); + snprintf (pstr_real0, sizeof (pstr_real0), numstr, 0.0); + snprintf (pstr_real1, sizeof (pstr_real1), "%%%d.3f ", + (int) maxlen); + break; + case VT_SHORT: + snprintf (pstr_int, sizeof (pstr_int), "%%%dhu ", (int) maxlen); + break; + case VT_LABEL: + if (maxlen == 0) // last column + snprintf (pstr_int, sizeof (pstr_int), NTXT ("%%s%%s")); + else if (maxlen + nspace >= MAX_LEN - 3) + snprintf (pstr_int, sizeof (pstr_int), NTXT ("%%s%%-%d.%ds "), + MAX_LEN - 7, MAX_LEN - 7); + else + snprintf (pstr_int, sizeof (pstr_int), NTXT ("%%s%%-%ds "), + (int) (maxlen + nspace)); + break; + default: + break; + } + } + + // Get 'Show Time' format + if (tvisible) + { + maxlen = hitem->maxtime_width; + if (mitem->get_visbits () & VAL_RATIO) + { + snprintf (numstr, sizeof (numstr), " %%%s#%d.0lf ", + sign, (int) (maxlen - 3)); + snprintf (pstr_real0, sizeof (pstr_real0), numstr, 0.0); + snprintf (pstr_real1, sizeof (pstr_real1), "%%s%%%s%d.3lf ", + sign, (int) maxlen); + } + else + { + snprintf (numstr, sizeof (numstr), "%%%s#%d.0lf ", + sign, (int) (maxlen - 3)); + snprintf (pstr_real0, sizeof (pstr_real0), numstr, 0.0); + snprintf (pstr_real1, sizeof (pstr_real1), "%%s%%%s%d.3lf ", + sign, (int) maxlen); + } + } + + // Copy format + if (*pstr_int) + fmt_int[index] = dbe_strdup (pstr_int); + else + fmt_int[index] = NULL; + + if (*pstr_real0) + fmt_real0[index] = dbe_strdup (pstr_real0); + else + fmt_real0[index] = NULL; + + if (*pstr_real1) + fmt_real1[index] = dbe_strdup (pstr_real1); + else + fmt_real1[index] = NULL; + + // Set total width + hitem->width = 0; + if (hitem->maxvalue_width > 0) + { + hitem->width += hitem->maxvalue_width; + prev = true; + } + else + prev = false; + + if (hitem->maxtime_width > 0) + { + if (prev) + hitem->width++; + hitem->width += hitem->maxtime_width; + prev = true; + } + + if (pvisible) + { + if (prev) + hitem->width++; + hitem->width += 6; // adjust to change format from xx.yy% + } + if (visible || tvisible || pvisible) + mitem->legend_width (hitem, 2); + } +} + +static char * +delTrailingBlanks (char *s) +{ + for (int i = (int) strlen (s) - 1; i >= 0 && s[i] == ' '; i--) + s[i] = 0; + return s; +} + +/** + * Print the 3-line header with column heads for the metrics + * Return offset of "Name" column (this is needed to print Callers-Callees) + */ +int +print_label (FILE *out_file, MetricList *metrics_list, + Metric::HistMetric *hist_metric, int space) +{ + char line0[2 * MAX_LEN], line1[2 * MAX_LEN]; + char line2[2 * MAX_LEN], line3[2 * MAX_LEN]; + int name_offset = 0; + *line0 = *line1 = *line2 = *line3 = '\0'; + Vector<Metric*> *mlist = metrics_list->get_items (); + for (int index = 0, mlist_sz = mlist->size (); index < mlist_sz; index++) + { + Metric *mitem = mlist->fetch (index); + if (mitem->is_visible () || mitem->is_tvisible () || mitem->is_pvisible ()) + { + Metric::HistMetric *hitem = hist_metric + index; + char *fmt; + if (index > 0 && mitem->get_type () == Metric::ONAME) + { + fmt = NTXT (" %-*s"); + name_offset = strlen (line1); + } + else + fmt = NTXT ("%-*s"); + int width = (int) hitem->width; + size_t len = strlen (line1); + snprintf (line1 + len, sizeof (line1) - len, fmt, width, + hitem->legend1); + len = strlen (line2); + snprintf (line2 + len, sizeof (line2) - len, fmt, width, + hitem->legend2); + len = strlen (line3); + snprintf (line3 + len, sizeof (line3) - len, fmt, width, + hitem->legend3); + len = strlen (line0); + snprintf (line0 + len, sizeof (line0) - len, fmt, width, + mitem->legend ? mitem->legend : NTXT ("")); + } + } + char *s = delTrailingBlanks (line0); + if (*s) + fprintf (out_file, NTXT ("%*s%s\n"), space, NTXT (""), s); + fprintf (out_file, NTXT ("%*s%s\n"), space, NTXT (""), delTrailingBlanks (line1)); + fprintf (out_file, NTXT ("%*s%s\n"), space, NTXT (""), delTrailingBlanks (line2)); + fprintf (out_file, NTXT ("%*s%s\n"), space, NTXT (""), delTrailingBlanks (line3)); + return name_offset; +} + +static int +print_one_visible (FILE *out_file, char *fmt_int, char *fmt_real0, char *fmt_real1, + TValue *value, int visbits) +{ + int nc = 0; + switch (value->tag) + { + case VT_DOUBLE: + if (value->d == 0.0) + nc = fprintf (out_file, fmt_real0); + else + { + if (visbits & VAL_RATIO) + { + if (value->d > 99.999) + nc = fprintf (out_file, fmt_real1, NTXT (">"), 99.999); + else + nc = fprintf (out_file, fmt_real1, NTXT (" "), value->d); + } + else + nc = fprintf (out_file, fmt_real1, NTXT (""), value->d); + } + break; + case VT_INT: + nc = fprintf (out_file, fmt_int, value->i); + break; + case VT_LLONG: + case VT_ULLONG: + nc = fprintf (out_file, fmt_int, value->ll); + break; + case VT_ADDRESS: + nc = fprintf (out_file, fmt_int, ADDRESS_SEG (value->ll), + ADDRESS_OFF (value->ll)); + break; + case VT_FLOAT: + if (value->f == 0.0) + nc = fprintf (out_file, fmt_real0); + else + nc = fprintf (out_file, fmt_real1, value->f); + break; + case VT_SHORT: + nc = fprintf (out_file, fmt_int, value->s); + break; + // ignoring the following cases (why?) + case VT_HRTIME: + case VT_LABEL: + case VT_OFFSET: + break; + } + return nc; +} + +static int +print_one_tvisible (FILE *out_file, char *fmt_real0, char *fmt_real1, + TValue *value, int visbits, int clock) +{ + int nc; + if (value->ll == 0LL) + nc = fprintf (out_file, fmt_real0); + else + { + if (visbits & VAL_RATIO) + { + if (value->d > 99.999) + nc = fprintf (out_file, fmt_real1, NTXT (">"), 99.999); + else + nc = fprintf (out_file, fmt_real1, NTXT (" "), value->d); + } + else + nc = fprintf (out_file, fmt_real1, "", 1.e-6 * value->ll / clock); + } + return nc; +} + +static void +print_one (FILE *out_file, Hist_data *data, Hist_data::HistItem *item, + char **fmt_int, char **fmt_real0, char **fmt_real1, + MetricList *metrics_list, Metric::HistMetric *hist_metric, + char *mark, Histable::NameFormat nfmt) +{ + Metric *mitem; + Metric::HistMetric *hitem; + int index, nc, np, i; + int visible, tvisible, pvisible; + TValue *value; + double percent; + + if (item->type == Module::AT_EMPTY) + { + fprintf (out_file, nl); + return; + } + + // set name_is_Total + int name_is_Total = 0; + + Vec_loop (Metric*, metrics_list->get_items (), index, mitem) + { + if (mitem->get_type () != Metric::ONAME) + continue; + name_is_Total = strcmp (item->obj->get_name (), GTXT ("<Total>")) == 0; + break; + } + + np = 0; + Vec_loop (Metric*, metrics_list->get_items (), index, mitem) + { + visible = mitem->is_visible (); + tvisible = mitem->is_tvisible (); + pvisible = mitem->is_pvisible (); + + // alignment + for (i = 0; i < np; i++) + fputc (' ', out_file); + + hitem = &hist_metric[index]; + nc = 0; + if (tvisible) + { + value = &(item->value[index]); + nc = print_one_tvisible (out_file, fmt_real0[index], fmt_real1[index], + value, mitem->get_visbits (), + dbeSession->get_clock (-1)); + } + else + nc = 0; + + if (visible) + { + if (mitem->get_vtype () == VT_LABEL) + { + value = &(item->value[index]); + if (value->tag == VT_OFFSET) + nc += fprintf (out_file, fmt_int[index], mark, + ((DataObject*) (item->obj))->get_offset_name ()); + else + nc += fprintf (out_file, fmt_int[index], mark, + item->obj->get_name (nfmt)); + } + else if (name_is_Total && + (strcmp (mitem->get_username (), "Block Covered %") == 0 + || strcmp (mitem->get_username (), "Instr Covered %") == 0)) + { + char stmp[128]; + snprintf (stmp, sizeof (stmp), fmt_int[index], 0); + + /* and now blank that '0' out */ + for (int ii = 0; ii < 128; ii++) + { + if (stmp[ii] != '0') + continue; + stmp[ii] = ' '; + break; + } + nc += fprintf (out_file, stmp); + } + else + nc += print_one_visible (out_file, fmt_int[index], fmt_real0[index], + fmt_real1[index], &(item->value[index]), + mitem->get_visbits ()); + } + + if (pvisible) + { + percent = data->get_percentage (item->value[index].to_double (), index); + if (percent == 0.0) + // adjust to change format from xx.yy% + nc += fprintf (out_file, NTXT ("%#4.0f "), 0.); + else + // adjust format below to change format from xx.yy% + nc += fprintf (out_file, NTXT ("%6.2f "), (100.0 * percent)); + } + np = (int) (hitem->width - nc); + } + fprintf (out_file, nl); +} + +void +print_content (FILE *out_file, Hist_data *data, + char **fmt_int, char **fmt_real0, char **fmt_real1, + MetricList *metrics_list, Metric::HistMetric *hist_metric, + int limit, Histable::NameFormat nfmt) +{ + // printing contents. + for (int i = 0; i < limit; i++) + { + Hist_data::HistItem *item = data->fetch (i); + print_one (out_file, data, item, fmt_int, fmt_real0, fmt_real1, + metrics_list, hist_metric, NTXT (" "), nfmt); + } +} + +er_print_histogram::er_print_histogram (DbeView *_dbev, Hist_data *data, + MetricList *metrics_list, + Print_mode disp_type, int limit, + char *sort_name, Histable *sobj, + bool show_load, bool show_header) +{ + hist_data = data; + mlist = metrics_list; + type = disp_type; + number_entries = limit; + sort_metric = sort_name; + sel_obj = sobj; + dbev = _dbev; + exp_idx1 = 0; + exp_idx2 = dbeSession->nexps () - 1; + load = show_load; + header = show_header; +} + +void +er_print_histogram::dump_list (int limit) +{ + Histable::NameFormat nfmt = dbev->get_name_format (); + StringBuilder sb; + char *title = NULL; // No title for some formats + enum PrintMode pm = dbev->get_printmode (); + + // create a header line, except for delimiter-separated list output + if (pm != PM_DELIM_SEP_LIST) + { + if (hist_data->type == Histable::FUNCTION) + sb.append (GTXT ("Functions sorted by metric: ")); + else if (hist_data->type == Histable::INSTR) + sb.append (GTXT ("PCs sorted by metric: ")); + else if (hist_data->type == Histable::LINE) + sb.append (GTXT ("Lines sorted by metric: ")); + else if (hist_data->type == Histable::DOBJECT) + sb.append (GTXT ("Dataobjects sorted by metric: ")); + else + sb.append (GTXT ("Objects sorted by metric: ")); + sb.append (sort_metric); + title = sb.toString (); + } + + switch (pm) + { + case PM_TEXT: + { + Metric::HistMetric *hist_metric = hist_data->get_histmetrics (); + fprintf (out_file, NTXT ("%s\n\n"), title); //print title + hist_data->print_label (out_file, hist_metric, 0); + hist_data->print_content (out_file, hist_metric, limit); + fprintf (out_file, nl); + break; + } + case PM_HTML: + { + print_html_title (out_file, title); + print_html_label (out_file, mlist); + print_html_content (out_file, hist_data, mlist, limit, nfmt); + print_html_trailer (out_file); + break; + } + case PM_DELIM_SEP_LIST: + { + char delim = dbev->get_printdelimiter (); + print_delim_label (out_file, mlist, delim); + print_delim_content (out_file, hist_data, mlist, limit, nfmt, delim); + print_delim_trailer (out_file, delim); + break; + } + } + free (title); +} + +void +er_print_histogram::dump_annotated_dataobjects (Vector<int> *marks, + int ithreshold) +{ + Metric::HistMetric *hist_metric; + char **fmt_int, **fmt_real0, **fmt_real1; + int no_metrics = mlist->get_items ()->size (); + int name_index = -1; + Histable::NameFormat nfmt = dbev->get_name_format (); + if (!dbeSession->is_datamode_available ()) + fprintf (out_file, + GTXT ("No dataspace information recorded in experiments\n\n")); + + Hist_data *layout_data = dbev->get_data_space ()->get_layout_data (hist_data, marks, ithreshold); + + for (int mind = 0; mind < no_metrics; mind++) + if (mlist->get_items ()->fetch (mind)->get_type () == Metric::ONAME) + name_index = mind; + + fmt_int = new char*[no_metrics]; + fmt_real0 = new char*[no_metrics]; + fmt_real1 = new char*[no_metrics]; + hist_metric = new Metric::HistMetric[no_metrics]; + + // use new layout_data to set metric format + get_width (hist_data, mlist, hist_metric); + get_format (fmt_int, fmt_real0, fmt_real1, mlist, hist_metric, 0); + snprintf (hist_metric[name_index].legend2, MAX_LEN, GTXT ("* +offset .element")); + print_label (out_file, mlist, hist_metric, 3); + fprintf (out_file, nl); + for (long i = 0; i < layout_data->size (); i++) + { + Hist_data::HistItem* item = layout_data->fetch (i); + if (marks->find (i) != -1) + fprintf (out_file, NTXT ("## ")); + else + fprintf (out_file, NTXT (" ")); + print_one (out_file, layout_data, item, fmt_int, fmt_real0, fmt_real1, + mlist, hist_metric, NTXT (" "), nfmt); + } + fprintf (out_file, nl); + + // free format strings. + for (int i = 0; i < no_metrics; i++) + { + free (fmt_int[i]); + free (fmt_real0[i]); + free (fmt_real1[i]); + } + delete[] fmt_int; + delete[] fmt_real0; + delete[] fmt_real1; + delete[] hist_metric; + delete layout_data; +} + +void +er_print_histogram::dump_detail (int limit) +{ + Histable *obj; + Hist_data *current_data; + Histable::Type htype; + TValue *values; + double dvalue, percent; + MetricList *prop_mlist = new MetricList (mlist); + Metric *mitem; + int index, i; + size_t max_len, len, smax_len, slen; + Vaddr pc; + Module *module; + LoadObject *loadobject; + char *sname, *oname, *lname, *alias, *mangle; + char fmt_name[MAX_LEN]; + char fmt_elem[MAX_LEN]; + char fmt_real1[MAX_LEN], fmt_real2[MAX_LEN]; + char fmt_int1[MAX_LEN], fmt_int2[MAX_LEN]; + char fmt_long1[MAX_LEN], fmt_long2[MAX_LEN], fmt_long3[MAX_LEN]; + char fmt_int0[MAX_LEN], fmt_long0[MAX_LEN]; + char numstr[MAX_LEN]; + + Histable::NameFormat nfmt = dbev->get_name_format (); + + // Check max. length of metrics names + max_len = smax_len = 0; + + Vec_loop (Metric*, prop_mlist->get_items (), index, mitem) + { + mitem->set_vvisible (true); + if (mitem->get_vtype () == VT_LABEL) + continue; + + if (mitem->get_subtype () != Metric::STATIC) + { + mitem->set_pvisible (true); + len = hist_data->value_maxlen (index); + if (max_len < len) + max_len = len; + slen = strlen (mitem->get_name ()); + if (smax_len < slen) + smax_len = slen; + } + } + + // now get the length of the other (non-performance-data) messages + if (hist_data->type == Histable::FUNCTION) + { + slen = strlen (GTXT ("Source File")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Object File")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Load Object")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Mangled Name")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Aliases")); + if (smax_len < slen) + smax_len = slen; + } + else if (hist_data->type == Histable::DOBJECT) + { + slen = strlen (GTXT ("Scope")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Type")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Member of")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Offset (bytes)")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Size (bytes)")); + if (smax_len < slen) + smax_len = slen; + slen = strlen (GTXT ("Elements")); + if (smax_len < slen) + smax_len = slen; + } + snprintf (fmt_name, sizeof (fmt_name), NTXT ("\t%%%ds: "), (int) smax_len); + snprintf (fmt_elem, sizeof (fmt_elem), NTXT ("\t%%%ds "), (int) smax_len); + snprintf (numstr, sizeof (numstr), "%%#%d.0lf ( %#1.0f %%%%%%%%)\n", + (int) (max_len - 3), 0.); + snprintf (fmt_real1, sizeof (fmt_real1), numstr, 0.0); + snprintf (fmt_real2, sizeof (fmt_real2), NTXT ("%%%d.3lf (%%5.1f%%%%)\n"), + (int) max_len); + snprintf (fmt_int0, sizeof (fmt_int0), NTXT ("%%%dd\n"), (int) max_len); + snprintf (numstr, sizeof (numstr), NTXT ("%%%dd ( %#1.0f %%%%%%%%)\n"), + (int) max_len, 0.); + snprintf (fmt_int1, sizeof (fmt_int1), numstr, 0); + snprintf (fmt_int2, sizeof (fmt_int2), NTXT ("%%%dd (%%5.1f%%%%)\n"), + (int) max_len); + snprintf (fmt_long0, sizeof (fmt_long0), NTXT ("%%%dllu\n"), (int) max_len); + snprintf (numstr, sizeof (numstr), NTXT ("%%%dd ( %#1.0f %%%%%%%%)\n"), + (int) max_len, 0.); + snprintf (fmt_long1, sizeof (fmt_long1), numstr, 0); + snprintf (fmt_long2, sizeof (fmt_long2), "%%%dllu (%%5.1f%%%%)\n", + (int) max_len); + snprintf (numstr, sizeof (numstr), NTXT ("\t%%%ds %%%%%dllu\n"), + (int) (smax_len + 1), (int) max_len); + snprintf (fmt_long3, sizeof (fmt_long3), numstr, GTXT ("Count:")); + snprintf (numstr, sizeof (numstr), "%%%dd ( %#1.0f %%%%%%%%) %%#%d.0lf\n", + (int) max_len, 0., (int) (max_len - 6)); + + // now loop over the objects + int num_printed_items = 0; + for (i = 0; i < hist_data->size (); i++) + { + if (hist_data->type == Histable::FUNCTION) + { + if (num_printed_items >= limit) + break; + obj = sel_obj ? sel_obj : hist_data->fetch (i)->obj; + htype = obj->get_type (); + + // ask the view for all the data for the object + // xxxxx may be expensive to rescan all packets via get_hist_data() + current_data = dbev->get_hist_data (prop_mlist, + htype, 0, Hist_data::SELF, obj); + if (current_data->size () == 0) + continue; + values = current_data->fetch (0)->value; + } + else + { + obj = hist_data->fetch (i)->obj; + DataObject *dobj = (DataObject*) obj; + if (sel_obj) + { + // print selected item and its members + if (sel_obj != obj + && (DataObject*) sel_obj != dobj->get_parent ()) + // not a match, advance to next item + continue; + } + else if (num_printed_items >= limit) + break; + htype = obj->get_type (); + values = hist_data->fetch (i)->value; + current_data = hist_data; + } + + if (num_printed_items) + // if this isn't the first one, add a blank line + fprintf (out_file, NTXT ("\n")); + num_printed_items++; + + // Print full object name + if (htype != Histable::DOBJECT) + fprintf (out_file, NTXT ("%s\n"), obj->get_name (nfmt)); + else + { + DataObject *dobj = (DataObject*) obj; + if (!dobj->get_parent ()) + fprintf (out_file, NTXT ("%s\n"), obj->get_name (nfmt)); + else + fprintf (out_file, NTXT (" %s\n"), obj->get_name (nfmt)); + } + + Vec_loop (Metric*, prop_mlist->get_items (), index, mitem) + { + if (mitem->get_vtype () == VT_LABEL) + continue; + if (mitem->get_subtype () == Metric::STATIC + && htype == Histable::DOBJECT) + continue; + fprintf (out_file, fmt_name, mitem->get_name ()); + + if (mitem->get_value_styles () & VAL_PERCENT) + { + dvalue = values[index].to_double (); + switch (mitem->get_vtype ()) + { + case VT_DOUBLE: + if (dvalue == 0.0) + fprintf (out_file, fmt_real1); + else + fprintf (out_file, fmt_real2, dvalue, 100.0 + * current_data->get_percentage (dvalue, index)); + break; + case VT_INT: + if (dvalue == 0.0) + fprintf (out_file, fmt_int1); + else + fprintf (out_file, fmt_int2, (int) dvalue, 100.0 + * current_data->get_percentage (dvalue, index)); + break; + case VT_LLONG: + case VT_ULLONG: + if (values[index].ll == 0LL) + { + if (mitem->is_time_val ()) + { + fprintf (out_file, fmt_real1); + fprintf (out_file, fmt_long3, 0LL); + } + else + fprintf (out_file, fmt_long1); + } + else + { + percent = 100.0 * + current_data->get_percentage (dvalue, index); + if (mitem->is_time_val ()) + { + dvalue /= 1.e+6 * dbeSession->get_clock (-1); + fprintf (out_file, fmt_real2, dvalue, percent); + fprintf (out_file, fmt_long3, values[index].ll); + } + else + fprintf (out_file, fmt_long2, values[index].ll, + percent); + } + break; + default: + break; + } + } + else + { + switch (mitem->get_vtype ()) + { + case VT_INT: + fprintf (out_file, fmt_int0, values[index].i); + break; + case VT_LLONG: + case VT_ULLONG: + fprintf (out_file, fmt_long0, values[index].ll); + break; + case VT_ADDRESS: + pc = values[index].ll; + fprintf (out_file, NTXT ("%u:0x%08x\n"), ADDRESS_SEG (pc), + ADDRESS_OFF (pc)); + break; + case VT_DOUBLE: + if (values[index].d == 0.0) + fprintf (out_file, fmt_real1); + else + fprintf (out_file, "\t%*.3lf\n", (int) (max_len - 5), values[index].d); + break; + default: + break; + } + } + } + + // now add the descriptive information about the object + if (htype != Histable::DOBJECT) + { + Function *func = (Function*) obj->convertto (Histable::FUNCTION); + if (func && func->get_type () == Histable::FUNCTION) + { + // Print the source/object/load-object files & aliases + oname = lname = alias = NULL; + sname = func->getDefSrcName (); + mangle = func->get_mangled_name (); + if (mangle && streq (func->get_name (), mangle)) + mangle = NULL; + module = func->module; + if (module) + { + oname = module->get_name (); + loadobject = module->loadobject; + if (loadobject) + { + lname = loadobject->get_pathname (); + alias = loadobject->get_alias (func); + } + } + + if (htype == Histable::INSTR && dbeSession->is_datamode_available ()) + alias = ((DbeInstr*) obj)->get_descriptor (); + + fprintf (out_file, fmt_name, GTXT ("Source File")); + if (sname) + fprintf (out_file, NTXT ("%s"), sname); + fprintf (out_file, NTXT ("\n")); + + fprintf (out_file, fmt_name, GTXT ("Object File")); + if (oname) + fprintf (out_file, NTXT ("%s"), oname); + fprintf (out_file, NTXT ("\n")); + + fprintf (out_file, fmt_name, GTXT ("Load Object")); + if (lname) + fprintf (out_file, NTXT ("%s"), lname); + fprintf (out_file, NTXT ("\n")); + + fprintf (out_file, fmt_name, GTXT ("Mangled Name")); + if (mangle) + fprintf (out_file, NTXT ("%s"), mangle); + fprintf (out_file, NTXT ("\n")); + fprintf (out_file, fmt_name, GTXT ("Aliases")); + if (alias) + fprintf (out_file, NTXT ("%s"), alias); + fprintf (out_file, NTXT ("\n")); + } + } + else + { + // Print the dataobject information + DataObject *dobj = (DataObject*) obj; + Histable *scope = dobj->get_scope (); + + // print the scope + fprintf (out_file, fmt_name, GTXT ("Scope")); + if (!scope) + fprintf (out_file, GTXT ("(Global)\n")); + else switch (scope->get_type ()) + { + case Histable::FUNCTION: + fprintf (out_file, NTXT ("%s(%s)\n"), + ((Function*) scope)->module->get_name (), + scope->get_name ()); + break; + case Histable::LOADOBJECT: + case Histable::MODULE: + default: + fprintf (out_file, NTXT ("%s\n"), scope->get_name ()); + } + + // print the type name + fprintf (out_file, fmt_name, GTXT ("Type")); + if (dobj->get_typename ()) + fprintf (out_file, NTXT ("%s\n"), dobj->get_typename ()); + else + fprintf (out_file, GTXT ("(Synthetic)\n")); + + // print the offset + if (dobj->get_offset () != -1) + { + if (dobj->get_parent ()) + { + fprintf (out_file, fmt_name, GTXT ("Member of")); + fprintf (out_file, NTXT ("%s\n"), dobj->get_parent ()->get_name ()); + } + fprintf (out_file, fmt_name, GTXT ("Offset (bytes)")); + fprintf (out_file, NTXT ("%lld\n"), (long long) dobj->get_offset ()); + } + // print the size + if (dobj->get_size ()) + { + fprintf (out_file, fmt_name, GTXT ("Size (bytes)")); + fprintf (out_file, NTXT ("%lld\n"), (long long) dobj->get_size ()); + } + } + if (hist_data->type == Histable::FUNCTION) + delete current_data; + } + if (num_printed_items == 0 && sel_obj) + fprintf (stderr, + GTXT ("Error: Specified item `%s' had no recorded metrics.\n"), + sel_obj->get_name ()); + delete prop_mlist; +} + +static Metric::HistMetric * +allocateHistMetric (int no_metrics) +{ + Metric::HistMetric *hist_metric = new Metric::HistMetric[no_metrics]; + for (int i = 0; i < no_metrics; i++) + { + Metric::HistMetric *hm = &hist_metric[i]; + hm->init (); + } + return hist_metric; +} + +void +er_print_histogram::dump_gprof (int limit) +{ + StringBuilder sb; + Histable *obj; + Hist_data *callers; + Hist_data *callees; + Hist_data *center; + + int no_metrics = mlist->get_items ()->size (); + Metric::HistMetric *hist_metric = allocateHistMetric (no_metrics); + for (int i = 0; i < limit; i++) + { + obj = sel_obj ? sel_obj : hist_data->fetch (i)->obj; + callers = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::CALLERS, obj); + callees = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::CALLEES, obj); + center = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::SELF, obj); + callers->update_max (hist_metric); + callees->update_max (hist_metric); + center->update_max (hist_metric); + callers->update_legend_width (hist_metric); + callers->print_label (out_file, hist_metric, 0); + callers->print_content (out_file, hist_metric, callers->size ()); + + if (center->size () > 0) + { + center->update_total (callers->get_totals ()); + sb.setLength (0); + center->print_row (&sb, 0, hist_metric, NTXT ("*")); + sb.toFileLn (out_file); + } + callees->print_content (out_file, hist_metric, callees->size ()); + fprintf (out_file, nl); + delete callers; + delete callees; + delete center; + } + delete[] hist_metric; +} + +// dump an annotated file +void +dump_anno_file (FILE *fp, Histable::Type type, Module *module, DbeView *dbev, + MetricList *mlist, TValue *ftotal, const char *srcFile, + Function *func, Vector<int> *marks, int threshold, int vis_bits, + int src_visible, bool hex_visible, bool src_only) +{ + int no_metrics, lspace, mspace, tspace, + remain, mindex, next_mark, hidx, + index; + Metric *mitem; + char **fmt_int, **fmt_real0, **fmt_real1, buf[MAX_LEN]; + Hist_data::HistItem *item; + + SourceFile *srcContext = NULL; + bool func_scope = dbev == NULL ? false : dbev->get_func_scope (); + if (srcFile) + { + srcContext = module->findSource (srcFile, false); + if (srcContext == NULL) + { + Vector<SourceFile*> *includes = module->includes; + char *bname = get_basename (srcFile); + for (int i = 0, sz = includes ? includes->size () : 0; i < sz; i++) + { + SourceFile *sf = includes->fetch (i); + if (streq (get_basename (sf->get_name ()), bname)) + { + srcContext = sf; + break; + } + } + } + if (func) + func_scope = true; + } + else if (func) + srcContext = func->getDefSrc (); + + Hist_data *hdata = module->get_data (dbev, mlist, type, ftotal, srcContext, + func, marks, threshold, vis_bits, + src_visible, hex_visible, + func_scope, src_only); + + if (hdata == NULL) + return; + + // force the name metric to be invisible + MetricList *nmlist = hdata->get_metric_list (); + nmlist->find_metric (GTXT ("name"), Metric::STATIC)->clear_all_visbits (); + no_metrics = nmlist->get_items ()->size (); + fmt_int = new char*[no_metrics]; + fmt_real0 = new char*[no_metrics]; + fmt_real1 = new char*[no_metrics]; + Metric::HistMetric *hist_metric = hdata->get_histmetrics (); + + // lspace is for max line number that's inserted; use to set width + int max_lineno = 0; + Vec_loop (Hist_data::HistItem*, hdata, hidx, item) + { + if (!item->obj) + continue; + if (item->obj->get_type () == Histable::LINE + && ((DbeLine*) item->obj)->lineno > max_lineno) + max_lineno = ((DbeLine*) item->obj)->lineno; + else if (item->obj->get_type () == Histable::INSTR + && ((DbeInstr*) item->obj)->lineno > max_lineno) + max_lineno = ((DbeInstr*) item->obj)->lineno; + } + + lspace = snprintf (buf, sizeof (buf), NTXT ("%d"), max_lineno); + + // mspace is the space needed for all metrics, and the mark, if any + mspace = 0; + if (nmlist->get_items ()->size () > 0) + { + mspace = 3; // mark "## " + Vec_loop (Metric*, nmlist->get_items (), index, mitem) + { + if (mitem->is_visible () || mitem->is_tvisible () + || mitem->is_pvisible ()) + mspace += (int) hist_metric[index].width; + } + } + tspace = 0; + remain = (mspace + lspace + 3) % 8; // " " before, ". " after line# + if (remain) + { // tab alignment + tspace = 8 - remain; + mspace += tspace; + } + mindex = 0; + next_mark = (mindex < marks->size ()) ? marks->fetch (mindex) : -1; + + // Print the header for this list + SourceFile *sf = srcContext ? srcContext : module->getMainSrc (); + char *src_name = sf->dbeFile->get_location_info (); + DbeFile *df = module->dbeFile; + if (df == NULL || (df->filetype & DbeFile::F_JAVACLASS) == 0) + df = module->loadobject->dbeFile; + char *lo_name = df->get_location_info (); + char *dot_o_name = lo_name; + if (module->dot_o_file) + dot_o_name = module->dot_o_file->dbeFile->get_location_info (); + fprintf (fp, GTXT ("Source file: %s\nObject file: %s\nLoad Object: %s\n\n"), + src_name, dot_o_name, lo_name); + + // Print metric labels + if (nmlist->get_items ()->size () != 0) + print_label (fp, nmlist, hist_metric, 3); + + // determine the name metric (not printed as a metric, though) + int lind = nmlist->get_listorder (GTXT ("name"), Metric::STATIC); + + // now loop over the data rows -- the lines in the annotated source/disasm, + // including index lines, compiler commentary, etc. + StringBuilder sb; + Vec_loop (Hist_data::HistItem*, hdata, hidx, item) + { + sb.setLength (0); + if (item->type == Module::AT_DIS || item->type == Module::AT_QUOTE + || item->type == Module::AT_SRC) + { + // does this line get a high-metric mark? + if (hidx == next_mark) + { + sb.append (NTXT ("## ")); + mindex++; + next_mark = (mindex < marks->size ()) ? marks->fetch (mindex) : -1; + } + else + sb.append (NTXT (" ")); + + hdata->print_row (&sb, hidx, hist_metric, NTXT (" ")); + sb.toFile (fp); + for (int i = sb.length (); i < mspace; i++) + { + fputc (' ', fp); + } + } + else + // this line does not get any metrics; insert blanks in lieu of them + for (int i = 0; i < mspace; i++) + fputc (' ', fp); + + switch (item->type) + { + case Module::AT_SRC_ONLY: + if (item->obj == NULL) + fprintf (fp, NTXT ("%*s. "), lspace + 1, "?"); + else + fprintf (fp, "%*d. ", lspace + 1, ((DbeLine*) item->obj)->lineno); + break; + + case Module::AT_SRC: + fprintf (fp, "%*d. ", lspace + 1, ((DbeLine*) item->obj)->lineno); + break; + case Module::AT_FUNC: + case Module::AT_QUOTE: + fprintf (fp, NTXT ("%*c"), lspace + 3, ' '); + break; + case Module::AT_DIS: + case Module::AT_DIS_ONLY: + if (item->obj == NULL || ((DbeInstr*) item->obj)->lineno == -1) + fprintf (fp, "%*c[%*s] ", lspace + 3, ' ', lspace, "?"); + else + fprintf (fp, "%*c[%*d] ", lspace + 3, ' ', lspace, + ((DbeInstr*) item->obj)->lineno); + break; + case Module::AT_COM: + case Module::AT_EMPTY: + break; + + } + if (item->value[lind].l == NULL) + item->value[lind].l = dbe_strdup (GTXT ("INTERNAL ERROR: missing line text")); + fprintf (fp, NTXT ("%s\n"), item->value[lind].l); + } + delete[] fmt_int; + delete[] fmt_real0; + delete[] fmt_real1; + delete hdata; +} + +void +er_print_histogram::dump_annotated () +{ + Vector<int> *marks = new Vector<int>; + Function *anno_func = (Function *) sel_obj; + Module *module = anno_func ? anno_func->module : NULL; + + if (hist_data->type == Histable::DOBJECT) + dump_annotated_dataobjects (marks, number_entries); // threshold + else if (number_entries == 0) + // Annotated source + dump_anno_file (out_file, Histable::LINE, module, dbev, mlist, + hist_data->get_totals ()->value, NULL, anno_func, marks, + dbev->get_thresh_src (), dbev->get_src_compcom (), + dbev->get_src_visible (), dbev->get_hex_visible (), true); + else + // Annotated disassembly + dump_anno_file (out_file, Histable::INSTR, module, dbev, mlist, + hist_data->get_totals ()->value, NULL, anno_func, marks, + dbev->get_thresh_dis (), dbev->get_dis_compcom (), + dbev->get_src_visible (), dbev->get_hex_visible (), true); +} + +void +er_print_histogram::data_dump () +{ + int limit; + if (hist_data->get_status () == Hist_data::SUCCESS) + { + if (sort_metric[0] == '\n') + { // csingle Callers-Callees entry + sort_metric++; + fprintf (out_file, NTXT ("%s\n\n"), sort_metric); + } + else if (!sel_obj && type != MODE_LIST) + { + if (hist_data->type == Histable::FUNCTION) + fprintf (out_file, + GTXT ("Functions sorted by metric: %s\n\n"), sort_metric); + else if (hist_data->type == Histable::DOBJECT) + fprintf (out_file, GTXT ("Dataobjects sorted by metric: %s\n\n"), + sort_metric); + else + fprintf (out_file, + GTXT ("Objects sorted by metric: %s\n\n"), sort_metric); + } + limit = hist_data->size (); + if ((number_entries > 0) && (number_entries < limit)) + limit = number_entries; + + switch (type) + { + case MODE_LIST: + dump_list (limit); + break; + case MODE_DETAIL: + dump_detail (limit); + break; + case MODE_GPROF: + dump_gprof (limit); + break; + case MODE_ANNOTATED: + dump_annotated (); + break; + } + } + else + fprintf (out_file, GTXT ("Get_Hist_data call failed %d\n"), + (int) hist_data->get_status ()); +} + +/* + * Class er_print_ctree to print functions call tree + */ +er_print_ctree::er_print_ctree (DbeView *_dbev, Vector<Histable*> *_cstack, + Histable *_sobj, int _limit) +{ + dbev = _dbev; + cstack = _cstack; + sobj = _sobj; + limit = _limit; + print_row = 0; + exp_idx1 = 0; + exp_idx2 = dbeSession->nexps () - 1; + load = false; + header = false; +} + +void +er_print_ctree::data_dump () +{ + StringBuilder sb; + Hist_data::HistItem *total; + sb.append (GTXT ("Functions Call Tree. Metric: ")); + char *s = dbev->getSort (MET_CALL_AGR); + sb.append (s); + free (s); + sb.toFileLn (out_file); + fprintf (out_file, NTXT ("\n")); + mlist = dbev->get_metric_list (MET_CALL_AGR); + + // Change cstack: add sobj to the end of cstack + cstack->append (sobj); + Hist_data *center = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::SELF, cstack); + Hist_data *callers = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::CALLERS, cstack); + Hist_data *callees = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::CALLEES, cstack); + + // Restore cstack + int last = cstack->size () - 1; + cstack->remove (last); + + // Prepare formats + int no_metrics = mlist->size (); + + // calculate max. width using data from callers, callees, center + hist_metric = allocateHistMetric (no_metrics); + callers->update_max (hist_metric); + callees->update_max (hist_metric); + center->update_max (hist_metric); + callers->update_legend_width (hist_metric); + callers->print_label (out_file, hist_metric, 0); // returns Name column offset + + print_row = 0; + // Pass real total to print_children() + total = center->get_totals (); + print_children (center, 0, sobj, NTXT (" "), total); + + // Free memory + cstack->reset (); + delete callers; + delete callees; + delete center; + delete[] hist_metric; +} + +/* + * Recursive method print_children prints Call Tree elements. + */ +void +er_print_ctree::print_children (Hist_data *data, int index, Histable *my_obj, + char * prefix, Hist_data::HistItem *total) +{ + StringBuilder buf; + const char *P0 = "+-"; + const char *P2 = " |"; + const char *P1 = " "; + + // If limit exceeded - return + ++print_row; + if (limit > 0 && print_row > limit) + return; + + if (my_obj == NULL) + return; // should never happen + + // Prepare prefix + buf.append (prefix); + if (buf.endsWith (P2)) + { + int len = buf.length () - 1; + buf.setLength (len); + } + buf.append (P0); + + // Change cstack: add my_obj to the end of cstack + cstack->append (my_obj); + + // Print current node info + char * my_prefix = buf.toString (); + + // Replace parent's total values with real total values + data->update_total (total); // Needed to to calculate percentage only + buf.setLength (0); + data->print_row (&buf, index, hist_metric, my_prefix); + buf.toFileLn (out_file); + free (my_prefix); + + // Get children + Hist_data *callees = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::CALLEES, cstack); + int nc = callees->size (); + if (nc > 0) + { + // Print children + Hist_data::HistItem *item; + Histable *ch_obj; + char *ch_prefix; + buf.setLength (0); + buf.append (prefix); + buf.append (P2); + ch_prefix = buf.toString (); + for (int i = 0; i < nc - 1; i++) + { + item = callees->fetch (i); + ch_obj = item->obj; + print_children (callees, i, ch_obj, ch_prefix, total); + } + free (ch_prefix); + buf.setLength (0); + buf.append (prefix); + buf.append (P1); + ch_prefix = buf.toString (); + item = callees->fetch (nc - 1); + ch_obj = item->obj; + print_children (callees, nc - 1, ch_obj, ch_prefix, total); + free (ch_prefix); + } + + // Restore cstack + int last = cstack->size () - 1; + cstack->remove (last); + delete callees; + return; +} + +er_print_gprof::er_print_gprof (DbeView *_dbev, Vector<Histable*> *_cstack) +{ + dbev = _dbev; + cstack = _cstack; + exp_idx1 = 0; + exp_idx2 = dbeSession->nexps () - 1; + load = false; + header = false; +} + +void +er_print_gprof::data_dump () +{ + StringBuilder sb; + sb.append (GTXT ("Callers and callees sorted by metric: ")); + char *s = dbev->getSort (MET_CALL); + sb.append (s); + free (s); + sb.toFileLn (out_file); + fprintf (out_file, NTXT ("\n")); + + MetricList *mlist = dbev->get_metric_list (MET_CALL); + Hist_data *center = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::SELF, cstack); + Hist_data *callers = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::CALLERS, cstack); + Hist_data *callees = dbev->get_hist_data (mlist, Histable::FUNCTION, 0, + Hist_data::CALLEES, cstack); + + mlist = center->get_metric_list (); + int no_metrics = mlist->get_items ()->size (); + + // update max. width for callers/callees/center function item + Metric::HistMetric *hist_metric = allocateHistMetric (no_metrics); + callers->update_max (hist_metric); + callees->update_max (hist_metric); + center->update_max (hist_metric); + + callers->update_legend_width (hist_metric); + int name_offset = callers->print_label (out_file, hist_metric, 0); // returns Name column offset + // Print Callers + sb.setLength (0); + for (int i = 0; i < name_offset; i++) + sb.append (NTXT ("=")); + if (name_offset > 0) + sb.append (NTXT (" ")); + char *line1 = sb.toString (); + char *line2; + if (callers->size () > 0) + line2 = GTXT ("Callers"); + else + line2 = GTXT ("No Callers"); + fprintf (out_file, NTXT ("%s%s\n"), line1, line2); + callers->print_content (out_file, hist_metric, callers->size ()); + + // Print Stack Fragment + line2 = GTXT ("Stack Fragment"); + fprintf (out_file, NTXT ("\n%s%s\n"), line1, line2); + + for (long i = 0, last = cstack->size () - 1; i <= last; ++i) + { + sb.setLength (0); + if (i == last && center->size () > 0) + { + center->update_total (callers->get_totals ()); // Needed to to calculate percentage only + center->print_row (&sb, center->size () - 1, hist_metric, NTXT (" ")); + } + else + { + for (int n = name_offset; n > 0; n--) + sb.append (NTXT (" ")); + if (name_offset > 0) + sb.append (NTXT (" ")); + sb.append (cstack->get (i)->get_name ()); + } + sb.toFileLn (out_file); + } + + // Print Callees + if (callees->size () > 0) + line2 = GTXT ("Callees"); + else + line2 = GTXT ("No Callees"); + fprintf (out_file, NTXT ("\n%s%s\n"), line1, line2); + callees->print_content (out_file, hist_metric, callees->size ()); + fprintf (out_file, nl); + free (line1); + delete callers; + delete callees; + delete center; + delete[] hist_metric; +} + +er_print_leaklist::er_print_leaklist (DbeView *_dbev, bool show_leak, + bool show_alloca, int _limit) +{ + dbev = _dbev; + leak = show_leak; + alloca = show_alloca; + limit = _limit; +} + +// Output routine for leak list only +void +er_print_leaklist::data_dump () +{ + CStack_data *lam; + CStack_data::CStack_item *lae; + int index; + if (!dbeSession->is_leaklist_available ()) + fprintf (out_file, GTXT ("No leak or allocation information recorded in experiments\n\n")); + + MetricList *origmlist = dbev->get_metric_list (MET_NORMAL); + if (leak) + { + // make a copy of the metric list, and set metrics for leaks + MetricList *nmlist = new MetricList (origmlist); + nmlist->set_metrics ("e.heapleakbytes:e.heapleakcnt:name", true, + dbev->get_derived_metrics ()); + + // now make a compacted version of it to get the right indices + MetricList *mlist = new MetricList (nmlist); + delete nmlist; + + // fetch the callstack data + lam = dbev->get_cstack_data (mlist); + + // now print it + if (lam && lam->size () != 0) + { + fprintf (out_file, GTXT ("Summary Results: Distinct Leaks = %d, Total Instances = %lld, Total Bytes Leaked = %lld\n\n"), + (int) lam->size (), lam->total->value[1].ll, + lam->total->value[0].ll); + + Vec_loop (CStack_data::CStack_item*, lam->cstack_items, index, lae) + { + fprintf (out_file, + GTXT ("Leak #%d, Instances = %lld, Bytes Leaked = %lld\n"), + index + 1, lae->value[1].ll, lae->value[0].ll); + if (lae->stack != NULL) + for (int i = lae->stack->size () - 1; i >= 0; i--) + { + DbeInstr *instr = lae->stack->fetch (i); + fprintf (out_file, NTXT (" %s\n"), instr->get_name ()); + } + fprintf (out_file, NTXT ("\n")); + if (index + 1 == limit) break; + } + } + else + fprintf (out_file, GTXT ("No leak information\n\n")); + delete lam; + delete mlist; + } + + if (alloca) + { + // make a copy of the metric list, and set metrics for leaks + MetricList *nmlist = new MetricList (origmlist); + nmlist->set_metrics ("e.heapallocbytes:e.heapalloccnt:name", + true, dbev->get_derived_metrics ()); + + // now make a compacted version of it to get the right indices + MetricList *mlist = new MetricList (nmlist); + delete nmlist; + + // fetch the callstack data + lam = dbev->get_cstack_data (mlist); + + // now print it + if (lam && lam->size () != 0) + { + fprintf (out_file, GTXT ("Summary Results: Distinct Allocations = %d, Total Instances = %lld, Total Bytes Allocated = %lld\n\n"), + (int) lam->size (), lam->total->value[1].ll, + lam->total->value[0].ll); + Vec_loop (CStack_data::CStack_item*, lam->cstack_items, index, lae) + { + fprintf (out_file, GTXT ("Allocation #%d, Instances = %lld, Bytes Allocated = %lld\n"), + index + 1, lae->value[1].ll, lae->value[0].ll); + if (lae->stack != NULL) + for (int i = lae->stack->size () - 1; i >= 0; i--) + { + DbeInstr *instr = lae->stack->fetch (i); + fprintf (out_file, NTXT (" %s\n"), instr->get_name ()); + } + fprintf (out_file, NTXT ("\n")); + if (index + 1 == limit) break; + } + } + else + fprintf (out_file, GTXT ("No allocation information\n\n")); + delete lam; + delete mlist; + } +} + +er_print_heapactivity::er_print_heapactivity (DbeView *_dbev, + Histable::Type _type, + bool _printStat, int _limit) +{ + dbev = _dbev; + type = _type; + printStat = _printStat; + limit = _limit; +} + +void +er_print_heapactivity::printCallStacks (Hist_data *hist_data) +{ + Hist_data::HistItem *hi; + HeapData *hData; + long stackId; + int size = hist_data->size (); + if (limit > 0 && limit < size) + size = limit; + + Histable::NameFormat fmt = dbev->get_name_format (); + for (int i = 0; i < size; i++) + { + hi = hist_data->fetch (i); + hData = (HeapData*) hi->obj; + stackId = hData->id; + if (i != 0) + fprintf (out_file, NTXT ("\n")); + + fprintf (out_file, NTXT ("%s\n"), hData->get_name (fmt)); + if (hData->getAllocCnt () > 0) + { + fprintf (out_file, GTXT ("Instances = %d "), + (int) (hData->getAllocCnt ())); + fprintf (out_file, GTXT ("Bytes Allocated = %lld\n"), + (long long) hData->getAllocBytes ()); + } + + if (hData->getLeakCnt () > 0) + { + fprintf (out_file, GTXT ("Instances = %d "), + (int) (hData->getLeakCnt ())); + fprintf (out_file, GTXT ("Bytes Leaked = %lld\n"), + (long long) hData->getLeakBytes ()); + } + + // There is no stack trace for <Total> + if (i == 0) + continue; + + // LIBRARY VISIBILITY pass extra argument if necessary to get hide stack + Vector<Histable*> *instrs = CallStack::getStackPCs ((void *) stackId); + if (instrs != NULL) + { + int stSize = instrs->size (); + for (int j = 0; j < stSize; j++) + { + Histable *instr = instrs->fetch (j); + if (instr != NULL) + fprintf (out_file, NTXT (" %s\n"), instr->get_name ()); + } + delete instrs; + } + } +} + +void +er_print_heapactivity::printStatistics (Hist_data *hist_data) +{ + Hist_data::HistItem *hi; + HeapData *hDataTotal; + hi = hist_data->fetch (0); + hDataTotal = (HeapData*) hi->obj; + Vector<hrtime_t> *pTimestamps; + if (hDataTotal->getPeakMemUsage () > 0) + { + fprintf (out_file, GTXT ("\nProcess With Highest Peak Memory Usage\n")); + fprintf (out_file, + "-------------------------------------------------------\n"); + fprintf (out_file, GTXT ("Heap size bytes %lld\n"), + (long long) hDataTotal->getPeakMemUsage ()); + fprintf (out_file, GTXT ("Experiment Id %d\n"), + (int) (hDataTotal->getUserExpId ())); + fprintf (out_file, GTXT ("Process Id %d\n"), + (int) (hDataTotal->getPid ())); + pTimestamps = hDataTotal->getPeakTimestamps (); + if (pTimestamps != NULL) + for (int i = 0; i < pTimestamps->size (); i++) + fprintf (out_file, + GTXT ("Time of peak %.3f (secs.)\n"), + (double) (pTimestamps->fetch (i) / (double) NANOSEC)); + } + + if (hDataTotal->getAllocCnt () > 0) + { + fprintf (out_file, GTXT ("\nMemory Allocations Statistics\n")); + fprintf (out_file, + GTXT ("Allocation Size Range Allocations \n")); + fprintf (out_file, + "-------------------------------------------------------\n"); + if (hDataTotal->getA0KB1KBCnt () > 0) + fprintf (out_file, NTXT (" 0KB - 1KB %d\n"), + hDataTotal->getA0KB1KBCnt ()); + if (hDataTotal->getA1KB8KBCnt () > 0) + fprintf (out_file, NTXT (" 1KB - 8KB %d\n"), + hDataTotal->getA1KB8KBCnt ()); + if (hDataTotal->getA8KB32KBCnt () > 0) + fprintf (out_file, NTXT (" 8KB - 32KB %d\n"), + hDataTotal->getA8KB32KBCnt ()); + if (hDataTotal->getA32KB128KBCnt () > 0) + fprintf (out_file, NTXT (" 32KB - 128KB %d\n"), + hDataTotal->getA32KB128KBCnt ()); + if (hDataTotal->getA128KB256KBCnt () > 0) + fprintf (out_file, NTXT (" 128KB - 256KB %d\n"), + hDataTotal->getA128KB256KBCnt ()); + if (hDataTotal->getA256KB512KBCnt () > 0) + fprintf (out_file, NTXT (" 256KB - 512KB %d\n"), + hDataTotal->getA256KB512KBCnt ()); + if (hDataTotal->getA512KB1000KBCnt () > 0) + fprintf (out_file, NTXT (" 512KB - 1000KB %d\n"), + hDataTotal->getA512KB1000KBCnt ()); + if (hDataTotal->getA1000KB10MBCnt () > 0) + fprintf (out_file, NTXT (" 1000KB - 10MB %d\n"), + hDataTotal->getA1000KB10MBCnt ()); + if (hDataTotal->getA10MB100MBCnt () > 0) + fprintf (out_file, NTXT (" 10MB - 100MB %d\n"), + hDataTotal->getA10MB100MBCnt ()); + if (hDataTotal->getA100MB1GBCnt () > 0) + fprintf (out_file, NTXT (" 100MB - 1GB %d\n"), + hDataTotal->getA100MB1GBCnt ()); + if (hDataTotal->getA1GB10GBCnt () > 0) + fprintf (out_file, NTXT (" 1GB - 10GB %d\n"), + hDataTotal->getA1GB10GBCnt ()); + if (hDataTotal->getA10GB100GBCnt () > 0) + fprintf (out_file, NTXT (" 10GB - 100GB %d\n"), + hDataTotal->getA10GB100GBCnt ()); + if (hDataTotal->getA100GB1TBCnt () > 0) + fprintf (out_file, NTXT (" 100GB - 1TB %d\n"), + hDataTotal->getA100GB1TBCnt ()); + if (hDataTotal->getA1TB10TBCnt () > 0) + fprintf (out_file, NTXT (" 1TB - 10TB %d\n"), + hDataTotal->getA1TB10TBCnt ()); + fprintf (out_file, GTXT ("\nSmallest allocation bytes %lld\n"), + (long long) hDataTotal->getASmallestBytes ()); + fprintf (out_file, GTXT ("Largest allocation bytes %lld\n"), + (long long) hDataTotal->getALargestBytes ()); + fprintf (out_file, GTXT ("Total allocations %d\n"), + hDataTotal->getAllocCnt ()); + fprintf (out_file, GTXT ("Total bytes %lld\n"), + (long long) hDataTotal->getAllocBytes ()); + } + + if (hDataTotal->getLeakCnt () > 0) + { + fprintf (out_file, GTXT ("\nMemory Leaks Statistics\n")); + fprintf (out_file, + GTXT ("Leak Size Range Leaks \n")); + fprintf (out_file, + "-------------------------------------------------------\n"); + if (hDataTotal->getL0KB1KBCnt () > 0) + fprintf (out_file, NTXT (" 0KB - 1KB %d\n"), + hDataTotal->getL0KB1KBCnt ()); + if (hDataTotal->getL1KB8KBCnt () > 0) + fprintf (out_file, NTXT (" 1KB - 8KB %d\n"), + hDataTotal->getL1KB8KBCnt ()); + if (hDataTotal->getL8KB32KBCnt () > 0) + fprintf (out_file, NTXT (" 8KB - 32KB %d\n"), + hDataTotal->getL8KB32KBCnt ()); + if (hDataTotal->getL32KB128KBCnt () > 0) + fprintf (out_file, NTXT (" 32KB - 128KB %d\n"), + hDataTotal->getL32KB128KBCnt ()); + if (hDataTotal->getL128KB256KBCnt () > 0) + fprintf (out_file, NTXT (" 128KB - 256KB %d\n"), + hDataTotal->getL128KB256KBCnt ()); + if (hDataTotal->getL256KB512KBCnt () > 0) + fprintf (out_file, NTXT (" 256KB - 512KB %d\n"), + hDataTotal->getL256KB512KBCnt ()); + if (hDataTotal->getL512KB1000KBCnt () > 0) + fprintf (out_file, NTXT (" 512KB - 1000KB %d\n"), + hDataTotal->getL512KB1000KBCnt ()); + if (hDataTotal->getL1000KB10MBCnt () > 0) + fprintf (out_file, NTXT (" 1000KB - 10MB %d\n"), + hDataTotal->getL1000KB10MBCnt ()); + if (hDataTotal->getL10MB100MBCnt () > 0) + fprintf (out_file, NTXT (" 10MB - 100MB %d\n"), + hDataTotal->getL10MB100MBCnt ()); + if (hDataTotal->getL100MB1GBCnt () > 0) + fprintf (out_file, NTXT (" 100MB - 1GB %d\n"), + hDataTotal->getL100MB1GBCnt ()); + if (hDataTotal->getL1GB10GBCnt () > 0) + fprintf (out_file, NTXT (" 1GB - 10GB %d\n"), + hDataTotal->getL1GB10GBCnt ()); + if (hDataTotal->getL10GB100GBCnt () > 0) + fprintf (out_file, NTXT (" 10GB - 100GB %d\n"), + hDataTotal->getL10GB100GBCnt ()); + if (hDataTotal->getL100GB1TBCnt () > 0) + fprintf (out_file, NTXT (" 100GB - 1TB %d\n"), + hDataTotal->getL100GB1TBCnt ()); + if (hDataTotal->getL1TB10TBCnt () > 0) + fprintf (out_file, NTXT (" 1TB - 10TB %d\n"), + hDataTotal->getL1TB10TBCnt ()); + fprintf (out_file, GTXT ("\nSmallest leaked bytes %lld\n"), + (long long) hDataTotal->getLSmallestBytes ()); + fprintf (out_file, GTXT ("Largest leaked bytes %lld\n"), + (long long) hDataTotal->getLLargestBytes ()); + fprintf (out_file, GTXT ("Total leaked %d \n"), + hDataTotal->getLeakCnt ()); + fprintf (out_file, GTXT ("Total bytes %lld\n"), + (long long) hDataTotal->getLeakBytes ()); + } + fprintf (out_file, NTXT ("\n")); +} + +void +er_print_heapactivity::data_dump () +{ + // get the list of heap events from DbeView + int numExps = dbeSession->nexps (); + if (!numExps) + { + fprintf (out_file, + GTXT ("There is no heap event information in the experiments\n")); + return; + } + MetricList *mlist = dbev->get_metric_list (MET_HEAP); + Hist_data *hist_data; + hist_data = dbev->get_hist_data (mlist, type, 0, Hist_data::ALL); + if (printStat) + printStatistics (hist_data); + else + printCallStacks (hist_data); +} + +er_print_ioactivity::er_print_ioactivity (DbeView *_dbev, Histable::Type _type, + bool _printStat, int _limit) +{ + dbev = _dbev; + type = _type; + printStat = _printStat; + limit = _limit; +} + +void +er_print_ioactivity::printCallStacks (Hist_data *hist_data) +{ + Hist_data::HistItem *hi; + FileData *fData; + long stackId; + int size = hist_data->size (); + if (limit > 0 && limit < size) + size = limit; + + for (int i = 0; i < size; i++) + { + hi = hist_data->fetch (i); + fData = (FileData*) hi->obj; + stackId = fData->id; + if (i != 0) + fprintf (out_file, NTXT ("\n")); + fprintf (out_file, NTXT ("%s\n"), fData->getFileName ()); + if (fData->getWriteCnt () > 0) + { + fprintf (out_file, GTXT ("Write Time=%.6f (secs.) "), + (double) (fData->getWriteTime () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Write Bytes=%lld "), + (long long) fData->getWriteBytes ()); + fprintf (out_file, GTXT ("Write Count=%d\n"), + (int) (fData->getWriteCnt ())); + } + if (fData->getReadCnt () > 0) + { + fprintf (out_file, GTXT ("Read Time=%.6f (secs.) "), + (double) (fData->getReadTime () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Read Bytes=%lld "), + (long long) fData->getReadBytes ()); + fprintf (out_file, GTXT ("Read Count=%d\n"), + (int) fData->getReadCnt ()); + } + if (fData->getOtherCnt () > 0) + { + fprintf (out_file, GTXT ("Other I/O Time=%.6f (secs.) "), + (double) (fData->getOtherTime () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Other I/O Count=%d\n"), + (int) (fData->getOtherCnt ())); + } + if (fData->getErrorCnt () > 0) + { + fprintf (out_file, GTXT ("I/O Error Time=%.6f (secs.) "), + (double) (fData->getErrorTime () / (double) NANOSEC)); + fprintf (out_file, GTXT ("I/O Error Count=%d\n"), + (int) (fData->getErrorCnt ())); + } + + // There is no stack trace for <Total> + if (i == 0) + continue; + + // LIBRARY VISIBILITY pass extra argument if necessary to get hide stack + Vector<Histable*> *instrs = CallStack::getStackPCs ((void *) stackId); + if (instrs != NULL) + { + int stSize = instrs->size (); + for (int j = 0; j < stSize; j++) + { + Histable *instr = instrs->fetch (j); + if (instr != NULL) + fprintf (out_file, " %s\n", instr->get_name ()); + } + delete instrs; + } + } +} + +void +er_print_ioactivity::printStatistics (Hist_data *hist_data) +{ + Hist_data::HistItem *hi; + FileData *fDataTotal; + + hi = hist_data->fetch (0); + fDataTotal = (FileData*) hi->obj; + + if (fDataTotal->getWriteCnt () > 0) + { + fprintf (out_file, + GTXT ("\nWrite Statistics\n")); + fprintf (out_file, + GTXT ("I/O Size Range Write Calls \n")); + fprintf (out_file, + "-------------------------------------------------------\n"); + if (fDataTotal->getW0KB1KBCnt () > 0) + fprintf (out_file, NTXT (" 0KB - 1KB %d\n"), + fDataTotal->getW0KB1KBCnt ()); + if (fDataTotal->getW1KB8KBCnt () > 0) + fprintf (out_file, NTXT (" 1KB - 8KB %d\n"), + fDataTotal->getW1KB8KBCnt ()); + if (fDataTotal->getW8KB32KBCnt () > 0) + fprintf (out_file, NTXT (" 8KB - 32KB %d\n"), + fDataTotal->getW8KB32KBCnt ()); + if (fDataTotal->getW32KB128KBCnt () > 0) + fprintf (out_file, NTXT (" 32KB - 128KB %d\n"), + fDataTotal->getW32KB128KBCnt ()); + if (fDataTotal->getW128KB256KBCnt () > 0) + fprintf (out_file, NTXT (" 128KB - 256KB %d\n"), + fDataTotal->getW128KB256KBCnt ()); + if (fDataTotal->getW256KB512KBCnt () > 0) + fprintf (out_file, NTXT (" 256KB - 512KB %d\n"), + fDataTotal->getW256KB512KBCnt ()); + if (fDataTotal->getW512KB1000KBCnt () > 0) + fprintf (out_file, NTXT (" 512KB - 1000KB %d\n"), + fDataTotal->getW512KB1000KBCnt ()); + if (fDataTotal->getW1000KB10MBCnt () > 0) + fprintf (out_file, NTXT (" 1000KB - 10MB %d\n"), + fDataTotal->getW1000KB10MBCnt ()); + if (fDataTotal->getW10MB100MBCnt () > 0) + fprintf (out_file, NTXT (" 10MB - 100MB %d\n"), + fDataTotal->getW10MB100MBCnt ()); + if (fDataTotal->getW100MB1GBCnt () > 0) + fprintf (out_file, NTXT (" 100MB - 1GB %d\n"), + fDataTotal->getW100MB1GBCnt ()); + if (fDataTotal->getW1GB10GBCnt () > 0) + fprintf (out_file, NTXT (" 1GB - 10GB %d\n"), + fDataTotal->getW1GB10GBCnt ()); + if (fDataTotal->getW10GB100GBCnt () > 0) + fprintf (out_file, NTXT (" 10GB - 100GB %d\n"), + fDataTotal->getW10GB100GBCnt ()); + if (fDataTotal->getW100GB1TBCnt () > 0) + fprintf (out_file, NTXT (" 100GB - 1TB %d\n"), + fDataTotal->getW100GB1TBCnt ()); + if (fDataTotal->getW1TB10TBCnt () > 0) + fprintf (out_file, NTXT (" 1TB - 10TB %d\n"), + fDataTotal->getW1TB10TBCnt ()); + fprintf (out_file, + GTXT ("\nLongest write %.6f (secs.)\n"), + (double) (fDataTotal->getWSlowestBytes () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Smallest write bytes %lld\n"), + (long long) fDataTotal->getWSmallestBytes ()); + fprintf (out_file, GTXT ("Largest write bytes %lld\n"), + (long long) fDataTotal->getWLargestBytes ()); + fprintf (out_file, + GTXT ("Total time %.6f (secs.)\n"), + (double) (fDataTotal->getWriteTime () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Total calls %d\n"), + fDataTotal->getWriteCnt ()); + fprintf (out_file, GTXT ("Total bytes %lld\n"), + (long long) fDataTotal->getWriteBytes ()); + } + + if (fDataTotal->getReadCnt () > 0) + { + fprintf (out_file, + GTXT ("\nRead Statistics\n")); + fprintf (out_file, + GTXT ("I/O Size Range Read Calls \n")); + fprintf (out_file, + "------------------------------------------------------\n"); + if (fDataTotal->getR0KB1KBCnt () > 0) + fprintf (out_file, NTXT (" 0KB - 1KB %d\n"), + fDataTotal->getR0KB1KBCnt ()); + if (fDataTotal->getR1KB8KBCnt () > 0) + fprintf (out_file, NTXT (" 1KB - 8KB %d\n"), + fDataTotal->getR1KB8KBCnt ()); + if (fDataTotal->getR8KB32KBCnt () > 0) + fprintf (out_file, NTXT (" 8KB - 32KB %d\n"), + fDataTotal->getR8KB32KBCnt ()); + if (fDataTotal->getR32KB128KBCnt () > 0) + fprintf (out_file, NTXT (" 32KB - 128KB %d\n"), + fDataTotal->getR32KB128KBCnt ()); + if (fDataTotal->getR128KB256KBCnt () > 0) + fprintf (out_file, NTXT (" 128KB - 256KB %d\n"), + fDataTotal->getR128KB256KBCnt ()); + if (fDataTotal->getR256KB512KBCnt () > 0) + fprintf (out_file, NTXT (" 256KB - 512KB %d\n"), + fDataTotal->getR256KB512KBCnt ()); + if (fDataTotal->getR512KB1000KBCnt () > 0) + fprintf (out_file, NTXT (" 512KB - 1000KB %d\n"), + fDataTotal->getR512KB1000KBCnt ()); + if (fDataTotal->getR1000KB10MBCnt () > 0) + fprintf (out_file, NTXT (" 1000KB - 10MB %d\n"), + fDataTotal->getR1000KB10MBCnt ()); + if (fDataTotal->getR10MB100MBCnt () > 0) + fprintf (out_file, NTXT (" 10MB - 100MB %d\n"), + fDataTotal->getR10MB100MBCnt ()); + if (fDataTotal->getR100MB1GBCnt () > 0) + fprintf (out_file, NTXT (" 100MB - 1GB %d\n"), + fDataTotal->getR100MB1GBCnt ()); + if (fDataTotal->getR1GB10GBCnt () > 0) + fprintf (out_file, NTXT (" 1GB - 10GB %d\n"), + fDataTotal->getR1GB10GBCnt ()); + if (fDataTotal->getR10GB100GBCnt () > 0) + fprintf (out_file, NTXT (" 10GB - 100GB %d\n"), + fDataTotal->getR10GB100GBCnt ()); + if (fDataTotal->getR100GB1TBCnt () > 0) + fprintf (out_file, NTXT (" 100GB - 1TB %d\n"), + fDataTotal->getR100GB1TBCnt ()); + if (fDataTotal->getR1TB10TBCnt () > 0) + fprintf (out_file, NTXT (" 1TB - 10TB %d\n"), + fDataTotal->getR1TB10TBCnt ()); + fprintf (out_file, + GTXT ("\nLongest time %.6f (secs.)\n"), + (double) (fDataTotal->getRSlowestBytes () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Smallest read bytes %lld\n"), + (long long) fDataTotal->getRSmallestBytes ()); + fprintf (out_file, GTXT ("Largest read bytes %lld\n"), + (long long) fDataTotal->getRLargestBytes ()); + fprintf (out_file, + GTXT ("Total time %.6f (secs.)\n"), + (double) (fDataTotal->getReadTime () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Total calls %d\n"), + fDataTotal->getReadCnt ()); + fprintf (out_file, GTXT ("Total bytes %lld\n"), + (long long) fDataTotal->getReadBytes ()); + } + + if (fDataTotal->getOtherCnt () > 0) + { + fprintf (out_file, GTXT ("\nOther I/O Statistics\n")); + fprintf (out_file, + "-----------------------------------------------------\n"); + fprintf (out_file, + GTXT ("Total time %.6f (secs.)\n"), + (double) (fDataTotal->getOtherTime () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Total calls %d \n"), + fDataTotal->getOtherCnt ()); + } + if (fDataTotal->getErrorCnt () > 0) + { + fprintf (out_file, GTXT ("\nI/O Error Statistics\n")); + fprintf (out_file, + "-----------------------------------------------------\n"); + fprintf (out_file, + GTXT ("Total time %.6f (secs.)\n"), + (double) (fDataTotal->getErrorTime () / (double) NANOSEC)); + fprintf (out_file, GTXT ("Total calls %d \n"), + fDataTotal->getErrorCnt ()); + } + fprintf (out_file, NTXT ("\n")); +} + +void +er_print_ioactivity::data_dump () +{ + // get the list of io events from DbeView + int numExps = dbeSession->nexps (); + if (!numExps) + { + fprintf (out_file, + GTXT ("There is no IO event information in the experiments\n")); + return; + } + + MetricList *mlist = dbev->get_metric_list (MET_IO); + Hist_data *hist_data = dbev->get_hist_data (mlist, type, 0, Hist_data::ALL); + if (type == Histable::IOCALLSTACK) + printCallStacks (hist_data); + else if (printStat) + printStatistics (hist_data); + else + { + Metric::HistMetric *hist_metric = hist_data->get_histmetrics (); + hist_data->print_label (out_file, hist_metric, 0); + hist_data->print_content (out_file, hist_metric, limit); + fprintf (out_file, nl); + } +} + +er_print_experiment::er_print_experiment (DbeView *_dbev, int bgn_idx, + int end_idx, bool show_load, + bool show_header, bool show_stat, + bool show_over, bool show_odetail) +{ + dbev = _dbev; + exp_idx1 = bgn_idx; + exp_idx2 = end_idx; + load = show_load; + header = show_header; + stat = show_stat; + over = show_over; + odetail = show_odetail; +} + +void +er_print_experiment::data_dump () +{ + int index, maxlen; + + maxlen = 0; + + if (stat) + { + snprintf (fmt1, sizeof (fmt1), NTXT ("%%50s")); + if (exp_idx2 > exp_idx1) + { + statistics_sum (maxlen); + fprintf (out_file, nl); + } + + for (index = exp_idx1; index <= exp_idx2; index++) + statistics_dump (index, maxlen); + } + else if (over) + { + snprintf (fmt1, sizeof (fmt1), NTXT ("%%30s")); + if (exp_idx2 > exp_idx1) + { + overview_sum (maxlen); + fprintf (out_file, nl); + } + + for (index = exp_idx1; index <= exp_idx2; index++) + overview_dump (index, maxlen); + } + else if (header) + for (index = exp_idx1; index <= exp_idx2; index++) + { + if (index != exp_idx1) + fprintf (out_file, + "----------------------------------------------------------------\n"); + header_dump (index); + } +} + +void +er_print_experiment::overview_sum (int &maxlen) +{ + int index; + Ovw_data *sum_data = new Ovw_data (); + for (index = exp_idx1; index <= exp_idx2; index++) + { + Ovw_data *ovw_data = dbev->get_ovw_data (index); + if (ovw_data == NULL) + continue; + sum_data->sum (ovw_data); + delete ovw_data; + } + + fprintf (out_file, GTXT ("<Sum across selected experiments>")); + fprintf (out_file, nl); + overview_summary (sum_data, maxlen); + fprintf (out_file, nl); + delete sum_data; +} + +void +er_print_experiment::overview_dump (int exp_idx, int &maxlen) +{ + Ovw_data *ovw_data; + Ovw_data::Ovw_item ovw_item_labels; + Ovw_data::Ovw_item ovw_item; + int index; + int size; + + ovw_data = dbev->get_ovw_data (exp_idx); + if (ovw_data == NULL) + return; + if (pr_params.header) + header_dump (exp_idx); + else if (odetail) + fprintf (out_file, GTXT ("Experiment: %s\n"), + dbeSession->get_exp (exp_idx)->get_expt_name ()); + + overview_summary (ovw_data, maxlen); + if (!odetail) + { + delete ovw_data; + return; + } + + //Get the collection params for the sample selection and display them. + fprintf (out_file, NTXT ("\n\n")); + fprintf (out_file, fmt1, GTXT ("Individual samples")); + fprintf (out_file, NTXT ("\n\n")); + + size = ovw_data->size (); + ovw_item_labels = ovw_data->get_labels (); + + for (index = 0; index < size; index++) + { + ovw_item = ovw_data->fetch (index); + fprintf (out_file, fmt1, GTXT ("Sample Number")); + fprintf (out_file, NTXT (": %d\n\n"), ovw_item.number); + overview_item (&ovw_item, &ovw_item_labels); + fprintf (out_file, nl); + } + + delete ovw_data; +} + +void +er_print_experiment::overview_summary (Ovw_data *ovw_data, int &maxlen) +{ + char buf[128]; + int len; + Ovw_data::Ovw_item totals; + Ovw_data::Ovw_item ovw_item_labels; + totals = ovw_data->get_totals (); + len = snprintf (buf, sizeof (buf), "%.3lf", tstodouble (totals.total.t)); + if (maxlen < len) + maxlen = len; + snprintf (buf, sizeof (buf), NTXT ("%%#%d.0lf ( %#1.0f %%%%%%%%)"), + maxlen - 3, 0.); + snprintf (fmt2, sizeof (fmt2), NTXT ("%%%d.3lf"), maxlen); + snprintf (fmt3, sizeof (fmt3), buf, 0.0); + snprintf (fmt4, sizeof (fmt4), NTXT ("%%%d.3lf (%%5.1f%%%%)"), maxlen); + fprintf (out_file, fmt1, GTXT ("Aggregated statistics for selected samples")); + fprintf (out_file, NTXT ("\n\n")); + + ovw_item_labels = ovw_data->get_labels (); + overview_item (&totals, &ovw_item_labels); +} + +void +er_print_experiment::overview_item (Ovw_data::Ovw_item *ovw_item, + Ovw_data::Ovw_item *ovw_item_labels) +{ + double start, end, total_value; + int index, size; + timestruc_t total_time = {0, 0}; + + start = tstodouble (ovw_item->start); + end = tstodouble (ovw_item->end); + + fprintf (out_file, fmt1, GTXT ("Start Label")); + fprintf (out_file, NTXT (": ")); + fprintf (out_file, NTXT ("%s"), ovw_item->start_label); + fprintf (out_file, nl); + fprintf (out_file, fmt1, GTXT ("End Label")); + fprintf (out_file, NTXT (": %s\n"), ovw_item->end_label); + + fprintf (out_file, fmt1, GTXT ("Start Time (sec.)")); + fprintf (out_file, NTXT (": ")); + if (start == -1.0) + fprintf (out_file, GTXT ("N/A")); + else + fprintf (out_file, fmt2, start); + fprintf (out_file, nl); + fprintf (out_file, fmt1, GTXT ("End Time (sec.)")); + fprintf (out_file, NTXT (": ")); + if (end == -1.0) + fprintf (out_file, GTXT ("N/A")); + else + fprintf (out_file, fmt2, end); + fprintf (out_file, nl); + fprintf (out_file, fmt1, GTXT ("Duration (sec.)")); + fprintf (out_file, NTXT (": ")); + fprintf (out_file, fmt2, tstodouble (ovw_item->duration)); + fprintf (out_file, NTXT ("\n")); + + size = ovw_item->size; + for (index = 0; index < size; index++) + tsadd (&total_time, &ovw_item->values[index].t); + + total_value = tstodouble (total_time); + fprintf (out_file, fmt1, GTXT ("Total Thread Time (sec.)")); + fprintf (out_file, NTXT (": ")); + fprintf (out_file, fmt2, tstodouble (ovw_item->tlwp)); + fprintf (out_file, NTXT ("\n")); + fprintf (out_file, fmt1, GTXT ("Average number of Threads")); + fprintf (out_file, NTXT (": ")); + if (tstodouble (ovw_item->duration) != 0) + fprintf (out_file, fmt2, ovw_item->nlwp); + else + fprintf (out_file, GTXT ("N/A")); + fprintf (out_file, NTXT ("\n\n")); + fprintf (out_file, fmt1, GTXT ("Process Times (sec.)")); + fprintf (out_file, NTXT (":\n")); + for (index = 1; index < size; index++) + { + overview_value (&ovw_item_labels->values[index], ovw_item_labels->type, + total_value); + overview_value (&ovw_item->values[index], ovw_item->type, + total_value); + fprintf (out_file, NTXT ("\n")); + } +} + +void +er_print_experiment::overview_value (Value *value, ValueTag value_tag, + double total_value) +{ + double dvalue; + switch (value_tag) + { + case VT_LABEL: + fprintf (out_file, fmt1, value->l); + fprintf (out_file, NTXT (": ")); + break; + case VT_HRTIME: + dvalue = tstodouble (value->t); + if (dvalue == 0.0) + fprintf (out_file, fmt3, 0., 0.); + else + fprintf (out_file, fmt4, dvalue, 100.0 * dvalue / total_value); + break; + case VT_INT: + fprintf (out_file, NTXT ("%d"), value->i); + break; + default: + fprintf (out_file, fmt3); + } +} + +void +er_print_experiment::statistics_sum (int &maxlen) +{ + int index; + int size, len; + Stats_data *sum_data = new Stats_data (); + for (index = exp_idx1; index <= exp_idx2; index++) + { + Stats_data *stats_data = dbev->get_stats_data (index); + if (stats_data == NULL) + continue; + sum_data->sum (stats_data); + delete stats_data; + } + + // get the maximum width of values + size = sum_data->size (); + for (index = 0; index < size; index++) + { + len = (int) sum_data->fetch (index).value.get_len (); + if (maxlen < len) + maxlen = len; + } + + // print overview average + overview_sum (maxlen); + + // print statistics data + snprintf (fmt2, sizeof (fmt2), NTXT (": %%%ds\n"), maxlen); + statistics_item (sum_data); + delete sum_data; +} + +void +er_print_experiment::statistics_dump (int exp_idx, int &maxlen) +{ + Stats_data *stats_data; + int index; + int size, len; + stats_data = dbev->get_stats_data (exp_idx); + if (stats_data == NULL) + return; + if (pr_params.header) + { + header_dump (exp_idx); + fprintf (out_file, nl); + } + else + fprintf (out_file, GTXT ("Experiment: %s\n"), + dbeSession->get_exp (exp_idx)->get_expt_name ()); + + // get the maximum width of values + size = stats_data->size (); + for (index = 0; index < size; index++) + { + len = (int) stats_data->fetch (index).value.get_len (); + if (maxlen < len) + maxlen = len; + } + + // print overview average + overview_dump (exp_idx, maxlen); + fprintf (out_file, nl); + + // print statistics data + snprintf (fmt2, sizeof (fmt2), NTXT (": %%%ds\n"), maxlen); + statistics_item (stats_data); + delete stats_data; +} + +void +er_print_experiment::statistics_item (Stats_data *stats_data) +{ + int size, index; + Stats_data::Stats_item stats_item; + char buf[256]; + size = stats_data->size (); + for (index = 0; index < size; index++) + { + stats_item = stats_data->fetch (index); + fprintf (out_file, fmt1, stats_item.label); + fprintf (out_file, fmt2, stats_item.value.to_str (buf, sizeof (buf))); + } + fprintf (out_file, nl); +} + +// Print annotated source or disassembly -- called by er_print only +void +print_anno_file (char *name, const char *sel, const char *srcFile, + bool isDisasm, FILE *dis_file, FILE *inp_file, FILE *out_file, + DbeView *dbev, bool xdefault) +{ + Histable *obj; + Function *func; + Module *module; + Vector<int> *marks; + Hist_data *hist_data; + char *errstr; + int index; + SourceFile *fitem; + int threshold; + int compcom_bits; + int src_visible; + bool hex_visible; + bool srcmetrics_visible; + + if ((name == NULL) || (strlen (name) == 0)) + { + fprintf (stderr, GTXT ("Error: No function or file has been specified.\n")); + return; + } + + // find the function from the name + if (!dbeSession->find_obj (dis_file, inp_file, obj, name, sel, + Histable::FUNCTION, xdefault)) + return; + + if (obj != NULL) + { + // source or disassembly for <Total>, <Unknown>, or @plt + if (obj->get_type () != Histable::FUNCTION) + { + fprintf (stderr, + GTXT ("Error: %s is not a real function; no source or disassembly available.\n"), + name); + return; + } + + func = (Function *) obj; + if (func->flags & FUNC_FLAG_SIMULATED) + { + fprintf (stderr, + GTXT ("Error: %s is not a real function; no source or disassembly available.\n"), + name); + return; + } + else if (dbev != NULL && isDisasm) + dbev->set_func_scope (true); + + // function found, set module + module = func->module; + int ix = module->loadobject->seg_idx; + if (dbev->get_lo_expand (ix) == LIBEX_HIDE) + { + char *lo_name = module->loadobject->get_name (); + fprintf (stderr, + GTXT ("Error: No source or disassembly available for hidden object %s.\n"), + lo_name); + return; + } + + if (srcFile) + { + Vector<SourceFile*> *sources = func->get_sources (); + bool found = false; + if (sources == NULL) + { + fitem = func->getDefSrc (); + found = (func->line_first > 0) + && strcmp (basename (srcFile), + basename (fitem->get_name ())) == 0; + } + else + { + Vec_loop (SourceFile*, sources, index, fitem) + { + if (strcmp (basename (srcFile), basename (fitem->get_name ())) == 0) + { + found = true; + break; + } + } + } + if (!found) + { + fprintf (stderr, GTXT ("Error: Source file context %s does not contribute to function `%s'.\n"), + srcFile, name); + return; + } + } + } + else + { + // function not found + if (sel && strrchr (sel, ':')) + { + // 'sel' was "@seg_num:address" or "file_name:address" + fprintf (stderr, + GTXT ("Error: No function with given name `%s %s' found.\n"), + name, sel); + return; + } + // search for a file of that name + if (!dbeSession->find_obj (dis_file, inp_file, obj, name, sel, + Histable::MODULE, xdefault)) + return; + + if (obj == NULL) + { // neither function nor file found + fprintf (stderr, GTXT ("Error: No function or file with given name `%s' found.\n"), + name); + return; + } + + func = NULL; + module = (Module *) obj; + int ix = module->loadobject->seg_idx; + if (dbev->get_lo_expand (ix) == LIBEX_HIDE) + { + char *lo_name = module->loadobject->get_name (); + fprintf (stderr, GTXT ("Error: No source or disassembly available for hidden object %s.\n"), + lo_name); + return; + } + if (name) + srcFile = name; + } + + if (module == NULL || module->get_name () == NULL) + { + fprintf (stderr, GTXT ("Error: Object name not recorded in experiment\n")); + return; + } + module->read_stabs (); + + if (!isDisasm && (module->file_name == NULL + || (module->flags & MOD_FLAG_UNKNOWN) != 0 + || *module->file_name == 0)) + { + fprintf (stderr, GTXT ("Error: Source location not recorded in experiment\n")); + return; + } + + MetricList *metric_list = dbev->get_metric_list (MET_NORMAL); + int sort_ref_index = metric_list->get_sort_ref_index (); + if (isDisasm) + metric_list->set_sort_ref_index (-1); + + // Ask DbeView to generate function-level data + // MSI: I think this is used only to get totals to compute percentages + hist_data = dbev->get_hist_data (metric_list, Histable::FUNCTION, 0, + Hist_data::ALL); + MetricList *nmlist = hist_data->get_metric_list (); + metric_list->set_sort_ref_index (sort_ref_index); + if (nmlist->get_items ()->size () != 0 + && hist_data->get_status () != Hist_data::SUCCESS) + { + errstr = DbeView::status_str (DbeView::DBEVIEW_NO_DATA); + if (errstr) + { + fprintf (stderr, GTXT ("Error: %s\n"), errstr); + free (errstr); + } + return; + } + + marks = new Vector<int>; + if (isDisasm) + { + threshold = dbev->get_thresh_dis (); + compcom_bits = dbev->get_dis_compcom (); + src_visible = dbev->get_src_visible (); + hex_visible = dbev->get_hex_visible (); + srcmetrics_visible = dbev->get_srcmetric_visible (); + } + else + { + threshold = dbev->get_thresh_src (); + compcom_bits = dbev->get_src_compcom (); + src_visible = SRC_NA; + hex_visible = false; + srcmetrics_visible = false; + } + + dump_anno_file (out_file, isDisasm ? Histable::INSTR : Histable::LINE, + module, dbev, nmlist, hist_data->get_totals ()->value, + srcFile, func, marks, threshold, compcom_bits, + src_visible, hex_visible, srcmetrics_visible); + + delete marks; + + errstr = module->anno_str (); + if (errstr) + { + fprintf (stderr, GTXT ("Error: %s\n"), errstr); + free (errstr); + } + delete hist_data; +} + +void +print_html_title (FILE *out_file, char *title) +{ + // This will print a header row for the report + fprintf (out_file, "<html><title>%s</title>\n", title); + fprintf (out_file, "<center><h3>%s</h3></center>\n", title); +} + +void +print_html_label (FILE *out_file, MetricList *metrics_list) +{ + int mlist_sz; + + // This will print a header row for the metrics + Vector<Metric*> *mlist = metrics_list->get_items (); + mlist_sz = mlist->size (); + + fprintf (out_file, "<style type=\"text/css\">\n"); + fprintf (out_file, "<!--\nBODY\n"); + fprintf (out_file, ".th_C { text-align:center; background-color:lightgoldenrodyellow; }\n"); + fprintf (out_file, ".th_CG { text-align:center; background-color:#ffff33; }\n"); + fprintf (out_file, ".th_L { text-align:left; background-color:lightgoldenrodyellow; }\n"); + fprintf (out_file, ".th_LG { text-align:left; background-color:#ffff33; }\n"); + fprintf (out_file, ".td_R { text-align:right; }\n"); + fprintf (out_file, ".td_RG { text-align:right; background-color:#ffff33; }\n"); + fprintf (out_file, ".td_L { text-align:left; }\n"); + fprintf (out_file, ".td_LG { text-align:left; background-color:#ffff33; }\n"); + fprintf (out_file, "-->\n</style>"); + fprintf (out_file, "<center><table border=1 cellspacing=2>\n<tr>"); + + for (int index = 0; index < mlist_sz; index++) + { + Metric *mitem = mlist->fetch (index); + int ncols = 0; + if (mitem->is_visible ()) + ncols++; + if (mitem->is_tvisible ()) + ncols++; + if (mitem->is_pvisible ()) + ncols++; + if (ncols == 0) + continue; + char *name = strdup (mitem->get_name ()); + char *name2 = split_metric_name (name); + const char *style = index == metrics_list->get_sort_ref_index () ? "G" : ""; + + // start the column, with colspan setting, legend, and sort metric indicator + if (ncols == 1) + { + if (mitem->get_vtype () == VT_LABEL) + // left-adjust the name metric + fprintf (out_file, + "<th class=\"th_L%s\">%s <br>%s %s <br>%s </th>", + style, mitem->legend == NULL ? " " : mitem->legend, + (index == metrics_list->get_sort_ref_index ()) ? "∇" : " ", + name, name2 == NULL ? " " : name2); + else + // but center the others + fprintf (out_file, + "<th class=\"th_C%s\">%s <br>%s %s <br>%s </th>", + style, mitem->legend == NULL ? " " : mitem->legend, + (index == metrics_list->get_sort_ref_index ()) ? + "∇" : " ", + name, name2 == NULL ? NTXT (" ") : name2); + } + else + // name metric can't span columns + fprintf (out_file, + "<th colspan=%d class=\"th_C%s\">%s <br>%s %s <br>%s </th>", + ncols, style, + mitem->legend == NULL ? " " : mitem->legend, + index == metrics_list->get_sort_ref_index () ? + "∇" : " ", + name, name2 == NULL ? " " : name2); + + free (name); + } + + // end this row, start the units row + fprintf (out_file, NTXT ("</tr>\n<tr>")); + + // now do the units row + for (int index = 0; index < mlist_sz; index++) + { + Metric *mitem = mlist->fetch (index); + const char *style = index == metrics_list->get_sort_ref_index () ? "G" : ""; + + if (mitem->is_tvisible ()) + fprintf (out_file, "<th class=\"th_C%s\"> (%s)</th>", style, + GTXT ("sec.")); + if (mitem->is_visible ()) + { + if (mitem->get_abbr_unit () == NULL) + fprintf (out_file, "<th class=\"th_C%s\"> </th>", style); + else + fprintf (out_file, "<th class=\"th_C%s\">(%s)</th>", style, + mitem->get_abbr_unit () == NULL ? " " + : mitem->get_abbr_unit ()); + } + if (mitem->is_pvisible ()) + fprintf (out_file, "<th class=\"th_C%s\"> (%%)</th>", style); + } + fprintf (out_file, NTXT ("</tr>\n")); +} + +void +print_html_content (FILE *out_file, Hist_data *data, MetricList *metrics_list, + int limit, Histable::NameFormat nfmt) +{ + Hist_data::HistItem *item; + + // printing contents. + for (int i = 0; i < limit; i++) + { + item = data->fetch (i); + print_html_one (out_file, data, item, metrics_list, nfmt); + } +} + +void +print_html_one (FILE *out_file, Hist_data *data, Hist_data::HistItem *item, + MetricList *metrics_list, Histable::NameFormat nfmt) +{ + Metric *mitem; + int index; + int visible, tvisible, pvisible; + TValue *value; + double percent; + + fprintf (out_file, NTXT ("<tr>")); + Vec_loop (Metric*, metrics_list->get_items (), index, mitem) + { + visible = mitem->is_visible (); + tvisible = mitem->is_tvisible (); + pvisible = mitem->is_pvisible (); + const char *style = index == metrics_list->get_sort_ref_index () ? "G" : ""; + + if (tvisible) + { + value = &(item->value[index]); + if (value->ll == 0LL) + fprintf (out_file, + "<td class=\"td_R%s\"><tt>0. </tt></td>", + style); + else + fprintf (out_file, "<td class=\"td_R%s\"><tt>%4.3lf</tt></td>", + style, 1.e-6 * value->ll / dbeSession->get_clock (-1)); + } + + if (visible) + { + if (mitem->get_vtype () == VT_LABEL) + { + value = &(item->value[index]); + char *r; + if (value->tag == VT_OFFSET) + r = ((DataObject*) (item->obj))->get_offset_name (); + else + r = item->obj->get_name (nfmt); + char *n = html_ize_name (r); + fprintf (out_file, NTXT ("<td class=\"td_L%s\">%s</td>"), style, n); + free (n); + } + else + { + value = &(item->value[index]); + switch (value->tag) + { + case VT_DOUBLE: + if (value->d == 0.0) + fprintf (out_file, + "<td class=\"td_R%s\"><tt>0. </tt></td>", + style); + else + fprintf (out_file, + "<td class=\"td_R%s\"><tt>%4.3lf</tt></td>", style, + value->d); + break; + case VT_INT: + fprintf (out_file, "<td class=\"td_R%s\"><tt>%d</tt></td>", + style, value->i); + break; + case VT_LLONG: + fprintf (out_file, "<td class=\"td_R%s\"><tt>%lld</td></tt>", + style, value->ll); + break; + case VT_ULLONG: + fprintf (out_file, "<td class=\"td_R%s\"><tt>%llu</td></tt>", + style, value->ull); + break; + case VT_ADDRESS: + fprintf (out_file, + "<td class=\"td_R%s\"><tt>%u:0x%08x</tt></td>", style, + ADDRESS_SEG (value->ll), ADDRESS_OFF (value->ll)); + break; + case VT_FLOAT: + if (value->f == 0.0) + fprintf (out_file, + "<td class=\"td_R%s\"><tt>0. </tt></td>", + style); + else + fprintf (out_file, + "<td class=\"td_R%s\"><tt>%4.3f</tt></td>", + style, value->f); + break; + case VT_SHORT: + fprintf (out_file, "<td class=\"td_R%s\"><tt>%d</tt></td>", + style, value->s); + break; + // ignoring the following cases (why?) + case VT_HRTIME: + case VT_LABEL: + case VT_OFFSET: + break; + } + } + } + + if (pvisible) + { + percent = data->get_percentage (item->value[index].to_double (), index); + if (percent == 0.0) + // adjust to change format from xx.yy% + fprintf (out_file, "<td class=\"td_R%s\">0. </td>", + style); + else + // adjust format below to change format from xx.yy% + fprintf (out_file, "<td class=\"td_R%s\">%3.2f</td>", style, + (100.0 * percent)); + } + } + fprintf (out_file, NTXT ("</tr>\n")); +} + +void +print_html_trailer (FILE *out_file) +{ + fprintf (out_file, NTXT ("</table></center></html>\n")); +} + +static char * +del_delim (char *s) +{ + size_t len = strlen (s); + if (len > 0) + s[len - 1] = 0; + return s; +} + +void +print_delim_label (FILE *out_file, MetricList *metrics_list, char delim) +{ + char line0[2 * MAX_LEN], line1[2 * MAX_LEN]; + char line2[2 * MAX_LEN], line3[2 * MAX_LEN]; + size_t len; + + // This will print four header rows for the metrics + line0[0] = 0; + line1[0] = 0; + line2[0] = 0; + line3[0] = 0; + Vector<Metric*> *mlist = metrics_list->get_items (); + for (int index = 0, mlist_sz = mlist->size (); index < mlist_sz; index++) + { + Metric *mitem = mlist->fetch (index); + if (!(mitem->is_visible () || mitem->is_tvisible () + || mitem->is_pvisible ())) + continue; + char *name = strdup (mitem->get_name ()); + char *name2 = split_metric_name (name); + + if (mitem->is_tvisible ()) + { + len = strlen (line0); + snprintf (line0 + len, sizeof (line0) - len, NTXT ("\"%s\"%c"), + mitem->legend == NULL ? NTXT ("") : mitem->legend, delim); + len = strlen (line1); + snprintf (line1 + len, sizeof (line1) - len, NTXT ("\"%s\"%c"), + name, delim); + len = strlen (line2); + snprintf (line2 + len, sizeof (line2) - len, NTXT ("\"%s\"%c"), + name2 == NULL ? NTXT ("") : name2, delim); + len = strlen (line3); + if (index == metrics_list->get_sort_ref_index ()) + snprintf (line3 + len, sizeof (line3) - len, NTXT ("\"V %s\"%c"), + GTXT ("(sec.)"), delim); + else + snprintf (line3 + len, sizeof (line3) - len, NTXT ("\" %s\"%c"), + GTXT ("(sec.)"), delim); + } + if (mitem->is_visible ()) + { + len = strlen (line0); + snprintf (line0 + len, sizeof (line0) - len, "\"%s\"%c", + mitem->legend == NULL ? "" : mitem->legend, delim); + + len = strlen (line1); + snprintf (line1 + len, sizeof (line1) - len, "\"%s\"%c", + name, delim); + + len = strlen (line2); + snprintf (line2 + len, sizeof (line2) - len, "\"%s\"%c", + name2 == NULL ? NTXT ("") : name2, delim); + + len = strlen (line3); + char *au = mitem->get_abbr_unit (); + + if (index == metrics_list->get_sort_ref_index ()) + { + if (au == NULL) + snprintf (line3 + len, sizeof (line3) - len, "\"V \"%c", delim); + else + snprintf (line3 + len, sizeof (line3) - len, "\"V (%s)\"%c", + au, delim); + } + else + { + if (au == NULL) + snprintf (line3 + len, sizeof (line3) - len, "\" \"%c", + delim); + else + snprintf (line3 + len, sizeof (line3) - len, "\" (%s)\"%c", + au, delim); + } + } + if (mitem->is_pvisible ()) + { + len = strlen (line0); + snprintf (line0 + len, sizeof (line0) - len, NTXT ("\"%s\"%c"), + mitem->legend == NULL ? NTXT ("") : mitem->legend, delim); + + len = strlen (line1); + snprintf (line1 + len, sizeof (line1) - len, NTXT ("\"%s\"%c"), + name, delim); + + len = strlen (line2); + snprintf (line2 + len, sizeof (line2) - len, NTXT ("\"%s\"%c"), + name2 == NULL ? NTXT ("") : name2, delim); + + len = strlen (line3); + if (index == metrics_list->get_sort_ref_index ()) + snprintf (line3 + len, sizeof (line3) - len, NTXT ("\"V %s\"%c"), + NTXT ("%%"), delim); + else + snprintf (line3 + len, sizeof (line3) - len, NTXT ("\" %s\"%c"), + NTXT ("%%"), delim); + } + free (name); + } + // now remove the trailing delimiter, and print the four lines + fprintf (out_file, NTXT ("%s\n"), del_delim (line0)); + fprintf (out_file, NTXT ("%s\n"), del_delim (line1)); + fprintf (out_file, NTXT ("%s\n"), del_delim (line2)); + fprintf (out_file, NTXT ("%s\n"), del_delim (line3)); +} + +void +print_delim_content (FILE *out_file, Hist_data *data, MetricList *metrics_list, + int limit, Histable::NameFormat nfmt, char delim) +{ + Hist_data::HistItem *item; + int i; + + // printing contents. + for (i = 0; i < limit; i++) + { + item = data->fetch (i); + print_delim_one (out_file, data, item, metrics_list, nfmt, delim); + } +} + +void +print_delim_trailer (FILE */*out_file*/, char /*delim*/) { } + +// EUGENE does this function work properly when "-compare ratio" is used? +// how about when the ratio is nonzero-divided-by-zero? +// EUGENE actually, review this entire file + +void +print_delim_one (FILE *out_file, Hist_data *data, Hist_data::HistItem *item, + MetricList *metrics_list, Histable::NameFormat nfmt, + char delim) +{ + Metric *mitem; + int index; + int visible, tvisible, pvisible; + TValue *value; + double percent; + size_t len; + + char line1[2 * MAX_LEN]; + *line1 = 0; + Vec_loop (Metric*, metrics_list->get_items (), index, mitem) + { + visible = mitem->is_visible (); + tvisible = mitem->is_tvisible (); + pvisible = mitem->is_pvisible (); + if (tvisible) + { + value = &(item->value[index]); + len = strlen (line1); + if (value->ll == 0LL) + snprintf (line1 + len, sizeof (line1) - len, "\"0.\"%c", delim); + else + snprintf (line1 + len, sizeof (line1) - len, "\"%4.3lf\"%c", + 1.e-6 * value->ll / dbeSession->get_clock (-1), + delim); + } + + if (visible) + { + len = strlen (line1); + if (mitem->get_vtype () == VT_LABEL) + { + value = &(item->value[index]); + char *r; + if (value->tag == VT_OFFSET) + r = ((DataObject*) (item->obj))->get_offset_name (); + else + r = item->obj->get_name (nfmt); + char *p = csv_ize_name (r, delim); + snprintf (line1 + len, sizeof (line1) - len, "\"%s\"%c", p, delim); + free (p); + } + else + { + value = &(item->value[index]); + switch (value->tag) + { + case VT_DOUBLE: + if (value->d == 0.0) + snprintf (line1 + len, sizeof (line1) - len, "\"0.\"%c", + delim); + else + snprintf (line1 + len, sizeof (line1) - len, "\"%4.3lf\"%c", + value->d, delim); + break; + case VT_INT: + snprintf (line1 + len, sizeof (line1) - len, "\"%d\"%c", + value->i, delim); + break; + case VT_LLONG: + snprintf (line1 + len, sizeof (line1) - len, "\"%lld\"%c", + value->ll, delim); + break; + case VT_ULLONG: + snprintf (line1 + len, sizeof (line1) - len, "\"%llu\"%c", + value->ull, delim); + break; + case VT_ADDRESS: + snprintf (line1 + len, sizeof (line1) - len, "\"%u:0x%08x\"%c", + ADDRESS_SEG (value->ll), + ADDRESS_OFF (value->ll), delim); + break; + case VT_FLOAT: + if (value->f == 0.0) + snprintf (line1 + len, sizeof (line1) - len, "\"0.\"%c", + delim); + else + snprintf (line1 + len, sizeof (line1) - len, "\"%4.3f\"%c", + value->f, delim); + break; + case VT_SHORT: + snprintf (line1 + len, sizeof (line1) - len, "\"%d\"%c", + value->s, delim); + break; + // ignoring the following cases (why?) + case VT_HRTIME: + case VT_LABEL: + case VT_OFFSET: + break; + } + } + } + + if (pvisible) + { + len = strlen (line1); + percent = data->get_percentage (item->value[index].to_double (), index); + if (percent == 0.0) + // adjust to change format from xx.yy% + snprintf (line1 + len, sizeof (line1) - len, "\"0.\"%c", delim); + else + // adjust format below to change format from xx.yy% + snprintf (line1 + len, sizeof (line1) - len, "\"%3.2f\"%c", + (100.0 * percent), delim); + } + } + fprintf (out_file, NTXT ("%s\n"), del_delim (line1)); +} + +char * +html_ize_name (char *name) +{ + StringBuilder sb; + for (size_t i = 0; i < strlen (name); i++) + { + switch (name[i]) + { + case ' ': sb.append (NTXT (" ")); + break; + case '"': sb.append (NTXT (""")); + break; + case '&': sb.append (NTXT ("&")); + break; + case '<': sb.append (NTXT ("<")); + break; + case '>': sb.append (NTXT (">")); + break; + default: sb.append (name[i]); + break; + } + } + char *ret = sb.toString (); + return ret; +} + +char * +csv_ize_name (char *name, char /*delim*/) +{ + StringBuilder sb; + for (size_t i = 0; i < strlen (name); i++) + sb.append (name[i]); + char *ret = sb.toString (); + return ret; +} + +// Split a metric name into two parts, replacing a blank with +// a zero and returning pointer to the rest of the string, or +// leaving the string unchanged, and returning NULL; + +char * +split_metric_name (char *name) +{ + // figure out the most even split of the name + size_t len = strlen (name); + char *middle = &name[len / 2]; + + // find the first blank + char *first = strchr (name, (int) ' '); + if (first == NULL) // no blanks + return NULL; + char *last = first; + char *p = first; + for (;;) + { + p = strchr (p + 1, (int) ' '); + if (p == NULL) + break; + if (p < middle) + { + first = p; + last = p; + } + else + { + last = p; + break; + } + } + // pick the better of the two + char *ret; + int f = (int) (middle - first); + int l = (int) (last - middle); + if ((first == last) || (f <= l)) + { + *first = '\0'; + ret = first + 1; + } + else + { + *last = '\0'; + ret = last + 1; + } + return ret; +} diff --git a/gprofng/src/Print.h b/gprofng/src/Print.h new file mode 100644 index 0000000..4bc6655 --- /dev/null +++ b/gprofng/src/Print.h @@ -0,0 +1,283 @@ +/* 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. */ + +#ifndef _PRINT_H +#define _PRINT_H + + +// Include files +#include <stdio.h> +#include <stdlib.h> +#include "dbe_types.h" +#include "Metric.h" +#include "Hist_data.h" +#include "Ovw_data.h" +#include "Stats_data.h" +#include "Emsg.h" +#include "Exp_Layout.h" +#include "DefaultMap.h" +#include "FileData.h" +#include "HeapData.h" +#include "HashMap.h" + +const char nl[] = "\n"; +const char tab[] = "\t"; + +// Printing options. +enum Print_destination +{ + DEST_PRINTER = 0, + DEST_FILE = 1, + DEST_OPEN_FILE = 2 +}; + +enum Print_mode +{ + MODE_LIST, + MODE_DETAIL, + MODE_GPROF, + MODE_ANNOTATED +}; + +struct Print_params +{ + Print_destination dest; // printer or file + char *name; // of printer or file + int ncopies; // # of copies + bool header; // print header first + FILE *openfile; // if destination is DEST_OPEN_FILE +}; + +class Experiment; +class MetricList; +class DbeView; +class Stack_coverage; +class Function; +class LoadObject; + +// Class Definitions +class er_print_common_display +{ +public: + er_print_common_display () + { + out_file = NULL; + pr_params.header = false; + } + + virtual ~er_print_common_display () { } + + // Open the file/printer to write to + int open (Print_params *); + + void + set_out_file (FILE *o) + { + out_file = o; + pr_params.dest = DEST_FILE; + } + + // Print the final output data. This function calls + // data_dump() to actually do the dumping of data. + bool print_output (); + + // Print the output in the appropriate format. + virtual void data_dump () = 0; + + void header_dump (int exp_idx); + + // Return the report. If the report size is greater than max, return truncated report + // Allocates memory, so the caller should free this memory. + char *get_output (int max); + +protected: + DbeView *dbev; + FILE *out_file; + Print_params pr_params; + char *tmp_file; + int exp_idx1, exp_idx2; + bool load; + bool header; +}; + +class er_print_histogram : public er_print_common_display +{ +public: + er_print_histogram (DbeView *dbv, Hist_data *data, MetricList *metrics_list, + Print_mode disp_type, int limit, char *sort_name, + Histable *sobj, bool show_load, bool show_header); + void data_dump (); + +private: + void dump_list (int limit); + void dump_detail (int limit); + void get_gprof_width (Metric::HistMetric *hist_metric, int limit); + void dump_gprof (int limit); + void dump_annotated_dataobjects (Vector<int> *marks, int threshold); + void dump_annotated (); + + Stack_coverage *stack_cov; + Hist_data *hist_data; + MetricList *mlist; + Print_mode type; + int number_entries; + char *sort_metric; + Histable *sel_obj; +}; + +class er_print_ctree : public er_print_common_display +{ +public: + er_print_ctree (DbeView *dbv, Vector<Histable*> *cstack, Histable *sobj, + int limit); + void data_dump (); + void print_children (Hist_data *data, int index, Histable *obj, char *prefix, + Hist_data::HistItem *total); + +private: + Vector<Histable*> *cstack; + Histable *sobj; + MetricList *mlist; + Metric::HistMetric *hist_metric; + char **fmt_int; + char **fmt_real0; + char **fmt_real1; + int limit; + int print_row; +}; + +class er_print_gprof : public er_print_common_display +{ +public: + er_print_gprof (DbeView *dbv, Vector<Histable*> *cstack); + void data_dump (); +private: + Vector<Histable*> *cstack; +}; + +class er_print_leaklist : public er_print_common_display +{ +public: + er_print_leaklist (DbeView *dbv, bool show_leak, + bool show_alloca, int limit); + void data_dump (); + +private: + bool leak; + bool alloca; + int limit; +}; + +class er_print_heapactivity : public er_print_common_display +{ +public: + er_print_heapactivity (DbeView *_dbev, Histable::Type _type, + bool _printStat, int _limit); + void data_dump (); + +private: + void printStatistics (Hist_data *hist_data); + void printCallStacks (Hist_data *hist_data); + + Histable::Type type; + bool printStat; + int limit; +}; + +class er_print_ioactivity : public er_print_common_display +{ +public: + er_print_ioactivity (DbeView *_dbev, Histable::Type _type, + bool _printStat, int _limit); + void data_dump (); + +private: + void printStatistics (Hist_data *hist_data); + void printCallStacks (Hist_data *hist_data); + + Histable::Type type; + bool printStat; + int limit; +}; + +class er_print_experiment : public er_print_common_display +{ +public: + er_print_experiment (DbeView *me, int bgn_idx, int end_idx, bool show_load, + bool show_header, bool show_stat, bool show_over, bool show_odetail); + void data_dump (); + +private: + char fmt1[32], fmt2[32], fmt3[32], fmt4[32]; + // buffers shared by the following functions + void overview_sum (int &maxlen); + void overview_dump (int exp_idx, int &maxlen); + void overview_summary (Ovw_data *ovw_data, int &maxlen); + void overview_item (Ovw_data::Ovw_item *ovw_item, + Ovw_data::Ovw_item *ovw_item_labels); + void overview_value (Value *value, ValueTag value_tag, + double total_value); + void statistics_sum (int &maxlen); + void statistics_dump (int exp_idx, int &maxlen); + void statistics_item (Stats_data *stats_data); + + bool stat; + bool over; + bool odetail; +}; + +// Print the header. Experiment name and the sample +// selection, along with the percentage. +char *pr_load_objects (Vector<LoadObject*> *loadobjects, char *lead); +char *pr_samples (Experiment *exp); +char *pr_mesgs (Emsg *msg, const char *null_str, const char *lead); +void print_load_object (FILE *out_file); +void print_header (Experiment *exp, FILE *out_file); + +// Print Function metrics +void get_width (Hist_data *data, MetricList *metrics_list, + Metric::HistMetric *hist_metric); +void get_format (char **fmt_int, char **fmt_real0, char **fmt_real1, + MetricList *metrics_list, Metric::HistMetric *hist_metric, + int nspace); +int print_label (FILE *out_file, MetricList *metrics_list, + Metric::HistMetric *hist_metric, int space); +void print_anno_file (char *name, const char *sel, const char *srcFile, + bool isDisasm, FILE *dis_file, FILE *inp_file, + FILE *out_file, DbeView *dbev, bool xdefault); +void print_html_title (FILE *out_file, char *title); +void print_html_label (FILE *out_file, MetricList *metrics_list); +void print_html_content (FILE *out_file, Hist_data *d, MetricList *metrics_list, + int limit, Histable::NameFormat nfmt); +void print_html_one (FILE *out_file, Hist_data *data, Hist_data::HistItem *item, + MetricList *metrics_list, Histable::NameFormat nfmt); +void print_html_trailer (FILE* out_file); +char *html_ize_name (char *name); +void print_delim_label (FILE *out_file, MetricList *metrics_list, char delim); +void print_delim_content (FILE *out_file, Hist_data *data, + MetricList *metrics_list, int limit, + Histable::NameFormat nfmt, char delim); +void print_delim_one (FILE *out_file, Hist_data *data, Hist_data::HistItem *item, + MetricList *metrics_list, Histable::NameFormat nfmt, char delim); +void print_delim_trailer (FILE* out_file, char delim); +char *csv_ize_name (char *name, char delim); +char *split_metric_name (char *name); + +#endif diff --git a/gprofng/src/QLParser.h b/gprofng/src/QLParser.h new file mode 100644 index 0000000..c4665e8 --- /dev/null +++ b/gprofng/src/QLParser.h @@ -0,0 +1,61 @@ +/* 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. */ + +#ifndef _QLPARSER_H +#define _QLPARSER_H + +#include <sstream> +#include <istream> +#include <iostream> +#include "Expression.h" + +/* This class contains parser inputs (a string, if non-NULL: if NULL, use cin), + and outputs (obtained via operator(), which resets the output + expression). The destructor deletes the returned expression to allow + exception throws on syntax error to clean up properly. */ + +namespace QL +{ + struct Result + { + std::stringstream streamify; + public: + std::istream in; + Expression *out; + + Result () : in (std::cin.rdbuf ()), out (NULL) { } + Result (const char *instr) : streamify (std::string (instr)), + in (streamify.rdbuf ()), out (NULL) { } + + Expression *operator() () + { + Expression *o = out; + out = NULL; + return o; + } + + ~Result () + { + delete out; + } + }; +}; + +#endif /* _QLPARSER_H */ diff --git a/gprofng/src/QLParser.tab.cc b/gprofng/src/QLParser.tab.cc new file mode 100644 index 0000000..4517b2e --- /dev/null +++ b/gprofng/src/QLParser.tab.cc @@ -0,0 +1,1453 @@ +// A Bison parser, made by GNU Bison 3.7.5. + +// Skeleton implementation for Bison LALR(1) parsers in C++ + +// Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc. + +// 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 of the License, 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, see <http://www.gnu.org/licenses/>. + +// As a special exception, you may create a larger work that contains +// part or all of the Bison parser skeleton and distribute that work +// under terms of your choice, so long as that work isn't itself a +// parser generator using the skeleton or a modified version thereof +// as a parser skeleton. Alternatively, if you modify or redistribute +// the parser skeleton itself, you may (at your option) remove this +// special exception, which will cause the skeleton and the resulting +// Bison output files to be licensed under the GNU General Public +// License without this special exception. + +// This special exception was added by the Free Software Foundation in +// version 2.2 of Bison. + +// DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, +// especially those whose name start with YY_ or yy_. They are +// private implementation details that can be changed or removed. + +// "%code top" blocks. +#line 28 "QLParser.yy" + +#include <stdio.h> +#include <string.h> +#include <string> + +#line 45 "QLParser.tab.cc" + + + + +#include "QLParser.tab.hh" + + +// Unqualified %code blocks. +#line 42 "QLParser.yy" + +namespace QL +{ + static QL::Parser::symbol_type yylex (QL::Result &result); +} + +#line 61 "QLParser.tab.cc" + + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include <libintl.h> // FIXME: INFRINGES ON USER NAME SPACE. +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + + +// Whether we are compiled with exception support. +#ifndef YY_EXCEPTIONS +# if defined __GNUC__ && !defined __EXCEPTIONS +# define YY_EXCEPTIONS 0 +# else +# define YY_EXCEPTIONS 1 +# endif +#endif + + + +// Enable debugging if requested. +#if YYDEBUG + +// A pseudo ostream that takes yydebug_ into account. +# define YYCDEBUG if (yydebug_) (*yycdebug_) + +# define YY_SYMBOL_PRINT(Title, Symbol) \ + do { \ + if (yydebug_) \ + { \ + *yycdebug_ << Title << ' '; \ + yy_print_ (*yycdebug_, Symbol); \ + *yycdebug_ << '\n'; \ + } \ + } while (false) + +# define YY_REDUCE_PRINT(Rule) \ + do { \ + if (yydebug_) \ + yy_reduce_print_ (Rule); \ + } while (false) + +# define YY_STACK_PRINT() \ + do { \ + if (yydebug_) \ + yy_stack_print_ (); \ + } while (false) + +#else // !YYDEBUG + +# define YYCDEBUG if (false) std::cerr +# define YY_SYMBOL_PRINT(Title, Symbol) YY_USE (Symbol) +# define YY_REDUCE_PRINT(Rule) static_cast<void> (0) +# define YY_STACK_PRINT() static_cast<void> (0) + +#endif // !YYDEBUG + +#define yyerrok (yyerrstatus_ = 0) +#define yyclearin (yyla.clear ()) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab +#define YYRECOVERING() (!!yyerrstatus_) + +#line 50 "QLParser.yy" +namespace QL { +#line 135 "QLParser.tab.cc" + + /// Build a parser object. + Parser::Parser (QL::Result &result_yyarg) +#if YYDEBUG + : yydebug_ (false), + yycdebug_ (&std::cerr), +#else + : +#endif + result (result_yyarg) + {} + + Parser::~Parser () + {} + + Parser::syntax_error::~syntax_error () YY_NOEXCEPT YY_NOTHROW + {} + + /*---------------. + | symbol kinds. | + `---------------*/ + + + + // by_state. + Parser::by_state::by_state () YY_NOEXCEPT + : state (empty_state) + {} + + Parser::by_state::by_state (const by_state& that) YY_NOEXCEPT + : state (that.state) + {} + + void + Parser::by_state::clear () YY_NOEXCEPT + { + state = empty_state; + } + + void + Parser::by_state::move (by_state& that) + { + state = that.state; + that.clear (); + } + + Parser::by_state::by_state (state_type s) YY_NOEXCEPT + : state (s) + {} + + Parser::symbol_kind_type + Parser::by_state::kind () const YY_NOEXCEPT + { + if (state == empty_state) + return symbol_kind::S_YYEMPTY; + else + return YY_CAST (symbol_kind_type, yystos_[+state]); + } + + Parser::stack_symbol_type::stack_symbol_type () + {} + + Parser::stack_symbol_type::stack_symbol_type (YY_RVREF (stack_symbol_type) that) + : super_type (YY_MOVE (that.state)) + { + switch (that.kind ()) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + value.YY_MOVE_OR_COPY< Expression * > (YY_MOVE (that.value)); + break; + + default: + break; + } + +#if 201103L <= YY_CPLUSPLUS + // that is emptied. + that.state = empty_state; +#endif + } + + Parser::stack_symbol_type::stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) that) + : super_type (s) + { + switch (that.kind ()) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + value.move< Expression * > (YY_MOVE (that.value)); + break; + + default: + break; + } + + // that is emptied. + that.kind_ = symbol_kind::S_YYEMPTY; + } + +#if YY_CPLUSPLUS < 201103L + Parser::stack_symbol_type& + Parser::stack_symbol_type::operator= (const stack_symbol_type& that) + { + state = that.state; + switch (that.kind ()) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + value.copy< Expression * > (that.value); + break; + + default: + break; + } + + return *this; + } + + Parser::stack_symbol_type& + Parser::stack_symbol_type::operator= (stack_symbol_type& that) + { + state = that.state; + switch (that.kind ()) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + value.move< Expression * > (that.value); + break; + + default: + break; + } + + // that is emptied. + that.state = empty_state; + return *this; + } +#endif + + template <typename Base> + void + Parser::yy_destroy_ (const char* yymsg, basic_symbol<Base>& yysym) const + { + if (yymsg) + YY_SYMBOL_PRINT (yymsg, yysym); + } + +#if YYDEBUG + template <typename Base> + void + Parser::yy_print_ (std::ostream& yyo, const basic_symbol<Base>& yysym) const + { + std::ostream& yyoutput = yyo; + YY_USE (yyoutput); + if (yysym.empty ()) + yyo << "empty symbol"; + else + { + symbol_kind_type yykind = yysym.kind (); + yyo << (yykind < YYNTOKENS ? "token" : "nterm") + << ' ' << yysym.name () << " ("; + YY_USE (yykind); + yyo << ')'; + } + } +#endif + + void + Parser::yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym) + { + if (m) + YY_SYMBOL_PRINT (m, sym); + yystack_.push (YY_MOVE (sym)); + } + + void + Parser::yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym) + { +#if 201103L <= YY_CPLUSPLUS + yypush_ (m, stack_symbol_type (s, std::move (sym))); +#else + stack_symbol_type ss (s, sym); + yypush_ (m, ss); +#endif + } + + void + Parser::yypop_ (int n) + { + yystack_.pop (n); + } + +#if YYDEBUG + std::ostream& + Parser::debug_stream () const + { + return *yycdebug_; + } + + void + Parser::set_debug_stream (std::ostream& o) + { + yycdebug_ = &o; + } + + + Parser::debug_level_type + Parser::debug_level () const + { + return yydebug_; + } + + void + Parser::set_debug_level (debug_level_type l) + { + yydebug_ = l; + } +#endif // YYDEBUG + + Parser::state_type + Parser::yy_lr_goto_state_ (state_type yystate, int yysym) + { + int yyr = yypgoto_[yysym - YYNTOKENS] + yystate; + if (0 <= yyr && yyr <= yylast_ && yycheck_[yyr] == yystate) + return yytable_[yyr]; + else + return yydefgoto_[yysym - YYNTOKENS]; + } + + bool + Parser::yy_pact_value_is_default_ (int yyvalue) + { + return yyvalue == yypact_ninf_; + } + + bool + Parser::yy_table_value_is_error_ (int yyvalue) + { + return yyvalue == yytable_ninf_; + } + + int + Parser::operator() () + { + return parse (); + } + + int + Parser::parse () + { + int yyn; + /// Length of the RHS of the rule being reduced. + int yylen = 0; + + // Error handling. + int yynerrs_ = 0; + int yyerrstatus_ = 0; + + /// The lookahead symbol. + symbol_type yyla; + + /// The return value of parse (). + int yyresult; + +#if YY_EXCEPTIONS + try +#endif // YY_EXCEPTIONS + { + YYCDEBUG << "Starting parse\n"; + + + /* Initialize the stack. The initial state will be set in + yynewstate, since the latter expects the semantical and the + location values to have been already stored, initialize these + stacks with a primary value. */ + yystack_.clear (); + yypush_ (YY_NULLPTR, 0, YY_MOVE (yyla)); + + /*-----------------------------------------------. + | yynewstate -- push a new symbol on the stack. | + `-----------------------------------------------*/ + yynewstate: + YYCDEBUG << "Entering state " << int (yystack_[0].state) << '\n'; + YY_STACK_PRINT (); + + // Accept? + if (yystack_[0].state == yyfinal_) + YYACCEPT; + + goto yybackup; + + + /*-----------. + | yybackup. | + `-----------*/ + yybackup: + // Try to take a decision without lookahead. + yyn = yypact_[+yystack_[0].state]; + if (yy_pact_value_is_default_ (yyn)) + goto yydefault; + + // Read a lookahead token. + if (yyla.empty ()) + { + YYCDEBUG << "Reading a token\n"; +#if YY_EXCEPTIONS + try +#endif // YY_EXCEPTIONS + { + symbol_type yylookahead (yylex (result)); + yyla.move (yylookahead); + } +#if YY_EXCEPTIONS + catch (const syntax_error& yyexc) + { + YYCDEBUG << "Caught exception: " << yyexc.what() << '\n'; + error (yyexc); + goto yyerrlab1; + } +#endif // YY_EXCEPTIONS + } + YY_SYMBOL_PRINT ("Next token is", yyla); + + if (yyla.kind () == symbol_kind::S_YYerror) + { + // The scanner already issued an error message, process directly + // to error recovery. But do not keep the error token as + // lookahead, it is too special and may lead us to an endless + // loop in error recovery. */ + yyla.kind_ = symbol_kind::S_YYUNDEF; + goto yyerrlab1; + } + + /* If the proper action on seeing token YYLA.TYPE is to reduce or + to detect an error, take that action. */ + yyn += yyla.kind (); + if (yyn < 0 || yylast_ < yyn || yycheck_[yyn] != yyla.kind ()) + { + goto yydefault; + } + + // Reduce or error. + yyn = yytable_[yyn]; + if (yyn <= 0) + { + if (yy_table_value_is_error_ (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + // Count tokens shifted since error; after three, turn off error status. + if (yyerrstatus_) + --yyerrstatus_; + + // Shift the lookahead token. + yypush_ ("Shifting", state_type (yyn), YY_MOVE (yyla)); + goto yynewstate; + + + /*-----------------------------------------------------------. + | yydefault -- do the default action for the current state. | + `-----------------------------------------------------------*/ + yydefault: + yyn = yydefact_[+yystack_[0].state]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + + /*-----------------------------. + | yyreduce -- do a reduction. | + `-----------------------------*/ + yyreduce: + yylen = yyr2_[yyn]; + { + stack_symbol_type yylhs; + yylhs.state = yy_lr_goto_state_ (yystack_[yylen].state, yyr1_[yyn]); + /* Variants are always initialized to an empty instance of the + correct type. The default '$$ = $1' action is NOT applied + when using variants. */ + switch (yyr1_[yyn]) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + yylhs.value.emplace< Expression * > (); + break; + + default: + break; + } + + + + // Perform the reduction. + YY_REDUCE_PRINT (yyn); +#if YY_EXCEPTIONS + try +#endif // YY_EXCEPTIONS + { + switch (yyn) + { + case 2: // S: %empty +#line 104 "QLParser.yy" + { result.out = new Expression (Expression::OP_NUM, (uint64_t) 1); } +#line 577 "QLParser.tab.cc" + break; + + case 3: // S: exp +#line 105 "QLParser.yy" + { result.out = new Expression (yystack_[0].value.as < Expression * > ()); } +#line 583 "QLParser.tab.cc" + break; + + case 4: // exp: exp DEG exp +#line 107 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_DEG, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 589 "QLParser.tab.cc" + break; + + case 5: // exp: exp MUL exp +#line 108 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_MUL, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 595 "QLParser.tab.cc" + break; + + case 6: // exp: exp DIV exp +#line 109 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_DIV, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 601 "QLParser.tab.cc" + break; + + case 7: // exp: exp REM exp +#line 110 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_REM, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 607 "QLParser.tab.cc" + break; + + case 8: // exp: exp ADD exp +#line 111 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_ADD, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 613 "QLParser.tab.cc" + break; + + case 9: // exp: exp MINUS exp +#line 112 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_MINUS, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 619 "QLParser.tab.cc" + break; + + case 10: // exp: exp LS exp +#line 113 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_LS, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 625 "QLParser.tab.cc" + break; + + case 11: // exp: exp RS exp +#line 114 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_RS, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 631 "QLParser.tab.cc" + break; + + case 12: // exp: exp LT exp +#line 115 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_LT, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 637 "QLParser.tab.cc" + break; + + case 13: // exp: exp LE exp +#line 116 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_LE, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 643 "QLParser.tab.cc" + break; + + case 14: // exp: exp GT exp +#line 117 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_GT, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 649 "QLParser.tab.cc" + break; + + case 15: // exp: exp GE exp +#line 118 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_GE, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 655 "QLParser.tab.cc" + break; + + case 16: // exp: exp EQ exp +#line 119 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_EQ, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 661 "QLParser.tab.cc" + break; + + case 17: // exp: exp NE exp +#line 120 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_NE, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 667 "QLParser.tab.cc" + break; + + case 18: // exp: exp BITAND exp +#line 121 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_BITAND, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 673 "QLParser.tab.cc" + break; + + case 19: // exp: exp BITXOR exp +#line 122 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_BITXOR, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 679 "QLParser.tab.cc" + break; + + case 20: // exp: exp BITOR exp +#line 123 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_BITOR, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 685 "QLParser.tab.cc" + break; + + case 21: // exp: exp AND exp +#line 124 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_AND, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 691 "QLParser.tab.cc" + break; + + case 22: // exp: exp OR exp +#line 125 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_OR, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 697 "QLParser.tab.cc" + break; + + case 23: // exp: exp NEQV exp +#line 126 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_NEQV, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 703 "QLParser.tab.cc" + break; + + case 24: // exp: exp EQV exp +#line 127 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_EQV, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 709 "QLParser.tab.cc" + break; + + case 25: // exp: exp QWE exp COLON exp +#line 128 "QLParser.yy" + { Expression colon = Expression (Expression::OP_COLON, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); + yylhs.value.as < Expression * > () = new Expression (Expression::OP_QWE, yystack_[4].value.as < Expression * > (), &colon); } +#line 716 "QLParser.tab.cc" + break; + + case 26: // exp: exp COMMA exp +#line 130 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_COMMA, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 722 "QLParser.tab.cc" + break; + + case 27: // exp: exp IN exp +#line 131 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_IN, yystack_[2].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 728 "QLParser.tab.cc" + break; + + case 28: // exp: exp SOME IN exp +#line 132 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_SOMEIN, yystack_[3].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 734 "QLParser.tab.cc" + break; + + case 29: // exp: exp ORDR IN exp +#line 133 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_ORDRIN, yystack_[3].value.as < Expression * > (), yystack_[0].value.as < Expression * > ()); } +#line 740 "QLParser.tab.cc" + break; + + case 30: // exp: term +#line 134 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (yystack_[0].value.as < Expression * > ()); } +#line 746 "QLParser.tab.cc" + break; + + case 31: // term: MINUS term +#line 136 "QLParser.yy" + { Expression num = Expression (Expression::OP_NUM, (uint64_t) 0); + yylhs.value.as < Expression * > () = new Expression (Expression::OP_MINUS, &num, yystack_[0].value.as < Expression * > ()); } +#line 753 "QLParser.tab.cc" + break; + + case 32: // term: NOT term +#line 138 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_NOT, yystack_[0].value.as < Expression * > (), NULL); } +#line 759 "QLParser.tab.cc" + break; + + case 33: // term: BITNOT term +#line 139 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_BITNOT, yystack_[0].value.as < Expression * > (), NULL); } +#line 765 "QLParser.tab.cc" + break; + + case 34: // term: "(" exp ")" +#line 140 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (yystack_[1].value.as < Expression * > ()); } +#line 771 "QLParser.tab.cc" + break; + + case 35: // term: FNAME "(" QSTR ")" +#line 141 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_FUNC, yystack_[3].value.as < Expression * > (), yystack_[1].value.as < Expression * > ()); } +#line 777 "QLParser.tab.cc" + break; + + case 36: // term: HASPROP "(" "name" ")" +#line 142 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_HASPROP, yystack_[1].value.as < Expression * > (), NULL); } +#line 783 "QLParser.tab.cc" + break; + + case 37: // term: JGROUP "(" QSTR ")" +#line 143 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_JAVA, yystack_[3].value.as < Expression * > (), yystack_[1].value.as < Expression * > ()); } +#line 789 "QLParser.tab.cc" + break; + + case 38: // term: JPARENT "(" QSTR ")" +#line 144 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_JAVA, yystack_[3].value.as < Expression * > (), yystack_[1].value.as < Expression * > ()); } +#line 795 "QLParser.tab.cc" + break; + + case 39: // term: FILEIOVFD "(" QSTR ")" +#line 145 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (Expression::OP_FILE, yystack_[3].value.as < Expression * > (), yystack_[1].value.as < Expression * > ()); } +#line 801 "QLParser.tab.cc" + break; + + case 40: // term: "number" +#line 146 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (yystack_[0].value.as < Expression * > ()); } +#line 807 "QLParser.tab.cc" + break; + + case 41: // term: "name" +#line 147 "QLParser.yy" + { yylhs.value.as < Expression * > () = new Expression (yystack_[0].value.as < Expression * > ()); } +#line 813 "QLParser.tab.cc" + break; + + +#line 817 "QLParser.tab.cc" + + default: + break; + } + } +#if YY_EXCEPTIONS + catch (const syntax_error& yyexc) + { + YYCDEBUG << "Caught exception: " << yyexc.what() << '\n'; + error (yyexc); + YYERROR; + } +#endif // YY_EXCEPTIONS + YY_SYMBOL_PRINT ("-> $$ =", yylhs); + yypop_ (yylen); + yylen = 0; + + // Shift the result of the reduction. + yypush_ (YY_NULLPTR, YY_MOVE (yylhs)); + } + goto yynewstate; + + + /*--------------------------------------. + | yyerrlab -- here on detecting error. | + `--------------------------------------*/ + yyerrlab: + // If not already recovering from an error, report this error. + if (!yyerrstatus_) + { + ++yynerrs_; + std::string msg = YY_("syntax error"); + error (YY_MOVE (msg)); + } + + + if (yyerrstatus_ == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + // Return failure if at end of input. + if (yyla.kind () == symbol_kind::S_YYEOF) + YYABORT; + else if (!yyla.empty ()) + { + yy_destroy_ ("Error: discarding", yyla); + yyla.clear (); + } + } + + // Else will try to reuse lookahead token after shifting the error token. + goto yyerrlab1; + + + /*---------------------------------------------------. + | yyerrorlab -- error raised explicitly by YYERROR. | + `---------------------------------------------------*/ + yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and + the label yyerrorlab therefore never appears in user code. */ + if (false) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + yypop_ (yylen); + yylen = 0; + YY_STACK_PRINT (); + goto yyerrlab1; + + + /*-------------------------------------------------------------. + | yyerrlab1 -- common code for both syntax error and YYERROR. | + `-------------------------------------------------------------*/ + yyerrlab1: + yyerrstatus_ = 3; // Each real token shifted decrements this. + // Pop stack until we find a state that shifts the error token. + for (;;) + { + yyn = yypact_[+yystack_[0].state]; + if (!yy_pact_value_is_default_ (yyn)) + { + yyn += symbol_kind::S_YYerror; + if (0 <= yyn && yyn <= yylast_ + && yycheck_[yyn] == symbol_kind::S_YYerror) + { + yyn = yytable_[yyn]; + if (0 < yyn) + break; + } + } + + // Pop the current state because it cannot handle the error token. + if (yystack_.size () == 1) + YYABORT; + + yy_destroy_ ("Error: popping", yystack_[0]); + yypop_ (); + YY_STACK_PRINT (); + } + { + stack_symbol_type error_token; + + + // Shift the error token. + error_token.state = state_type (yyn); + yypush_ ("Shifting", YY_MOVE (error_token)); + } + goto yynewstate; + + + /*-------------------------------------. + | yyacceptlab -- YYACCEPT comes here. | + `-------------------------------------*/ + yyacceptlab: + yyresult = 0; + goto yyreturn; + + + /*-----------------------------------. + | yyabortlab -- YYABORT comes here. | + `-----------------------------------*/ + yyabortlab: + yyresult = 1; + goto yyreturn; + + + /*-----------------------------------------------------. + | yyreturn -- parsing is finished, return the result. | + `-----------------------------------------------------*/ + yyreturn: + if (!yyla.empty ()) + yy_destroy_ ("Cleanup: discarding lookahead", yyla); + + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + yypop_ (yylen); + YY_STACK_PRINT (); + while (1 < yystack_.size ()) + { + yy_destroy_ ("Cleanup: popping", yystack_[0]); + yypop_ (); + } + + return yyresult; + } +#if YY_EXCEPTIONS + catch (...) + { + YYCDEBUG << "Exception caught: cleaning lookahead and stack\n"; + // Do not try to display the values of the reclaimed symbols, + // as their printers might throw an exception. + if (!yyla.empty ()) + yy_destroy_ (YY_NULLPTR, yyla); + + while (1 < yystack_.size ()) + { + yy_destroy_ (YY_NULLPTR, yystack_[0]); + yypop_ (); + } + throw; + } +#endif // YY_EXCEPTIONS + } + + void + Parser::error (const syntax_error& yyexc) + { + error (yyexc.what ()); + } + +#if YYDEBUG || 0 + const char * + Parser::symbol_name (symbol_kind_type yysymbol) + { + return yytname_[yysymbol]; + } +#endif // #if YYDEBUG || 0 + + + + + + const signed char Parser::yypact_ninf_ = -3; + + const signed char Parser::yytable_ninf_ = -1; + + const short + Parser::yypact_[] = + { + 0, 0, -3, -3, -2, 1, 8, 13, 14, 0, + 0, 0, 2, 142, -3, 50, 7, 15, 9, 11, + 12, -3, -3, -3, -3, 0, 6, 38, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -3, 21, 22, 48, 49, 51, 188, 0, 0, + 221, 96, 95, 95, 95, 95, 95, 95, 95, 141, + 141, 141, 141, 141, 141, 17, 17, 17, 17, 17, + 17, 17, 17, -3, -3, -3, -3, -3, 188, 188, + 0, 221 + }; + + const signed char + Parser::yydefact_[] = + { + 2, 0, 40, 41, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 30, 0, 0, 0, 0, 0, + 0, 31, 32, 33, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 34, 0, 0, 0, 0, 0, 27, 0, 0, + 26, 0, 21, 22, 24, 23, 18, 20, 19, 16, + 17, 12, 14, 13, 15, 10, 11, 8, 9, 5, + 6, 7, 4, 35, 36, 37, 38, 39, 28, 29, + 0, 25 + }; + + const signed char + Parser::yypgoto_[] = + { + -3, -3, -1, 4 + }; + + const signed char + Parser::yydefgoto_[] = + { + 0, 12, 13, 14 + }; + + const signed char + Parser::yytable_[] = + { + 15, 16, 24, 1, 17, 2, 3, 4, 5, 6, + 7, 18, 8, 21, 22, 23, 19, 20, 52, 58, + 54, 53, 55, 56, 57, 83, 84, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 9, 59, 85, 86, 51, 87, 0, 88, 89, 10, + 0, 11, 0, 25, 26, 27, 28, 0, 29, 0, + 0, 0, 30, 0, 31, 50, 32, 33, 34, 35, + 36, 0, 37, 0, 38, 0, 39, 0, 40, 91, + 41, 0, 42, 0, 43, 0, 44, 0, 45, 0, + 46, 0, 47, 0, 48, 0, 49, 0, 50, 25, + 26, 27, 28, 0, 29, 0, 90, 0, 30, 0, + 31, 0, 32, 33, 34, 35, 36, 37, 37, 38, + 38, 39, 39, 40, 40, 41, 41, 42, 42, 43, + 43, 44, 44, 45, 45, 46, 46, 47, 47, 48, + 48, 49, 49, 50, 50, 25, 26, 27, 28, 0, + 29, 0, 0, 0, 30, 0, 31, 0, 32, 33, + 34, 35, 36, -1, 37, -1, 38, -1, 39, -1, + 40, -1, 41, -1, 42, 43, 43, 44, 44, 45, + 45, 46, 46, 47, 47, 48, 48, 49, 49, 50, + 50, -1, -1, -1, 28, 0, 29, 0, 0, 0, + 30, 0, 31, 0, 32, 33, 34, 35, 36, 0, + 37, 0, 38, 0, 39, 0, 40, 0, 41, 0, + 42, 0, 43, 0, 44, 0, 45, 0, 46, 29, + 47, 0, 48, 30, 49, 31, 50, 32, 33, 34, + 35, 36, 0, 37, 0, 38, 0, 39, 0, 40, + 0, 41, 0, 42, 0, 43, 0, 44, 0, 45, + 0, 46, 0, 47, 0, 48, 0, 49, 0, 50 + }; + + const signed char + Parser::yycheck_[] = + { + 1, 3, 0, 3, 3, 5, 6, 7, 8, 9, + 10, 3, 12, 9, 10, 11, 3, 3, 11, 13, + 11, 6, 11, 11, 25, 4, 4, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 50, 13, 4, 4, 4, 4, -1, 58, 59, 59, + -1, 61, -1, 13, 14, 15, 16, -1, 18, -1, + -1, -1, 22, -1, 24, 58, 26, 27, 28, 29, + 30, -1, 32, -1, 34, -1, 36, -1, 38, 90, + 40, -1, 42, -1, 44, -1, 46, -1, 48, -1, + 50, -1, 52, -1, 54, -1, 56, -1, 58, 13, + 14, 15, 16, -1, 18, -1, 20, -1, 22, -1, + 24, -1, 26, 27, 28, 29, 30, 32, 32, 34, + 34, 36, 36, 38, 38, 40, 40, 42, 42, 44, + 44, 46, 46, 48, 48, 50, 50, 52, 52, 54, + 54, 56, 56, 58, 58, 13, 14, 15, 16, -1, + 18, -1, -1, -1, 22, -1, 24, -1, 26, 27, + 28, 29, 30, 32, 32, 34, 34, 36, 36, 38, + 38, 40, 40, 42, 42, 44, 44, 46, 46, 48, + 48, 50, 50, 52, 52, 54, 54, 56, 56, 58, + 58, 13, 14, 15, 16, -1, 18, -1, -1, -1, + 22, -1, 24, -1, 26, 27, 28, 29, 30, -1, + 32, -1, 34, -1, 36, -1, 38, -1, 40, -1, + 42, -1, 44, -1, 46, -1, 48, -1, 50, 18, + 52, -1, 54, 22, 56, 24, 58, 26, 27, 28, + 29, 30, -1, 32, -1, 34, -1, 36, -1, 38, + -1, 40, -1, 42, -1, 44, -1, 46, -1, 48, + -1, 50, -1, 52, -1, 54, -1, 56, -1, 58 + }; + + const signed char + Parser::yystos_[] = + { + 0, 3, 5, 6, 7, 8, 9, 10, 12, 50, + 59, 61, 64, 65, 66, 65, 3, 3, 3, 3, + 3, 66, 66, 66, 0, 13, 14, 15, 16, 18, + 22, 24, 26, 27, 28, 29, 30, 32, 34, 36, + 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, + 58, 4, 11, 6, 11, 11, 11, 65, 13, 13, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 4, 4, 4, 4, 4, 65, 65, + 20, 65 + }; + + const signed char + Parser::yyr1_[] = + { + 0, 63, 64, 64, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66 + }; + + const signed char + Parser::yyr2_[] = + { + 0, 2, 0, 1, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 5, 3, 3, 4, 4, + 1, 2, 2, 2, 3, 4, 4, 4, 4, 4, + 1, 1 + }; + + +#if YYDEBUG + // YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + // First, the terminals, then, starting at \a YYNTOKENS, nonterminals. + const char* + const Parser::yytname_[] = + { + "\"end of file\"", "error", "\"invalid token\"", "\"(\"", "\")\"", + "\"number\"", "\"name\"", "FNAME", "HASPROP", "JGROUP", "JPARENT", + "QSTR", "FILEIOVFD", "IN", "SOME", "ORDR", "COMMA", "\",\"", "QWE", + "\"?\"", "COLON", "\":\"", "AND", "\"&&\"", "OR", "\"|\"", "EQV", "NEQV", + "BITAND", "BITOR", "BITXOR", "\"^\"", "EQ", "\"=\"", "NE", "\"!=\"", + "LT", "\"<\"", "GT", "\">\"", "LE", "\"<=\"", "GE", "\">=\"", "LS", + "\"<<\"", "RS", "\">>\"", "ADD", "\"+\"", "MINUS", "\"-\"", "MUL", + "\"*\"", "DIV", "\"/\"", "REM", "\"%\"", "DEG", "NOT", "\"!\"", "BITNOT", + "\"~\"", "$accept", "S", "exp", "term", YY_NULLPTR + }; +#endif + + +#if YYDEBUG + const unsigned char + Parser::yyrline_[] = + { + 0, 104, 104, 105, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 130, 131, 132, 133, + 134, 136, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147 + }; + + void + Parser::yy_stack_print_ () const + { + *yycdebug_ << "Stack now"; + for (stack_type::const_iterator + i = yystack_.begin (), + i_end = yystack_.end (); + i != i_end; ++i) + *yycdebug_ << ' ' << int (i->state); + *yycdebug_ << '\n'; + } + + void + Parser::yy_reduce_print_ (int yyrule) const + { + int yylno = yyrline_[yyrule]; + int yynrhs = yyr2_[yyrule]; + // Print the symbols being reduced, and their result. + *yycdebug_ << "Reducing stack by rule " << yyrule - 1 + << " (line " << yylno << "):\n"; + // The symbols being reduced. + for (int yyi = 0; yyi < yynrhs; yyi++) + YY_SYMBOL_PRINT (" $" << yyi + 1 << " =", + yystack_[(yynrhs) - (yyi + 1)]); + } +#endif // YYDEBUG + + +#line 50 "QLParser.yy" +} // QL +#line 1210 "QLParser.tab.cc" + +#line 149 "QLParser.yy" + + +namespace QL +{ + static Parser::symbol_type + unget_ret (std::istream &in, char c, Parser::symbol_type tok) + { + in.putback (c); + return tok; + } + + static Expression * + processName (char *name) + { + int propID = dbeSession->getPropIdByName (name); + if (propID != PROP_NONE) + { + Expression *expr = new Expression (Expression::OP_NUM, (uint64_t) propID); + Expression *ret = new Expression (Expression::OP_NAME, expr); + delete expr; + return ret; + } + + // If a name is not statically known try user defined objects + Expression *expr = dbeSession->findObjDefByName (name); + if (expr != NULL) + return expr->copy(); + + throw Parser::syntax_error ("Name not found"); + } + + static Parser::symbol_type + yylex (QL::Result &result) + { + int base = 0; + int c; + + do + c = result.in.get (); + while (result.in && (c == ' ' || c == '\t')); + if (!result.in) + return Parser::make_YYEOF (); + + switch (c) + { + case '\n': return Parser::make_YYEOF (); + case '(': return Parser::make_LPAR () ; + case ')': return Parser::make_RPAR (); + case ',': return Parser::make_COMMA (); + case '%': return Parser::make_REM (); + case '/': return Parser::make_DIV (); + case '*': return Parser::make_MUL (); + case '-': return Parser::make_MINUS (); + case '+': return Parser::make_ADD (); + case '~': return Parser::make_BITNOT (); + case '^': return Parser::make_BITXOR (); + case '?': return Parser::make_QWE (); + case ':': return Parser::make_COLON (); + case '|': + c = result.in.get (); + if (c == '|') + return Parser::make_OR (); + else + return unget_ret (result.in, c, Parser::make_BITOR ()); + case '&': + c = result.in.get (); + if (c == '&') + return Parser::make_AND (); + else + return unget_ret (result.in, c, Parser::make_BITAND ()); + case '!': + c = result.in.get (); + if (c == '=') + return Parser::make_NE (); + else + return unget_ret (result.in, c, Parser::make_NOT ()); + case '=': + c = result.in.get (); + if (c == '=') + return Parser::make_EQ (); + else + throw Parser::syntax_error ("Syntax error after ="); + case '<': + c = result.in.get (); + if (c == '=') + return Parser::make_LE (); + else if (c == '<') + return Parser::make_LS (); + else + return unget_ret (result.in, c, Parser::make_LT ()); + case '>': + c = result.in.get (); + if (c == '=') + return Parser::make_GE (); + else if (c == '>') + return Parser::make_RS (); + else + return unget_ret (result.in, c, Parser::make_GT ()); + case '"': + { + int maxsz = 16; + char *str = (char *) malloc (maxsz); + char *ptr = str; + + for (;;) + { + c = result.in.get (); + if (!result.in) + { + free (str); + throw Parser::syntax_error ("Unclosed \""); + } + + switch (c) + { + case '"': + *ptr = (char)0; + // XXX omazur: need new string type + return Parser::make_QSTR (new Expression (Expression::OP_NUM, (uint64_t) str)); + case 0: + case '\n': + free (str); + throw Parser::syntax_error ("Multiline strings are not supported"); + default: + if (ptr - str >= maxsz) + { + size_t len = ptr - str; + maxsz = maxsz > 8192 ? maxsz + 8192 : maxsz * 2; + char *new_s = (char *) realloc (str, maxsz); + str = new_s; + ptr = str + len; + } + *ptr++ = c; + } + } + } + default: + if (c == '0') + { + base = 8; + c = result.in.get (); + if ( c == 'x' ) + { + base = 16; + c = result.in.get (); + } + } + else if (c >= '1' && c <='9') + base = 10; + + if (base) + { + uint64_t lval = 0; + for (;;) + { + int digit = -1; + switch (c) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + digit = c - '0'; + break; + case '8': case '9': + if (base > 8) + digit = c - '0'; + break; + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + if (base == 16) + digit = c - 'a' + 10; + break; + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + if (base == 16) + digit = c - 'A' + 10; + break; + } + if (digit == -1) + { + result.in.putback (c); + break; + } + lval = lval * base + digit; + c = result.in.get (); + } + return Parser::make_NUM (new Expression (Expression::OP_NUM, lval)); + } + + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) + { + char name[32]; // omazur XXX: accept any length + name[0] = (char)c; + for (size_t i = 1; i < sizeof (name); i++) + { + c = result.in.get (); + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || (c == '_')) + name[i] = c; + else + { + name[i] = (char)0; + result.in.putback (c); + break; + } + } + + if (strcasecmp (name, NTXT ("IN")) == 0) + return Parser::make_IN (); + else if (strcasecmp (name, NTXT ("SOME")) == 0) + return Parser::make_SOME (); + else if (strcasecmp (name, NTXT ("ORDERED")) == 0) + return Parser::make_ORDR (); + else if (strcasecmp (name, NTXT ("TRUE")) == 0) + return Parser::make_NUM (new Expression (Expression::OP_NUM, (uint64_t) 1)); + else if (strcasecmp (name, NTXT ("FALSE")) == 0) + return Parser::make_NUM (new Expression (Expression::OP_NUM, (uint64_t) 0)); + else if (strcasecmp (name, NTXT ("FNAME")) == 0) + return Parser::make_FNAME (new Expression (Expression::OP_NUM, Expression::FUNC_FNAME)); + else if (strcasecmp (name, NTXT ("HAS_PROP")) == 0) + return Parser::make_HASPROP (); + else if (strcasecmp (name, NTXT ("JGROUP")) == 0) + return Parser::make_JGROUP (new Expression (Expression::OP_NUM, Expression::JAVA_JGROUP)); + else if (strcasecmp (name, NTXT ("JPARENT")) == 0 ) + return Parser::make_JPARENT (new Expression (Expression::OP_NUM, Expression::JAVA_JPARENT)); + else if (strcasecmp (name, NTXT ("DNAME")) == 0) + return Parser::make_FNAME (new Expression (Expression::OP_NUM, Expression::FUNC_DNAME)); + else if (strcasecmp (name, NTXT ("FILEIOVFD")) == 0 ) + return Parser::make_FILEIOVFD (new Expression (Expression::OP_NUM, (uint64_t) 0)); + + return Parser::make_NAME (processName (name)); + } + + throw Parser::syntax_error ("Syntax error"); + } + } + void + Parser::error (const std::string &) + { + // do nothing for now + } +} + diff --git a/gprofng/src/QLParser.tab.hh b/gprofng/src/QLParser.tab.hh new file mode 100644 index 0000000..eaf2cb5 --- /dev/null +++ b/gprofng/src/QLParser.tab.hh @@ -0,0 +1,2038 @@ +// A Bison parser, made by GNU Bison 3.7.5. + +// Skeleton interface for Bison LALR(1) parsers in C++ + +// Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc. + +// 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 of the License, 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, see <http://www.gnu.org/licenses/>. + +// As a special exception, you may create a larger work that contains +// part or all of the Bison parser skeleton and distribute that work +// under terms of your choice, so long as that work isn't itself a +// parser generator using the skeleton or a modified version thereof +// as a parser skeleton. Alternatively, if you modify or redistribute +// the parser skeleton itself, you may (at your option) remove this +// special exception, which will cause the skeleton and the resulting +// Bison output files to be licensed under the GNU General Public +// License without this special exception. + +// This special exception was added by the Free Software Foundation in +// version 2.2 of Bison. + + +/** + ** \file QLParser.tab.hh + ** Define the QL::parser class. + */ + +// C++ LALR(1) parser skeleton written by Akim Demaille. + +// DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, +// especially those whose name start with YY_ or yy_. They are +// private implementation details that can be changed or removed. + +#ifndef YY_YY_QLPARSER_TAB_HH_INCLUDED +# define YY_YY_QLPARSER_TAB_HH_INCLUDED +// "%code requires" blocks. +#line 33 "QLParser.yy" + +#include "QLParser.h" +#include "DbeSession.h" +#include "Expression.h" +#include "Table.h" +#include "i18n.h" + +#line 57 "QLParser.tab.hh" + +# include <cassert> +# include <cstdlib> // std::abort +# include <iostream> +# include <stdexcept> +# include <string> +# include <vector> + +#if defined __cplusplus +# define YY_CPLUSPLUS __cplusplus +#else +# define YY_CPLUSPLUS 199711L +#endif + +// Support move semantics when possible. +#if 201103L <= YY_CPLUSPLUS +# define YY_MOVE std::move +# define YY_MOVE_OR_COPY move +# define YY_MOVE_REF(Type) Type&& +# define YY_RVREF(Type) Type&& +# define YY_COPY(Type) Type +#else +# define YY_MOVE +# define YY_MOVE_OR_COPY copy +# define YY_MOVE_REF(Type) Type& +# define YY_RVREF(Type) const Type& +# define YY_COPY(Type) const Type& +#endif + +// Support noexcept when possible. +#if 201103L <= YY_CPLUSPLUS +# define YY_NOEXCEPT noexcept +# define YY_NOTHROW +#else +# define YY_NOEXCEPT +# define YY_NOTHROW throw () +#endif + +// Support constexpr when possible. +#if 201703 <= YY_CPLUSPLUS +# define YY_CONSTEXPR constexpr +#else +# define YY_CONSTEXPR +#endif + +#include <typeinfo> +#ifndef YY_ASSERT +# include <cassert> +# define YY_ASSERT assert +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YY_USE(E) ((void) (E)) +#else +# define YY_USE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast<Type> (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +#line 50 "QLParser.yy" +namespace QL { +#line 192 "QLParser.tab.hh" + + + + + /// A Bison parser. + class Parser + { + public: +#ifndef YYSTYPE + /// A buffer to store and retrieve objects. + /// + /// Sort of a variant, but does not keep track of the nature + /// of the stored data, since that knowledge is available + /// via the current parser state. + class semantic_type + { + public: + /// Type of *this. + typedef semantic_type self_type; + + /// Empty construction. + semantic_type () YY_NOEXCEPT + : yybuffer_ () + , yytypeid_ (YY_NULLPTR) + {} + + /// Construct and fill. + template <typename T> + semantic_type (YY_RVREF (T) t) + : yytypeid_ (&typeid (T)) + { + YY_ASSERT (sizeof (T) <= size); + new (yyas_<T> ()) T (YY_MOVE (t)); + } + +#if 201103L <= YY_CPLUSPLUS + /// Non copyable. + semantic_type (const self_type&) = delete; + /// Non copyable. + self_type& operator= (const self_type&) = delete; +#endif + + /// Destruction, allowed only if empty. + ~semantic_type () YY_NOEXCEPT + { + YY_ASSERT (!yytypeid_); + } + +# if 201103L <= YY_CPLUSPLUS + /// Instantiate a \a T in here from \a t. + template <typename T, typename... U> + T& + emplace (U&&... u) + { + YY_ASSERT (!yytypeid_); + YY_ASSERT (sizeof (T) <= size); + yytypeid_ = & typeid (T); + return *new (yyas_<T> ()) T (std::forward <U>(u)...); + } +# else + /// Instantiate an empty \a T in here. + template <typename T> + T& + emplace () + { + YY_ASSERT (!yytypeid_); + YY_ASSERT (sizeof (T) <= size); + yytypeid_ = & typeid (T); + return *new (yyas_<T> ()) T (); + } + + /// Instantiate a \a T in here from \a t. + template <typename T> + T& + emplace (const T& t) + { + YY_ASSERT (!yytypeid_); + YY_ASSERT (sizeof (T) <= size); + yytypeid_ = & typeid (T); + return *new (yyas_<T> ()) T (t); + } +# endif + + /// Instantiate an empty \a T in here. + /// Obsolete, use emplace. + template <typename T> + T& + build () + { + return emplace<T> (); + } + + /// Instantiate a \a T in here from \a t. + /// Obsolete, use emplace. + template <typename T> + T& + build (const T& t) + { + return emplace<T> (t); + } + + /// Accessor to a built \a T. + template <typename T> + T& + as () YY_NOEXCEPT + { + YY_ASSERT (yytypeid_); + YY_ASSERT (*yytypeid_ == typeid (T)); + YY_ASSERT (sizeof (T) <= size); + return *yyas_<T> (); + } + + /// Const accessor to a built \a T (for %printer). + template <typename T> + const T& + as () const YY_NOEXCEPT + { + YY_ASSERT (yytypeid_); + YY_ASSERT (*yytypeid_ == typeid (T)); + YY_ASSERT (sizeof (T) <= size); + return *yyas_<T> (); + } + + /// Swap the content with \a that, of same type. + /// + /// Both variants must be built beforehand, because swapping the actual + /// data requires reading it (with as()), and this is not possible on + /// unconstructed variants: it would require some dynamic testing, which + /// should not be the variant's responsibility. + /// Swapping between built and (possibly) non-built is done with + /// self_type::move (). + template <typename T> + void + swap (self_type& that) YY_NOEXCEPT + { + YY_ASSERT (yytypeid_); + YY_ASSERT (*yytypeid_ == *that.yytypeid_); + std::swap (as<T> (), that.as<T> ()); + } + + /// Move the content of \a that to this. + /// + /// Destroys \a that. + template <typename T> + void + move (self_type& that) + { +# if 201103L <= YY_CPLUSPLUS + emplace<T> (std::move (that.as<T> ())); +# else + emplace<T> (); + swap<T> (that); +# endif + that.destroy<T> (); + } + +# if 201103L <= YY_CPLUSPLUS + /// Move the content of \a that to this. + template <typename T> + void + move (self_type&& that) + { + emplace<T> (std::move (that.as<T> ())); + that.destroy<T> (); + } +#endif + + /// Copy the content of \a that to this. + template <typename T> + void + copy (const self_type& that) + { + emplace<T> (that.as<T> ()); + } + + /// Destroy the stored \a T. + template <typename T> + void + destroy () + { + as<T> ().~T (); + yytypeid_ = YY_NULLPTR; + } + + private: +#if YY_CPLUSPLUS < 201103L + /// Non copyable. + semantic_type (const self_type&); + /// Non copyable. + self_type& operator= (const self_type&); +#endif + + /// Accessor to raw memory as \a T. + template <typename T> + T* + yyas_ () YY_NOEXCEPT + { + void *yyp = yybuffer_.yyraw; + return static_cast<T*> (yyp); + } + + /// Const accessor to raw memory as \a T. + template <typename T> + const T* + yyas_ () const YY_NOEXCEPT + { + const void *yyp = yybuffer_.yyraw; + return static_cast<const T*> (yyp); + } + + /// An auxiliary type to compute the largest semantic type. + union union_type + { + // "number" + // "name" + // FNAME + // JGROUP + // JPARENT + // QSTR + // FILEIOVFD + // exp + // term + char dummy1[sizeof (Expression *)]; + }; + + /// The size of the largest semantic type. + enum { size = sizeof (union_type) }; + + /// A buffer to store semantic values. + union + { + /// Strongest alignment constraints. + long double yyalign_me; + /// A buffer large enough to store any of the semantic values. + char yyraw[size]; + } yybuffer_; + + /// Whether the content is built: if defined, the name of the stored type. + const std::type_info *yytypeid_; + }; + +#else + typedef YYSTYPE semantic_type; +#endif + + /// Syntax errors thrown from user actions. + struct syntax_error : std::runtime_error + { + syntax_error (const std::string& m) + : std::runtime_error (m) + {} + + syntax_error (const syntax_error& s) + : std::runtime_error (s.what ()) + {} + + ~syntax_error () YY_NOEXCEPT YY_NOTHROW; + }; + + /// Token kinds. + struct token + { + enum token_kind_type + { + L_YYEMPTY = -2, + L_YYEOF = 0, // "end of file" + L_YYerror = 256, // error + L_YYUNDEF = 257, // "invalid token" + L_LPAR = 258, // "(" + L_RPAR = 259, // ")" + L_NUM = 260, // "number" + L_NAME = 261, // "name" + L_FNAME = 262, // FNAME + L_HASPROP = 263, // HASPROP + L_JGROUP = 264, // JGROUP + L_JPARENT = 265, // JPARENT + L_QSTR = 266, // QSTR + L_FILEIOVFD = 267, // FILEIOVFD + L_IN = 268, // IN + L_SOME = 269, // SOME + L_ORDR = 270, // ORDR + L_COMMA = 271, // COMMA + L_QWE = 273, // QWE + L_COLON = 275, // COLON + L_AND = 277, // AND + L_OR = 279, // OR + L_EQV = 281, // EQV + L_NEQV = 282, // NEQV + L_BITAND = 283, // BITAND + L_BITOR = 284, // BITOR + L_BITXOR = 285, // BITXOR + L_EQ = 287, // EQ + L_NE = 289, // NE + L_LT = 291, // LT + L_GT = 293, // GT + L_LE = 295, // LE + L_GE = 297, // GE + L_LS = 299, // LS + L_RS = 301, // RS + L_ADD = 303, // ADD + L_MINUS = 305, // MINUS + L_MUL = 307, // MUL + L_DIV = 309, // DIV + L_REM = 311, // REM + L_DEG = 313, // DEG + L_NOT = 314, // NOT + L_BITNOT = 316 // BITNOT + }; + /// Backward compatibility alias (Bison 3.6). + typedef token_kind_type yytokentype; + }; + + /// Token kind, as returned by yylex. + typedef token::yytokentype token_kind_type; + + /// Backward compatibility alias (Bison 3.6). + typedef token_kind_type token_type; + + /// Symbol kinds. + struct symbol_kind + { + enum symbol_kind_type + { + YYNTOKENS = 63, ///< Number of tokens. + S_YYEMPTY = -2, + S_YYEOF = 0, // "end of file" + S_YYerror = 1, // error + S_YYUNDEF = 2, // "invalid token" + S_LPAR = 3, // "(" + S_RPAR = 4, // ")" + S_NUM = 5, // "number" + S_NAME = 6, // "name" + S_FNAME = 7, // FNAME + S_HASPROP = 8, // HASPROP + S_JGROUP = 9, // JGROUP + S_JPARENT = 10, // JPARENT + S_QSTR = 11, // QSTR + S_FILEIOVFD = 12, // FILEIOVFD + S_IN = 13, // IN + S_SOME = 14, // SOME + S_ORDR = 15, // ORDR + S_COMMA = 16, // COMMA + S_17_ = 17, // "," + S_QWE = 18, // QWE + S_19_ = 19, // "?" + S_COLON = 20, // COLON + S_21_ = 21, // ":" + S_AND = 22, // AND + S_23_ = 23, // "&&" + S_OR = 24, // OR + S_25_ = 25, // "|" + S_EQV = 26, // EQV + S_NEQV = 27, // NEQV + S_BITAND = 28, // BITAND + S_BITOR = 29, // BITOR + S_BITXOR = 30, // BITXOR + S_31_ = 31, // "^" + S_EQ = 32, // EQ + S_33_ = 33, // "=" + S_NE = 34, // NE + S_35_ = 35, // "!=" + S_LT = 36, // LT + S_37_ = 37, // "<" + S_GT = 38, // GT + S_39_ = 39, // ">" + S_LE = 40, // LE + S_41_ = 41, // "<=" + S_GE = 42, // GE + S_43_ = 43, // ">=" + S_LS = 44, // LS + S_45_ = 45, // "<<" + S_RS = 46, // RS + S_47_ = 47, // ">>" + S_ADD = 48, // ADD + S_49_ = 49, // "+" + S_MINUS = 50, // MINUS + S_51_ = 51, // "-" + S_MUL = 52, // MUL + S_53_ = 53, // "*" + S_DIV = 54, // DIV + S_55_ = 55, // "/" + S_REM = 56, // REM + S_57_ = 57, // "%" + S_DEG = 58, // DEG + S_NOT = 59, // NOT + S_60_ = 60, // "!" + S_BITNOT = 61, // BITNOT + S_62_ = 62, // "~" + S_YYACCEPT = 63, // $accept + S_S = 64, // S + S_exp = 65, // exp + S_term = 66 // term + }; + }; + + /// (Internal) symbol kind. + typedef symbol_kind::symbol_kind_type symbol_kind_type; + + /// The number of tokens. + static const symbol_kind_type YYNTOKENS = symbol_kind::YYNTOKENS; + + /// A complete symbol. + /// + /// Expects its Base type to provide access to the symbol kind + /// via kind (). + /// + /// Provide access to semantic value. + template <typename Base> + struct basic_symbol : Base + { + /// Alias to Base. + typedef Base super_type; + + /// Default constructor. + basic_symbol () + : value () + {} + +#if 201103L <= YY_CPLUSPLUS + /// Move constructor. + basic_symbol (basic_symbol&& that) + : Base (std::move (that)) + , value () + { + switch (this->kind ()) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + value.move< Expression * > (std::move (that.value)); + break; + + default: + break; + } + + } +#endif + + /// Copy constructor. + basic_symbol (const basic_symbol& that); + + /// Constructors for typed symbols. +#if 201103L <= YY_CPLUSPLUS + basic_symbol (typename Base::kind_type t) + : Base (t) + {} +#else + basic_symbol (typename Base::kind_type t) + : Base (t) + {} +#endif + +#if 201103L <= YY_CPLUSPLUS + basic_symbol (typename Base::kind_type t, Expression *&& v) + : Base (t) + , value (std::move (v)) + {} +#else + basic_symbol (typename Base::kind_type t, const Expression *& v) + : Base (t) + , value (v) + {} +#endif + + /// Destroy the symbol. + ~basic_symbol () + { + clear (); + } + + /// Destroy contents, and record that is empty. + void clear () YY_NOEXCEPT + { + // User destructor. + symbol_kind_type yykind = this->kind (); + basic_symbol<Base>& yysym = *this; + (void) yysym; + switch (yykind) + { + case symbol_kind::S_NUM: // "number" +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 682 "QLParser.tab.hh" + break; + + case symbol_kind::S_NAME: // "name" +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 688 "QLParser.tab.hh" + break; + + case symbol_kind::S_FNAME: // FNAME +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 694 "QLParser.tab.hh" + break; + + case symbol_kind::S_JGROUP: // JGROUP +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 700 "QLParser.tab.hh" + break; + + case symbol_kind::S_JPARENT: // JPARENT +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 706 "QLParser.tab.hh" + break; + + case symbol_kind::S_QSTR: // QSTR +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 712 "QLParser.tab.hh" + break; + + case symbol_kind::S_FILEIOVFD: // FILEIOVFD +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 718 "QLParser.tab.hh" + break; + + case symbol_kind::S_exp: // exp +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 724 "QLParser.tab.hh" + break; + + case symbol_kind::S_term: // term +#line 100 "QLParser.yy" + { delete yysym.value.template as < Expression * > (); } +#line 730 "QLParser.tab.hh" + break; + + default: + break; + } + + // Value type destructor. +switch (yykind) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + value.template destroy< Expression * > (); + break; + + default: + break; + } + + Base::clear (); + } + +#if YYDEBUG || 0 + /// The user-facing name of this symbol. + const char *name () const YY_NOEXCEPT + { + return Parser::symbol_name (this->kind ()); + } +#endif // #if YYDEBUG || 0 + + + /// Backward compatibility (Bison 3.6). + symbol_kind_type type_get () const YY_NOEXCEPT; + + /// Whether empty. + bool empty () const YY_NOEXCEPT; + + /// Destructive move, \a s is emptied into this. + void move (basic_symbol& s); + + /// The semantic value. + semantic_type value; + + private: +#if YY_CPLUSPLUS < 201103L + /// Assignment operator. + basic_symbol& operator= (const basic_symbol& that); +#endif + }; + + /// Type access provider for token (enum) based symbols. + struct by_kind + { + /// Default constructor. + by_kind (); + +#if 201103L <= YY_CPLUSPLUS + /// Move constructor. + by_kind (by_kind&& that); +#endif + + /// Copy constructor. + by_kind (const by_kind& that); + + /// The symbol kind as needed by the constructor. + typedef token_kind_type kind_type; + + /// Constructor from (external) token numbers. + by_kind (kind_type t); + + /// Record that this symbol is empty. + void clear () YY_NOEXCEPT; + + /// Steal the symbol kind from \a that. + void move (by_kind& that); + + /// The (internal) type number (corresponding to \a type). + /// \a empty when empty. + symbol_kind_type kind () const YY_NOEXCEPT; + + /// Backward compatibility (Bison 3.6). + symbol_kind_type type_get () const YY_NOEXCEPT; + + /// The symbol kind. + /// \a S_YYEMPTY when empty. + symbol_kind_type kind_; + }; + + /// Backward compatibility for a private implementation detail (Bison 3.6). + typedef by_kind by_type; + + /// "External" symbols: returned by the scanner. + struct symbol_type : basic_symbol<by_kind> + { + /// Superclass. + typedef basic_symbol<by_kind> super_type; + + /// Empty symbol. + symbol_type () {} + + /// Constructor for valueless symbols, and symbols from each type. +#if 201103L <= YY_CPLUSPLUS + symbol_type (int tok) + : super_type(token_type (tok)) +#else + symbol_type (int tok) + : super_type(token_type (tok)) +#endif + { + YY_ASSERT (tok == token::L_YYEOF + || (token::L_YYerror <= tok && tok <= token::L_RPAR) + || tok == token::L_HASPROP + || (token::L_IN <= tok && tok <= 317)); + } +#if 201103L <= YY_CPLUSPLUS + symbol_type (int tok, Expression * v) + : super_type(token_type (tok), std::move (v)) +#else + symbol_type (int tok, const Expression *& v) + : super_type(token_type (tok), v) +#endif + { + YY_ASSERT ((token::L_NUM <= tok && tok <= token::L_FNAME) + || (token::L_JGROUP <= tok && tok <= token::L_FILEIOVFD)); + } + }; + + /// Build a parser object. + Parser (QL::Result &result_yyarg); + virtual ~Parser (); + +#if 201103L <= YY_CPLUSPLUS + /// Non copyable. + Parser (const Parser&) = delete; + /// Non copyable. + Parser& operator= (const Parser&) = delete; +#endif + + /// Parse. An alias for parse (). + /// \returns 0 iff parsing succeeded. + int operator() (); + + /// Parse. + /// \returns 0 iff parsing succeeded. + virtual int parse (); + +#if YYDEBUG + /// The current debugging stream. + std::ostream& debug_stream () const YY_ATTRIBUTE_PURE; + /// Set the current debugging stream. + void set_debug_stream (std::ostream &); + + /// Type for debugging levels. + typedef int debug_level_type; + /// The current debugging level. + debug_level_type debug_level () const YY_ATTRIBUTE_PURE; + /// Set the current debugging level. + void set_debug_level (debug_level_type l); +#endif + + /// Report a syntax error. + /// \param msg a description of the syntax error. + virtual void error (const std::string& msg); + + /// Report a syntax error. + void error (const syntax_error& err); + +#if YYDEBUG || 0 + /// The user-facing name of the symbol whose (internal) number is + /// YYSYMBOL. No bounds checking. + static const char *symbol_name (symbol_kind_type yysymbol); +#endif // #if YYDEBUG || 0 + + + // Implementation of make_symbol for each symbol type. +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_YYEOF () + { + return symbol_type (token::L_YYEOF); + } +#else + static + symbol_type + make_YYEOF () + { + return symbol_type (token::L_YYEOF); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_YYerror () + { + return symbol_type (token::L_YYerror); + } +#else + static + symbol_type + make_YYerror () + { + return symbol_type (token::L_YYerror); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_YYUNDEF () + { + return symbol_type (token::L_YYUNDEF); + } +#else + static + symbol_type + make_YYUNDEF () + { + return symbol_type (token::L_YYUNDEF); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_LPAR () + { + return symbol_type (token::L_LPAR); + } +#else + static + symbol_type + make_LPAR () + { + return symbol_type (token::L_LPAR); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_RPAR () + { + return symbol_type (token::L_RPAR); + } +#else + static + symbol_type + make_RPAR () + { + return symbol_type (token::L_RPAR); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_NUM (Expression * v) + { + return symbol_type (token::L_NUM, std::move (v)); + } +#else + static + symbol_type + make_NUM (const Expression *& v) + { + return symbol_type (token::L_NUM, v); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_NAME (Expression * v) + { + return symbol_type (token::L_NAME, std::move (v)); + } +#else + static + symbol_type + make_NAME (const Expression *& v) + { + return symbol_type (token::L_NAME, v); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_FNAME (Expression * v) + { + return symbol_type (token::L_FNAME, std::move (v)); + } +#else + static + symbol_type + make_FNAME (const Expression *& v) + { + return symbol_type (token::L_FNAME, v); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_HASPROP () + { + return symbol_type (token::L_HASPROP); + } +#else + static + symbol_type + make_HASPROP () + { + return symbol_type (token::L_HASPROP); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_JGROUP (Expression * v) + { + return symbol_type (token::L_JGROUP, std::move (v)); + } +#else + static + symbol_type + make_JGROUP (const Expression *& v) + { + return symbol_type (token::L_JGROUP, v); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_JPARENT (Expression * v) + { + return symbol_type (token::L_JPARENT, std::move (v)); + } +#else + static + symbol_type + make_JPARENT (const Expression *& v) + { + return symbol_type (token::L_JPARENT, v); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_QSTR (Expression * v) + { + return symbol_type (token::L_QSTR, std::move (v)); + } +#else + static + symbol_type + make_QSTR (const Expression *& v) + { + return symbol_type (token::L_QSTR, v); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_FILEIOVFD (Expression * v) + { + return symbol_type (token::L_FILEIOVFD, std::move (v)); + } +#else + static + symbol_type + make_FILEIOVFD (const Expression *& v) + { + return symbol_type (token::L_FILEIOVFD, v); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_IN () + { + return symbol_type (token::L_IN); + } +#else + static + symbol_type + make_IN () + { + return symbol_type (token::L_IN); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_SOME () + { + return symbol_type (token::L_SOME); + } +#else + static + symbol_type + make_SOME () + { + return symbol_type (token::L_SOME); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_ORDR () + { + return symbol_type (token::L_ORDR); + } +#else + static + symbol_type + make_ORDR () + { + return symbol_type (token::L_ORDR); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_COMMA () + { + return symbol_type (token::L_COMMA); + } +#else + static + symbol_type + make_COMMA () + { + return symbol_type (token::L_COMMA); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_QWE () + { + return symbol_type (token::L_QWE); + } +#else + static + symbol_type + make_QWE () + { + return symbol_type (token::L_QWE); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_COLON () + { + return symbol_type (token::L_COLON); + } +#else + static + symbol_type + make_COLON () + { + return symbol_type (token::L_COLON); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_AND () + { + return symbol_type (token::L_AND); + } +#else + static + symbol_type + make_AND () + { + return symbol_type (token::L_AND); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_OR () + { + return symbol_type (token::L_OR); + } +#else + static + symbol_type + make_OR () + { + return symbol_type (token::L_OR); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_EQV () + { + return symbol_type (token::L_EQV); + } +#else + static + symbol_type + make_EQV () + { + return symbol_type (token::L_EQV); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_NEQV () + { + return symbol_type (token::L_NEQV); + } +#else + static + symbol_type + make_NEQV () + { + return symbol_type (token::L_NEQV); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_BITAND () + { + return symbol_type (token::L_BITAND); + } +#else + static + symbol_type + make_BITAND () + { + return symbol_type (token::L_BITAND); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_BITOR () + { + return symbol_type (token::L_BITOR); + } +#else + static + symbol_type + make_BITOR () + { + return symbol_type (token::L_BITOR); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_BITXOR () + { + return symbol_type (token::L_BITXOR); + } +#else + static + symbol_type + make_BITXOR () + { + return symbol_type (token::L_BITXOR); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_EQ () + { + return symbol_type (token::L_EQ); + } +#else + static + symbol_type + make_EQ () + { + return symbol_type (token::L_EQ); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_NE () + { + return symbol_type (token::L_NE); + } +#else + static + symbol_type + make_NE () + { + return symbol_type (token::L_NE); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_LT () + { + return symbol_type (token::L_LT); + } +#else + static + symbol_type + make_LT () + { + return symbol_type (token::L_LT); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_GT () + { + return symbol_type (token::L_GT); + } +#else + static + symbol_type + make_GT () + { + return symbol_type (token::L_GT); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_LE () + { + return symbol_type (token::L_LE); + } +#else + static + symbol_type + make_LE () + { + return symbol_type (token::L_LE); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_GE () + { + return symbol_type (token::L_GE); + } +#else + static + symbol_type + make_GE () + { + return symbol_type (token::L_GE); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_LS () + { + return symbol_type (token::L_LS); + } +#else + static + symbol_type + make_LS () + { + return symbol_type (token::L_LS); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_RS () + { + return symbol_type (token::L_RS); + } +#else + static + symbol_type + make_RS () + { + return symbol_type (token::L_RS); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_ADD () + { + return symbol_type (token::L_ADD); + } +#else + static + symbol_type + make_ADD () + { + return symbol_type (token::L_ADD); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_MINUS () + { + return symbol_type (token::L_MINUS); + } +#else + static + symbol_type + make_MINUS () + { + return symbol_type (token::L_MINUS); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_MUL () + { + return symbol_type (token::L_MUL); + } +#else + static + symbol_type + make_MUL () + { + return symbol_type (token::L_MUL); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_DIV () + { + return symbol_type (token::L_DIV); + } +#else + static + symbol_type + make_DIV () + { + return symbol_type (token::L_DIV); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_REM () + { + return symbol_type (token::L_REM); + } +#else + static + symbol_type + make_REM () + { + return symbol_type (token::L_REM); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_DEG () + { + return symbol_type (token::L_DEG); + } +#else + static + symbol_type + make_DEG () + { + return symbol_type (token::L_DEG); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_NOT () + { + return symbol_type (token::L_NOT); + } +#else + static + symbol_type + make_NOT () + { + return symbol_type (token::L_NOT); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_BITNOT () + { + return symbol_type (token::L_BITNOT); + } +#else + static + symbol_type + make_BITNOT () + { + return symbol_type (token::L_BITNOT); + } +#endif + + + private: +#if YY_CPLUSPLUS < 201103L + /// Non copyable. + Parser (const Parser&); + /// Non copyable. + Parser& operator= (const Parser&); +#endif + + + /// Stored state numbers (used for stacks). + typedef signed char state_type; + + /// Compute post-reduction state. + /// \param yystate the current state + /// \param yysym the nonterminal to push on the stack + static state_type yy_lr_goto_state_ (state_type yystate, int yysym); + + /// Whether the given \c yypact_ value indicates a defaulted state. + /// \param yyvalue the value to check + static bool yy_pact_value_is_default_ (int yyvalue); + + /// Whether the given \c yytable_ value indicates a syntax error. + /// \param yyvalue the value to check + static bool yy_table_value_is_error_ (int yyvalue); + + static const signed char yypact_ninf_; + static const signed char yytable_ninf_; + + /// Convert a scanner token kind \a t to a symbol kind. + /// In theory \a t should be a token_kind_type, but character literals + /// are valid, yet not members of the token_type enum. + static symbol_kind_type yytranslate_ (int t); + +#if YYDEBUG || 0 + /// For a symbol, its name in clear. + static const char* const yytname_[]; +#endif // #if YYDEBUG || 0 + + + // Tables. + // YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + // STATE-NUM. + static const short yypact_[]; + + // YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + // Performed when YYTABLE does not specify something else to do. Zero + // means the default is an error. + static const signed char yydefact_[]; + + // YYPGOTO[NTERM-NUM]. + static const signed char yypgoto_[]; + + // YYDEFGOTO[NTERM-NUM]. + static const signed char yydefgoto_[]; + + // YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + // positive, shift that token. If negative, reduce the rule whose + // number is the opposite. If YYTABLE_NINF, syntax error. + static const signed char yytable_[]; + + static const signed char yycheck_[]; + + // YYSTOS[STATE-NUM] -- The (internal number of the) accessing + // symbol of state STATE-NUM. + static const signed char yystos_[]; + + // YYR1[YYN] -- Symbol number of symbol that rule YYN derives. + static const signed char yyr1_[]; + + // YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. + static const signed char yyr2_[]; + + +#if YYDEBUG + // YYRLINE[YYN] -- Source line where rule number YYN was defined. + static const unsigned char yyrline_[]; + /// Report on the debug stream that the rule \a r is going to be reduced. + virtual void yy_reduce_print_ (int r) const; + /// Print the state stack on the debug stream. + virtual void yy_stack_print_ () const; + + /// Debugging level. + int yydebug_; + /// Debug stream. + std::ostream* yycdebug_; + + /// \brief Display a symbol kind, value and location. + /// \param yyo The output stream. + /// \param yysym The symbol. + template <typename Base> + void yy_print_ (std::ostream& yyo, const basic_symbol<Base>& yysym) const; +#endif + + /// \brief Reclaim the memory associated to a symbol. + /// \param yymsg Why this token is reclaimed. + /// If null, print nothing. + /// \param yysym The symbol. + template <typename Base> + void yy_destroy_ (const char* yymsg, basic_symbol<Base>& yysym) const; + + private: + /// Type access provider for state based symbols. + struct by_state + { + /// Default constructor. + by_state () YY_NOEXCEPT; + + /// The symbol kind as needed by the constructor. + typedef state_type kind_type; + + /// Constructor. + by_state (kind_type s) YY_NOEXCEPT; + + /// Copy constructor. + by_state (const by_state& that) YY_NOEXCEPT; + + /// Record that this symbol is empty. + void clear () YY_NOEXCEPT; + + /// Steal the symbol kind from \a that. + void move (by_state& that); + + /// The symbol kind (corresponding to \a state). + /// \a symbol_kind::S_YYEMPTY when empty. + symbol_kind_type kind () const YY_NOEXCEPT; + + /// The state number used to denote an empty symbol. + /// We use the initial state, as it does not have a value. + enum { empty_state = 0 }; + + /// The state. + /// \a empty when empty. + state_type state; + }; + + /// "Internal" symbol: element of the stack. + struct stack_symbol_type : basic_symbol<by_state> + { + /// Superclass. + typedef basic_symbol<by_state> super_type; + /// Construct an empty symbol. + stack_symbol_type (); + /// Move or copy construction. + stack_symbol_type (YY_RVREF (stack_symbol_type) that); + /// Steal the contents from \a sym to build this. + stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) sym); +#if YY_CPLUSPLUS < 201103L + /// Assignment, needed by push_back by some old implementations. + /// Moves the contents of that. + stack_symbol_type& operator= (stack_symbol_type& that); + + /// Assignment, needed by push_back by other implementations. + /// Needed by some other old implementations. + stack_symbol_type& operator= (const stack_symbol_type& that); +#endif + }; + + /// A stack with random access from its top. + template <typename T, typename S = std::vector<T> > + class stack + { + public: + // Hide our reversed order. + typedef typename S::iterator iterator; + typedef typename S::const_iterator const_iterator; + typedef typename S::size_type size_type; + typedef typename std::ptrdiff_t index_type; + + stack (size_type n = 200) + : seq_ (n) + {} + +#if 201103L <= YY_CPLUSPLUS + /// Non copyable. + stack (const stack&) = delete; + /// Non copyable. + stack& operator= (const stack&) = delete; +#endif + + /// Random access. + /// + /// Index 0 returns the topmost element. + const T& + operator[] (index_type i) const + { + return seq_[size_type (size () - 1 - i)]; + } + + /// Random access. + /// + /// Index 0 returns the topmost element. + T& + operator[] (index_type i) + { + return seq_[size_type (size () - 1 - i)]; + } + + /// Steal the contents of \a t. + /// + /// Close to move-semantics. + void + push (YY_MOVE_REF (T) t) + { + seq_.push_back (T ()); + operator[] (0).move (t); + } + + /// Pop elements from the stack. + void + pop (std::ptrdiff_t n = 1) YY_NOEXCEPT + { + for (; 0 < n; --n) + seq_.pop_back (); + } + + /// Pop all elements from the stack. + void + clear () YY_NOEXCEPT + { + seq_.clear (); + } + + /// Number of elements on the stack. + index_type + size () const YY_NOEXCEPT + { + return index_type (seq_.size ()); + } + + /// Iterator on top of the stack (going downwards). + const_iterator + begin () const YY_NOEXCEPT + { + return seq_.begin (); + } + + /// Bottom of the stack. + const_iterator + end () const YY_NOEXCEPT + { + return seq_.end (); + } + + /// Present a slice of the top of a stack. + class slice + { + public: + slice (const stack& stack, index_type range) + : stack_ (stack) + , range_ (range) + {} + + const T& + operator[] (index_type i) const + { + return stack_[range_ - i]; + } + + private: + const stack& stack_; + index_type range_; + }; + + private: +#if YY_CPLUSPLUS < 201103L + /// Non copyable. + stack (const stack&); + /// Non copyable. + stack& operator= (const stack&); +#endif + /// The wrapped container. + S seq_; + }; + + + /// Stack type. + typedef stack<stack_symbol_type> stack_type; + + /// The stack. + stack_type yystack_; + + /// Push a new state on the stack. + /// \param m a debug message to display + /// if null, no trace is output. + /// \param sym the symbol + /// \warning the contents of \a s.value is stolen. + void yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym); + + /// Push a new look ahead token on the state on the stack. + /// \param m a debug message to display + /// if null, no trace is output. + /// \param s the state + /// \param sym the symbol (for its value and location). + /// \warning the contents of \a sym.value is stolen. + void yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym); + + /// Pop \a n symbols from the stack. + void yypop_ (int n = 1); + + /// Constants. + enum + { + yylast_ = 279, ///< Last index in yytable_. + yynnts_ = 4, ///< Number of nonterminal symbols. + yyfinal_ = 24 ///< Termination state number. + }; + + + // User arguments. + QL::Result &result; + + }; + + inline + Parser::symbol_kind_type + Parser::yytranslate_ (int t) + { + // YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to + // TOKEN-NUM as returned by yylex. + static + const signed char + translate_table[] = + { + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62 + }; + // Last valid token kind. + const int code_max = 317; + + if (t <= 0) + return symbol_kind::S_YYEOF; + else if (t <= code_max) + return YY_CAST (symbol_kind_type, translate_table[t]); + else + return symbol_kind::S_YYUNDEF; + } + + // basic_symbol. + template <typename Base> + Parser::basic_symbol<Base>::basic_symbol (const basic_symbol& that) + : Base (that) + , value () + { + switch (this->kind ()) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + value.copy< Expression * > (YY_MOVE (that.value)); + break; + + default: + break; + } + + } + + + + template <typename Base> + Parser::symbol_kind_type + Parser::basic_symbol<Base>::type_get () const YY_NOEXCEPT + { + return this->kind (); + } + + template <typename Base> + bool + Parser::basic_symbol<Base>::empty () const YY_NOEXCEPT + { + return this->kind () == symbol_kind::S_YYEMPTY; + } + + template <typename Base> + void + Parser::basic_symbol<Base>::move (basic_symbol& s) + { + super_type::move (s); + switch (this->kind ()) + { + case symbol_kind::S_NUM: // "number" + case symbol_kind::S_NAME: // "name" + case symbol_kind::S_FNAME: // FNAME + case symbol_kind::S_JGROUP: // JGROUP + case symbol_kind::S_JPARENT: // JPARENT + case symbol_kind::S_QSTR: // QSTR + case symbol_kind::S_FILEIOVFD: // FILEIOVFD + case symbol_kind::S_exp: // exp + case symbol_kind::S_term: // term + value.move< Expression * > (YY_MOVE (s.value)); + break; + + default: + break; + } + + } + + // by_kind. + inline + Parser::by_kind::by_kind () + : kind_ (symbol_kind::S_YYEMPTY) + {} + +#if 201103L <= YY_CPLUSPLUS + inline + Parser::by_kind::by_kind (by_kind&& that) + : kind_ (that.kind_) + { + that.clear (); + } +#endif + + inline + Parser::by_kind::by_kind (const by_kind& that) + : kind_ (that.kind_) + {} + + inline + Parser::by_kind::by_kind (token_kind_type t) + : kind_ (yytranslate_ (t)) + {} + + inline + void + Parser::by_kind::clear () YY_NOEXCEPT + { + kind_ = symbol_kind::S_YYEMPTY; + } + + inline + void + Parser::by_kind::move (by_kind& that) + { + kind_ = that.kind_; + that.clear (); + } + + inline + Parser::symbol_kind_type + Parser::by_kind::kind () const YY_NOEXCEPT + { + return kind_; + } + + inline + Parser::symbol_kind_type + Parser::by_kind::type_get () const YY_NOEXCEPT + { + return this->kind (); + } + +#line 50 "QLParser.yy" +} // QL +#line 2034 "QLParser.tab.hh" + + + + +#endif // !YY_YY_QLPARSER_TAB_HH_INCLUDED diff --git a/gprofng/src/QLParser.yy b/gprofng/src/QLParser.yy new file mode 100644 index 0000000..689d0e1 --- /dev/null +++ b/gprofng/src/QLParser.yy @@ -0,0 +1,390 @@ +/* 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. */ + +// To rebuild QLParser.tab.cc and QLParser.tab.hh, use bison 3.6 or newer: +// cd gprofng/src && bison QLParser.yy + +// For "api.parser.class" +%require "3.3" +%language "C++" + +%code top { +#include <stdio.h> +#include <string.h> +#include <string> +} +%code requires { +#include "QLParser.h" +#include "DbeSession.h" +#include "Expression.h" +#include "Table.h" +#include "i18n.h" +} + +%code +{ +namespace QL +{ + static QL::Parser::symbol_type yylex (QL::Result &result); +} +} + +%defines +%define api.namespace {QL} +%define api.parser.class {Parser} +%define api.token.constructor +%define api.value.type variant +// Later: api.value.automove +%define api.token.prefix {L_} +%define parse.assert +%param {QL::Result &result} + +%start S + +%token LPAR "(" + RPAR ")" + NUM "number" + NAME "name" + FNAME + HASPROP + JGROUP + JPARENT + QSTR + FILEIOVFD + +%nonassoc IN SOME ORDR +%left COMMA "," +%right QWE "?" + COLON ":" +%left AND "&&" + OR "|" + EQV NEQV + BITAND BITOR + BITXOR "^" +%nonassoc EQ "=" + NE "!=" + LT "<" + GT ">" + LE "<=" + GE ">=" +%left LS "<<" + RS ">>" + ADD "+" + MINUS "-" + MUL "*" + DIV "/" + REM "%" +%right DEG + NOT "!" + BITNOT "~" + +%type <Expression *> QSTR NUM NAME FNAME JGROUP JPARENT FILEIOVFD exp term + +%destructor { delete $$; } <*>; + +%% + +S: /* empty */ { result.out = new Expression (Expression::OP_NUM, (uint64_t) 1); } +| exp { result.out = new Expression ($1); } + +exp: exp DEG exp { $$ = new Expression (Expression::OP_DEG, $1, $3); } /* dead? */ + | exp MUL exp { $$ = new Expression (Expression::OP_MUL, $1, $3); } + | exp DIV exp { $$ = new Expression (Expression::OP_DIV, $1, $3); } + | exp REM exp { $$ = new Expression (Expression::OP_REM, $1, $3); } + | exp ADD exp { $$ = new Expression (Expression::OP_ADD, $1, $3); } + | exp MINUS exp { $$ = new Expression (Expression::OP_MINUS, $1, $3); } + | exp LS exp { $$ = new Expression (Expression::OP_LS, $1, $3); } + | exp RS exp { $$ = new Expression (Expression::OP_RS, $1, $3); } + | exp LT exp { $$ = new Expression (Expression::OP_LT, $1, $3); } + | exp LE exp { $$ = new Expression (Expression::OP_LE, $1, $3); } + | exp GT exp { $$ = new Expression (Expression::OP_GT, $1, $3); } + | exp GE exp { $$ = new Expression (Expression::OP_GE, $1, $3); } + | exp EQ exp { $$ = new Expression (Expression::OP_EQ, $1, $3); } + | exp NE exp { $$ = new Expression (Expression::OP_NE, $1, $3); } + | exp BITAND exp { $$ = new Expression (Expression::OP_BITAND, $1, $3); } + | exp BITXOR exp { $$ = new Expression (Expression::OP_BITXOR, $1, $3); } + | exp BITOR exp { $$ = new Expression (Expression::OP_BITOR, $1, $3); } + | exp AND exp { $$ = new Expression (Expression::OP_AND, $1, $3); } + | exp OR exp { $$ = new Expression (Expression::OP_OR, $1, $3); } + | exp NEQV exp { $$ = new Expression (Expression::OP_NEQV, $1, $3); } /* dead? */ + | exp EQV exp { $$ = new Expression (Expression::OP_EQV, $1, $3); } /* dead? */ + | exp QWE exp COLON exp { Expression colon = Expression (Expression::OP_COLON, $3, $5); + $$ = new Expression (Expression::OP_QWE, $1, &colon); } + | exp COMMA exp { $$ = new Expression (Expression::OP_COMMA, $1, $3); } + | exp IN exp { $$ = new Expression (Expression::OP_IN, $1, $3); } + | exp SOME IN exp { $$ = new Expression (Expression::OP_SOMEIN, $1, $4); } + | exp ORDR IN exp { $$ = new Expression (Expression::OP_ORDRIN, $1, $4); } + | term { $$ = new Expression ($1); } + +term: MINUS term { Expression num = Expression (Expression::OP_NUM, (uint64_t) 0); + $$ = new Expression (Expression::OP_MINUS, &num, $2); } + | NOT term { $$ = new Expression (Expression::OP_NOT, $2, NULL); } + | BITNOT term { $$ = new Expression (Expression::OP_BITNOT, $2, NULL); } + | LPAR exp RPAR { $$ = new Expression ($2); } + | FNAME LPAR QSTR RPAR { $$ = new Expression (Expression::OP_FUNC, $1, $3); } + | HASPROP LPAR NAME RPAR { $$ = new Expression (Expression::OP_HASPROP, $3, NULL); } + | JGROUP LPAR QSTR RPAR { $$ = new Expression (Expression::OP_JAVA, $1, $3); } + | JPARENT LPAR QSTR RPAR { $$ = new Expression (Expression::OP_JAVA, $1, $3); } + | FILEIOVFD LPAR QSTR RPAR { $$ = new Expression (Expression::OP_FILE, $1, $3); } + | NUM { $$ = new Expression ($1); } + | NAME { $$ = new Expression ($1); } + +%% + +namespace QL +{ + static Parser::symbol_type + unget_ret (std::istream &in, char c, Parser::symbol_type tok) + { + in.putback (c); + return tok; + } + + static Expression * + processName (char *name) + { + int propID = dbeSession->getPropIdByName (name); + if (propID != PROP_NONE) + { + Expression *expr = new Expression (Expression::OP_NUM, (uint64_t) propID); + Expression *ret = new Expression (Expression::OP_NAME, expr); + delete expr; + return ret; + } + + // If a name is not statically known try user defined objects + Expression *expr = dbeSession->findObjDefByName (name); + if (expr != NULL) + return expr->copy(); + + throw Parser::syntax_error ("Name not found"); + } + + static Parser::symbol_type + yylex (QL::Result &result) + { + int base = 0; + int c; + + do + c = result.in.get (); + while (result.in && (c == ' ' || c == '\t')); + if (!result.in) + return Parser::make_YYEOF (); + + switch (c) + { + case '\n': return Parser::make_YYEOF (); + case '(': return Parser::make_LPAR () ; + case ')': return Parser::make_RPAR (); + case ',': return Parser::make_COMMA (); + case '%': return Parser::make_REM (); + case '/': return Parser::make_DIV (); + case '*': return Parser::make_MUL (); + case '-': return Parser::make_MINUS (); + case '+': return Parser::make_ADD (); + case '~': return Parser::make_BITNOT (); + case '^': return Parser::make_BITXOR (); + case '?': return Parser::make_QWE (); + case ':': return Parser::make_COLON (); + case '|': + c = result.in.get (); + if (c == '|') + return Parser::make_OR (); + else + return unget_ret (result.in, c, Parser::make_BITOR ()); + case '&': + c = result.in.get (); + if (c == '&') + return Parser::make_AND (); + else + return unget_ret (result.in, c, Parser::make_BITAND ()); + case '!': + c = result.in.get (); + if (c == '=') + return Parser::make_NE (); + else + return unget_ret (result.in, c, Parser::make_NOT ()); + case '=': + c = result.in.get (); + if (c == '=') + return Parser::make_EQ (); + else + throw Parser::syntax_error ("Syntax error after ="); + case '<': + c = result.in.get (); + if (c == '=') + return Parser::make_LE (); + else if (c == '<') + return Parser::make_LS (); + else + return unget_ret (result.in, c, Parser::make_LT ()); + case '>': + c = result.in.get (); + if (c == '=') + return Parser::make_GE (); + else if (c == '>') + return Parser::make_RS (); + else + return unget_ret (result.in, c, Parser::make_GT ()); + case '"': + { + int maxsz = 16; + char *str = (char *) malloc (maxsz); + char *ptr = str; + + for (;;) + { + c = result.in.get (); + if (!result.in) + { + free (str); + throw Parser::syntax_error ("Unclosed \""); + } + + switch (c) + { + case '"': + *ptr = (char)0; + // XXX omazur: need new string type + return Parser::make_QSTR (new Expression (Expression::OP_NUM, (uint64_t) str)); + case 0: + case '\n': + free (str); + throw Parser::syntax_error ("Multiline strings are not supported"); + default: + if (ptr - str >= maxsz) + { + size_t len = ptr - str; + maxsz = maxsz > 8192 ? maxsz + 8192 : maxsz * 2; + char *new_s = (char *) realloc (str, maxsz); + str = new_s; + ptr = str + len; + } + *ptr++ = c; + } + } + } + default: + if (c == '0') + { + base = 8; + c = result.in.get (); + if ( c == 'x' ) + { + base = 16; + c = result.in.get (); + } + } + else if (c >= '1' && c <='9') + base = 10; + + if (base) + { + uint64_t lval = 0; + for (;;) + { + int digit = -1; + switch (c) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + digit = c - '0'; + break; + case '8': case '9': + if (base > 8) + digit = c - '0'; + break; + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + if (base == 16) + digit = c - 'a' + 10; + break; + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + if (base == 16) + digit = c - 'A' + 10; + break; + } + if (digit == -1) + { + result.in.putback (c); + break; + } + lval = lval * base + digit; + c = result.in.get (); + } + return Parser::make_NUM (new Expression (Expression::OP_NUM, lval)); + } + + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) + { + char name[32]; // omazur XXX: accept any length + name[0] = (char)c; + for (size_t i = 1; i < sizeof (name); i++) + { + c = result.in.get (); + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || (c == '_')) + name[i] = c; + else + { + name[i] = (char)0; + result.in.putback (c); + break; + } + } + + if (strcasecmp (name, NTXT ("IN")) == 0) + return Parser::make_IN (); + else if (strcasecmp (name, NTXT ("SOME")) == 0) + return Parser::make_SOME (); + else if (strcasecmp (name, NTXT ("ORDERED")) == 0) + return Parser::make_ORDR (); + else if (strcasecmp (name, NTXT ("TRUE")) == 0) + return Parser::make_NUM (new Expression (Expression::OP_NUM, (uint64_t) 1)); + else if (strcasecmp (name, NTXT ("FALSE")) == 0) + return Parser::make_NUM (new Expression (Expression::OP_NUM, (uint64_t) 0)); + else if (strcasecmp (name, NTXT ("FNAME")) == 0) + return Parser::make_FNAME (new Expression (Expression::OP_NUM, Expression::FUNC_FNAME)); + else if (strcasecmp (name, NTXT ("HAS_PROP")) == 0) + return Parser::make_HASPROP (); + else if (strcasecmp (name, NTXT ("JGROUP")) == 0) + return Parser::make_JGROUP (new Expression (Expression::OP_NUM, Expression::JAVA_JGROUP)); + else if (strcasecmp (name, NTXT ("JPARENT")) == 0 ) + return Parser::make_JPARENT (new Expression (Expression::OP_NUM, Expression::JAVA_JPARENT)); + else if (strcasecmp (name, NTXT ("DNAME")) == 0) + return Parser::make_FNAME (new Expression (Expression::OP_NUM, Expression::FUNC_DNAME)); + else if (strcasecmp (name, NTXT ("FILEIOVFD")) == 0 ) + return Parser::make_FILEIOVFD (new Expression (Expression::OP_NUM, (uint64_t) 0)); + + return Parser::make_NAME (processName (name)); + } + + throw Parser::syntax_error ("Syntax error"); + } + } + void + Parser::error (const std::string &) + { + // do nothing for now + } +} + diff --git a/gprofng/src/SAXParser.h b/gprofng/src/SAXParser.h new file mode 100644 index 0000000..dde3e06 --- /dev/null +++ b/gprofng/src/SAXParser.h @@ -0,0 +1,49 @@ +/* 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. */ + +/* + * javax/xml/parsers/SAXParser.java + * + * Based on JavaTM 2 Platform Standard Ed. 5.0 + */ + +#ifndef _SAXParser_h +#define _SAXParser_h + +class File; +class DefaultHandler; +class SAXException; + +class SAXParser +{ +public: + + virtual ~SAXParser () { } + virtual void reset () { } + virtual void parse (File*, DefaultHandler*) = 0; + virtual bool isNamespaceAware () = 0; + virtual bool isValidating () = 0; + +protected: + + SAXParser () { } +}; + +#endif /* _SAXParser_h */ diff --git a/gprofng/src/SAXParserFactory.cc b/gprofng/src/SAXParserFactory.cc new file mode 100644 index 0000000..7d9e851 --- /dev/null +++ b/gprofng/src/SAXParserFactory.cc @@ -0,0 +1,666 @@ +/* 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 "util.h" +#include "vec.h" +#include "DefaultHandler.h" +#include "SAXParser.h" +#include "SAXParserFactory.h" +#include "StringBuilder.h" + +/* + * Private implementation of Attributes + */ +class AttributesP : public Attributes +{ +public: + AttributesP (); + ~AttributesP (); + int getLength (); + const char *getQName (int index); + const char *getValue (int index); + int getIndex (const char *qName); + const char *getValue (const char *qName); + void append (char *qName, char *value); + +private: + Vector<char*> *names; + Vector<char*> *values; +}; + +AttributesP::AttributesP () +{ + names = new Vector<char*>; + values = new Vector<char*>; +} + +AttributesP::~AttributesP () +{ + Destroy (names); + Destroy (values); +} + +int +AttributesP::getLength () +{ + return names->size (); +} + +const char * +AttributesP::getQName (int index) +{ + if (index < 0 || index >= names->size ()) + return NULL; + return names->fetch (index); +} + +const char * +AttributesP::getValue (int index) +{ + if (index < 0 || index >= values->size ()) + return NULL; + return values->fetch (index); +} + +int +AttributesP::getIndex (const char *qName) +{ + for (int idx = 0; idx < names->size (); idx++) + if (strcmp (names->fetch (idx), qName) == 0) + return idx; + return -1; +} + +const char * +AttributesP::getValue (const char *qName) +{ + for (int idx = 0; idx < names->size (); idx++) + if (strcmp (names->fetch (idx), qName) == 0) + return values->fetch (idx); + return NULL; +} + +void +AttributesP::append (char *qName, char *value) +{ + names->append (qName); + values->append (value); +} + +/* + * Implementation of SAXException + */ +SAXException::SAXException () +{ + message = strdup ("null"); +} + +SAXException::SAXException (const char *_message) +{ + if (_message == NULL) + message = strdup ("null"); + else + message = strdup (_message); +} + +SAXException::~SAXException () +{ + free (message); +} + +char * +SAXException::getMessage () +{ + return message; +} + +/* + * SAXParseException + */ +SAXParseException::SAXParseException (char *message, int _lineNumber, int _columnNumber) +: SAXException (message == NULL ? GTXT ("XML parse error") : message) +{ + lineNumber = _lineNumber; + columnNumber = _columnNumber; +} + +/* + * Private implementation of SAXParser + */ +class SAXParserP : public SAXParser +{ +public: + SAXParserP (); + ~SAXParserP (); + void reset (); + void parse (File*, DefaultHandler*); + + bool + isNamespaceAware () + { + return false; + } + + bool + isValidating () + { + return false; + } + +private: + + static const int CH_EOF = -1; + + void nextch (); + bool isWSpace (); + void skipWSpaces (); + void scanString (const char *str); + char *parseName (); + char *parseString (); + char *decodeString (char *str); + Attributes *parseAttributes (); + void parseTag (); + void parseDocument (); + void parsePart (int idx); + + DefaultHandler *dh; + int bufsz; + char *buffer; + int cntsz; + int idx; + int curch; + int line; + int column; +}; + +SAXParserP::SAXParserP () +{ + dh = NULL; + bufsz = 0x2000; + buffer = (char*) malloc (bufsz); + cntsz = 0; + idx = 0; + line = 1; + column = 0; +} + +SAXParserP::~SAXParserP () +{ + free (buffer); +} + +void +SAXParserP::reset () +{ + dh = NULL; + bufsz = 8192; + buffer = (char*) realloc (buffer, bufsz); + cntsz = 0; + idx = 0; + line = 1; + column = 0; +} + +void +SAXParserP::parse (File *f, DefaultHandler *_dh) +{ + if (_dh == NULL) + return; + dh = _dh; + FILE *file = (FILE*) f; + int rem = bufsz; + cntsz = 0; + idx = 0; + for (;;) + { + int n = (int) fread (buffer + cntsz, 1, rem, file); + if (ferror (file) || n <= 0) + break; + cntsz += n; + if (feof (file)) + break; + rem -= n; + if (rem == 0) + { + int oldbufsz = bufsz; + bufsz = bufsz >= 0x100000 ? bufsz + 0x100000 : bufsz * 2; + buffer = (char*) realloc (buffer, bufsz); + rem = bufsz - oldbufsz; + } + } + nextch (); + parseDocument (); +} + +static int +hex (char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'a' && c <= 'f') + return 10 + (c - 'a'); + return -1; +} + +void +SAXParserP::nextch () +{ + curch = idx >= cntsz ? CH_EOF : buffer[idx++]; + if (curch == '\n') + { + line += 1; + column = 0; + } + else + column += 1; +} + +bool +SAXParserP::isWSpace () +{ + return curch == ' ' || curch == '\t' || curch == '\n' || curch == '\r'; +} + +void +SAXParserP::skipWSpaces () +{ + while (isWSpace ()) + nextch (); +} + +void +SAXParserP::scanString (const char *str) +{ + if (str == NULL || *str == '\0') + return; + for (;;) + { + if (curch == CH_EOF) + break; + else if (curch == *str) + { + const char *p = str; + for (;;) + { + p += 1; + nextch (); + if (*p == '\0') + return; + if (curch != *p) + break; + } + } + nextch (); + } +} + +char * +SAXParserP::parseName () +{ + StringBuilder *name = new StringBuilder (); + + if ((curch >= 'A' && curch <= 'Z') || (curch >= 'a' && curch <= 'z')) + { + name->append ((char) curch); + nextch (); + while (isalnum (curch) != 0 || curch == '_') + { + name->append ((char) curch); + nextch (); + } + } + + char *res = name->toString (); + delete name; + return res; +} + +/** + * Replaces encoded XML characters with original characters + * Attention: this method reuses the same string that is passed as the argument + * @param str + * @return str + */ +char * +SAXParserP::decodeString (char * str) +{ + // Check if string has %22% and replace it with double quotes + // Also replace all other special combinations. + char *from = str; + char *to = str; + if (strstr (from, "%") || strstr (from, "&")) + { + int len = strlen (from); + for (int i = 0; i < len; i++) + { + int nch = from[i]; + // Process &...; combinations + if (nch == '&' && i + 3 < len) + { + if (from[i + 2] == 't' && from[i + 3] == ';') + { + // check < > + if (from[i + 1] == 'l') + { + nch = '<'; + i += 3; + } + else if (from[i + 1] == 'g') + { + nch = '>'; + i += 3; + } + } + else if (i + 4 < len && from[i + 4] == ';') + { + // check & + if (from[i + 1] == 'a' && from[i + 2] == 'm' && from[i + 3] == 'p') + { + nch = '&'; + i += 4; + } + } + else if ((i + 5 < len) && (from[i + 5] == ';')) + { + // check ' " + if (from[i + 1] == 'a' && from[i + 2] == 'p' + && from[i + 3] == 'o' && from[i + 4] == 's') + { + nch = '\''; + i += 5; + } + if (from[i + 1] == 'q' && from[i + 2] == 'u' && from[i + 3] == 'o' && from[i + 4] == 't') + { + nch = '"'; + i += 5; + } + } + } + // Process %XX% combinations + if (nch == '%' && i + 3 < len && from[i + 3] == '%') + { + int ch = hex (from[i + 1]); + if (ch >= 0) + { + int ch2 = hex (from[i + 2]); + if (ch2 >= 0) + { + ch = ch * 16 + ch2; + nch = ch; + i += 3; + } + } + } + *to++ = (char) nch; + } + *to = '\0'; + } + return str; +} + +char * +SAXParserP::parseString () +{ + StringBuilder *str = new StringBuilder (); + int quote = '>'; + if (curch == '"') + { + quote = curch; + nextch (); + } + for (;;) + { + if (curch == CH_EOF) + break; + if (curch == quote) + { + nextch (); + break; + } + str->append ((char) curch); + nextch (); + } + + char *res = str->toString (); + // Decode XML characters + res = decodeString (res); + delete str; + return res; +} + +Attributes * +SAXParserP::parseAttributes () +{ + AttributesP *attrs = new AttributesP (); + + for (;;) + { + skipWSpaces (); + char *name = parseName (); + if (name == NULL || *name == '\0') + { + free (name); + break; + } + skipWSpaces (); + if (curch != '=') + { + SAXParseException *e = new SAXParseException (NULL, line, column); + dh->error (e); + scanString (">"); + free (name); + return attrs; + } + nextch (); + skipWSpaces (); + char *value = parseString (); + attrs->append (name, value); + } + return attrs; +} + +void +SAXParserP::parseTag () +{ + skipWSpaces (); + bool empty = false; + char *name = parseName (); + if (name == NULL || *name == '\0') + { + SAXParseException *e = new SAXParseException (NULL, line, column); + dh->error (e); + scanString (">"); + free (name); + return; + } + + Attributes *attrs = parseAttributes (); + if (curch == '/') + { + nextch (); + empty = true; + } + if (curch == '>') + nextch (); + else + { + empty = false; + SAXParseException *e = new SAXParseException (NULL, line, column); + dh->error (e); + scanString (">"); + } + if (curch == CH_EOF) + { + free (name); + delete attrs; + return; + } + dh->startElement (NULL, NULL, name, attrs); + if (empty) + { + dh->endElement (NULL, NULL, name); + free (name); + delete attrs; + return; + } + + StringBuilder *chars = new StringBuilder (); + bool wspaces = true; + for (;;) + { + if (curch == CH_EOF) + break; + else if (curch == '<') + { + if (chars->length () > 0) + { + char *str = chars->toString (); + // Decode XML characters + str = decodeString (str); + if (wspaces) + dh->ignorableWhitespace (str, 0, chars->length ()); + else + dh->characters (str, 0, chars->length ()); + free (str); + chars->setLength (0); + wspaces = true; + } + nextch (); + if (curch == '/') + { + nextch (); + char *ename = parseName (); + if (ename && *ename != '\0') + { + if (strcmp (name, ename) == 0) + { + skipWSpaces (); + if (curch == '>') + { + nextch (); + dh->endElement (NULL, NULL, name); + free (ename); + break; + } + SAXParseException *e = new SAXParseException (NULL, line, column); + dh->error (e); + } + else + { + SAXParseException *e = new SAXParseException (NULL, line, column); + dh->error (e); + } + scanString (">"); + } + free (ename); + } + else + parseTag (); + } + else + { + if (!isWSpace ()) + wspaces = false; + chars->append ((char) curch); + nextch (); + } + } + + free (name); + delete attrs; + delete chars; + return; +} + +void +SAXParserP::parseDocument () +{ + dh->startDocument (); + for (;;) + { + if (curch == CH_EOF) + break; + if (curch == '<') + { + nextch (); + if (curch == '?') + scanString ("?>"); + else if (curch == '!') + scanString (">"); + else + parseTag (); + } + else + nextch (); + } + dh->endDocument (); +} + +/* + * Private implementation of SAXParserFactory + */ +class SAXParserFactoryP : public SAXParserFactory +{ +public: + SAXParserFactoryP () { } + ~SAXParserFactoryP () { } + SAXParser *newSAXParser (); + + void + setFeature (const char *, bool) { } + + bool + getFeature (const char *) + { + return false; + } +}; + +SAXParser * +SAXParserFactoryP::newSAXParser () +{ + return new SAXParserP (); +} + +/* + * SAXParserFactory + */ +const char *SAXParserFactory::DEFAULT_PROPERTY_NAME = "javax.xml.parsers.SAXParserFactory"; + +SAXParserFactory * +SAXParserFactory::newInstance () +{ + return new SAXParserFactoryP (); +} + +void +DefaultHandler::dump_startElement (const char *qName, Attributes *attrs) +{ + fprintf (stderr, NTXT ("DefaultHandler::startElement qName='%s'\n"), STR (qName)); + for (int i = 0, sz = attrs ? attrs->getLength () : 0; i < sz; i++) + { + const char *qn = attrs->getQName (i); + const char *vl = attrs->getValue (i); + fprintf (stderr, NTXT (" %d '%s' = '%s'\n"), i, STR (qn), STR (vl)); + } +} diff --git a/gprofng/src/SAXParserFactory.h b/gprofng/src/SAXParserFactory.h new file mode 100644 index 0000000..8e2c366 --- /dev/null +++ b/gprofng/src/SAXParserFactory.h @@ -0,0 +1,75 @@ +/* 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. */ + +/* + * javax/xml/parsers/SAXParserFactory.java + * + * Based on JavaTM 2 Platform Standard Ed. 5.0 + */ + +#ifndef _SAXParserFactory_h +#define _SAXParserFactory_h + +class SAXParser; + +class SAXParserFactory +{ +public: + static SAXParserFactory *newInstance (); + + virtual ~SAXParserFactory () { } + virtual SAXParser *newSAXParser () = 0; + virtual void setFeature (const char *name, bool value) = 0; + virtual bool getFeature (const char *name) = 0; + + void + setNamespaceAware (bool awareness) + { + namespaceAware = awareness; + } + + void + setValidating (bool _validating) + { + validating = _validating; + } + + bool + isNamespaceAware () + { + return namespaceAware; + } + + bool + isValidating () + { + return validating; + } + +protected: + SAXParserFactory () { } + +private: + static const char *DEFAULT_PROPERTY_NAME; + bool validating; + bool namespaceAware; +}; + +#endif /* _SAXParserFactory_h */ diff --git a/gprofng/src/Sample.cc b/gprofng/src/Sample.cc new file mode 100644 index 0000000..7c00334 --- /dev/null +++ b/gprofng/src/Sample.cc @@ -0,0 +1,94 @@ +/* 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 <assert.h> +#include "Sample.h" +#include "util.h" +#include "Exp_Layout.h" + +Sample::Sample (int num) +{ + number = num; + prusage = NULL; + start_time = end_time = 0; + start_label = end_label = NULL; + validated = false; +} + +Sample::~Sample () +{ + delete prusage; + free (start_label); + free (end_label); +} + +PrUsage * +Sample::get_usage () +{ + if (validated == false) + { + validate_usage (); + validated = true; + } + return prusage; +} + +void +Sample::validate_usage () +{ + if (prusage == NULL || validated) + return; + validated = true; + + // Make sure that none of the times are negative, force to zero if so + if (prusage->pr_utime < 0) + prusage->pr_utime = 0; + if (prusage->pr_stime < 0) + prusage->pr_stime = 0; + if (prusage->pr_ttime < 0) + prusage->pr_ttime = 0; + if (prusage->pr_tftime < 0) + prusage->pr_tftime = 0; + if (prusage->pr_dftime < 0) + prusage->pr_dftime = 0; + if (prusage->pr_kftime < 0) + prusage->pr_kftime = 0; + if (prusage->pr_ltime < 0) + prusage->pr_ltime = 0; + if (prusage->pr_slptime < 0) + prusage->pr_slptime = 0; + if (prusage->pr_wtime < 0) + prusage->pr_wtime = 0; + if (prusage->pr_stoptime < 0) + prusage->pr_stoptime = 0; + if (prusage->pr_rtime < 0) + prusage->pr_rtime = 0; + + // Now make sure that the sum of states is >= prusage->pr_rtime + hrtime_t sum = prusage->pr_utime + prusage->pr_stime + prusage->pr_ttime + + prusage->pr_tftime + prusage->pr_dftime + prusage->pr_kftime + + prusage->pr_ltime + prusage->pr_slptime + prusage->pr_wtime + + prusage->pr_stoptime; + + sum = sum - prusage->pr_rtime; + if (sum < 0)// increment sleep time to make it match + prusage->pr_slptime = prusage->pr_slptime - sum; +} diff --git a/gprofng/src/Sample.h b/gprofng/src/Sample.h new file mode 100644 index 0000000..312bdcc --- /dev/null +++ b/gprofng/src/Sample.h @@ -0,0 +1,80 @@ +/* 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. */ + +#ifndef _SAMPLE_H +#define _SAMPLE_H + +// A data Sample object represents a single sample's worth of data. This +// object is private and is only used by Experiment and Sample_sel. + +#include "dbe_types.h" + +class PrUsage; + +class Sample +{ + friend class Experiment; // see post_process(), read_overview_file() +public: + Sample (int num); + ~Sample (); + PrUsage *get_usage (); + + char * + get_start_label () + { + return start_label; + } + + char * + get_end_label () + { + return end_label; + } + + hrtime_t + get_start_time () + { + return start_time; + } + + hrtime_t + get_end_time () + { + return end_time; + } + + int + get_number () + { + return number; + } + +private: + void validate_usage (); // Make sure usage data is consistent + bool validated; // if validation performed + char *start_label; // sample start label + char *end_label; // sample end label + hrtime_t start_time; // sample start time + hrtime_t end_time; // sample end time + PrUsage *prusage; // process usage data + int number; // sample number +}; + +#endif /* _SAMPLE_H */ diff --git a/gprofng/src/SegMem.h b/gprofng/src/SegMem.h new file mode 100644 index 0000000..fbfe727 --- /dev/null +++ b/gprofng/src/SegMem.h @@ -0,0 +1,76 @@ +/* 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. */ + +#ifndef _SEGMEM_H +#define _SEGMEM_H + +#include "dbe_types.h" +class Histable; + +class SegMem +{ +public: + + // The various segments types. + enum Seg_mode + { + READ, + WRITE, + EXEC, + UNKNOWN + }; + + void + set_file_offset (uint64_t fo) + { + file_offset = fo; + } + + uint64_t + get_file_offset () + { + return file_offset; + } + + void + set_mode (Seg_mode sm) + { + mode = sm; + } + + Seg_mode + get_mode () + { + return mode; + } + + Size size; // Size of this instance + Histable *obj; // Pointer to Segment/Function object + Vaddr base; // Base address + hrtime_t load_time; + hrtime_t unload_time; + Size page_size; + +private: + uint64_t file_offset; + Seg_mode mode; +}; + +#endif /* _SEGMEM_H */ diff --git a/gprofng/src/Settings.cc b/gprofng/src/Settings.cc new file mode 100644 index 0000000..965b917 --- /dev/null +++ b/gprofng/src/Settings.cc @@ -0,0 +1,1586 @@ +/* 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 <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/param.h> + +#include "enums.h" +#include "Settings.h" +#include "DbeSession.h" +#include "Command.h" +#include "Application.h" +#include "MemorySpace.h" +#include "StringBuilder.h" +#include "Table.h" +#include "Emsg.h" +#include "util.h" +#include "i18n.h" + +// Commands for compiler commentary +static const char *comp_cmd[] = { + NTXT ("basic"), + NTXT ("version"), + NTXT ("warn"), + NTXT ("parallel"), + NTXT ("query"), + NTXT ("loop"), + NTXT ("pipe"), + NTXT ("inline"), + NTXT ("memops"), + NTXT ("fe"), + NTXT ("codegen"), + NTXT ("src"), + NTXT ("asrc"), + NTXT ("nosrc"), + NTXT ("hex"), + NTXT ("nohex"), + NTXT ("threshold"), + NTXT ("cf") +}; + +static const int comp_vis[] = { + CCMV_BASIC, + CCMV_VER, + CCMV_WARN, + CCMV_PAR, + CCMV_QUERY, + CCMV_LOOP, + CCMV_PIPE, + CCMV_INLINE, + CCMV_MEMOPS, + CCMV_FE, + CCMV_CG, + COMP_SRC, + COMP_SRC_METRIC, + COMP_NOSRC, + COMP_HEX, + COMP_NOHEX, + COMP_THRESHOLD, + COMP_CMPLINE +}; + +const int comp_size = sizeof (comp_cmd) / sizeof (char *); + +// Commands for timeline +typedef enum +{ + TLCMD_INVALID, + TLCMD_ENTITY_MODE, + TLCMD_ALIGN, + TLCMD_DEPTH +} TLModeSubcommand; + +typedef struct +{ + const char * cmdText; + TLModeSubcommand cmdType; + int cmdId; +} TLModeCmd; +static const TLModeCmd tlmode_cmd[] = { + // MODE commands + {NTXT ("lwp"), TLCMD_ENTITY_MODE, PROP_LWPID}, + {NTXT ("thread"), TLCMD_ENTITY_MODE, PROP_THRID}, + {NTXT ("cpu"), TLCMD_ENTITY_MODE, PROP_CPUID}, + {NTXT ("experiment"), TLCMD_ENTITY_MODE, PROP_EXPID}, + // ALIGN commands + {NTXT ("root"), TLCMD_ALIGN, TLSTACK_ALIGN_ROOT}, + {NTXT ("leaf"), TLCMD_ALIGN, TLSTACK_ALIGN_LEAF}, + // DEPTH commands + {NTXT ("depth"), TLCMD_DEPTH, 0 /* don't care */} +}; + +static const int tlmode_size = sizeof (tlmode_cmd) / sizeof (TLModeCmd); + +// Constructor + +Settings::Settings (Application *_app) +{ + // Remember the application + app = _app; + + // Clear all default strings + str_vmode = NULL; + str_en_desc = NULL; + str_datamode = NULL; + str_scompcom = NULL; + str_sthresh = NULL; + str_dcompcom = NULL; + str_dthresh = NULL; + str_dmetrics = NULL; + str_dsort = NULL; + str_tlmode = NULL; + str_tldata = NULL; + str_tabs = NULL; + str_rtabs = NULL; + str_search_path = NULL; + str_name_format = NULL; + str_limit = NULL; + str_printmode = NULL; + str_compare = NULL; + preload_libdirs = NULL; + pathmaps = new Vector<pathmap_t*>; + lo_expands = new Vector<lo_expand_t*>; + lo_expand_default = LIBEX_SHOW; + is_loexpand_default = true; + tabs_processed = false; + + // set default-default values + name_format = Histable::NA; + view_mode = VMODE_USER; + en_desc = false; + en_desc_cmp = NULL; + en_desc_usr = NULL; + src_compcom = 2147483647; + dis_compcom = 2147483647; +#define DEFAULT_SRC_DIS_THRESHOLD 75 + threshold_src = DEFAULT_SRC_DIS_THRESHOLD; + threshold_dis = DEFAULT_SRC_DIS_THRESHOLD; + src_visible = true; + srcmetric_visible = false; + hex_visible = false; + cmpline_visible = true; + funcline_visible = true; + tldata = NULL; + tlmode = 0; + stack_align = 0; + stack_depth = 0; + limit = 0; + // print mode is initialized after the .rc files are read + print_delim = ','; + compare_mode = CMP_DISABLE; + machinemodel = NULL; + ignore_no_xhwcprof = false; + ignore_fs_warn = false; + + // construct the master list of tabs + buildMasterTabList (); + + indx_tab_state = new Vector<bool>; + indx_tab_order = new Vector<int>; + mem_tab_state = new Vector<bool>; + mem_tab_order = new Vector<int>; + + // note that the .rc files are not read here, but later +} + +// Constructor for duplicating an existing Settings class + +Settings::Settings (Settings * _settings) +{ + int index; + app = _settings->app; + + // Copy all default strings + str_vmode = dbe_strdup (_settings->str_vmode); + str_en_desc = dbe_strdup (_settings->str_en_desc); + str_datamode = dbe_strdup (_settings->str_datamode); + str_scompcom = dbe_strdup (_settings->str_scompcom); + str_sthresh = dbe_strdup (_settings->str_sthresh); + str_dcompcom = dbe_strdup (_settings->str_dcompcom); + str_dthresh = dbe_strdup (_settings->str_dthresh); + str_dmetrics = dbe_strdup (_settings->str_dmetrics); + str_dsort = dbe_strdup (_settings->str_dsort); + str_tlmode = dbe_strdup (_settings->str_tlmode); + str_tldata = dbe_strdup (_settings->str_tldata); + str_tabs = dbe_strdup (_settings->str_tabs); + str_rtabs = dbe_strdup (_settings->str_rtabs); + str_search_path = dbe_strdup (_settings->str_search_path); + str_name_format = dbe_strdup (_settings->str_name_format); + str_limit = dbe_strdup (_settings->str_limit); + str_printmode = dbe_strdup (_settings->str_printmode); + str_compare = dbe_strdup (_settings->str_compare); + preload_libdirs = dbe_strdup (_settings->preload_libdirs); + + // replicate the pathmap vector + pathmap_t *thismap; + pathmap_t *newmap; + pathmaps = new Vector<pathmap_t*>; + + Vec_loop (pathmap_t*, _settings->pathmaps, index, thismap) + { + newmap = new pathmap_t; + newmap->old_prefix = dbe_strdup (thismap->old_prefix); + newmap->new_prefix = dbe_strdup (thismap->new_prefix); + pathmaps->append (newmap); + } + + // replicate the lo_expand vector and default + lo_expand_t *this_lo_ex; + lo_expand_t *new_lo_ex; + lo_expand_default = _settings->lo_expand_default; + is_loexpand_default = _settings->is_loexpand_default; + lo_expands = new Vector<lo_expand_t*>; + + Vec_loop (lo_expand_t*, _settings->lo_expands, index, this_lo_ex) + { + new_lo_ex = new lo_expand_t; + new_lo_ex->libname = dbe_strdup (this_lo_ex->libname); + new_lo_ex->expand = this_lo_ex->expand; + lo_expands->append (new_lo_ex); + } + tabs_processed = _settings->tabs_processed; + + // Copy the various values from the _settings instance + name_format = _settings->name_format; + view_mode = _settings->view_mode; + en_desc = false; + en_desc_cmp = NULL; + en_desc_usr = NULL; + if (_settings->en_desc_usr) + set_en_desc (_settings->en_desc_usr, true); + src_compcom = _settings->src_compcom; + dis_compcom = _settings->dis_compcom; + threshold_src = _settings->threshold_src; + threshold_dis = _settings->threshold_dis; + src_visible = _settings->src_visible; + srcmetric_visible = _settings->srcmetric_visible; + hex_visible = _settings->hex_visible; + cmpline_visible = _settings->cmpline_visible; + funcline_visible = _settings->funcline_visible; + tldata = dbe_strdup (_settings->tldata); + tlmode = _settings->tlmode; + stack_align = _settings->stack_align; + stack_depth = _settings->stack_depth; + limit = _settings->limit; + print_mode = _settings->print_mode; + print_delim = _settings->print_delim; + compare_mode = _settings->compare_mode; + machinemodel = dbe_strdup (_settings->machinemodel); + ignore_no_xhwcprof = _settings->ignore_no_xhwcprof; + ignore_fs_warn = _settings->ignore_fs_warn; + + // copy the tab list, too + tab_list = new Vector<DispTab*>; + DispTab *dsptab; + + Vec_loop (DispTab*, _settings->tab_list, index, dsptab) + { + DispTab *ntab; + ntab = new DispTab (dsptab->type, dsptab->order, dsptab->visible, dsptab->cmdtoken); + ntab->setAvailability (dsptab->available); + tab_list->append (ntab); + } + + // construct the master list of memory tabs & copy order + index = _settings->mem_tab_state->size (); + mem_tab_state = new Vector<bool>(index); + mem_tab_order = new Vector<int>(index); + for (int i = 0; i < index; i++) + { + mem_tab_state->append (false); + mem_tab_order->append (_settings->mem_tab_order->fetch (i)); + } + + // construct the master list of index tabs & copy order + index = _settings->indx_tab_state->size (); + indx_tab_state = new Vector<bool>(index); + indx_tab_order = new Vector<int>(index); + for (int i = 0; i < index; i++) + indx_tab_order->append (_settings->indx_tab_order->fetch (i)); + set_IndxTabState (_settings->indx_tab_state); +} + +Settings::~Settings () +{ + for (int i = 0; i < pathmaps->size (); ++i) + { + pathmap_t *pmap = pathmaps->fetch (i); + free (pmap->old_prefix); + free (pmap->new_prefix); + delete pmap; + } + delete pathmaps; + + for (int i = 0; i < lo_expands->size (); ++i) + { + lo_expand_t *lo_ex = lo_expands->fetch (i); + free (lo_ex->libname); + delete lo_ex; + } + delete lo_expands; + + tab_list->destroy (); + delete tab_list; + delete indx_tab_state; + delete indx_tab_order; + delete mem_tab_state; + delete mem_tab_order; + + free (str_vmode); + free (str_en_desc); + free (str_datamode); + free (str_scompcom); + free (str_sthresh); + free (str_dcompcom); + free (str_dthresh); + free (str_dmetrics); + free (str_dsort); + free (str_tlmode); + free (str_tldata); + free (str_tabs); + free (str_rtabs); + free (str_search_path); + free (str_name_format); + free (str_limit); + free (str_compare); + free (str_printmode); + free (preload_libdirs); + free (tldata); + free (en_desc_usr); + if (en_desc_cmp) + { + regfree (en_desc_cmp); + delete en_desc_cmp; + } +} + +/** + * Read .er.rc file from the specified location + * @param path + * @return + */ +char * +Settings::read_rc (char *path) +{ + StringBuilder sb; + Emsgqueue *commentq = new Emsgqueue (NTXT ("setting_commentq")); + + // Check file name + if (NULL == path) + return dbe_strdup (GTXT ("Error: empty file name")); + bool override = true; + set_rc (path, true, commentq, override); + Emsg *msg = commentq->fetch (); + while (msg != NULL) + { + char *str = msg->get_msg (); + sb.append (str); + msg = msg->next; + } + return sb.toString (); +} + +void +Settings::read_rc (bool ipc_or_rdt_mode) +{ + bool override = false; + + // Read file from the current working directory + char *rc_path = realpath (NTXT ("./.gprofng.rc"), NULL); + if (rc_path) + set_rc (rc_path, true, app->get_comments_queue (), override, ipc_or_rdt_mode); + + // Read file from the user's home directory + char *home = getenv (NTXT ("HOME")); + if (home) + { + char *strbuf = dbe_sprintf (NTXT ("%s/.gprofng.rc"), home); + char *home_rc_path = realpath (strbuf, NULL); + if (home_rc_path) + { + if (rc_path == NULL || strcmp (rc_path, home_rc_path) != 0) + set_rc (home_rc_path, true, app->get_comments_queue (), override, ipc_or_rdt_mode); + free (home_rc_path); + } + free (strbuf); + } + free (rc_path); + + // Read system-wide file + rc_path = dbe_sprintf (NTXT ("%s/../etc/gprofng.rc"), app->get_run_dir ()); + if (access (rc_path, R_OK | F_OK) != 0) + { + StringBuilder sb; + sb.sprintf (GTXT ("Warning: Default gprofng.rc file (%s) missing; configuration error "), rc_path); + Emsg *m = new Emsg (CMSG_COMMENT, sb); + app->get_comments_queue ()->append (m); + } + else + set_rc (rc_path, false, app->get_comments_queue (), override); + free (rc_path); + is_loexpand_default = true; + if (str_printmode == NULL) + { + // only if there's none set + print_mode = PM_TEXT; + str_printmode = dbe_strdup (NTXT ("text")); + } +} + + +// Handle various settings from reading the name .rc file +// This function is called for each .rc file read, and, for +// some settings, it accumulates the strings from the files. +// For others, it accepts the first appearance for a setting in a +// .rc file, and ignores subsequent appearances from other files. +// Error messages are appended to the Emsgqueue specified by the caller + +#define MAXARGS 20 + +void +Settings::set_rc (const char *path, bool msg, Emsgqueue *commentq, + bool override, bool ipc_or_rdt_mode) +{ + CmdType cmd_type; + int arg_count, cparam; + char *cmd, *end_cmd, *strbuf; + char *arglist[MAXARGS]; + StringBuilder sb; + + FILE *fptr = fopen (path, NTXT ("r")); + if (fptr == NULL) + return; + + if (msg) + { + sb.sprintf (GTXT ("Processed %s for default settings"), path); + Emsg *m = new Emsg (CMSG_COMMENT, sb); + commentq->append (m); + } + int line_no = 0; + end_cmd = NULL; + while (!feof (fptr)) + { + char *script = read_line (fptr); + if (script == NULL) + continue; + line_no++; + strtok (script, NTXT ("\n")); + + // extract the command + cmd = strtok (script, NTXT (" \t")); + if (cmd == NULL || *cmd == '#' || *cmd == '\n') + { + free (script); + continue; + } + char *remainder = strtok (NULL, NTXT ("\n")); + // now extract the arguments + int nargs = 0; + for (;;) + { + if (nargs >= MAXARGS) + { + if (!msg) + { + msg = true; // suppress repeats of header + Emsg *m = new Emsg (CMSG_COMMENT, GTXT ("Processed system gprofng.rc file for default settings")); + commentq->append (m); + } + sb.sprintf (GTXT ("Warning: more than %d arguments to %s command, line %d\n"), + MAXARGS, cmd, line_no); + Emsg *m = new Emsg (CMSG_COMMENT, sb); + commentq->append (m); + break; + } + + char *nextarg = strtok (remainder, NTXT ("\n")); + if (nextarg == NULL || *nextarg == '#') + break; + arglist[nargs++] = parse_qstring (nextarg, &end_cmd); + remainder = end_cmd; + if (remainder == NULL) + break; + // skip any blanks or tabs to get to next argument + while (*remainder == ' ' || *remainder == '\t') + remainder++; + } + cmd_type = Command::get_command (cmd, arg_count, cparam); + // check for extra arguments + if ((cmd_type != UNKNOWN_CMD && cmd_type != INDXOBJDEF) && (nargs > arg_count)) + { + if (!msg) + { + msg = true; // suppress repeats of header + Emsg *m = new Emsg (CMSG_COMMENT, GTXT ("Processed system gprofng.rc file for default settings")); + commentq->append (m); + } + sb.sprintf (GTXT ("Warning: extra arguments to %s command, line %d\n"), cmd, line_no); + Emsg *m = new Emsg (CMSG_COMMENT, sb); + commentq->append (m); + } + if (nargs < arg_count) + { + if (!msg) + { + msg = true; // suppress repeats of header + Emsg *m = new Emsg (CMSG_COMMENT, GTXT ("Processed system gprofng.rc file for default settings")); + commentq->append (m); + } + sb.sprintf (GTXT ("Error: missing arguments to %s command, line %d\n"), + cmd, line_no); + Emsg *m = new Emsg (CMSG_COMMENT, sb); + commentq->append (m); + + // ignore this command + free (script); + continue; + } + if (ipc_or_rdt_mode && (cmd_type != ADDPATH) && (cmd_type != PATHMAP)) + { + free (script); + continue; + } + switch (cmd_type) + { + case SCOMPCOM: + if (!str_scompcom || override) + { + str_scompcom = dbe_strdup (arglist[0]); + proc_compcom (arglist[0], true, true); + } + break; + case STHRESH: + if (!str_sthresh || override) + { + str_sthresh = dbe_strdup (arglist[0]); + proc_thresh (arglist[0], true, true); + break; + } + break; + case DCOMPCOM: + if (!str_dcompcom || override) + { + str_dcompcom = dbe_strdup (arglist[0]); + proc_compcom (arglist[0], false, true); + } + break; + case COMPCOM: + // process as if it were for both source and disassembly + // note that if it is set, subsequent SCOMPCOM and DCOMPCOM + // will be ignored + if (!str_scompcom || override) + { + str_scompcom = dbe_strdup (arglist[0]); + proc_compcom (arglist[0], true, true); + } + if (!str_dcompcom || override) + { + str_dcompcom = dbe_strdup (arglist[0]); + proc_compcom (arglist[0], false, true); + } + break; + case DTHRESH: + if (!str_dthresh || override) + { + str_dthresh = dbe_strdup (arglist[0]); + proc_thresh (arglist[0], false, true); + } + break; + case DMETRICS: + // append new settings to old, if necessary + if (str_dmetrics) + { + char *name = strstr (str_dmetrics, ":name"); + if (name == NULL) + strbuf = dbe_sprintf ("%s:%s", str_dmetrics, arglist[0]); + else + { + char * next = strstr (name + 1, ":"); + if (next == NULL) + { + name[0] = '\0'; + strbuf = dbe_sprintf ("%s:%s:name", str_dmetrics, arglist[0]); + } + else + strbuf = dbe_sprintf ("%s:%s", str_dmetrics, arglist[0]); + } + free (str_dmetrics); + str_dmetrics = strbuf; + } + else + str_dmetrics = dbe_strdup (arglist[0]); + break; + case DSORT: + // append new settings to old, if necessary + if (str_dsort) + { + strbuf = dbe_sprintf (NTXT ("%s:%s"), str_dsort, arglist[0]); + free (str_dsort); + str_dsort = strbuf; + } + else + str_dsort = dbe_strdup (arglist[0]); + break; + case TLMODE: + if (!str_tlmode || override) + { + str_tlmode = dbe_strdup (arglist[0]); + proc_tlmode (arglist[0], true); + } + break; + case TLDATA: + if (!str_tldata || override) + { + str_tldata = dbe_strdup (arglist[0]); + proc_tldata (arglist[0], true); + } + break; + case TABS: + if (!str_tabs || override) + // the string is processed later, after all .rc files are read + str_tabs = dbe_strdup (arglist[0]); + break; + case RTABS: + if (!str_rtabs || override) + // the string is processed later, after all .rc files are read + str_rtabs = dbe_strdup (arglist[0]); + break; + case ADDPATH: + if (str_search_path) + { + strbuf = dbe_sprintf (NTXT ("%s:%s"), str_search_path, arglist[0]); + free (str_search_path); + str_search_path = strbuf; + } + else + str_search_path = dbe_strdup (arglist[0]); + break; + case PATHMAP: + { + char *err = add_pathmap (pathmaps, arglist[0], arglist[1]); + free (err); // XXX error is not reported + break; + } + case LIBDIRS: + if (preload_libdirs == NULL) + preload_libdirs = dbe_strdup (arglist[0]); + break; + case NAMEFMT: + if (name_format == Histable::NA) + set_name_format (arglist[0]); + break; + case VIEWMODE: + if (!str_vmode || override) + { + str_vmode = dbe_strdup (arglist[0]); + set_view_mode (arglist[0], true); + } + break; + case EN_DESC: + if (!str_en_desc || override) + { + str_en_desc = dbe_strdup (arglist[0]); + set_en_desc (arglist[0], true); + } + break; + case LIMIT: + if (!str_limit || override) + { + str_limit = dbe_strdup (arglist[0]); + set_limit (arglist[0], true); + } + break; + case PRINTMODE: + if (!str_printmode || override) + set_printmode (arglist[0]); + break; + case COMPARE: + if (!str_compare || override) + { + char *s = arglist[0]; + if (s) + str_compare = dbe_strdup (s); + else + s = NTXT (""); + if (strcasecmp (s, NTXT ("OFF")) == 0 + || strcmp (s, NTXT ("0")) == 0) + set_compare_mode (CMP_DISABLE); + else if (strcasecmp (s, NTXT ("ON")) == 0 + || strcmp (s, NTXT ("1")) == 0) + set_compare_mode (CMP_ENABLE); + else if (strcasecmp (s, NTXT ("DELTA")) == 0) + set_compare_mode (CMP_DELTA); + else if (strcasecmp (s, NTXT ("RATIO")) == 0) + set_compare_mode (CMP_RATIO); + else + { + sb.sprintf (GTXT (" .er.rc:%d The argument of 'compare' should be 'on', 'off', 'delta', or 'ratio'"), + (int) line_no); + Emsg *m = new Emsg (CMSG_COMMENT, sb); + commentq->append (m); + } + } + break; + + case INDXOBJDEF: + { + char *ret = dbeSession->indxobj_define (arglist[0], NULL, arglist[1], (nargs >= 3) ? PTXT (arglist[2]) : NULL, (nargs >= 4) ? PTXT (arglist[3]) : NULL); + if (ret != NULL) + { + sb.sprintf (GTXT (" %s: line %d `%s %s %s'\n"), + ret, line_no, cmd, arglist[0], arglist[1]); + Emsg *m = new Emsg (CMSG_COMMENT, sb); + commentq->append (m); + } + break; + } +#ifdef sparc + //XXX: should be conditional on the experiment ARCH, not dbe ARCH + case IGNORE_NO_XHWCPROF: + // ignore absence of -xhwcprof info for dataspace profiling + set_ignore_no_xhwcprof (true); + break; +#endif // sparc + case IGNORE_FS_WARN: + // ignore file system warning in experiments + set_ignore_fs_warn (true); + break; + case OBJECT_SHOW: + // Add the named libraries to the lib_expands array + set_libexpand (arglist[0], LIBEX_SHOW, true); + break; + case OBJECT_HIDE: + // Add the named libraries to the lib_expands array + set_libexpand (arglist[0], LIBEX_HIDE, true); + break; + case OBJECT_API: + // Add the named libraries to the lib_expands array + set_libexpand (arglist[0], LIBEX_API, true); + break; + case COMMENT: + // ignore the line + break; + default: + { + // unexpected command in an rc file + if (!msg) + { + // if quiet, can remain so no longer + msg = true; + Emsg *m = new Emsg (CMSG_COMMENT, GTXT ("Processed system gprofng.rc file for default settings")); + commentq->append (m); + } + sb.sprintf (GTXT (" Unrecognized .gprofng.rc command on line %d: `%.64s'"), + line_no, cmd); + Emsg *m = new Emsg (CMSG_COMMENT, sb); + commentq->append (m); + break; + } + } + free (script); + } + fclose (fptr); +} + +Cmd_status +Settings::set_view_mode (char *arg, bool rc) +{ + if (!strcasecmp (arg, NTXT ("user"))) + view_mode = VMODE_USER; + else if (!strcasecmp (arg, NTXT ("expert"))) + view_mode = VMODE_EXPERT; + else if (!strcasecmp (arg, NTXT ("machine"))) + view_mode = VMODE_MACHINE; + else if (!rc) + return CMD_BAD_ARG; + return CMD_OK; +} + +Cmd_status +Settings::set_en_desc (char *arg, bool rc) +{ + regex_t *regex_desc = NULL; + + // cases below should be similar to Coll_Ctrl::set_follow_mode() cases + if (!strcasecmp (arg, NTXT ("on"))) + en_desc = true; + else if (!strcasecmp (arg, NTXT ("off"))) + en_desc = false; + else if (arg[0] == '=' && arg[1] != 0) + { + // user has specified a string matching specification + int ercode; + { // compile regex_desc + char * str = dbe_sprintf (NTXT ("^%s$"), arg + 1); + regex_desc = new regex_t; + memset (regex_desc, 0, sizeof (regex_t)); + ercode = regcomp (regex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + free (str); + } + if (ercode) + { + // syntax error in parsing string + delete regex_desc; + if (!rc) + return CMD_BAD_ARG; + return CMD_OK; + } + en_desc = true; + } + else + { + if (!rc) + return CMD_BAD_ARG; + return CMD_OK; + } + free (en_desc_usr); + en_desc_usr = dbe_strdup (arg); + if (en_desc_cmp) + { + regfree (en_desc_cmp); + delete en_desc_cmp; + } + en_desc_cmp = regex_desc; + return CMD_OK; +} + +// See if a descendant matches either the lineage or the executable name +bool +Settings::check_en_desc (const char *lineage, const char *targname) +{ + bool rc; + if (en_desc_cmp == NULL) + return en_desc; // no specification was set, use the binary on/off value + if (lineage == NULL) // user doesn't care about specification + return en_desc; // use the binary on/off specification + if (!regexec (en_desc_cmp, lineage, 0, NULL, 0)) + rc = true; // this one matches user specification + else if (targname == NULL) + rc = false; //a NULL name does not match any expression + else if (!regexec (en_desc_cmp, targname, 0, NULL, 0)) + rc = true; // this one matches the executable name + else + rc = false; + return rc; +} + +char * +Settings::set_limit (char *arg, bool) +{ + limit = (int) strtol (arg, (char **) NULL, 10); + return NULL; +} + +char * +Settings::set_printmode (char *arg) +{ + if (arg == NULL) + return dbe_sprintf (GTXT ("The argument to '%s' must be '%s' or '%s' or a single-character"), + NTXT ("printmode"), NTXT ("text"), NTXT ("html")); + if (strlen (arg) == 1) + { + print_mode = PM_DELIM_SEP_LIST; + print_delim = arg[0]; + } + else if (!strcasecmp (arg, NTXT ("text"))) + print_mode = PM_TEXT; + else if (!strcasecmp (arg, NTXT ("html"))) + print_mode = PM_HTML; + else + return dbe_sprintf (GTXT ("The argument to '%s' must be '%s' or '%s' or a single-character"), + NTXT ("printmode"), NTXT ("text"), NTXT ("html")); + free (str_printmode); + str_printmode = dbe_strdup (arg); + return NULL; +} + +Cmd_status +Settings::proc_compcom (const char *cmd, bool isSrc, bool rc) +{ + int ck_compcom_bits, ck_threshold; + bool ck_hex_visible = false; + bool ck_src_visible = false; + bool ck_srcmetric_visible = false; + bool got_compcom_bits, got_threshold, got_src_visible, got_srcmetric_visible; + bool got_hex_visible, got; + int len, i; + char *mcmd, *param; + int flag, value = 0; + Cmd_status status; + char buf[BUFSIZ], *list; + + if (cmd == NULL) + return CMD_BAD; + ck_compcom_bits = 0; + ck_threshold = 0; + got_compcom_bits = got_threshold = got_src_visible = false; + got_srcmetric_visible = got_hex_visible = false; + snprintf (buf, sizeof (buf), NTXT ("%s"), cmd); + list = buf; + while ((mcmd = strtok (list, NTXT (":"))) != NULL) + { + list = NULL; + // if "all" or "none" + if (!strcasecmp (mcmd, Command::ALL_CMD)) + { + got_compcom_bits = true; + ck_compcom_bits = CCMV_ALL; + continue; + } + else if (!strcasecmp (mcmd, Command::NONE_CMD)) + { + got_compcom_bits = true; + ck_compcom_bits = 0; + continue; + } + + // Find parameter after '=' + param = strchr (mcmd, '='); + if (param) + { + *param = '\0'; + param++; + } + status = CMD_OK; + got = false; + flag = 0; + len = (int) strlen (mcmd); + for (i = 0; status == CMD_OK && i < comp_size; i++) + if (!strncasecmp (mcmd, comp_cmd[i], len)) + { + if (got) // Ambiguous comp_com command + status = CMD_AMBIGUOUS; + else + { + got = true; + flag = comp_vis[i]; + // Check argument + if (flag == COMP_THRESHOLD) + { + if (param == NULL) + status = CMD_BAD_ARG; + else + { + value = (int) strtol (param, ¶m, 10); + if (value < 0 || value > 100) + status = CMD_OUTRANGE; + } + } + else if (param != NULL) + status = CMD_BAD_ARG; + } + } + + // Not valid comp_com command + if (!got) + status = CMD_INVALID; + if (status != CMD_OK) + { + if (!rc) + return status; + continue; + } + + // Set bits + switch (flag) + { + case COMP_CMPLINE: + cmpline_visible = true; + break; + case COMP_FUNCLINE: + funcline_visible = true; + break; + case COMP_THRESHOLD: + got_threshold = true; + ck_threshold = value; + break; + case COMP_SRC: + got_src_visible = true; + ck_src_visible = true; + break; + case COMP_SRC_METRIC: + got_srcmetric_visible = true; + ck_srcmetric_visible = true; + got_src_visible = true; + ck_src_visible = true; + break; + case COMP_NOSRC: + got_src_visible = true; + ck_src_visible = false; + break; + case COMP_HEX: + got_hex_visible = true; + ck_hex_visible = true; + break; + case COMP_NOHEX: + got_hex_visible = true; + ck_hex_visible = false; + break; + case CCMV_BASIC: + got_compcom_bits = true; + ck_compcom_bits = CCMV_BASIC; + break; + default: + got_compcom_bits = true; + ck_compcom_bits |= flag; + } + } + + // No error, update + if (got_compcom_bits) + { + if (isSrc) + src_compcom = ck_compcom_bits; + else + dis_compcom = ck_compcom_bits; + } + if (got_threshold) + { + if (isSrc) + threshold_src = ck_threshold; + else + threshold_dis = ck_threshold; + } + if (got_src_visible) + src_visible = ck_src_visible; + if (got_srcmetric_visible) + srcmetric_visible = ck_srcmetric_visible; + if (got_hex_visible) + hex_visible = ck_hex_visible; + return CMD_OK; +} + +// Process a threshold setting +Cmd_status +Settings::proc_thresh (char *cmd, bool isSrc, bool rc) +{ + int value; + if (cmd == NULL) + value = DEFAULT_SRC_DIS_THRESHOLD; // the default + else + value = (int) strtol (cmd, &cmd, 10); + if (value < 0 || value > 100) + { + if (!rc) + return CMD_OUTRANGE; + value = DEFAULT_SRC_DIS_THRESHOLD; + } + if (isSrc) + threshold_src = value; + else + threshold_dis = value; + return CMD_OK; +} + +// return any error string from processing visibility settings +char * +Settings::get_compcom_errstr (Cmd_status status, const char *cmd) +{ + int i; + StringBuilder sb; + switch (status) + { + case CMD_BAD: + sb.append (GTXT ("No commentary classes has been specified.")); + break; + case CMD_AMBIGUOUS: + sb.append (GTXT ("Ambiguous commentary classes: ")); + break; + case CMD_BAD_ARG: + sb.append (GTXT ("Invalid argument for commentary classes: ")); + break; + case CMD_OUTRANGE: + sb.append (GTXT ("Out of range commentary classes argument: ")); + break; + case CMD_INVALID: + sb.append (GTXT ("Invalid commentary classes: ")); + break; + case CMD_OK: + break; + } + if (cmd) + sb.append (cmd); + sb.append (GTXT ("\nAvailable commentary classes: ")); + for (i = 0; i < comp_size; i++) + { + sb.append (comp_cmd[i]); + if (i == comp_size - 1) + sb.append (NTXT ("=#\n")); + else + sb.append (NTXT (":")); + } + return sb.toString (); +} + +// Process a timeline-mode setting +Cmd_status +Settings::proc_tlmode (char *cmd, bool rc) +{ + bool got_tlmode, got_stack_align, got_stack_depth, got; + int ck_tlmode = 0, ck_stack_align = 0, ck_stack_depth = 0; + int len, i; + char *mcmd, *param; + int cmd_id, value = 0; + TLModeSubcommand cmd_type; + Cmd_status status; + char buf[BUFSIZ], *list; + if (cmd == NULL) + return CMD_BAD; + got_tlmode = got_stack_align = got_stack_depth = false; + snprintf (buf, sizeof (buf), NTXT ("%s"), cmd); + list = buf; + while ((mcmd = strtok (list, NTXT (":"))) != NULL) + { + list = NULL; + + // Find parameter after '=' + param = strchr (mcmd, '='); + if (param) + { + *param = '\0'; + param++; + } + status = CMD_OK; + got = false; + cmd_id = 0; + cmd_type = TLCMD_INVALID; + len = (int) strlen (mcmd); + for (i = 0; status == CMD_OK && i < tlmode_size; i++) + { + if (!strncasecmp (mcmd, tlmode_cmd[i].cmdText, len)) + { + if (got) // Ambiguous timeline mode + status = CMD_AMBIGUOUS; + else + { + got = true; + cmd_type = tlmode_cmd[i].cmdType; + cmd_id = tlmode_cmd[i].cmdId; + + // Check argument + if (cmd_type == TLCMD_DEPTH) + { + if (param == NULL) + status = CMD_BAD_ARG; + else + { + value = (int) strtol (param, ¶m, 10); + if (value <= 0 || value > 256) + status = CMD_OUTRANGE; + } + } + else if (param != NULL) + status = CMD_BAD_ARG; + } + } + } + + // Not valid timeline mode + if (!got) + status = CMD_INVALID; + if (status != CMD_OK) + { + if (!rc) + return status; + continue; + } + + // Set bits + switch (cmd_type) + { + case TLCMD_ENTITY_MODE: + got_tlmode = true; + ck_tlmode = cmd_id; + break; + case TLCMD_ALIGN: + got_stack_align = true; + ck_stack_align = cmd_id; + break; + case TLCMD_DEPTH: + got_stack_depth = true; + ck_stack_depth = value; + break; + default: + break; + } + } + + // No error, update + if (got_tlmode) + tlmode = ck_tlmode; + if (got_stack_align) + stack_align = ck_stack_align; + if (got_stack_depth) + stack_depth = ck_stack_depth; + return CMD_OK; +} + +// Process timeline data specification +Cmd_status +Settings::proc_tldata (const char *cmd, bool /* if true, ignore any error */) +{ + free (tldata); + tldata = dbe_strdup (cmd); // let GUI parse it + return CMD_OK; +} + +void +Settings::set_tldata (const char* _tldata_str) +{ + free (tldata); + tldata = dbe_strdup (_tldata_str); +} + +char* +Settings::get_tldata () +{ + return dbe_strdup (tldata); +} + +Cmd_status +Settings::set_name_format (char *arg) +{ + char *colon = strchr (arg, ':'); + size_t arg_len = (colon) ? (colon - arg) : strlen (arg); + Histable::NameFormat fname_fmt = Histable::NA; + if (!strncasecmp (arg, NTXT ("long"), arg_len)) + fname_fmt = Histable::LONG; + else if (!strncasecmp (arg, NTXT ("short"), arg_len)) + fname_fmt = Histable::SHORT; + else if (!strncasecmp (arg, NTXT ("mangled"), arg_len)) + fname_fmt = Histable::MANGLED; + else + return CMD_BAD_ARG; + + bool soname_fmt = false; + if (colon && (colon + 1)) + { + colon++; + if (!strcasecmp (colon, NTXT ("soname"))) + soname_fmt = true; + else if (!strcasecmp (colon, NTXT ("nosoname"))) + soname_fmt = false; + else + return CMD_BAD_ARG; + } + name_format = Histable::make_fmt (fname_fmt, soname_fmt); + return CMD_OK; +} + +void +Settings::buildMasterTabList () +{ + tab_list = new Vector<DispTab*>; + int i = -1; + + // Add tabs for all the known reports + tab_list->append (new DispTab (DSP_DEADLOCKS, i, false, DEADLOCK_EVNTS)); + tab_list->append (new DispTab (DSP_FUNCTION, i, false, FUNCS)); + tab_list->append (new DispTab (DSP_TIMELINE, i, false, TIMELINE)); + tab_list->append (new DispTab (DSP_CALLTREE, i, false, CALLTREE)); + tab_list->append (new DispTab (DSP_CALLFLAME, i, false, CALLFLAME)); + tab_list->append (new DispTab (DSP_DUALSOURCE, i, false, DUALSOURCE)); + tab_list->append (new DispTab (DSP_SOURCE_DISASM, i, false, SOURCEDISAM)); + tab_list->append (new DispTab (DSP_SOURCE, i, false, SOURCE)); + tab_list->append (new DispTab (DSP_LINE, i, false, HOTLINES)); + tab_list->append (new DispTab (DSP_DISASM, i, false, DISASM)); + tab_list->append (new DispTab (DSP_PC, i, false, HOTPCS)); + tab_list->append (new DispTab (DSP_LEAKLIST, i, false, LEAKS)); + tab_list->append (new DispTab (DSP_IOACTIVITY, i, false, IOACTIVITY)); + tab_list->append (new DispTab (DSP_HEAPCALLSTACK, i, false, HEAP)); + tab_list->append (new DispTab (DSP_IFREQ, i, false, IFREQ)); + tab_list->append (new DispTab (DSP_CALLER, i, false, GPROF)); + tab_list->append (new DispTab (DSP_STATIS, i, false, STATISTICS)); + tab_list->append (new DispTab (DSP_EXP, i, false, HEADER)); +} + +// Update tablist based on data availability +void +Settings::updateTabAvailability () +{ + int index; + DispTab *dsptab; + + Vec_loop (DispTab*, tab_list, index, dsptab) + { + if (dsptab->type == DSP_DATAOBJ) + dsptab->setAvailability (dbeSession->is_datamode_available ()); + else if (dsptab->type == DSP_DLAYOUT) + dsptab->setAvailability (dbeSession->is_datamode_available ()); + else if (dsptab->type == DSP_LEAKLIST) + dsptab->setAvailability (false); + else if (dsptab->type == DSP_IOACTIVITY) + dsptab->setAvailability (dbeSession->is_iodata_available ()); + else if (dsptab->type == DSP_HEAPCALLSTACK) + dsptab->setAvailability (dbeSession->is_heapdata_available ()); + else if (dsptab->type == DSP_TIMELINE) + dsptab->setAvailability (dbeSession->is_timeline_available ()); + else if (dsptab->type == DSP_IFREQ) + dsptab->setAvailability (dbeSession->is_ifreq_available ()); + else if (dsptab->type == DSP_RACES) + dsptab->setAvailability (dbeSession->is_racelist_available ()); + else if (dsptab->type == DSP_DEADLOCKS) + dsptab->setAvailability (dbeSession->is_deadlocklist_available ()); + else if (dsptab->type == DSP_DUALSOURCE) + dsptab->setAvailability (dbeSession->is_racelist_available () + || dbeSession->is_deadlocklist_available ()); + } +} + +// Process a tab setting +Cmd_status +Settings::proc_tabs (bool _rdtMode) +{ + int arg_cnt, cparam; + int count = 0; + int index; + DispTab *dsptab; + char *cmd; + if (tabs_processed == true) + return CMD_OK; + tabs_processed = true; + if (_rdtMode == true) + { + if (str_rtabs == NULL) + str_rtabs = strdup ("header"); + cmd = str_rtabs; + } + else + { + if (str_tabs == NULL) + str_tabs = strdup ("header"); + cmd = str_tabs; + } + if (strcmp (cmd, NTXT ("none")) == 0) + return CMD_OK; + Vector <char *> *tokens = split_str (cmd, ':'); + for (long j = 0, sz = VecSize (tokens); j < sz; j++) + { + char *tabname = tokens->get (j); + // search for this tab command token + CmdType c = Command::get_command (tabname, arg_cnt, cparam); + if (c == INDXOBJ) + { + // set the bit for this subtype + indx_tab_state->store (cparam, true); + indx_tab_order->store (cparam, count++); + } + else + { + // search for this tab type in the regular tabs + Vec_loop (DispTab*, tab_list, index, dsptab) + { + if (dsptab->cmdtoken == c) + { + dsptab->visible = true; + dsptab->order = count++; + break; + } + } + } + free (tabname); + } + delete tokens; + return CMD_OK; +} + +void +Settings::set_MemTabState (Vector<bool>*selected) +{ + if (selected->size () == 0) + return; + for (int j = 0; j < mem_tab_state->size (); j++) + mem_tab_state->store (j, selected->fetch (j)); +} + +// define a new memory object type + +void +Settings::mobj_define (MemObjType_t */* mobj */, bool state) +{ + if (mem_tab_state->size () == 0) + state = true; + mem_tab_state->append (state); + mem_tab_order->append (-1); +} + +void +Settings::set_IndxTabState (Vector<bool>*selected) +{ + for (int j = 0; j < selected->size (); j++) + indx_tab_state->store (j, selected->fetch (j)); +} + +// define a new index object type +void +Settings::indxobj_define (int type, bool state) +{ + indx_tab_state->store (type, state); + indx_tab_order->store (type, -1); +} + +void +Settings::set_pathmaps (Vector<pathmap_t*> *newPathMap) +{ + if (pathmaps) + { + pathmaps->destroy (); + delete pathmaps; + } + pathmaps = newPathMap; +} + +static char * +get_canonical_name (const char *fname) +{ + char *nm = dbe_strdup (fname); + for (size_t len = strlen (nm); (len > 0) && (nm[len - 1] == '/'); len--) + nm[len - 1] = 0; + return nm; +} + +char * +Settings::add_pathmap (Vector<pathmap_t*> *v, const char *from, const char *to) +{ + // Check for errors + if (from == NULL || to == NULL) + return dbe_strdup (GTXT ("Pathmap can have neither from nor to as NULL\n")); + if (strcmp (from, to) == 0) + return dbe_strdup (GTXT ("Pathmap from must differ from to\n")); + char *old_prefix = get_canonical_name (from); + char *new_prefix = get_canonical_name (to); + + // Check the pathmap list + for (int i = 0, sz = v->size (); i < sz; i++) + { + pathmap_t *pmp = v->get (i); + if ((strcmp (pmp->old_prefix, old_prefix) == 0) &&(strcmp (pmp->new_prefix, new_prefix) == 0)) + { + char *s = dbe_sprintf (GTXT ("Pathmap from `%s' to `%s' already exists\n"), old_prefix, new_prefix); + free (old_prefix); + free (new_prefix); + return s; + } + } + // construct a map for this pair + pathmap_t *thismap = new pathmap_t; + thismap->old_prefix = old_prefix; + thismap->new_prefix = new_prefix; + v->append (thismap); + return NULL; +} + +// Set all shared object expands back to .rc file defaults, +// as stored in the DbeSession Settings +bool +Settings::set_libdefaults () +{ + // See if this is unchanged + if (is_loexpand_default == true) + return false; // no change + + // replicate the DbeSession's lo_expand vector and default settings + lo_expand_t *this_lo_ex; + lo_expand_t *new_lo_ex; + int index; + lo_expand_default = dbeSession->get_settings ()->lo_expand_default; + lo_expands = new Vector<lo_expand_t*>; + Vec_loop (lo_expand_t*, dbeSession->get_settings ()->lo_expands, index, this_lo_ex) + { + new_lo_ex = new lo_expand_t; + new_lo_ex->libname = dbe_strdup (this_lo_ex->libname); + new_lo_ex->expand = this_lo_ex->expand; + lo_expands->append (new_lo_ex); + } + is_loexpand_default = true; + return true; +} + +bool +Settings::set_libexpand (char *cov, enum LibExpand expand, bool rc) +{ + int index; + lo_expand_t *loe; + bool change = false; + if (cov == NULL || !strcasecmp (cov, Command::ALL_CMD)) + { // set all libraries + // set the default + if (lo_expand_default != expand) + { + lo_expand_default = expand; + change = true; + is_loexpand_default = false; + } + + // and force any explicit settings to match, too + Vec_loop (lo_expand_t*, lo_expands, index, loe) + { + if (loe->expand != expand) + { + loe->expand = expand; + change = true; + is_loexpand_default = false; + } + } + + } + else + { // parsing coverage + Vector <char *> *tokens = split_str (cov, ','); + for (long j = 0, sz = VecSize (tokens); j < sz; j++) + { + char *lo_name = tokens->get (j); + char *newname = get_basename (lo_name); + bool found = false; + Vec_loop (lo_expand_t*, lo_expands, index, loe) + { + if (strcmp (loe->libname, newname) == 0) + { + if (loe->expand != expand) + { + if (rc == false) + { + loe->expand = expand; + change = true; + is_loexpand_default = false; + } + } + found = true; + break; + } + } + + if (found == false) + { + // construct a map for this pair + lo_expand_t *thisloe; + thisloe = new lo_expand_t; + thisloe->libname = dbe_strdup (newname); + thisloe->expand = expand; + change = true; + is_loexpand_default = false; + + // add it to the vector + lo_expands->append (thisloe); + } + free (lo_name); + } + delete tokens; + } + return change; +} + +enum LibExpand +Settings::get_lo_setting (char *name) +{ + int index; + lo_expand_t *loe; + char *lo_name = get_basename (name); + Vec_loop (lo_expand_t*, lo_expands, index, loe) + { + if (strcmp (loe->libname, lo_name) == 0) + return loe->expand; + } + return lo_expand_default; +} diff --git a/gprofng/src/Settings.h b/gprofng/src/Settings.h new file mode 100644 index 0000000..fc696e8 --- /dev/null +++ b/gprofng/src/Settings.h @@ -0,0 +1,425 @@ +/* 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. */ + +#ifndef _SETTINGS_H +#define _SETTINGS_H + +#include <stdio.h> +#include <regex.h> + +#include "gp-defs.h" +#include "Histable.h" +#include "MemorySpace.h" +#include "Metric.h" +#include "dbe_types.h" +#include "dbe_structs.h" +#include "enums.h" +#include "vec.h" + +class Emsgqueue; +class Application; + +struct DispTab; + +// Settings object + +class Settings +{ +public: + friend class DbeView; + friend class DbeSession; + + Settings (Application *_app); + Settings (Settings *_settings); + virtual ~Settings (); + void read_rc (bool ipc_or_rdt_mode); // read all rc files + char *read_rc (char *path); // read rc file + void buildMasterTabList (); // build list of Tabs that can be invoked + void updateTabAvailability (); // update for datamode, leaklist + Cmd_status set_name_format (char *str); // from a string + + Vector<DispTab*> * + get_TabList () // Get the list of tabs for this view + { + return tab_list; + } + + Vector<bool> * + get_MemTabState () // Get the list and order of memory tabs for this view + { + return mem_tab_state; + } + + Vector<int> * + get_MemTabOrder () + { + return mem_tab_order; + } + + // Set the list of memory tabs for this view + void set_MemTabState (Vector<bool>*sel); + + // add a newly-defined memory object tab + void mobj_define (MemObjType_t *, bool state); + + // add a newly-defined index object tab + void indxobj_define (int type, bool state); + + Vector<bool> * + get_IndxTabState () // Get the list and order of index tabs for this view + { + return indx_tab_state; + } + + Vector<int> * + get_IndxTabOrder () + { + return indx_tab_order; + } + + // Set the list of index tabs for this view + void set_IndxTabState (Vector<bool>*sel); + + void + set_name_format (int fname_fmt, bool soname_fmt) + { + name_format = Histable::make_fmt (fname_fmt, soname_fmt); + } + + Histable::NameFormat + get_name_format () + { + return name_format; + } + + // public methods for setting and accessing the settings + Cmd_status set_view_mode (char *str, bool rc); // from a string + + void + set_view_mode (VMode mode) + { + view_mode = mode; + } + + VMode + get_view_mode () + { + return view_mode; + } + + // set the en_desc expression/on/off + Cmd_status set_en_desc (char *str, bool rc); // from a string + // check if the lineage or the target name matches the en_desc expression + bool check_en_desc (const char *lineage, const char *targname); + + char *set_limit (char *str, bool rc); // from a string + + char * + set_limit (int _limit) + { + limit = _limit; + return NULL; + } + + int + get_limit () + { + return limit; + } + + char *set_printmode (char *_pmode); + + // processing compiler commentary visibility bits + Cmd_status proc_compcom (const char *cmd, bool isSrc, bool rc); + + // return any error string from processing visibility settings + char *get_compcom_errstr (Cmd_status status, const char *cmd); + + // methods for setting and getting strings, and individual settings + + char * + get_str_scompcom () + { + return str_scompcom; + } + + char * + get_str_dcompcom () + { + return str_dcompcom; + } + + int + get_src_compcom () + { + return src_compcom; + } + + int + get_dis_compcom () + { + return dis_compcom; + } + + void + set_cmpline_visible (bool v) + { + cmpline_visible = v; + } + + void + set_funcline_visible (bool v) + { + funcline_visible = v; + } + + void + set_src_visible (int v) + { + src_visible = v; + } + + int + get_src_visible () + { + return src_visible; + } + + void + set_srcmetric_visible (bool v) + { + srcmetric_visible = v; + } + + bool + get_srcmetric_visible () + { + return srcmetric_visible; + } + + void + set_hex_visible (bool v) + { + hex_visible = v; + } + + bool + get_hex_visible () + { + return hex_visible; + } + + // processing and accessing the threshold settings + Cmd_status proc_thresh (char *cmd, bool isSrc, bool rc); + + int + get_thresh_src () + { + return threshold_src; + } + + int + get_thresh_dis () + { + return threshold_dis; + } + + // process a tlmode setting + Cmd_status proc_tlmode (char *cmd, bool rc); + + void + set_tlmode (int _tlmode) + { + tlmode = _tlmode; + } + + int + get_tlmode () + { + return tlmode; + } + + void + set_stack_align (int _stack_align) + { + stack_align = _stack_align; + } + + int + get_stack_align () + { + return stack_align; + } + + void + set_stack_depth (int _stack_depth) + { + stack_depth = _stack_depth; + } + + int + get_stack_depth () + { + return stack_depth; + } + + // process a tabs setting: called when the tab list is requested + Cmd_status proc_tabs (bool _rdtMode); + + Cmd_status proc_tldata (const char *cmd, bool rc); // process a tldata setting + void set_tldata (const char* tldata_string); + char *get_tldata (); + + char * + get_default_metrics () + { + return str_dmetrics; + } + + char * + get_default_sort () + { + return str_dsort; + } + + void + set_ignore_no_xhwcprof (bool v) // ignore no xhwcprof errors for dataspace + { + ignore_no_xhwcprof = v; + } + + bool + get_ignore_no_xhwcprof () + { + return ignore_no_xhwcprof; + } + + void + set_ignore_fs_warn (bool v) // ignore filesystem warnings in experiments + { + ignore_fs_warn = v; + } + + bool + get_ignore_fs_warn () + { + return ignore_fs_warn; + } + + // add a pathmap + static char *add_pathmap (Vector<pathmap_t*> *v, const char *from, const char *to); + void set_pathmaps (Vector<pathmap_t*> *newPathMap); + + // add a LoadObject expansion setting + bool set_libexpand (char *, enum LibExpand, bool); + enum LibExpand get_lo_setting (char *); + + // set LoadObject expansion defaults back to .rc specifications + bool set_libdefaults (); + + void + set_compare_mode (int mode) + { + compare_mode = mode; + } + + int + get_compare_mode () + { + return compare_mode; + } + + char * + get_machinemodel () + { + return dbe_strdup (machinemodel); + } + + char *preload_libdirs; + +protected: // data + Application *app; + + // default strings from .rc file + char *str_vmode; + char *str_en_desc; + char *str_datamode; + char *str_scompcom; + char *str_sthresh; + char *str_dcompcom; + char *str_dthresh; + char *str_dmetrics; + char *str_dsort; + char *str_tlmode; + char *str_tldata; + char *str_tabs; + char *str_rtabs; + char *str_search_path; + char *str_name_format; + char *str_limit; + char *str_printmode; + char *str_compare; + + bool tabs_processed; + + // Processed settings + bool en_desc; // controls for reading descendant processes + char * en_desc_usr; // selective descendants: user specificaton + regex_t * en_desc_cmp; // selective descendants: compiled specification + Histable::NameFormat name_format; // long/short/mangled naming for C++/Java + VMode view_mode; // Java mode + int src_compcom; // compiler commentary visibility for anno-src + int dis_compcom; // compiler commentary visibility for anno-dis + int threshold_src; // threshold for anno-src + int threshold_dis; // threshold for anno-dis + int cmpline_visible; // show compile-line flags + int funcline_visible; // show compile-line flags + int src_visible; // show source in disasm + bool srcmetric_visible; // show metrics for source in disasm + bool hex_visible; // show hex code in disasm + char* tldata; // timeline data type string + int tlmode; // timeline mode for bars + int stack_align; // timeline stack alignment + int stack_depth; // timeline stack depth + int limit; // print limit + enum PrintMode print_mode;// print mode + char print_delim; // the delimiter, if print mode = PM_DELIM_SEP_LIST + int compare_mode; // compare mode + + char *machinemodel; // machine model for Memory Objects + + bool ignore_no_xhwcprof; // ignore no -xhwcprof data in dataspace + bool ignore_fs_warn; // ignore file-system recording warning + + void set_rc (const char *path, bool msg, Emsgqueue *commentq, + bool override, bool ipc_or_rdt_mode = false); + + Vector<DispTab*> *tab_list; + Vector<pathmap_t*> *pathmaps; + Vector<lo_expand_t*> *lo_expands; + enum LibExpand lo_expand_default; + bool is_loexpand_default; + Vector<bool> *mem_tab_state; + Vector<int> *mem_tab_order; + Vector<bool> *indx_tab_state; + Vector<int> *indx_tab_order; +}; + +#endif /* ! _SETTINGS_H */ diff --git a/gprofng/src/SourceFile.cc b/gprofng/src/SourceFile.cc new file mode 100644 index 0000000..bd0a1f1 --- /dev/null +++ b/gprofng/src/SourceFile.cc @@ -0,0 +1,229 @@ +/* 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 <unistd.h> + +#include "util.h" +#include "DbeSession.h" +#include "Function.h" +#include "SourceFile.h" +#include "DefaultMap.h" +#include "DbeFile.h" +#include "LoadObject.h" +#include "Module.h" + +int SourceFile::curId = 0; + +SourceFile::SourceFile (const char *file_name) +{ + status = OS_NOTREAD; + srcLines = NULL; + srcInode = -1; + lines = NULL; + dbeLines = NULL; + functions = new DefaultMap<Function *, Function *>(); + dbeFile = new DbeFile (file_name); + dbeFile->filetype |= DbeFile::F_SOURCE | DbeFile::F_FILE; + set_name ((char *) file_name); + srcMTime = (time_t) 0; + isTmpFile = false; + flags = 0; + read_stabs = false; + id = (uint64_t) ((Histable::SOURCEFILE << 24) + curId) << 32; + curId++; +} + +SourceFile::~SourceFile () +{ + destroy_map (DbeLine *, dbeLines); + delete functions; + delete dbeFile; + if (lines) + { + lines->destroy (); + delete lines; + } + if (srcLines) + { + free (srcLines->get (0)); + delete srcLines; + } + if (isTmpFile) + unlink (name); +} + +void +SourceFile::set_name (char* _name) +{ + name = dbe_strdup (_name); +} + +char* +SourceFile::get_name (NameFormat) +{ + return name; +} + +bool +SourceFile::readSource () +{ + if (srcLines) + return true; + status = OS_NOSRC; + char *location = dbeFile->get_location (); + if (location == NULL) + return false; + if (!isTmpFile) + srcMTime = dbeFile->sbuf.st_mtime; + srcInode = dbeFile->sbuf.st_ino; + size_t srcLen = dbeFile->sbuf.st_size; + int fd = open64 (location, O_RDONLY); + if (fd == -1) + { + status = OS_NOSRC; + return false; + } + char *srcMap = (char *) malloc (srcLen + 1); + int64_t sz = read_from_file (fd, srcMap, srcLen); + if (sz != (int64_t) srcLen) + append_msg (CMSG_ERROR, GTXT ("%s: Can read only %lld bytes instead %lld"), + location, (long long) sz, (long long) srcLen); + srcMap[sz] = 0; + close (fd); + + // Count the number of lines in the file, converting <nl> to zero + srcLines = new Vector<char*>(); + srcLines->append (srcMap); + for (int64_t i = 0; i < sz; i++) + { + if (srcMap[i] == '\r') + { // Window style + srcMap[i] = 0; + if (i + 1 < sz && srcMap[i + 1] != '\n') + srcLines->append (srcMap + i + 1); + } + else if (srcMap[i] == '\n') + { + srcMap[i] = '\0'; + if (i + 1 < sz) + srcLines->append (srcMap + i + 1); + } + } + if (dbeLines) + { + Vector<DbeLine *> *v = dbeLines->values (); + for (long i = 0, sz1 = v ? v->size () : 0; i < sz1; i++) + { + DbeLine *p = v->get (i); + if (p->lineno >= srcLines->size ()) + append_msg (CMSG_ERROR, GTXT ("Wrong line number %d. '%s' has only %d lines"), + p->lineno, dbeFile->get_location (), srcLines->size ()); + } + delete v; + } + status = OS_OK; + return true; +} + +char * +SourceFile::getLine (int lineno) +{ + assert (srcLines != NULL); + if (lineno > 0 && lineno <= srcLines->size ()) + return srcLines->get (lineno - 1); + return NTXT (""); +} + +DbeLine * +SourceFile::find_dbeline (Function *func, int lineno) +{ + if (lineno < 0 || (lineno == 0 && func == NULL)) + return NULL; + DbeLine *dbeLine = NULL; + if (lines) + { // the source is available + if (lineno > lines->size ()) + { + if (dbeLines) + dbeLine = dbeLines->get (lineno); + if (dbeLine == NULL) + append_msg (CMSG_ERROR, + GTXT ("Wrong line number %d. '%s' has only %d lines"), + lineno, dbeFile->get_location (), lines->size ()); + } + else + { + dbeLine = lines->fetch (lineno); + if (dbeLine == NULL) + { + dbeLine = new DbeLine (NULL, this, lineno); + lines->store (lineno, dbeLine); + } + } + } + if (dbeLine == NULL) + { // the source is not yet read or lineno is wrong + if (dbeLines == NULL) + dbeLines = new DefaultMap<int, DbeLine *>(); + dbeLine = dbeLines->get (lineno); + if (dbeLine == NULL) + { + dbeLine = new DbeLine (NULL, this, lineno); + dbeLines->put (lineno, dbeLine); + } + } + + for (DbeLine *last = dbeLine;; last = last->dbeline_func_next) + { + if (last->func == func) + return last; + if (last->dbeline_func_next == NULL) + { + DbeLine *dl = new DbeLine (func, this, lineno); + if (functions->get (func) == NULL) + functions->put (func, func); + last->dbeline_func_next = dl; + dl->dbeline_base = dbeLine; + return dl; + } + } +} + +Vector<Function *> * +SourceFile::get_functions () +{ + if (!read_stabs) + { + // Create all DbeLines for this Source + read_stabs = true; + Vector<LoadObject *> *lobjs = dbeSession->get_LoadObjects (); + for (long i = 0, sz = VecSize (lobjs); i < sz; i++) + { + LoadObject *lo = lobjs->get (i); + for (long i1 = 0, sz1 = VecSize (lo->seg_modules); i1 < sz1; i1++) + { + Module *mod = lo->seg_modules->get (i1); + mod->read_stabs (); + } + } + } + return functions->keySet (); +} diff --git a/gprofng/src/SourceFile.h b/gprofng/src/SourceFile.h new file mode 100644 index 0000000..8e90682 --- /dev/null +++ b/gprofng/src/SourceFile.h @@ -0,0 +1,117 @@ +/* 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. */ + +#ifndef _SOURCEFILE_H +#define _SOURCEFILE_H + +#include "Histable.h" +#include "Map.h" + +template <typename Key_t, typename Value_t> class Map; + +#define SOURCE_FLAG_UNKNOWN 0x01 + +class SourceFile : public HistableFile +{ +public: + + enum OpenStatus + { + OS_OK, + OS_NOTREAD, + OS_NOSRC, + OS_TIMESRC + }; + + SourceFile (const char *file_name); + virtual ~SourceFile (); + virtual void set_name (char *); + virtual char *get_name (NameFormat = NA); + + bool readSource (); + Vector<Function *> *get_functions (); + DbeLine *find_dbeline (Function *func, int lineno); + char *getLine (int lineno); + + int + getLineCount () + { + return srcLines ? srcLines->size () : 0; + } + + ino64_t + getInode () + { + return srcInode; + } + + time_t + getMTime () + { + return srcMTime; + } + + void + setMTime (time_t tm) + { + srcMTime = tm; + } + + bool + isTmp () + { + return isTmpFile; + } + + void + setTmp (bool set) + { + isTmpFile = set; + } + + Histable_type + get_type () + { + return SOURCEFILE; + } + + DbeLine * + find_dbeline (int lineno) + { + return find_dbeline (NULL, lineno); + } + + unsigned int flags; + +private: + static int curId; // object id + OpenStatus status; + ino64_t srcInode; // Inode number of source file + time_t srcMTime; // Creating time for source + Vector<char *> *srcLines; // array of pointers to lines in source + bool isTmpFile; // Temporary src file to be deleted + + Vector<DbeLine*> *lines; + Map<int, DbeLine*> *dbeLines; + Map<Function *, Function *> *functions; + bool read_stabs; +}; + +#endif diff --git a/gprofng/src/Stabs.cc b/gprofng/src/Stabs.cc new file mode 100644 index 0000000..9f1247d --- /dev/null +++ b/gprofng/src/Stabs.cc @@ -0,0 +1,2650 @@ +/* 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 <sys/param.h> + +#include "util.h" +#include "Elf.h" +#include "Dwarf.h" +#include "stab.h" +#include "DbeSession.h" +#include "CompCom.h" +#include "Stabs.h" +#include "LoadObject.h" +#include "Module.h" +#include "Function.h" +#include "info.h" +#include "StringBuilder.h" +#include "DbeFile.h" +#include "StringMap.h" + +#define DISASM_REL_NONE 0 /* symtab search only */ +#define DISASM_REL_ONLY 1 /* relocation search only */ +#define DISASM_REL_TARG 2 /* relocatoin then symtab */ + +/////////////////////////////////////////////////////////////////////////////// +// class StabReader +class StabReader +{ +public: + StabReader (Elf *_elf, Platform_t platform, int StabSec, int StabStrSec); + ~StabReader () { }; + char *get_type_name (int t); + char *get_stab (struct stab *np, bool comdat); + void parse_N_OPT (Module *mod, char *str); + int stabCnt; + int stabNum; + +private: + Elf *elf; + char *StabData; + char *StabStrtab; + char *StabStrtabEnd; + int StrTabSize; + int StabEntSize; +}; + +/////////////////////////////////////////////////////////////////////////////// +// class Symbol + +class Symbol +{ +public: + Symbol (Vector<Symbol*> *vec = NULL); + + ~Symbol () + { + free (name); + } + + inline Symbol * + cardinal () + { + return alias ? alias : this; + } + + static void dump (Vector<Symbol*> *vec, char*msg); + + Function *func; + Sp_lang_code lang_code; + uint64_t value; // st_value used in sym_name() + uint64_t save; + int64_t size; + uint64_t img_offset; // image offset in the ELF file + char *name; + Symbol *alias; + int local_ind; + int flags; + bool defined; +}; + +Symbol::Symbol (Vector<Symbol*> *vec) +{ + func = NULL; + lang_code = Sp_lang_unknown; + value = 0; + save = 0; + size = 0; + img_offset = 0; + name = NULL; + alias = NULL; + local_ind = -1; + flags = 0; + defined = false; + if (vec) + vec->append (this); +} + +void +Symbol::dump (Vector<Symbol*> *vec, char*msg) +{ + if (!DUMP_ELF_SYM || vec == NULL || vec->size () == 0) + return; + printf (NTXT ("======= Symbol::dump: %s =========\n" + " value | img_offset | flags|local_ind|\n"), msg); + for (int i = 0; i < vec->size (); i++) + { + Symbol *sp = vec->fetch (i); + printf (NTXT (" %3d %8lld |0x%016llx |%5d |%8d |%s\n"), + i, (long long) sp->value, (long long) sp->img_offset, sp->flags, + sp->local_ind, sp->name ? sp->name : NTXT ("NULL")); + } + printf (NTXT ("\n===== END of Symbol::dump: %s =========\n\n"), msg); +} + +// end of class Symbol +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// class Reloc +class Reloc +{ +public: + Reloc (); + ~Reloc (); + uint64_t type; + uint64_t value; + uint64_t addend; + char *name; +}; + +Reloc::Reloc () +{ + type = 0; + value = 0; + addend = 0; + name = NULL; +} + +Reloc::~Reloc () +{ + free (name); +} +// end of class Reloc +/////////////////////////////////////////////////////////////////////////////// + +enum +{ + SYM_PLT = 1 << 0, + SYM_UNDEF = 1 << 1 +}; + +enum Section_type +{ + COMM1_SEC = 0x10000000, + COMM_SEC = 0x20000000, + INFO_SEC = 0x30000000, + LOOP_SEC = 0x40000000 +}; + +struct cpf_stabs_t +{ + uint32_t type; // Archive::AnalyzerInfoType + uint32_t offset; // offset in .__analyzer_info + Module *module; // table for appropriate Module +}; + +static char *get_info_com (int type, int32_t copy_inout); +static char *get_lp_com (unsigned hints, int parallel, char *dep); +static int ComCmp (const void *a, const void *b); +static ino64_t _src_inode = 0; +static char *_src_name; + +// Comparing name +static int +SymNameCmp (const void *a, const void *b) +{ + Symbol *item1 = *((Symbol **) a); + Symbol *item2 = *((Symbol **) b); + return (item1->name == NULL) ? -1 : + (item2->name == NULL) ? 1 : strcmp (item1->name, item2->name); +} + +// Comparing value: for sorting +static int +SymValueCmp (const void *a, const void *b) +{ + Symbol *item1 = *((Symbol **) a); + Symbol *item2 = *((Symbol **) b); + return (item1->value > item2->value) ? 1 : + (item1->value == item2->value) ? SymNameCmp (a, b) : -1; +} + +// Comparing value: for searching (source name is always NULL) +static int +SymFindCmp (const void *a, const void *b) +{ + Symbol *item1 = *((Symbol **) a); + Symbol *item2 = *((Symbol **) b); + if (item1->value < item2->value) + return -1; + if (item1->value < item2->value + item2->size + || item1->value == item2->value) // item2->size == 0 + return 0; + return 1; +} + +// Comparing value for sorting. It is used only for searching aliases. +static int +SymImgOffsetCmp (const void *a, const void *b) +{ + Symbol *item1 = *((Symbol **) a); + Symbol *item2 = *((Symbol **) b); + return (item1->img_offset > item2->img_offset) ? 1 : + (item1->img_offset == item2->img_offset) ? SymNameCmp (a, b) : -1; +} + +static int +RelValueCmp (const void *a, const void *b) +{ + Reloc *item1 = *((Reloc **) a); + Reloc *item2 = *((Reloc **) b); + return (item1->value > item2->value) ? 1 : + (item1->value == item2->value) ? 0 : -1; +} + +Stabs * +Stabs::NewStabs (char *_path, char *lo_name) +{ + Stabs *stabs = new Stabs (_path, lo_name); + if (stabs->status != Stabs::DBGD_ERR_NONE) + { + delete stabs; + return NULL; + } + return stabs; +} + +Stabs::Stabs (char *_path, char *_lo_name) +{ + path = dbe_strdup (_path); + lo_name = dbe_strdup (_lo_name); + SymLstByName = NULL; + pltSym = NULL; + SymLst = new Vector<Symbol*>; + RelLst = new Vector<Reloc*>; + RelPLTLst = new Vector<Reloc*>; + LocalLst = new Vector<Symbol*>; + LocalFile = new Vector<char*>; + LocalFileIdx = new Vector<int>; + last_PC_to_sym = NULL; + dwarf = NULL; + elfDbg = NULL; + elfDis = NULL; + stabsModules = NULL; + textsz = 0; + wsize = Wnone; + st_check_symtab = st_check_relocs = false; + status = DBGD_ERR_NONE; + + if (openElf (false) == NULL) + return; + switch (elfDis->elf_getclass ()) + { + case ELFCLASS32: + wsize = W32; + break; + case ELFCLASS64: + wsize = W64; + break; + } + isRelocatable = elfDis->elf_getehdr ()->e_type == ET_REL; + for (unsigned int pnum = 0; pnum < elfDis->elf_getehdr ()->e_phnum; pnum++) + { + Elf_Internal_Phdr *phdr = elfDis->get_phdr (pnum); + if (phdr->p_type == PT_LOAD && phdr->p_flags == (PF_R | PF_X)) + { + if (textsz == 0) + textsz = phdr->p_memsz; + else + { + textsz = 0; + break; + } + } + } +} + +Stabs::~Stabs () +{ + delete pltSym; + delete SymLstByName; + Destroy (SymLst); + Destroy (RelLst); + Destroy (RelPLTLst); + Destroy (LocalFile); + delete elfDis; + delete dwarf; + delete LocalLst; + delete LocalFileIdx; + delete stabsModules; + free (path); + free (lo_name); +} + +Elf * +Stabs::openElf (char *fname, Stab_status &st) +{ + Elf::Elf_status elf_status; + Elf *elf = Elf::elf_begin (fname, &elf_status); + if (elf == NULL) + { + switch (elf_status) + { + case Elf::ELF_ERR_CANT_OPEN_FILE: + case Elf::ELF_ERR_CANT_MMAP: + case Elf::ELF_ERR_BIG_FILE: + st = DBGD_ERR_CANT_OPEN_FILE; + break; + case Elf::ELF_ERR_BAD_ELF_FORMAT: + default: + st = DBGD_ERR_BAD_ELF_FORMAT; + break; + } + return NULL; + } + if (elf->elf_version (EV_CURRENT) == EV_NONE) + { + // ELF library out of date + delete elf; + st = DBGD_ERR_BAD_ELF_LIB; + return NULL; + } + + Elf_Internal_Ehdr *ehdrp = elf->elf_getehdr (); + if (ehdrp == NULL) + { + // check machine + delete elf; + st = DBGD_ERR_BAD_ELF_FORMAT; + return NULL; + } + switch (ehdrp->e_machine) + { + case EM_SPARC: + platform = Sparc; + break; + case EM_SPARC32PLUS: + platform = Sparcv8plus; + break; + case EM_SPARCV9: + platform = Sparcv9; + break; + case EM_386: + // case EM_486: + platform = Intel; + break; + case EM_X86_64: + platform = Amd64; + break; + case EM_AARCH64: + platform = Aarch64; + break; + default: + platform = Unknown; + break; + } + return elf; +} + +Elf * +Stabs::openElf (bool dbg_info) +{ + if (status != DBGD_ERR_NONE) + return NULL; + if (elfDis == NULL) + { + elfDis = openElf (path, status); + if (elfDis == NULL) + return NULL; + } + if (!dbg_info) + return elfDis; + if (elfDbg == NULL) + { + elfDbg = elfDis->find_ancillary_files (lo_name); + if (elfDbg == NULL) + elfDbg = elfDis; + } + return elfDbg; +} + +bool +Stabs::read_symbols (Vector<Function*> *functions) +{ + if (openElf (true) == NULL) + return false; + check_Symtab (); + check_Relocs (); + if (functions) + { + Function *fp; + int index; + Vec_loop (Function*, functions, index, fp) + { + fp->img_fname = path; + } + } + return true; +} + +char * +Stabs::sym_name (uint64_t target, uint64_t instr, int flag) +{ + long index; + if (flag == DISASM_REL_ONLY || flag == DISASM_REL_TARG) + { + Reloc *relptr = new Reloc; + relptr->value = instr; + index = RelLst->bisearch (0, -1, &relptr, RelValueCmp); + if (index >= 0) + { + delete relptr; + return RelLst->fetch (index)->name; + } + if (!is_relocatable ()) + { + relptr->value = target; + index = RelPLTLst->bisearch (0, -1, &relptr, RelValueCmp); + if (index >= 0) + { + delete relptr; + return RelPLTLst->fetch (index)->name; + } + } + delete relptr; + } + if (flag == DISASM_REL_NONE || flag == DISASM_REL_TARG || !is_relocatable ()) + { + Symbol *sptr; + sptr = map_PC_to_sym (target); + if (sptr && sptr->value == target) + return sptr->name; + } + return NULL; +} + +Symbol * +Stabs::map_PC_to_sym (uint64_t pc) +{ + if (pc == 0) + return NULL; + if (last_PC_to_sym && last_PC_to_sym->value <= pc + && last_PC_to_sym->value + last_PC_to_sym->size > pc) + return last_PC_to_sym; + Symbol *sym = new Symbol; + sym->value = pc; + long index = SymLst->bisearch (0, -1, &sym, SymFindCmp); + delete sym; + if (index >= 0) + { + last_PC_to_sym = SymLst->fetch (index)->cardinal (); + return last_PC_to_sym; + } + return NULL; +} + +Function * +Stabs::map_PC_to_func (uint64_t pc, uint64_t &low_pc, Vector<Function*> *functions) +{ + int index; + Function *func; + Symbol *sptr = map_PC_to_sym (pc); + if (sptr == NULL) + return NULL; + if (sptr->func) + { + low_pc = sptr->value; + return sptr->func; + } + if (functions) + { + Vec_loop (Function*, functions, index, func) + { + if (func->img_offset == sptr->img_offset) + { + sptr->func = func->cardinal (); + low_pc = sptr->value; + return sptr->func; + } + } + } + return NULL; +} + +Stabs::Stab_status +Stabs::read_stabs (ino64_t srcInode, Module *module, Vector<ComC*> *comComs, + bool readDwarf) +{ + if (module) + module->setIncludeFile (NULL); + + if (openElf (true) == NULL) + return status; + check_Symtab (); + + // read compiler commentary from .compcom1, .compcom, + // .info, .loops, and .loopview sections + if (comComs) + { + _src_inode = srcInode; + _src_name = module && module->file_name ? get_basename (module->file_name) : NULL; + if (!check_Comm (comComs)) + // .loops, and .loopview are now in .compcom + check_Loop (comComs); + + // should not read it after .info goes into .compcom + check_Info (comComs); + comComs->sort (ComCmp); + } + + // get stabs info + Stab_status statusStabs = DBGD_ERR_NO_STABS; +#define SRC_LINE_STABS(sec, secStr, comdat) \ + if ((elfDbg->sec) && (elfDbg->secStr) && \ + srcline_Stabs(module, elfDbg->sec, elfDbg->secStr, comdat) == DBGD_ERR_NONE) \ + statusStabs = DBGD_ERR_NONE + + SRC_LINE_STABS (stabExcl, stabExclStr, false); + SRC_LINE_STABS (stab, stabStr, false); + SRC_LINE_STABS (stabIndex, stabIndexStr, true); + + // read Dwarf, if any sections found + if (elfDbg->dwarf && readDwarf) + { + openDwarf ()->srcline_Dwarf (module); + if (dwarf && dwarf->status == DBGD_ERR_NONE) + return DBGD_ERR_NONE; + } + return statusStabs; +} + +static int +ComCmp (const void *a, const void *b) +{ + ComC *item1 = *((ComC **) a); + ComC *item2 = *((ComC **) b); + return (item1->line > item2->line) ? 1 : + (item1->line < item2->line) ? -1 : + (item1->sec > item2->sec) ? 1 : + (item1->sec < item2->sec) ? -1 : 0; +} + +static int +check_src_name (char *srcName) +{ + if (_src_name && srcName && streq (_src_name, get_basename (srcName))) + return 1; + if (_src_inode == (ino64_t) - 1) + return 0; + DbeFile *dbeFile = dbeSession->getDbeFile (srcName, DbeFile::F_SOURCE); + char *path = dbeFile->get_location (); + return (path == NULL || dbeFile->sbuf.st_ino != _src_inode) ? 0 : 1; +} + +bool +Stabs::check_Comm (Vector<ComC*> *comComs) +{ + int sz = comComs->size (); + Elf *elf = openElf (true); + if (elf == NULL) + return false; + + for (unsigned int sec = 1; sec < elf->elf_getehdr ()->e_shnum; sec++) + { + char *name = elf->get_sec_name (sec); + if (name == NULL) + continue; + Section_type sec_type; + if (streq (name, NTXT (".compcom"))) + sec_type = COMM_SEC; + else if (streq (name, NTXT (".compcom1"))) + sec_type = COMM1_SEC; + else + continue; + + // find header, set messages id & visibility if succeed + CompComment *cc = new CompComment (elf, sec); + int cnt = cc->compcom_open ((CheckSrcName) check_src_name); + // process messages + for (int index = 0; index < cnt; index++) + { + int visible; + compmsg msg; + char *str = cc->compcom_format (index, &msg, visible); + if (str) + { + ComC *citem = new ComC; + citem->sec = sec_type + index; + citem->type = msg.msg_type; + citem->visible = visible; + citem->line = (msg.lineno < 1) ? 1 : msg.lineno; + citem->com_str = str; + comComs->append (citem); + } + } + delete cc; + } + return (sz != comComs->size ()); +} + +static int +targetOffsetCmp (const void *a, const void *b) +{ + uint32_t o1 = ((target_info_t *) a)->offset; + uint32_t o2 = ((target_info_t *) b)->offset; + return (o1 >= o2); +} + +void +Stabs::check_AnalyzerInfo () +{ + Elf *elf = openElf (true); + if ((elf == NULL) || (elf->analyzerInfo == 0)) + { + Dprintf (DEBUG_STABS, NTXT ("Stabs::check_AnalyzerInfo: Null AnalyzerInfo section\n")); + return; // inappropriate, but ignored anyway + } + Elf_Data *data = elf->elf_getdata (elf->analyzerInfo); + int InfoSize = (int) data->d_size; + char *InfoData = (char *) data->d_buf; + int InfoAlign = (int) data->d_align; + AnalyzerInfoHdr h; + unsigned infoHdr_sz = sizeof (AnalyzerInfoHdr); + int table, entry; + int read = 0; + Module *mitem; + int index = 0; + if (InfoSize <= 0) + return; + uint64_t baseAddr = elf->get_baseAddr (); + Dprintf (DEBUG_STABS, NTXT ("Stabs::check_AnalyzerInfo size=%d @0x%lx (align=%d) base=0x%llx\n"), + InfoSize, (ul_t) InfoData, InfoAlign, (long long) baseAddr); + Dprintf (DEBUG_STABS, NTXT ("analyzerInfoMap has %lld entries\n"), (long long) analyzerInfoMap.size ()); + if (analyzerInfoMap.size () == 0) + { + Dprintf (DEBUG_STABS, NTXT ("No analyzerInfoMap available!\n")); + return; + } + + // verify integrity of analyzerInfoMap before reading analyzerInfo + unsigned count = 0; + Module *lastmod = NULL; + for (index = 0; index < analyzerInfoMap.size (); index++) + { + cpf_stabs_t map = analyzerInfoMap.fetch (index); + if (map.type > 3) + { + Dprintf (DEBUG_STABS, NTXT ("analyzerInfo contains table of unknown type %d for %s\n"), + map.type, map.module->get_name ()); + return; + } + if (map.module != lastmod) + { + if (lastmod != NULL) + Dprintf (DEBUG_STABS, "analyzerInfo contains %d 0x0 offset tables for %s\n", + count, lastmod->get_name ()); + count = 0; + } + count += (map.offset == 0x0); // only check for 0x0 tables for now + if (count > 4) + { + Dprintf (DEBUG_STABS, NTXT ("analyzerInfo contains too many 0x0 offset tables for %s\n"), + map.module->get_name ()); + return; + } + lastmod = map.module; + } + + index = 0; + while ((index < analyzerInfoMap.size ()) && (read < InfoSize)) + { + for (table = 0; table < 3; table++) + { // memory operations (ld, st, prefetch) + // read the table header + memcpy ((void *) &h, (const void *) InfoData, infoHdr_sz); + InfoData += infoHdr_sz; + read += infoHdr_sz; + + // use map for appropriate module + cpf_stabs_t map = analyzerInfoMap.fetch (index); + index++; + mitem = map.module; + Dprintf (DEBUG_STABS, "Table %d offset=0x%04x " + "text_labelref=0x%08llx entries=%d version=%d\n" + "itype %d offset=0x%04x module=%s\n", table, read, + (long long) (h.text_labelref - baseAddr), h.entries, + h.version, map.type, map.offset, map.module->get_name ()); + // read the table entries + for (entry = 0; entry < h.entries; entry++) + { + memop_info_t *m = new memop_info_t; + unsigned memop_info_sz = sizeof (memop_info_t); + memcpy ((void *) m, (const void *) InfoData, memop_info_sz); + InfoData += memop_info_sz; + read += memop_info_sz; + m->offset += (uint32_t) (h.text_labelref - baseAddr); + Dprintf (DEBUG_STABS, NTXT ("%4d(%d): offset=0x%04x id=0x%08x sig=0x%08x dtid=0x%08x\n"), + entry, table, m->offset, m->id, m->signature, m->datatype_id); + switch (table) + { + case CPF_INSTR_TYPE_LD: + mitem->ldMemops.append (m); + break; + case CPF_INSTR_TYPE_ST: + mitem->stMemops.append (m); + break; + case CPF_INSTR_TYPE_PREFETCH: + mitem->pfMemops.append (m); + break; + } + } + // following re-alignment should be redundant + //InfoData+=(read%InfoAlign); read+=(read%InfoAlign); // re-align + } + for (table = 3; table < 4; table++) + { // branch targets + memcpy ((void *) &h, (const void *) InfoData, infoHdr_sz); + InfoData += infoHdr_sz; + read += infoHdr_sz; + + // use map for appropriate module + cpf_stabs_t map = analyzerInfoMap.fetch (index); + index++; + mitem = map.module; + Dprintf (DEBUG_STABS, "Table %d offset=0x%04x " + "text_labelref=0x%08llx entries=%d version=%d\n" + "itype %d offset=0x%04x module=%s\n", table, read, + (long long) (h.text_labelref - baseAddr), h.entries, + h.version, map.type, map.offset, map.module->get_name ()); + for (entry = 0; entry < h.entries; entry++) + { + target_info_t *t = new target_info_t; + unsigned target_info_sz = sizeof (target_info_t); + memcpy ((void *) t, (const void *) InfoData, target_info_sz); + InfoData += target_info_sz; + read += target_info_sz; + t->offset += (uint32_t) (h.text_labelref - baseAddr); + Dprintf (DEBUG_STABS, NTXT ("%4d(%d): offset=0x%04x\n"), entry, + table, t->offset); + // the list of branch targets needs to be in offset sorted order + // and doing it here before archiving avoids the need to do it + // each time the archive is read. + mitem->bTargets.incorporate (t, targetOffsetCmp); + } + Dprintf (DEBUG_STABS, NTXT ("bTargets for %s has %lld items (last=0x%04x)\n"), + mitem->get_name (), (long long) mitem->bTargets.size (), + (mitem->bTargets.fetch (mitem->bTargets.size () - 1))->offset); + Dprintf (DEBUG_STABS, "read=%d at end of bTargets (InfoData=0x%lx)\n", + read, (ul_t) InfoData); + InfoData += (read % InfoAlign); + read += (read % InfoAlign); // re-align + Dprintf (DEBUG_STABS, "read=%d at end of bTargets (InfoData=0x%lx)\n", + read, (ul_t) InfoData); + } + Dprintf (DEBUG_STABS, "Stabs::check_AnalyzerInfo bytes read=%lld (index=%lld/%lld)\n", + (long long) read, (long long) index, + (long long) analyzerInfoMap.size ()); + } +} + +void +Stabs::check_Info (Vector<ComC*> *comComs) +{ + Elf *elf = openElf (true); + if (elf == NULL || elf->info == 0) + return; + Elf_Data *data = elf->elf_getdata (elf->info); + uint64_t InfoSize = data->d_size; + char *InfoData = (char *) data->d_buf; + bool get_src = false; + for (int h_num = 0; InfoSize; h_num++) + { + if (InfoSize < sizeof (struct info_header)) + return; + struct info_header *h = (struct info_header*) InfoData; + if (h->endian != '\0' || h->magic[0] != 'S' || h->magic[1] != 'U' + || h->magic[2] != 'N') + return; + if (h->len < InfoSize || h->len < sizeof (struct info_header) || (h->len & 3)) + return; + + char *fname = InfoData + sizeof (struct info_header); + InfoData += h->len; + InfoSize -= h->len; + get_src = check_src_name (fname); + for (uint32_t e_num = 0; e_num < h->cnt; ++e_num) + { + if (InfoSize < sizeof (struct entry_header)) + return; + struct entry_header *e = (struct entry_header*) InfoData; + if (InfoSize < e->len) + return; + int32_t copy_inout = 0; + if (e->len > sizeof (struct entry_header)) + if (e->type == F95_COPYINOUT) + copy_inout = *(int32_t*) (InfoData + sizeof (struct entry_header)); + InfoData += e->len; + InfoSize -= e->len; + if (get_src) + { + ComC *citem = new ComC; + citem->sec = INFO_SEC + h_num; + citem->type = e->msgnum & 0xFFFFFF; + citem->visible = CCMV_ALL; + citem->line = e->line; + citem->com_str = get_info_com (citem->type, copy_inout); + comComs->append (citem); + } + } + if (get_src) + break; + } +} + +static char * +get_info_com (int type, int32_t copy_inout) +{ + switch (type) + { + case 1: + return dbe_sprintf (GTXT ("In the call below, parameter number %d caused a copy-in -- loop(s) inserted"), + copy_inout); + case 2: + return dbe_sprintf (GTXT ("In the call below, parameter number %d caused a copy-out -- loop(s) inserted"), + copy_inout); + case 3: + return dbe_sprintf (GTXT ("In the call below, parameter number %d caused a copy-in and a copy-out -- loops inserted"), + copy_inout); + case 4: + return dbe_strdup (GTXT ("Alignment of variables in common block may cause performance degradation")); + case 5: + return dbe_strdup (GTXT ("DO statement bounds lead to no executions of the loop")); + default: + return dbe_strdup (NTXT ("")); + } +} + +void +Stabs::check_Loop (Vector<ComC*> *comComs) +{ + Elf *elf = openElf (true); + if (elf == NULL) + return; + + StringBuilder sb; + for (unsigned int sec = 1; sec < elf->elf_getehdr ()->e_shnum; sec++) + { + char *name = elf->get_sec_name (sec); + if (name == NULL) + continue; + if (!streq (name, NTXT (".loops")) && !streq (name, NTXT (".loopview"))) + continue; + + Elf_Data *data = elf->elf_getdata (sec); + size_t LoopSize = (size_t) data->d_size, len; + char *LoopData = (char *) data->d_buf; + int remainder, i; + char src[2 * MAXPATHLEN], buf1[MAXPATHLEN], buf2[MAXPATHLEN]; + char **dep_str = NULL; + bool get_src = false; + while ((LoopSize > 0) && !get_src && + (strncmp (LoopData, NTXT ("Source:"), 7) == 0)) + { + // The first three items in a .loops subsection are three strings. + // Source: ... + // Version: ... + // Number of loops: ... + sscanf (LoopData, NTXT ("%*s%s"), src); + len = strlen (LoopData) + 1; + LoopData += len; + LoopSize -= len; + sscanf (LoopData, NTXT ("%*s%*s%s"), buf1); + // double version = atof(buf1); + len = strlen (LoopData) + 1; + LoopData += len; + LoopSize -= len; + get_src = check_src_name (src); + sscanf (LoopData, NTXT ("%*s%*s%*s%s%s"), buf1, buf2); + int n_loop = atoi (buf1); + int n_depend = atoi (buf2); + len = strlen (LoopData) + 1; + LoopData += len; + LoopSize -= len; + if (get_src && (n_loop > 0)) + { + dep_str = new char*[n_loop]; + for (i = 0; i < n_loop; i++) + dep_str[i] = NULL; + } + + // printf("Source: %s\nVersion: %f\nLoop#: %d\nDepend#: %d\n", + // src, version, n_loop, n_depend); + + // Read in the strings that contain the list of variables that cause + // data dependencies inside of loops. Not every loop has such a list + // of variables. + // + // Example: if loop #54 has data dependencies caused by the + // variables named i, j and foo, then the string that represents + // this in the .loops section looks like this: + // + // .asciz "54:i.j.foo" + // + // The variable names are delimited with . + // + // For now, store these strings in an array, and add them into + // the loop structure when we read in the numeric loop info + // (that's what we read in next.) + // + // printf("\tDependenncies:\n"); + for (i = 0; i < n_depend; i++) + { + len = strlen (LoopData) + 1; + LoopData += len; + LoopSize -= len; + if (dep_str != NULL) + { + char *dep_buf1 = dbe_strdup (LoopData); + char *ptr = strtok (dep_buf1, NTXT (":")); + if (ptr != NULL) + { + int index = atoi (ptr); + bool dep_first = true; + sb.setLength (0); + while ((ptr = strtok (NULL, NTXT (", "))) != NULL) + { + if (dep_first) + dep_first = false; + else + sb.append (NTXT (", ")); + sb.append (ptr); + } + if (sb.length () > 0 && index < n_loop) + dep_str[index] = sb.toString (); + } + free (dep_buf1); + } + } + + // Adjust Data pointer so that it is word aligned. + remainder = (int) (((unsigned long) LoopData) % 4); + if (remainder != 0) + { + len = 4 - remainder; + LoopData += len; + LoopSize -= len; + } + + // Read in the loop info, one loop at a time. + for (i = 0; i < n_loop; i++) + { + int loopid = *((int *) LoopData); + LoopData += 4; + int line_no = *((int *) LoopData); + if (line_no < 1) // compiler has trouble on this + line_no = 1; + LoopData += 4; + // int nest = *((int *) LoopData); + LoopData += 4; + int parallel = *((int *) LoopData); + LoopData += 4; + unsigned hints = *((unsigned *) LoopData); + LoopData += 4; + // int count = *((int *) LoopData); + LoopData += 4; + LoopSize -= 24; + if (!get_src || (loopid >= n_loop)) + continue; + ComC *citem = new ComC; + citem->sec = LOOP_SEC + i; + citem->type = hints; + citem->visible = CCMV_ALL; + citem->line = line_no; + citem->com_str = get_lp_com (hints, parallel, dep_str[loopid]); + comComs->append (citem); + } + if (dep_str) + { + for (i = 0; i < n_loop; i++) + free (dep_str[i]); + delete[] dep_str; + dep_str = NULL; + } + } + } +} + +static char * +get_lp_com (unsigned hints, int parallel, char *dep) +{ + StringBuilder sb; + if (parallel == -1) + sb.append (GTXT ("Loop below is serial, but parallelizable: ")); + else if (parallel == 0) + sb.append (GTXT ("Loop below is not parallelized: ")); + else + sb.append (GTXT ("Loop below is parallelized: ")); + switch (hints) + { + case 0: + // No loop mesg will print + // strcat(com, GTXT("no hint available")); + break; + case 1: + sb.append (GTXT ("loop contains procedure call")); + break; + case 2: + sb.append (GTXT ("compiler generated two versions of this loop")); + break; + case 3: + { + StringBuilder sb_tmp; + sb_tmp.sprintf (GTXT ("the variable(s) \"%s\" cause a data dependency in this loop"), + dep ? dep : GTXT ("<Unknown>")); + sb.append (&sb_tmp); + } + break; + case 4: + sb.append (GTXT ("loop was significantly transformed during optimization")); + break; + case 5: + sb.append (GTXT ("loop may or may not hold enough work to be profitably parallelized")); + break; + case 6: + sb.append (GTXT ("loop was marked by user-inserted pragma")); + break; + case 7: + sb.append (GTXT ("loop contains multiple exits")); + break; + case 8: + sb.append (GTXT ("loop contains I/O, or other function calls, that are not MT safe")); + break; + case 9: + sb.append (GTXT ("loop contains backward flow of control")); + break; + case 10: + sb.append (GTXT ("loop may have been distributed")); + break; + case 11: + sb.append (GTXT ("two loops or more may have been fused")); + break; + case 12: + sb.append (GTXT ("two or more loops may have been interchanged")); + break; + default: + break; + } + return sb.toString (); +} + +StabReader::StabReader (Elf *_elf, Platform_t platform, int StabSec, int StabStrSec) +{ + stabCnt = -1; + stabNum = 0; + if (_elf == NULL) + return; + elf = _elf; + + // Get ELF data + Elf_Data *data = elf->elf_getdata (StabSec); + if (data == NULL) + return; + uint64_t stabSize = data->d_size; + StabData = (char *) data->d_buf; + Elf_Internal_Shdr *shdr = elf->get_shdr (StabSec); + if (shdr == NULL) + return; + + // GCC bug: sh_entsize is 20 for 64 apps on Linux + StabEntSize = (platform == Amd64 || platform == Sparcv9) ? 12 : (unsigned) shdr->sh_entsize; + if (stabSize == 0 || StabEntSize == 0) + return; + data = elf->elf_getdata (StabStrSec); + if (data == NULL) + return; + shdr = elf->get_shdr (StabStrSec); + if (shdr == NULL) + return; + StabStrtab = (char *) data->d_buf; + StabStrtabEnd = StabStrtab + shdr->sh_size; + StrTabSize = 0; + stabCnt = (int) (stabSize / StabEntSize); +} + +char * +StabReader::get_stab (struct stab *np, bool comdat) +{ + struct stab *stbp = (struct stab *) (StabData + stabNum * StabEntSize); + stabNum++; + *np = *stbp; + np->n_desc = elf->decode (stbp->n_desc); + np->n_strx = elf->decode (stbp->n_strx); + np->n_value = elf->decode (stbp->n_value); + switch (np->n_type) + { + case N_UNDF: + case N_ILDPAD: + // Start of new stab section (or padding) + StabStrtab += StrTabSize; + StrTabSize = np->n_value; + } + + char *str = NULL; + if (np->n_strx) + { + if (comdat && np->n_type == N_FUN && np->n_other == 1) + { + if (np->n_strx == 1) + StrTabSize++; + str = StabStrtab + StrTabSize; + // Each COMDAT string must be sized to find the next string: + StrTabSize += strlen (str) + 1; + } + else + str = StabStrtab + np->n_strx; + if (str >= StabStrtabEnd) + str = NULL; + } + if (DEBUG_STABS) + { + char buf[128]; + char *s = get_type_name (np->n_type); + if (s == NULL) + { + snprintf (buf, sizeof (buf), NTXT ("n_type=%d"), np->n_type); + s = buf; + } + if (str) + { + Dprintf (DEBUG_STABS, NTXT ("%4d: .stabs \"%s\",%s,0x%x,0x%x,0x%x\n"), + stabNum - 1, str, s, (int) np->n_other, (int) np->n_desc, + (int) np->n_value); + } + else + Dprintf (DEBUG_STABS, NTXT ("%4d: .stabn %s,0x%x,0x%x,0x%x\n"), + stabNum - 1, s, (int) np->n_other, (int) np->n_desc, + (int) np->n_value); + } + return str; +} + +void +StabReader::parse_N_OPT (Module *mod, char *str) +{ + if (mod == NULL || str == NULL) + return; + for (char *s = str; 1; s++) + { + switch (*s) + { + case 'd': + if (s[1] == 'i' && s[2] == ';') + { + delete mod->dot_o_file; + mod->dot_o_file = NULL; + } + break; + case 's': + if ((s[1] == 'i' || s[1] == 'n') && s[2] == ';') + { + delete mod->dot_o_file; + mod->dot_o_file = NULL; + } + break; + } + s = strchr (s, ';'); + if (s == NULL) + break; + } +} + +Stabs::Stab_status +Stabs::srcline_Stabs (Module *module, unsigned int StabSec, + unsigned int StabStrSec, bool comdat) +{ + StabReader *stabReader = new StabReader (openElf (true), platform, StabSec, StabStrSec); + int tot = stabReader->stabCnt; + if (tot < 0) + { + delete stabReader; + return DBGD_ERR_NO_STABS; + } + int n, lineno; + char *sbase, *n_so = NTXT (""), curr_src[2 * MAXPATHLEN]; + Function *newFunc; + Sp_lang_code _lang_code = module->lang_code; + Vector<Function*> *functions = module->functions; + bool no_stabs = true; + *curr_src = '\0'; + Function *func = NULL; + int phase = 0; + int stabs_level = 0; + int xline = 0; + + // Find module + for (n = 0; n < tot; n++) + { + struct stab stb; + char *str = stabReader->get_stab (&stb, comdat); + if (stb.n_type == N_UNDF) + phase = 0; + else if (stb.n_type == N_SO) + { + if (str == NULL || *str == '\0') + continue; + if (phase == 0) + { + phase = 1; + n_so = str; + continue; + } + phase = 0; + sbase = str; + if (*str == '/') + { + if (streq (sbase, module->file_name)) + break; + } + else + { + size_t last = strlen (n_so); + if (n_so[last - 1] == '/') + last--; + if (strncmp (n_so, module->file_name, last) == 0 && + module->file_name[last] == '/' && + streq (sbase, module->file_name + last + 1)) + break; + } + } + } + if (n >= tot) + { + delete stabReader; + return DBGD_ERR_NO_STABS; + } + + Include *includes = new Include; + includes->new_src_file (module->getMainSrc (), 0, NULL); + module->hasStabs = true; + *curr_src = '\0'; + phase = 0; + for (n++; n < tot; n++) + { + struct stab stb; + char *str = stabReader->get_stab (&stb, comdat); + int n_desc = (int) ((unsigned short) stb.n_desc); + switch (stb.n_type) + { + case N_UNDF: + case N_SO: + case N_ENDM: + n = tot; + break; + case N_ALIAS: + if (str == NULL) + break; + if (is_fortran (_lang_code)) + { + char *p = strchr (str, ':'); + if (p && streq (p + 1, NTXT ("FMAIN"))) + { + Function *afunc = find_func (NTXT ("MAIN"), functions, true); + if (afunc) + afunc->set_match_name (dbe_strndup (str, p - str)); + break; + } + } + case N_FUN: + case N_OUTL: + if (str == NULL) + break; + if (*str == '@') + { + str++; + if (*str == '>' || *str == '<') + str++; + } + if (stabs_level != 0) + break; + + // find address of the enclosed function + newFunc = find_func (str, functions, is_fortran (_lang_code)); + if (newFunc == NULL) + break; + if (func) + while (func->popSrcFile ()) + ; + func = newFunc; + + // First line info to cover function from the beginning + lineno = xline + n_desc; + if (lineno > 0) + { + // Set the chain of includes for the new function + includes->push_src_files (func); + func->add_PC_info (0, lineno); + no_stabs = false; + } + break; + case N_ENTRY: + break; + case N_CMDLINE: + if (str && !module->comp_flags) + { + char *comp_flags = strchr (str, ';'); + if (comp_flags) + { + module->comp_flags = dbe_strdup (comp_flags + 1); + module->comp_dir = dbe_strndup (str, comp_flags - str); + } + } + break; + case N_LBRAC: + stabs_level++; + break; + case N_RBRAC: + stabs_level--; + break; + case N_XLINE: + xline = n_desc << 16; + break; + case N_SLINE: + if (func == NULL) + break; + no_stabs = false; + lineno = xline + n_desc; + if (func->line_first <= 0) + { + // Set the chain of includes for the new function + includes->push_src_files (func); + func->add_PC_info (0, lineno); + break; + } + if (func->curr_srcfile == NULL) + includes->push_src_files (func); + if (func->line_first != lineno || + !streq (curr_src, func->getDefSrc ()->get_name ())) + func->add_PC_info (stb.n_value, lineno); + break; + case N_OPT: + if ((str != NULL) && streq (str, NTXT ("gcc2_compiled."))) + _lang_code = Sp_lang_gcc; + switch (elfDbg->elf_getehdr ()->e_type) + { + case ET_EXEC: + case ET_DYN: + // set the real object timestamp from the executable's N_OPT stab + // due to bug #4796329 + module->real_timestamp = stb.n_value; + break; + default: + module->curr_timestamp = stb.n_value; + break; + } + break; + case N_GSYM: + if ((str == NULL) || strncmp (str, NTXT ("__KAI_K"), 7)) + break; + str += 7; + if (!strncmp (str, NTXT ("CC_"), 3)) + _lang_code = Sp_lang_KAI_KCC; + else if (!strncmp (str, NTXT ("cc_"), 3)) + _lang_code = Sp_lang_KAI_Kcc; + else if (!strncmp (str, NTXT ("PTS_"), 4) && + (_lang_code != Sp_lang_KAI_KCC) && + (_lang_code != Sp_lang_KAI_Kcc)) + _lang_code = Sp_lang_KAI_KPTS; + break; + case N_BINCL: + includes->new_include_file (module->setIncludeFile (str), func); + break; + case N_EINCL: + includes->end_include_file (func); + break; + case N_SOL: + if (str == NULL) + break; + lineno = xline + n_desc; + if (lineno > 0 && func && func->line_first <= 0) + { + includes->push_src_files (func); + func->add_PC_info (0, lineno); + no_stabs = false; + } + if (streq (sbase, str)) + { + module->setIncludeFile (NULL); + snprintf (curr_src, sizeof (curr_src), NTXT ("%s"), module->file_name); + includes->new_src_file (module->getMainSrc (), lineno, func); + } + else + { + if (streq (sbase, get_basename (str))) + { + module->setIncludeFile (NULL); + snprintf (curr_src, sizeof (curr_src), NTXT ("%s"), module->file_name); + includes->new_src_file (module->setIncludeFile (curr_src), lineno, func); + } + else + { + if (*str == '/') + snprintf (curr_src, sizeof (curr_src), NTXT ("%s"), str); + else + { + size_t last = strlen (n_so); + if (last == 0 || n_so[last - 1] != '/') + snprintf (curr_src, sizeof (curr_src), NTXT ("%s/%s"), n_so, str); + else + snprintf (curr_src, sizeof (curr_src), NTXT ("%s%s"), n_so, str); + } + includes->new_src_file (module->setIncludeFile (curr_src), lineno, func); + } + } + break; + } + } + delete includes; + delete stabReader; + return no_stabs ? DBGD_ERR_NO_STABS : DBGD_ERR_NONE; +}//srcline_Stabs + +static bool +cmp_func_name (char *fname, size_t len, char *name, bool fortran) +{ + return (strncmp (name, fname, len) == 0 + && (name[len] == 0 + || (fortran && name[len] == '_' && name[len + 1] == 0))); +} + +Function * +Stabs::find_func (char *fname, Vector<Function*> *functions, bool fortran, bool inner_names) +{ + char *arg, *name; + Function *item; + int index; + size_t len; + + len = strlen (fname); + arg = strchr (fname, ':'); + if (arg != NULL) + { + if (arg[1] == 'P') // Prototype for function + return NULL; + len -= strlen (arg); + } + + Vec_loop (Function*, functions, index, item) + { + name = item->get_mangled_name (); + if (cmp_func_name (fname, len, name, fortran)) + return item->cardinal (); + } + + if (inner_names) + { + // Dwarf subprograms may only have plain (non-linker) names + // Retry with inner names only + + Vec_loop (Function*, functions, index, item) + { + name = strrchr (item->get_mangled_name (), '.'); + if (!name) continue; + name++; + if (cmp_func_name (fname, len, name, fortran)) + return item->cardinal (); + } + } + return NULL; +} + +Map<const char*, Symbol*> * +Stabs::get_elf_symbols () +{ + Elf *elf = openElf (false); + if (elf->elfSymbols == NULL) + { + Map<const char*, Symbol*> *elfSymbols = new StringMap<Symbol*>(128, 128); + elf->elfSymbols = elfSymbols; + for (int i = 0, sz = SymLst ? SymLst->size () : 0; i < sz; i++) + { + Symbol *sym = SymLst->fetch (i); + elfSymbols->put (sym->name, sym); + } + } + return elf->elfSymbols; +} + +void +Stabs::read_dwarf_from_dot_o (Module *mod) +{ + Dprintf (DEBUG_STABS, NTXT ("stabsModules: %s\n"), STR (mod->get_name ())); + Vector<Module*> *mods = mod->dot_o_file->seg_modules; + char *bname = get_basename (mod->get_name ()); + for (int i1 = 0, sz1 = mods ? mods->size () : 0; i1 < sz1; i1++) + { + Module *m = mods->fetch (i1); + Dprintf (DEBUG_STABS, NTXT (" MOD: %s\n"), STR (m->get_name ())); + if (dbe_strcmp (bname, get_basename (m->get_name ())) == 0) + { + mod->indexStabsLink = m; + m->indexStabsLink = mod; + break; + } + } + if (mod->indexStabsLink) + { + mod->dot_o_file->objStabs->openDwarf ()->srcline_Dwarf (mod->indexStabsLink); + Map<const char*, Symbol*> *elfSymbols = get_elf_symbols (); + Vector<Function*> *funcs = mod->indexStabsLink->functions; + for (int i1 = 0, sz1 = funcs ? funcs->size () : 0; i1 < sz1; i1++) + { + Function *f1 = funcs->fetch (i1); + Symbol *sym = elfSymbols->get (f1->get_mangled_name ()); + if (sym == NULL) + continue; + Dprintf (DEBUG_STABS, NTXT (" Symbol: %s func=%p\n"), STR (sym->name), sym->func); + Function *f = sym->func; + if (f->indexStabsLink) + continue; + f->indexStabsLink = f1; + f1->indexStabsLink = f; + f->copy_PCInfo (f1); + } + } +} + +Stabs::Stab_status +Stabs::read_archive (LoadObject *lo) +{ + if (openElf (true) == NULL) + return status; + check_Symtab (); + if (elfDbg->dwarf) + openDwarf ()->archive_Dwarf (lo); + + // get Module/Function lists from stabs info + Stab_status statusStabs = DBGD_ERR_NO_STABS; +#define ARCHIVE_STABS(sec, secStr, comdat) \ + if ((elfDbg->sec) != 0 && (elfDbg->secStr) != 0 && \ + archive_Stabs(lo, elfDbg->sec, elfDbg->secStr, comdat) == DBGD_ERR_NONE) \ + statusStabs = DBGD_ERR_NONE + + // prefer index stabs (where they exist) since they're most appropriate + // for loadobjects and might have N_CPROF stabs for ABS/CPF + ARCHIVE_STABS (stabIndex, stabIndexStr, true); + ARCHIVE_STABS (stabExcl, stabExclStr, false); + ARCHIVE_STABS (stab, stabStr, false); + + // Add all unassigned functions to the <unknown> module + Symbol *sitem, *alias; + int index; + Vec_loop (Symbol*, SymLst, index, sitem) + { + if (sitem->func || (sitem->size == 0) || (sitem->flags & SYM_UNDEF)) + continue; + alias = sitem->alias; + if (alias) + { + if (alias->func == NULL) + { + alias->func = createFunction (lo, lo->noname, alias); + alias->func->alias = alias->func; + } + if (alias != sitem) + { + sitem->func = createFunction (lo, alias->func->module, sitem); + sitem->func->alias = alias->func; + } + } + else + sitem->func = createFunction (lo, lo->noname, sitem); + } + if (pltSym) + { + pltSym->func = createFunction (lo, lo->noname, pltSym); + pltSym->func->flags |= FUNC_FLAG_PLT; + } + + // need Module association, so this must be done after handling Modules + check_AnalyzerInfo (); + + if (dwarf && dwarf->status == DBGD_ERR_NONE) + return DBGD_ERR_NONE; + return statusStabs; +}//read_archive + +Function * +Stabs::createFunction (LoadObject *lo, Module *module, Symbol *sym) +{ + Function *func = dbeSession->createFunction (); + func->module = module; + func->img_fname = path; + func->img_offset = (off_t) sym->img_offset; + func->save_addr = sym->save; + func->size = (uint32_t) sym->size; + func->set_name (sym->name); + func->elfSym = sym; + module->functions->append (func); + lo->functions->append (func); + return func; +} + +void +Stabs::fixSymtabAlias () +{ + int ind, i, k; + Symbol *sym, *bestAlias; + SymLst->sort (SymImgOffsetCmp); + ind = SymLst->size () - 1; + for (i = 0; i < ind; i++) + { + bestAlias = SymLst->fetch (i); + if (bestAlias->img_offset == 0) // Ignore this bad symbol + continue; + sym = SymLst->fetch (i + 1); + if (bestAlias->img_offset != sym->img_offset) + { + if ((bestAlias->size == 0) || + (sym->img_offset < bestAlias->img_offset + bestAlias->size)) + bestAlias->size = sym->img_offset - bestAlias->img_offset; + continue; + } + + // Find a "best" alias + size_t bestLen = strlen (bestAlias->name); + int64_t maxSize = bestAlias->size; + for (k = i + 1; k <= ind; k++) + { + sym = SymLst->fetch (k); + if (bestAlias->img_offset != sym->img_offset) + { // no more aliases + if ((maxSize == 0) || + (sym->img_offset < bestAlias->img_offset + maxSize)) + maxSize = sym->img_offset - bestAlias->img_offset; + break; + } + if (maxSize < sym->size) + maxSize = sym->size; + size_t len = strlen (sym->name); + if (len < bestLen) + { + bestAlias = sym; + bestLen = len; + } + } + for (; i < k; i++) + { + sym = SymLst->fetch (i); + sym->alias = bestAlias; + sym->size = maxSize; + } + i--; + } +} + +void +Stabs::check_Symtab () +{ + if (st_check_symtab) + return; + st_check_symtab = true; + + Elf *elf = openElf (true); + if (elf == NULL) + return; + if (elfDis->plt != 0) + { + Elf_Internal_Shdr *shdr = elfDis->get_shdr (elfDis->plt); + if (shdr) + { + pltSym = new Symbol (); + pltSym->value = shdr->sh_addr; + pltSym->size = shdr->sh_size; + pltSym->img_offset = shdr->sh_offset; + pltSym->name = dbe_strdup (NTXT ("@plt")); + pltSym->flags |= SYM_PLT; + } + } + if (elf->symtab) + readSymSec (elf->symtab, elf); + else + { + readSymSec (elf->SUNW_ldynsym, elf); + readSymSec (elf->dynsym, elf); + } +} + +void +Stabs::readSymSec (unsigned int sec, Elf *elf) +{ + Symbol *sitem; + Sp_lang_code local_lcode; + if (sec == 0) + return; + // Get ELF data + Elf_Data *data = elf->elf_getdata (sec); + if (data == NULL) + return; + uint64_t SymtabSize = data->d_size; + Elf_Internal_Shdr *shdr = elf->get_shdr (sec); + + if ((SymtabSize == 0) || (shdr->sh_entsize == 0)) + return; + Elf_Data *data_str = elf->elf_getdata (shdr->sh_link); + if (data_str == NULL) + return; + char *Strtab = (char *) data_str->d_buf; + + // read func symbolic table + for (unsigned int n = 0, tot = SymtabSize / shdr->sh_entsize; n < tot; n++) + { + Elf_Internal_Sym Sym; + elf->elf_getsym (data, n, &Sym); + const char *st_name = Sym.st_name < data_str->d_size ? + (Strtab + Sym.st_name) : NTXT ("no_name"); + switch (GELF_ST_TYPE (Sym.st_info)) + { + case STT_FUNC: + // Skip UNDEF symbols (bug 4817083) + if (Sym.st_shndx == 0) + { + if (Sym.st_value == 0) + break; + sitem = new Symbol (SymLst); + sitem->flags |= SYM_UNDEF; + if (pltSym) + sitem->img_offset = (uint32_t) (pltSym->img_offset + + Sym.st_value - pltSym->value); + } + else + { + Elf_Internal_Shdr *shdrp = elfDis->get_shdr (Sym.st_shndx); + if (shdrp == NULL) + break; + sitem = new Symbol (SymLst); + sitem->img_offset = (uint32_t) (shdrp->sh_offset + + Sym.st_value - shdrp->sh_addr); + } + sitem->size = Sym.st_size; + sitem->name = dbe_strdup (st_name); + sitem->value = is_relocatable () ? sitem->img_offset : Sym.st_value; + if (GELF_ST_BIND (Sym.st_info) == STB_LOCAL) + { + sitem->local_ind = LocalFile->size () - 1; + LocalLst->append (sitem); + } + break; + case STT_NOTYPE: + if (streq (st_name, NTXT ("gcc2_compiled."))) + { + sitem = new Symbol (SymLst); + sitem->lang_code = Sp_lang_gcc; + sitem->name = dbe_strdup (st_name); + sitem->local_ind = LocalFile->size () - 1; + LocalLst->append (sitem); + } + break; + case STT_OBJECT: + if (!strncmp (st_name, NTXT ("__KAI_KPTS_"), 11)) + local_lcode = Sp_lang_KAI_KPTS; + else if (!strncmp (st_name, NTXT ("__KAI_KCC_"), 10)) + local_lcode = Sp_lang_KAI_KCC; + else if (!strncmp (st_name, NTXT ("__KAI_Kcc_"), 10)) + local_lcode = Sp_lang_KAI_Kcc; + else + break; + sitem = new Symbol (LocalLst); + sitem->lang_code = local_lcode; + sitem->name = dbe_strdup (st_name); + break; + case STT_FILE: + { + int last = LocalFile->size () - 1; + if (last >= 0 && LocalFileIdx->fetch (last) == LocalLst->size ()) + { + // There were no local functions in the latest file. + free (LocalFile->get (last)); + LocalFile->store (last, dbe_strdup (st_name)); + } + else + { + LocalFile->append (dbe_strdup (st_name)); + LocalFileIdx->append (LocalLst->size ()); + } + break; + } + } + } + fixSymtabAlias (); + SymLst->sort (SymValueCmp); + get_save_addr (elf->need_swap_endian); + dump (); +}//check_Symtab + +void +Stabs::check_Relocs () +{ + // We may have many relocation tables to process: .rela.text%foo, + // rela.text%bar, etc. On Intel, compilers generate .rel.text sections + // which have to be processed as well. A lot of rework is needed here. + Symbol *sptr = NULL; + if (st_check_relocs) + return; + st_check_relocs = true; + + Elf *elf = openElf (false); + if (elf == NULL) + return; + for (unsigned int sec = 1; sec < elf->elf_getehdr ()->e_shnum; sec++) + { + bool use_rela, use_PLT; + char *name = elf->get_sec_name (sec); + if (name == NULL) + continue; + if (strncmp (name, NTXT (".rela.text"), 10) == 0) + { + use_rela = true; + use_PLT = false; + } + else if (streq (name, NTXT (".rela.plt"))) + { + use_rela = true; + use_PLT = true; + } + else if (strncmp (name, NTXT (".rel.text"), 9) == 0) + { + use_rela = false; + use_PLT = false; + } + else if (streq (name, NTXT (".rel.plt"))) + { + use_rela = false; + use_PLT = true; + } + else + continue; + + Elf_Internal_Shdr *shdr = elf->get_shdr (sec); + if (shdr == NULL) + continue; + + // Get ELF data + Elf_Data *data = elf->elf_getdata (sec); + if (data == NULL) + continue; + uint64_t ScnSize = data->d_size; + uint64_t EntSize = shdr->sh_entsize; + if ((ScnSize == 0) || (EntSize == 0)) + continue; + int tot = (int) (ScnSize / EntSize); + + // Get corresponding text section + Elf_Internal_Shdr *shdr_txt = elf->get_shdr (shdr->sh_info); + if (shdr_txt == NULL) + continue; + if (!(shdr_txt->sh_flags & SHF_EXECINSTR)) + continue; + + // Get corresponding symbol table section + Elf_Internal_Shdr *shdr_sym = elf->get_shdr (shdr->sh_link); + if (shdr_sym == NULL) + continue; + Elf_Data *data_sym = elf->elf_getdata (shdr->sh_link); + + // Get corresponding string table section + Elf_Data *data_str = elf->elf_getdata (shdr_sym->sh_link); + if (data_str == NULL) + continue; + char *Strtab = (char*) data_str->d_buf; + for (int n = 0; n < tot; n++) + { + Elf_Internal_Sym sym; + Elf_Internal_Rela rela; + char *symName; + if (use_rela) + elf->elf_getrela (data, n, &rela); + else + { + // GElf_Rela is extended GElf_Rel + elf->elf_getrel (data, n, &rela); + rela.r_addend = 0; + } + + int ndx = (int) GELF_R_SYM (rela.r_info); + elf->elf_getsym (data_sym, ndx, &sym); + switch (GELF_ST_TYPE (sym.st_info)) + { + case STT_FUNC: + case STT_OBJECT: + case STT_NOTYPE: + if (sym.st_name == 0 || sym.st_name >= data_str->d_size) + continue; + symName = Strtab + sym.st_name; + break; + case STT_SECTION: + { + Elf_Internal_Shdr *secHdr = elf->get_shdr (sym.st_shndx); + if (secHdr == NULL) + continue; + if (sptr == NULL) + sptr = new Symbol; + sptr->value = secHdr->sh_offset + rela.r_addend; + long index = SymLst->bisearch (0, -1, &sptr, SymFindCmp); + if (index == -1) + continue; + Symbol *sp = SymLst->fetch (index); + if (sptr->value != sp->value) + continue; + symName = sp->name; + break; + } + default: + continue; + } + Reloc *reloc = new Reloc; + reloc->name = dbe_strdup (symName); + reloc->type = GELF_R_TYPE (rela.r_info); + reloc->value = use_PLT ? rela.r_offset + : rela.r_offset + shdr_txt->sh_offset; + reloc->addend = rela.r_addend; + if (use_PLT) + RelPLTLst->append (reloc); + else + RelLst->append (reloc); + } + } + delete sptr; + RelLst->sort (RelValueCmp); +} //check_Relocs + +void +Stabs::get_save_addr (bool need_swap_endian) +{ + if (elfDis->is_Intel ()) + { + for (int j = 0, sz = SymLst ? SymLst->size () : 0; j < sz; j++) + { + Symbol *sitem = SymLst->fetch (j); + sitem->save = 0; + } + return; + } + for (int j = 0, sz = SymLst ? SymLst->size () : 0; j < sz; j++) + { + Symbol *sitem = SymLst->fetch (j); + sitem->save = FUNC_NO_SAVE; + + // If an image offset is not known skip it. + // Works for artificial symbols like '@plt' as well. + if (sitem->img_offset == 0) + continue; + + bool is_o7_moved = false; + int64_t off = sitem->img_offset; + for (int i = 0; i < sitem->size; i += 4) + { + unsigned int cmd; + if (elfDis->get_data (off, sizeof (cmd), &cmd) == NULL) + break; + if (need_swap_endian) + SWAP_ENDIAN (cmd); + off += sizeof (cmd); + if ((cmd & 0xffffc000) == 0x9de38000) + { // save %sp, ??, %sp + sitem->save = i; + break; + } + else if ((cmd & 0xc0000000) == 0x40000000 || // call ?? + (cmd & 0xfff80000) == 0xbfc00000) + { // jmpl ??, %o7 + if (!is_o7_moved) + { + sitem->save = FUNC_ROOT; + break; + } + } + else if ((cmd & 0xc1ffe01f) == 0x8010000f) // or %g0,%o7,?? + is_o7_moved = true; + } + } +} + +uint64_t +Stabs::mapOffsetToAddress (uint64_t img_offset) +{ + Elf *elf = openElf (false); + if (elf == NULL) + return 0; + if (is_relocatable ()) + return img_offset; + for (unsigned int sec = 1; sec < elf->elf_getehdr ()->e_shnum; sec++) + { + Elf_Internal_Shdr *shdr = elf->get_shdr (sec); + if (shdr == NULL) + continue; + if (img_offset >= (uint64_t) shdr->sh_offset + && img_offset < (uint64_t) (shdr->sh_offset + shdr->sh_size)) + return shdr->sh_addr + (img_offset - shdr->sh_offset); + } + return 0; +} + +Stabs::Stab_status +Stabs::archive_Stabs (LoadObject *lo, unsigned int StabSec, + unsigned int StabStrSec, bool comdat) +{ + StabReader *stabReader = new StabReader (openElf (true), platform, StabSec, StabStrSec); + int tot = stabReader->stabCnt; + if (tot < 0) + { + delete stabReader; + return DBGD_ERR_NO_STABS; + } + + char *sbase = NTXT (""), *arg, *fname, sname[2 * MAXPATHLEN]; + int lastMod, phase, stabs_level, modCnt = 0; + Function *func = NULL; + Module *mod; +#define INIT_MOD phase = 0; stabs_level = 0; *sname = '\0'; mod = NULL + + bool updateStabsMod = false; + if (comdat && ((elfDbg->elf_getehdr ()->e_type == ET_EXEC) || (elfDbg->elf_getehdr ()->e_type == ET_DYN))) + { + if (stabsModules == NULL) + stabsModules = new Vector<Module*>(); + updateStabsMod = true; + } + INIT_MOD; + lastMod = lo->seg_modules->size (); + + for (int n = 0; n < tot; n++) + { + struct stab stb; + char *str = stabReader->get_stab (&stb, comdat); + switch (stb.n_type) + { + case N_FUN: + // Ignore a COMDAT function, if there are two or more modules in 'lo' + if (comdat && stb.n_other == 1 && modCnt > 1) + break; + case N_OUTL: + case N_ALIAS: + case N_ENTRY: + if (mod == NULL || str == NULL + || (stb.n_type != N_ENTRY && stabs_level != 0)) + break; + if (*str == '@') + { + str++; + if (*str == '>' || *str == '<') + str++; + } + + fname = dbe_strdup (str); + arg = strchr (fname, ':'); + if (arg != NULL) + { + if (!strncmp (arg, NTXT (":P"), 2)) + { // just prototype + free (fname); + break; + } + *arg = '\0'; + } + + func = append_Function (mod, fname); + free (fname); + break; + case N_CMDLINE: + if (str && mod) + { + char *comp_flags = strchr (str, ';'); + if (comp_flags) + { + mod->comp_flags = dbe_strdup (comp_flags + 1); + mod->comp_dir = dbe_strndup (str, comp_flags - str); + } + } + break; + case N_LBRAC: + stabs_level++; + break; + case N_RBRAC: + stabs_level--; + break; + case N_UNDF: + INIT_MOD; + break; + case N_ENDM: + INIT_MOD; + break; + case N_OPT: + stabReader->parse_N_OPT (mod, str); + if (mod && (str != NULL) && streq (str, NTXT ("gcc2_compiled."))) + // Is it anachronism ? + mod->lang_code = Sp_lang_gcc; + break; + case N_GSYM: + if (mod && (str != NULL)) + { + if (strncmp (str, NTXT ("__KAI_K"), 7)) + break; + str += 7; + if (!strncmp (str, NTXT ("CC_"), 3)) + mod->lang_code = Sp_lang_KAI_KCC; + else if (!strncmp (str, NTXT ("cc_"), 3)) + mod->lang_code = Sp_lang_KAI_Kcc; + else if (!strncmp (str, NTXT ("PTS_"), 4) && + (mod->lang_code != Sp_lang_KAI_KCC) && + (mod->lang_code != Sp_lang_KAI_Kcc)) + mod->lang_code = Sp_lang_KAI_KPTS; + } + break; + case N_SO: + if (str == NULL || *str == '\0') + { + INIT_MOD; + break; + } + if (phase == 0) + { + phase = 1; + sbase = str; + } + else + { + if (*str == '/') + sbase = str; + else + { + size_t last = strlen (sbase); + if (last == 0 || sbase[last - 1] != '/') + snprintf (sname, sizeof (sname), NTXT ("%s/%s"), sbase, str); + else + snprintf (sname, sizeof (sname), NTXT ("%s%s"), sbase, str); + sbase = sname; + } + mod = append_Module (lo, sbase, lastMod); + if (updateStabsMod) + stabsModules->append (mod); + mod->hasStabs = true; + modCnt++; + if ((mod->lang_code != Sp_lang_gcc) && + (mod->lang_code != Sp_lang_KAI_KPTS) && + (mod->lang_code != Sp_lang_KAI_KCC) && + (mod->lang_code != Sp_lang_KAI_Kcc)) + mod->lang_code = (Sp_lang_code) stb.n_desc; + *sname = '\0'; + phase = 0; + } + break; + case N_OBJ: + if (str == NULL) + break; + if (phase == 0) + { + phase = 1; + sbase = str; + } + else + { + if (*str == '/') + sbase = str; + else + { + size_t last = strlen (sbase); + if (last == 0 || sbase[last - 1] != '/') + snprintf (sname, sizeof (sname), NTXT ("%s/%s"), sbase, str); + else + snprintf (sname, sizeof (sname), NTXT ("%s%s"), sbase, str); + sbase = sname; + } + if (mod && (mod->dot_o_file == NULL)) + { + if (strcmp (sbase, NTXT ("/")) == 0) + mod->set_name (dbe_strdup (path)); + else + { + mod->set_name (dbe_strdup (sbase)); + mod->dot_o_file = mod->createLoadObject (sbase); + } + } + *sname = '\0'; + phase = 0; + } + break; + case N_CPROF: + cpf_stabs_t map; + Dprintf (DEBUG_STABS, NTXT ("N_CPROF n_desc=%x n_value=0x%04x mod=%s\n"), + stb.n_desc, stb.n_value, (mod == NULL) ? NTXT ("???") : mod->get_name ()); + map.type = stb.n_desc; + map.offset = stb.n_value; + map.module = mod; + analyzerInfoMap.append (map); + break; + } + } + delete stabReader; + return func ? DBGD_ERR_NONE : DBGD_ERR_NO_STABS; +} + +Module * +Stabs::append_Module (LoadObject *lo, char *name, int lastMod) +{ + Module *module; + int size; + Symbol *sitem; + + if (lo->seg_modules != NULL) + { + size = lo->seg_modules->size (); + if (size < lastMod) + lastMod = size; + for (int i = 0; i < lastMod; i++) + { + module = lo->seg_modules->fetch (i); + if (module->linkerStabName && streq (module->linkerStabName, name)) + return module; + } + } + module = dbeSession->createModule (lo, NULL); + module->set_file_name (dbe_strdup (name)); + module->linkerStabName = dbe_strdup (module->file_name); + + // Append all functions with 'local_ind == -1' to the module. + if (LocalLst->size () > 0) + { + sitem = LocalLst->fetch (0); + if (!sitem->defined && sitem->local_ind == -1) + // Append all functions with 'local_ind == -1' to the module. + append_local_funcs (module, 0); + } + + // Append local func + char *basename = get_basename (name); + size = LocalFile->size (); + for (int i = 0; i < size; i++) + { + if (streq (basename, LocalFile->fetch (i))) + { + int local_ind = LocalFileIdx->fetch (i); + if (local_ind >= LocalLst->size ()) + break; + sitem = LocalLst->fetch (local_ind); + if (!sitem->defined) + { + append_local_funcs (module, local_ind); + break; + } + } + } + return module; +} + +void +Stabs::append_local_funcs (Module *module, int first_ind) +{ + Symbol *sitem = LocalLst->fetch (first_ind); + int local_ind = sitem->local_ind; + int size = LocalLst->size (); + for (int i = first_ind; i < size; i++) + { + sitem = LocalLst->fetch (i); + if (sitem->local_ind != local_ind) + break; + sitem->defined = true; + + // 3rd party compiled. e.g., Gcc or KAI compiled + if (sitem->lang_code != Sp_lang_unknown) + { + if (module->lang_code == Sp_lang_unknown) + module->lang_code = sitem->lang_code; + continue; + } + if (sitem->func) + continue; + Function *func = dbeSession->createFunction (); + sitem->func = func; + func->img_fname = path; + func->img_offset = (off_t) sitem->img_offset; + func->save_addr = (uint32_t) sitem->save; + func->size = (uint32_t) sitem->size; + func->module = module; + func->set_name (sitem->name); + module->functions->append (func); + module->loadobject->functions->append (func); + } +} + +Function * +Stabs::append_Function (Module *module, char *fname) +{ + Symbol *sitem, *sptr; + Function *func; + long sid, index; + char *name; + if (SymLstByName == NULL) + { + SymLstByName = SymLst->copy (); + SymLstByName->sort (SymNameCmp); + } + sptr = new Symbol; + if (module->lang_code == N_SO_FORTRAN || module->lang_code == N_SO_FORTRAN90) + { + char *fortran = dbe_sprintf (NTXT ("%s_"), fname); // FORTRAN name + sptr->name = fortran; + sid = SymLstByName->bisearch (0, -1, &sptr, SymNameCmp); + if (sid == -1) + { + free (fortran); + sptr->name = fname; + sid = SymLstByName->bisearch (0, -1, &sptr, SymNameCmp); + } + else + fname = fortran; + } + else + { + sptr->name = fname; + sid = SymLstByName->bisearch (0, -1, &sptr, SymNameCmp); + } + sptr->name = NULL; + delete sptr; + + if (sid == -1) + { + Vec_loop (Symbol*, SymLstByName, index, sitem) + { + if (strncmp (sitem->name, NTXT ("$X"), 2) == 0 + || strncmp (sitem->name, NTXT (".X"), 2) == 0) + { + char *n = strchr (((sitem->name) + 2), (int) '.'); + if (n != NULL) + name = n + 1; + else + name = sitem->name; + } + else + name = sitem->name; + if (name != NULL && fname != NULL && (strcmp (name, fname) == 0)) + { + sid = index; + break; + } + } + } + if (sid != -1) + { + sitem = SymLstByName->fetch (sid); + if (sitem->alias) + sitem = sitem->alias; + if (sitem->func) + return sitem->func; + sitem->func = func = dbeSession->createFunction (); + func->img_fname = path; + func->img_offset = (off_t) sitem->img_offset; + func->save_addr = (uint32_t) sitem->save; + func->size = (uint32_t) sitem->size; + } + else + func = dbeSession->createFunction (); + + func->module = module; + func->set_name (fname); + module->functions->append (func); + module->loadobject->functions->append (func); + return func; +} + +Function * +Stabs::append_Function (Module *module, char *linkerName, uint64_t pc) +{ + Dprintf (DEBUG_STABS, NTXT ("Stabs::append_Function: module=%s linkerName=%s pc=0x%llx\n"), + STR (module->get_name ()), STR (linkerName), (unsigned long long) pc); + long i; + Symbol *sitem = NULL, *sp; + Function *func; + sp = new Symbol; + if (pc) + { + sp->value = pc; + i = SymLst->bisearch (0, -1, &sp, SymFindCmp); + if (i != -1) + sitem = SymLst->fetch (i); + } + + if (!sitem && linkerName) + { + if (SymLstByName == NULL) + { + SymLstByName = SymLst->copy (); + SymLstByName->sort (SymNameCmp); + } + sp->name = linkerName; + i = SymLstByName->bisearch (0, -1, &sp, SymNameCmp); + sp->name = NULL; + if (i != -1) + sitem = SymLstByName->fetch (i); + } + delete sp; + + if (!sitem) + return NULL; + if (sitem->alias) + sitem = sitem->alias; + if (sitem->func) + return sitem->func; + + sitem->func = func = dbeSession->createFunction (); + func->img_fname = path; + func->img_offset = (off_t) sitem->img_offset; + func->save_addr = (uint32_t) sitem->save; + func->size = (uint32_t) sitem->size; + func->module = module; + func->set_name (sitem->name); //XXXX ?? Now call it to set obj->name + module->functions->append (func); + module->loadobject->functions->append (func); + return func; +}// Stabs::append_Function + +Dwarf * +Stabs::openDwarf () +{ + if (dwarf == NULL) + { + dwarf = new Dwarf (this); + check_Symtab (); + } + return dwarf; +} + +void +Stabs::read_hwcprof_info (Module *module) +{ + openDwarf ()->read_hwcprof_info (module); +} + +void +Stabs::dump () +{ + if (!DUMP_ELF_SYM) + return; + printf (NTXT ("\n======= Stabs::dump: %s =========\n"), path ? path : NTXT ("NULL")); + int i, sz; + if (LocalFile) + { + sz = LocalFile->size (); + for (i = 0; i < sz; i++) + printf (" %3d: %5d '%s'\n", i, LocalFileIdx->fetch (i), + LocalFile->fetch (i)); + } + Symbol::dump (SymLst, NTXT ("SymLst")); + Symbol::dump (LocalLst, NTXT ("LocalLst")); + printf (NTXT ("\n===== END of Stabs::dump: %s =========\n\n"), + path ? path : NTXT ("NULL")); +} + +/////////////////////////////////////////////////////////////////////////////// +// Class Include +Include::Include () +{ + stack = new Vector<SrcFileInfo*>; +} + +Include::~Include () +{ + Destroy (stack); +} + +void +Include::new_src_file (SourceFile *source, int lineno, Function *func) +{ + for (int index = stack->size () - 1; index >= 0; index--) + { + if (source == stack->fetch (index)->srcfile) + { + for (int i = stack->size () - 1; i > index; i--) + { + delete stack->remove (i); + if (func && func->line_first > 0) + func->popSrcFile (); + } + return; + } + } + if (func && func->line_first > 0) + func->pushSrcFile (source, lineno); + + SrcFileInfo *sfinfo = new SrcFileInfo; + sfinfo->srcfile = source; + sfinfo->lineno = lineno; + stack->append (sfinfo); +} + +void +Include::push_src_files (Function *func) +{ + int index; + SrcFileInfo *sfinfo; + + if (func->line_first <= 0 && stack->size () > 0) + { + sfinfo = stack->fetch (stack->size () - 1); + func->setDefSrc (sfinfo->srcfile); + } + Vec_loop (SrcFileInfo*, stack, index, sfinfo) + { + func->pushSrcFile (sfinfo->srcfile, sfinfo->lineno); + } +} + +void +Include::new_include_file (SourceFile *source, Function *func) +{ + if (stack->size () == 1 && stack->fetch (0)->srcfile == source) + // workaroud for gcc; gcc creates 'N_BINCL' stab for main source + return; + if (func && func->line_first > 0) + func->pushSrcFile (source, 0); + + SrcFileInfo *sfinfo = new SrcFileInfo; + sfinfo->srcfile = source; + sfinfo->lineno = 0; + stack->append (sfinfo); +} + +void +Include::end_include_file (Function *func) +{ + int index = stack->size () - 1; + if (index > 0) + { + delete stack->remove (index); + if (func && func->line_first > 0) + func->popSrcFile (); + } +} + +#define RET_S(x) if (t == x) return (char *) #x +char * +StabReader::get_type_name (int t) +{ + RET_S (N_UNDF); + RET_S (N_ABS); + RET_S (N_TEXT); + RET_S (N_DATA); + RET_S (N_BSS); + RET_S (N_COMM); + RET_S (N_FN); + RET_S (N_EXT); + RET_S (N_TYPE); + RET_S (N_GSYM); + RET_S (N_FNAME); + RET_S (N_FUN); + RET_S (N_OUTL); + RET_S (N_STSYM); + RET_S (N_TSTSYM); + RET_S (N_LCSYM); + RET_S (N_TLCSYM); + RET_S (N_MAIN); + RET_S (N_ROSYM); + RET_S (N_FLSYM); + RET_S (N_TFLSYM); + RET_S (N_PC); + RET_S (N_CMDLINE); + RET_S (N_OBJ); + RET_S (N_OPT); + RET_S (N_RSYM); + RET_S (N_SLINE); + RET_S (N_XLINE); + RET_S (N_ILDPAD); + RET_S (N_SSYM); + RET_S (N_ENDM); + RET_S (N_SO); + RET_S (N_MOD); + RET_S (N_EMOD); + RET_S (N_READ_MOD); + RET_S (N_ALIAS); + RET_S (N_LSYM); + RET_S (N_BINCL); + RET_S (N_SOL); + RET_S (N_PSYM); + RET_S (N_EINCL); + RET_S (N_ENTRY); + RET_S (N_SINCL); + RET_S (N_LBRAC); + RET_S (N_EXCL); + RET_S (N_USING); + RET_S (N_ISYM); + RET_S (N_ESYM); + RET_S (N_PATCH); + RET_S (N_CONSTRUCT); + RET_S (N_DESTRUCT); + RET_S (N_CODETAG); + RET_S (N_FUN_CHILD); + RET_S (N_RBRAC); + RET_S (N_BCOMM); + RET_S (N_TCOMM); + RET_S (N_ECOMM); + RET_S (N_XCOMM); + RET_S (N_ECOML); + RET_S (N_WITH); + RET_S (N_LENG); + RET_S (N_CPROF); + RET_S (N_BROWS); + RET_S (N_FUN_PURE); + RET_S (N_FUN_ELEMENTAL); + RET_S (N_FUN_RECURSIVE); + RET_S (N_FUN_AMD64_PARMDUMP); + RET_S (N_SYM_OMP_TLS); + RET_S (N_SO_AS); + RET_S (N_SO_C); + RET_S (N_SO_ANSI_C); + RET_S (N_SO_CC); + RET_S (N_SO_FORTRAN); + RET_S (N_SO_FORTRAN77); + RET_S (N_SO_PASCAL); + RET_S (N_SO_FORTRAN90); + RET_S (N_SO_JAVA); + RET_S (N_SO_C99); + return NULL; +} diff --git a/gprofng/src/Stabs.h b/gprofng/src/Stabs.h new file mode 100644 index 0000000..1a7443a --- /dev/null +++ b/gprofng/src/Stabs.h @@ -0,0 +1,160 @@ +/* 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. */ + +#ifndef _STABS_H +#define _STABS_H + +#include "dbe_structs.h" +#include "vec.h" + +enum cpf_instr_type_t { + CPF_INSTR_TYPE_LD = 0, // profiled load instruction + CPF_INSTR_TYPE_ST, // profiled store instruction + CPF_INSTR_TYPE_PREFETCH, // profiled prefetch instruction + CPF_INSTR_TYPE_BRTARGET, // branch target + CPF_INSTR_TYPE_UNKNOWN, // unidentified instruction + CPF_INSTR_TYPE_NTYPES // total # of instr types +}; + +class Function; +class LoadObject; +class Module; +class ComC; +class Elf; +class Dwarf; +class Symbol; +class Reloc; +struct cpf_stabs_t; +class SourceFile; +template <typename Key_t, typename Value_t> class Map; + +class Include { + public: + typedef struct { + SourceFile *srcfile; + int lineno; + } SrcFileInfo; + Include(); + ~Include(); + void new_src_file(SourceFile *source, int lineno, Function *func = NULL); + void new_include_file(SourceFile *source, Function *func); + void end_include_file(Function *func); + void push_src_files(Function *func); + + private: + Vector<SrcFileInfo*> *stack; +}; + +// Stabs object +class Stabs { + public: + + enum Stab_status { + DBGD_ERR_NONE, + DBGD_ERR_CANT_OPEN_FILE, + DBGD_ERR_BAD_ELF_LIB, + DBGD_ERR_BAD_ELF_FORMAT, + DBGD_ERR_NO_STABS, + DBGD_ERR_BAD_STABS, + DBGD_ERR_NO_DWARF, + DBGD_ERR_CHK_SUM + }; + + static Stabs *NewStabs(char *_path, char *lo_name); + Stabs(char *_path, char *_lo_name); + ~Stabs(); + + bool is_relocatable(){ return isRelocatable; } + long long get_textsz() { return textsz; } + Platform_t get_platform() { return platform; } + WSize_t get_class() { return wsize;} + Stab_status get_status() { return status;} + + Stab_status read_stabs(ino64_t srcInode, Module *module, Vector<ComC*> *comComs, bool readDwarf = false); + Stab_status read_archive(LoadObject *lo); + bool read_symbols(Vector<Function*> *functions); + uint64_t mapOffsetToAddress(uint64_t img_offset); + char *sym_name(uint64_t target, uint64_t instr, int flag); + Elf *openElf (bool dbg_info = false); + void read_hwcprof_info(Module *module); + void dump(); + void read_dwarf_from_dot_o(Module *mod); + + static bool is_fortran(Sp_lang_code lc) { return (lc == Sp_lang_fortran) || (lc == Sp_lang_fortran90); } + static Function *find_func(char *fname, Vector<Function*> *functions, bool fortran, bool inner_names=false); + Module *append_Module(LoadObject *lo, char *name, int lastMod = 0); + Function *append_Function(Module *module, char *fname); + Function *append_Function(Module *module, char *linkerName, uint64_t pc); + Function *map_PC_to_func(uint64_t pc, uint64_t &low_pc, Vector<Function*> *functions); + char *path; // path to the object file + char *lo_name; // User name of load object + + private: + Elf *elfDbg; // ELF with debug info + Elf *elfDis; // ELF for disasm + Stab_status status; // current stabs status + + long long textsz; // text segment size + Platform_t platform; // Sparc, Sparcv9, Intel + WSize_t wsize; // word size: 32 or 64 + bool isRelocatable; + Symbol *last_PC_to_sym; + + Vector<cpf_stabs_t> analyzerInfoMap; // stabs->section mapping + + bool check_Comm(Vector<ComC*> *comComs); + void check_Info(Vector<ComC*> *comComs); + void check_Loop(Vector<ComC*> *comComs); + void check_AnalyzerInfo(); + void append_local_funcs(Module *module, int first_ind); + Stab_status srcline_Stabs (Module *module, unsigned int StabSec, unsigned int StabStrSec, bool comdat); + Stab_status archive_Stabs (LoadObject *lo, unsigned int StabSec, unsigned int StabStrSec, bool comdat); + + // Interface with Elf Symbol Table + void check_Symtab(); + void readSymSec(unsigned int sec, Elf *elf); + void check_Relocs(); + void get_save_addr(bool need_swap_endian); + Symbol *map_PC_to_sym(uint64_t pc); + Symbol *pltSym; + Vector<Symbol*> *SymLst; // list of func symbols + Vector<Symbol*> *SymLstByName; // list of func symbols sorted by Name + Vector<Reloc*> *RelLst; // list of text relocations + Vector<Reloc*> *RelPLTLst; // list of PLT relocations + Vector<Symbol*> *LocalLst; // list of local func symbols + Vector<char*> *LocalFile; // list of local files + Vector<int> *LocalFileIdx; // start index in LocalLst + + Elf *openElf(char *fname, Stab_status &st); + Map<const char*, Symbol*> *get_elf_symbols(); + Dwarf *dwarf; + + bool st_check_symtab, st_check_relocs; + Function *createFunction(LoadObject *lo, Module *module, Symbol *sym); + void fixSymtabAlias(); + + // Interface with dwarf + Dwarf *openDwarf(); + + Vector<Module*> *stabsModules; + static char *get_type_name(int t); +}; + +#endif /* _STABS_H */ diff --git a/gprofng/src/Stats_data.cc b/gprofng/src/Stats_data.cc new file mode 100644 index 0000000..2595bc7 --- /dev/null +++ b/gprofng/src/Stats_data.cc @@ -0,0 +1,203 @@ +/* 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 <search.h> // For the tsearch stuff +#include <assert.h> +//#include <stdlib.h> +#include "Table.h" +#include "Sample.h" +#include "Stats_data.h" +#include "util.h" +#include "i18n.h" + +//XXX: The fundamental problem with this package of routines is that +//XXX: they should look a lot more like overview data. The result +//XXX: is that it is not possible to share as much code as would +//XXX: otherwise be possible. + +int +Stats_data::size () +{ + // Return the number of Stats_item values associated with "this". + if (stats_items == NULL) + return 0; + return stats_items->size (); +} + +Stats_data::Stats_item +Stats_data::fetch (int index) +{ + // Routine will return the "index"'th Stats_item associated with "this". + assert (index >= 0 && index < stats_items->size ()); + return *(stats_items->fetch (index)); +} + +Stats_data::Stats_data () +{ + // this constructor for use with sum() + packets = NULL; + stats_items = NULL; +} + +Stats_data::Stats_data (DataView *_packets) +{ + packets = _packets; + stats_items = NULL; + compute_data (); // reads all data +} + +Stats_data::~Stats_data () +{ + if (stats_items) + { + stats_items->destroy (); + delete stats_items; + } +} + +void +Stats_data::sum (Stats_data *data) +{ + int index; + Stats_item *stats_item, *data_item; + if (stats_items == NULL) + { + stats_items = new Vector<Stats_item*>; + Vec_loop (Stats_item*, data->stats_items, index, data_item) + { + stats_item = create_stats_item (data_item->value.ll, data_item->label); + stats_items->append (stats_item); + } + } + else + { + Vec_loop (Stats_item*, data->stats_items, index, data_item) + { + stats_items->fetch (index)->value.ll += data_item->value.ll; + } + } +} + +Stats_data::Stats_item * +Stats_data::create_stats_item (long long v, char *l) +{ + Stats_data::Stats_item *st_it; + st_it = new Stats_data::Stats_item; + st_it->label = l; + st_it->value.sign = false; + st_it->value.ll = v; + st_it->value.tag = VT_LLONG; + return st_it; +} + +PrUsage * +Stats_data::fetchPrUsage (long index) +{ + // Return the data values corresponding to the "index"'th sample. + PrUsage *prusage; + if (packets->getSize () > 0) + { + Sample* sample = (Sample*) packets->getObjValue (PROP_SMPLOBJ, index); + prusage = sample->get_usage (); + if (prusage != NULL) + return prusage; + } + return new PrUsage; +} + +void +Stats_data::compute_data () +{ + Stats_data::Stats_item *stats_item; + PrUsage *tots, *temp; + stats_items = new Vector<Stats_data::Stats_item*>; + + // Precomputation is needed. + long size = packets->getSize (); + tots = new PrUsage (); + for (long index = 0; index < size; index++) + { + temp = fetchPrUsage (index); + tots->pr_tstamp += temp->pr_tstamp; + tots->pr_create += temp->pr_create; + tots->pr_term += temp->pr_term; + tots->pr_rtime += temp->pr_rtime; + tots->pr_utime += temp->pr_utime; + tots->pr_stime += temp->pr_stime; + tots->pr_ttime += temp->pr_ttime; + tots->pr_tftime += temp->pr_tftime; + tots->pr_dftime += temp->pr_dftime; + tots->pr_kftime += temp->pr_kftime; + tots->pr_slptime += temp->pr_slptime; + tots->pr_ltime += temp->pr_ltime; + tots->pr_wtime += temp->pr_wtime; + tots->pr_stoptime += temp->pr_stoptime; + tots->pr_minf += temp->pr_minf; + tots->pr_majf += temp->pr_majf; + tots->pr_nswap += temp->pr_nswap; + tots->pr_inblk += temp->pr_inblk; + tots->pr_oublk += temp->pr_oublk; + tots->pr_msnd += temp->pr_msnd; + tots->pr_mrcv += temp->pr_mrcv; + tots->pr_sigs += temp->pr_sigs; + tots->pr_vctx += temp->pr_vctx; + tots->pr_ictx += temp->pr_ictx; + tots->pr_sysc += temp->pr_sysc; + tots->pr_ioch += temp->pr_ioch; + } + stats_item = create_stats_item ((long long) tots->pr_minf, + GTXT ("Minor Page Faults")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_majf, + GTXT ("Major Page Faults")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_nswap, + GTXT ("Process swaps")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_inblk, + GTXT ("Input blocks")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_oublk, + GTXT ("Output blocks")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_msnd, + GTXT ("Messages sent")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_mrcv, + GTXT ("Messages received")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_sigs, + GTXT ("Signals handled")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_vctx, + GTXT ("Voluntary context switches")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_ictx, + GTXT ("Involuntary context switches")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_sysc, + GTXT ("System calls")); + stats_items->append (stats_item); + stats_item = create_stats_item ((long long) tots->pr_ioch, + GTXT ("Characters of I/O")); + stats_items->append (stats_item); + delete tots; +} diff --git a/gprofng/src/Stats_data.h b/gprofng/src/Stats_data.h new file mode 100644 index 0000000..2f6b6a7 --- /dev/null +++ b/gprofng/src/Stats_data.h @@ -0,0 +1,59 @@ +/* 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. */ + +#ifndef _STATS_DATA_H +#define _STATS_DATA_H + +// A Stats_data object is used to obtain the data needed to display +// a statistics display. + +#include "vec.h" +#include "Exp_Layout.h" + +class DataView; + +class Stats_data +{ +public: + + struct Stats_item + { + char *label; // statistic label + TValue value; // statistic value + }; + + Stats_data (); + Stats_data (DataView *packets); + ~Stats_data (); + int size (); // Return the total number of items. + Stats_item fetch (int index); + void sum (Stats_data *data); + +private: + + PrUsage * fetchPrUsage (long index); + void compute_data (); // Perform any initial computation. + Stats_data::Stats_item *create_stats_item (long long, char *); + + Vector<Stats_item*> *stats_items; // Actual statistics values + DataView *packets; +}; + +#endif /* _STATS_DATA_H */ diff --git a/gprofng/src/StringBuilder.cc b/gprofng/src/StringBuilder.cc new file mode 100644 index 0000000..a9656d9 --- /dev/null +++ b/gprofng/src/StringBuilder.cc @@ -0,0 +1,585 @@ +/* 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <values.h> +#include <stdarg.h> + +#include "gp-defs.h" +#include "StringBuilder.h" +#include "i18n.h" + +StringBuilder::StringBuilder () +{ + count = 0; + maxCapacity = 16; + value = (char *) malloc (maxCapacity); + memset (value, 0, maxCapacity); +} + +StringBuilder::StringBuilder (int capacity) +{ + count = 0; + maxCapacity = capacity; + value = (char *) malloc (maxCapacity); + memset (value, 0, maxCapacity); +} + +StringBuilder::~StringBuilder () +{ + free (value); +} + +void +StringBuilder::ensureCapacity (int minimumCapacity) +{ + if (minimumCapacity > maxCapacity) + expandCapacity (minimumCapacity); +} + +void +StringBuilder::expandCapacity (int minimumCapacity) +{ + int newCapacity = (maxCapacity + 1) * 2; + if (newCapacity < 0) + newCapacity = MAXINT; + else if (minimumCapacity > newCapacity) + newCapacity = minimumCapacity; + char *newValue = (char *) malloc (newCapacity); + maxCapacity = newCapacity; + memcpy (newValue, value, count); + memset (newValue + count, 0, maxCapacity - count); + free (value); + value = newValue; +} + +void +StringBuilder::trimToSize () +{ + if (count < maxCapacity) + { + char *newValue = (char *) malloc (count); + maxCapacity = count; + memcpy (newValue, value, count); + free (value); + value = newValue; + } +} + +void +StringBuilder::trim () +{ + while (count > 0) + { + if (value[count - 1] != ' ') + break; + count--; + } +} + +void +StringBuilder::setLength (int newLength) +{ + if (newLength < 0) + return; + if (newLength > maxCapacity) + expandCapacity (newLength); + if (count < newLength) + { + for (; count < newLength; count++) + value[count] = '\0'; + } + else + count = newLength; +} + +char +StringBuilder::charAt (int index) +{ + if (index < 0 || index >= count) + return 0; + return value[index]; +} + +void +StringBuilder::getChars (int srcBegin, int srcEnd, char dst[], int dstBegin) +{ + if (srcBegin < 0) + return; + if (srcEnd < 0 || srcEnd > count) + return; + if (srcBegin > srcEnd) + return; + memcpy (dst + dstBegin, value + srcBegin, srcEnd - srcBegin); +} + +void +StringBuilder::setCharAt (int index, char ch) +{ + if (index < 0 || index >= count) + return; + value[index] = ch; +} + +StringBuilder * +StringBuilder::append (StringBuilder *sb) +{ + if (sb == NULL) + return append (NTXT ("null")); + int len = sb->count; + int newcount = count + len; + if (newcount > maxCapacity) + expandCapacity (newcount); + sb->getChars (0, len, value, count); + count = newcount; + return this; +} + +StringBuilder * +StringBuilder::append (const char str[]) +{ + int len = (int) strlen (str); + int newCount = count + len; + if (newCount > maxCapacity) + expandCapacity (newCount); + memcpy (value + count, str, len); + count = newCount; + return this; +} + +StringBuilder * +StringBuilder::append (const char str[], int offset, int len) +{ + int newCount = count + len; + if (newCount > maxCapacity) + expandCapacity (newCount); + memcpy (value + count, str + offset, len); + count = newCount; + return this; +} + +StringBuilder * +StringBuilder::append (bool b) +{ + if (b) + append (NTXT ("true")); + else + append (NTXT ("false")); + return this; +} + +StringBuilder * +StringBuilder::append (char c) +{ + int newCount = count + 1; + if (newCount > maxCapacity) + { + expandCapacity (newCount); + } + value[count++] = c; + return this; +} + +StringBuilder * +StringBuilder::append (int i) +{ + char buf[16]; + snprintf (buf, sizeof (buf), NTXT ("%d"), i); + append (buf); + return this; +} + +StringBuilder * +StringBuilder::append (unsigned int i) +{ + char buf[16]; + snprintf (buf, sizeof (buf), NTXT ("%u"), i); + append (buf); + return this; +} + +StringBuilder * +StringBuilder::append (long lng) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%ld"), lng); + append (buf); + return this; +} + +StringBuilder * +StringBuilder::append (unsigned long lng) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%lu"), lng); + append (buf); + return this; +} + +StringBuilder * +StringBuilder::append (long long lng) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%lld"), lng); + append (buf); + return this; +} + +StringBuilder * +StringBuilder::append (unsigned long long lng) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%llu"), lng); + append (buf); + return this; +} + +StringBuilder * +StringBuilder::append (float f) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%f"), (double) f); + append (buf); + return this; +} + +StringBuilder * +StringBuilder::append (double d) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%f"), d); + append (buf); + return this; +} + +StringBuilder * +StringBuilder::_delete (int start, int end) +{ + if (start < 0) + return this; + if (end > count) + end = count; + if (start > end) + return this; + int len = end - start; + if (len > 0) + { + memcpy (value + start, value + start + len, count - end); + count -= len; + } + return this; +} + +StringBuilder * +StringBuilder::deleteCharAt (int index) +{ + if (index < 0 || index >= count) + return this; + memcpy (value + index, value + index + 1, count - index - 1); + count--; + return this; +} + +bool +StringBuilder::endsWith (const char str[]) +{ + if (str == NULL) + { + if (count == 0) + return true; + return false; + } + int len = (int) strlen (str); + if (len == 0) + return true; + int start = count - len; + if (start < 0) + return false; + int res = strncmp ((const char *) (value + start), str, len); + if (res != 0) + return false; + return true; +} + +StringBuilder * +StringBuilder::insert (int index, const char str[], int offset, int len) +{ + if (index < 0 || index > count) + return this; + if (offset < 0 || len < 0 || offset > ((int) strlen (str)) - len) + return this; + int newCount = count + len; + if (newCount > maxCapacity) + expandCapacity (newCount); + memcpy (value + index + len, value + index, count - index); + memcpy (value + index, str + offset, len); + count = newCount; + return this; +} + +StringBuilder * +StringBuilder::insert (int offset, const char str[]) +{ + if (offset < 0 || offset > count) + return this; + int len = (int) strlen (str); + int newCount = count + len; + if (newCount > maxCapacity) + expandCapacity (newCount); + memcpy (value + offset + len, value + offset, count - offset); + memcpy (value + offset, str, len); + count = newCount; + return this; +} + +StringBuilder * +StringBuilder::insert (int offset, bool b) +{ + return insert (offset, b ? NTXT ("true") : NTXT ("false")); +} + +StringBuilder * +StringBuilder::insert (int offset, char c) +{ + int newCount = count + 1; + if (newCount > maxCapacity) + expandCapacity (newCount); + memcpy (value + offset + 1, value + offset, count - offset); + value[offset] = c; + count = newCount; + return this; +} + +StringBuilder * +StringBuilder::insert (int offset, int i) +{ + char buf[16]; + snprintf (buf, sizeof (buf), NTXT ("%d"), i); + insert (offset, buf); + return this; +} + +StringBuilder * +StringBuilder::insert (int offset, long l) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%ld"), l); + insert (offset, buf); + return this; +} + +StringBuilder * +StringBuilder::insert (int offset, float f) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%f"), (double) f); + insert (offset, buf); + return this; +} + +StringBuilder * +StringBuilder::insert (int offset, double d) +{ + char buf[32]; + snprintf (buf, sizeof (buf), NTXT ("%f"), d); + insert (offset, buf); + return this; +} + +StringBuilder * +StringBuilder::reverse () +{ + int n = count - 1; + for (int j = (n - 1) >> 1; j >= 0; --j) + { + char temp = value[j]; + char temp2 = value[n - j]; + value[j] = temp2; + value[n - j] = temp; + } + return this; +} + +//String *StringBuilder::toString(); +char * +StringBuilder::toString () +{ + char *str = (char *) malloc (count + 1); + memcpy (str, value, count); + str[count] = '\0'; + return str; +} + +void +StringBuilder::toFile (FILE *fp) +{ + append ('\0'); + count--; + fprintf (fp, NTXT ("%s"), value); +} + +void +StringBuilder::toFileLn (FILE *fp) +{ + trim (); + append ('\0'); + count--; + fprintf (fp, NTXT ("%s\n"), value); +} + +StringBuilder * +StringBuilder::sprintf (const char *fmt, ...) +{ + int cnt; + setLength (0); + + va_list vp; + va_start (vp, fmt); + cnt = vsnprintf (value, maxCapacity, fmt, vp); + va_end (vp); + if (cnt < maxCapacity) + { + count = cnt; + return this; + } + + // Have to count the trailing zero + ensureCapacity (cnt + 1); + va_start (vp, fmt); + count = vsnprintf (value, maxCapacity, fmt, vp); + va_end (vp); + return this; +} + +StringBuilder * +StringBuilder::appendf (const char *fmt, ...) +{ + va_list vp; + va_start (vp, fmt); + int cnt = vsnprintf (value + count, maxCapacity - count, fmt, vp); + va_end (vp); + if (cnt + count < maxCapacity) + { + count += cnt; + return this; + } + + // Have to count the trailing zero + ensureCapacity (count + cnt + 1); + va_start (vp, fmt); + count += vsnprintf (value + count, maxCapacity - count, fmt, vp); + va_end (vp); + return this; +} + +int +StringBuilder::indexOf (const char str[]) +{ + return indexOf (str, 0); +} + +int +StringBuilder::indexOf (const char str[], int fromIndex) +{ + int len = (int) strlen (str); + if (fromIndex >= count) + return len == 0 ? count : -1; + if (fromIndex < 0) + fromIndex = 0; + if (len == 0) + return fromIndex; + + char first = str[0]; + int max = (count - len); + + for (int i = fromIndex; i <= max; i++) + { + /* Look for first character. */ + if (value[i] != first) + while (++i <= max && value[i] != first) + ; + /* Found first character, now look at the rest of v2 */ + if (i <= max) + { + int j = i + 1; + int end = j + len - 1; + for (int k = 1; j < end && value[j] == str[k]; j++, k++) + ; + if (j == end) /* Found whole string. */ + return i; + } + } + return -1; +} + +int +StringBuilder::lastIndexOf (const char str[]) +{ + return lastIndexOf (str, count); +} + +int +StringBuilder::lastIndexOf (const char str[], int fromIndex) +{ + /* + * Check arguments; return immediately where possible. For + * consistency, don't check for null str. + */ + int len = (int) strlen (str); + int rightIndex = count - len; + if (fromIndex < 0) + return -1; + if (fromIndex > rightIndex) + fromIndex = rightIndex; + /* Empty string always matches. */ + if (len == 0) + return fromIndex; + + int strLastIndex = len - 1; + char strLastChar = str[strLastIndex]; + int min = len - 1; + int i = min + fromIndex; + + while (true) + { + while (i >= min && value[i] != strLastChar) + i--; + if (i < min) + return -1; + + int j = i - 1; + int start = j - (len - 1); + int k = strLastIndex - 1; + while (j > start) + { + if (value[j--] != str[k--]) + { + i--; + break; + } + } + if (j == start) + return start + 1; + } +} + diff --git a/gprofng/src/StringBuilder.h b/gprofng/src/StringBuilder.h new file mode 100644 index 0000000..5fe1a51 --- /dev/null +++ b/gprofng/src/StringBuilder.h @@ -0,0 +1,101 @@ +/* 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. */ + +/* + * java/lang/StringBuilder + * + * Based on JavaTM 2 Platform Standard Ed. 5.0 + */ + +#ifndef _StringBuilder_h +#define _StringBuilder_h + +class StringBuilder +{ +public: + StringBuilder (); + StringBuilder (int capacity); + virtual ~StringBuilder (); + + int + length () + { + return count; + } + + int + capacity () + { + return maxCapacity; + } + + bool endsWith (const char str[]); + void ensureCapacity (int minimumCapacity); + void expandCapacity (int minimumCapacity); + void trimToSize (); + void trim (); + void setLength (int newLength); + char charAt (int index); + void getChars (int srcBegin, int srcEnd, char dst[], int dstBegin); + void setCharAt (int index, char ch); + StringBuilder *append (StringBuilder *sb); + StringBuilder *append (const char str[]); + StringBuilder *append (const char str[], int offset, int len); + StringBuilder *append (bool b); + StringBuilder *append (char c); + StringBuilder *append (int i); + StringBuilder *append (unsigned int i); + StringBuilder *append (long lng); + StringBuilder *append (unsigned long i); + StringBuilder *append (long long lng); + StringBuilder *append (unsigned long long lng); + StringBuilder *append (float f); + StringBuilder *append (double d); + StringBuilder *_delete (int start, int end); + StringBuilder *deleteCharAt (int index); + StringBuilder *insert (int index, const char str[], int offset, int len); + StringBuilder *insert (int offset, const char str[]); + StringBuilder *insert (int offset, bool b); + StringBuilder *insert (int offset, char c); + StringBuilder *insert (int offset, int i); + StringBuilder *insert (int offset, long l); + StringBuilder *insert (int offset, float f); + StringBuilder *insert (int offset, double d); + StringBuilder *reverse (); + char *toString (); + void toFile (FILE *fp); + void toFileLn (FILE *fp); + + // Not in Java + StringBuilder *appendf (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + StringBuilder *sprintf (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + int indexOf (const char str[]); + int indexOf (const char str[], int fromIndex); + int lastIndexOf (const char str[]); + int lastIndexOf (const char str[], int fromIndex); + +private: + char *value; + int count; + int maxCapacity; +}; + +#endif /* _StringBuilder_h */ diff --git a/gprofng/src/StringMap.h b/gprofng/src/StringMap.h new file mode 100644 index 0000000..31898f4 --- /dev/null +++ b/gprofng/src/StringMap.h @@ -0,0 +1,238 @@ +/* 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. */ + +/* + * String Map implementation. + */ + +#ifndef _DBE_STRINGMAP_H +#define _DBE_STRINGMAP_H + +#include <assert.h> +#include <vec.h> +#include <Map.h> +#include <util.h> + +template <typename Value_t> +class StringMap : public Map<const char*, Value_t> +{ +public: + + StringMap (int htable_size = 1024, int chunk_size = 16384); + ~StringMap (); + void clear (); + void put (const char *key, Value_t val); + Value_t get (const char *key); + Value_t get (const char *key, typename Map<const char*, Value_t>::Relation rel); + Value_t remove (const char*); + Vector<const char*> *keySet (); + Vector<Value_t> *values (); + +private: + + static unsigned + hash (const char *key) + { + return (unsigned) crc64 (key, strlen (key)); + } + + struct Entry + { + char *key; + Value_t val; + }; + + int CHUNK_SIZE, HTABLE_SIZE; + int entries; + int nchunks; + Entry **chunks; + Vector<Entry*> *index; + Entry **hashTable; +}; + +template <typename Value_t> +StringMap<Value_t>::StringMap (int htable_size, int chunk_size) +{ + HTABLE_SIZE = htable_size; + CHUNK_SIZE = chunk_size; + entries = 0; + nchunks = 0; + chunks = NULL; + index = new Vector<Entry*>; + hashTable = new Entry*[HTABLE_SIZE]; + for (int i = 0; i < HTABLE_SIZE; i++) + hashTable[i] = NULL; +} + +template <typename Value_t> +StringMap<Value_t>::~StringMap () +{ + for (int i = 0; i < entries; ++i) + { + Entry *entry = index->fetch (i); + free (entry->key); + } + for (int i = 0; i < nchunks; i++) + delete[] chunks[i]; + delete[] chunks; + delete index; + delete[] hashTable; +} + +template <typename Value_t> +void +StringMap<Value_t>::clear () +{ + for (int i = 0; i < entries; ++i) + { + Entry *entry = index->fetch (i); + free (entry->key); + } + entries = 0; + index->reset (); + for (int i = 0; i < HTABLE_SIZE; i++) + hashTable[i] = NULL; +} + +template <typename Value_t> +void +StringMap<Value_t>::put (const char *key, Value_t val) +{ + unsigned idx = hash (key) % HTABLE_SIZE; + Entry *entry = hashTable[idx]; + if (entry && strcmp (entry->key, key) == 0) + { + entry->val = val; + return; + } + int lo = 0; + int hi = entries - 1; + while (lo <= hi) + { + int md = (lo + hi) / 2; + entry = index->fetch (md); + int cmp = strcmp (entry->key, key); + if (cmp < 0) + lo = md + 1; + else if (cmp > 0) + hi = md - 1; + else + { + entry->val = val; + return; + } + } + if (entries >= nchunks * CHUNK_SIZE) + { + nchunks++; + + // Reallocate Entry chunk array + Entry **new_chunks = new Entry*[nchunks]; + for (int i = 0; i < nchunks - 1; i++) + new_chunks[i] = chunks[i]; + delete[] chunks; + chunks = new_chunks; + + // Allocate new chunk for entries. + chunks[nchunks - 1] = new Entry[CHUNK_SIZE]; + } + entry = &chunks[entries / CHUNK_SIZE][entries % CHUNK_SIZE]; + entry->key = strdup (key); + entry->val = val; + index->insert (lo, entry); + hashTable[idx] = entry; + entries++; +} + +template <typename Value_t> +Value_t +StringMap<Value_t>::get (const char *key) +{ + unsigned idx = hash (key) % HTABLE_SIZE; + Entry *entry = hashTable[idx]; + if (entry && strcmp (entry->key, key) == 0) + return entry->val; + int lo = 0; + int hi = entries - 1; + while (lo <= hi) + { + int md = (lo + hi) / 2; + entry = index->fetch (md); + int cmp = strcmp (entry->key, key); + if (cmp < 0) + lo = md + 1; + else if (cmp > 0) + hi = md - 1; + else + { + hashTable[idx] = entry; + return entry->val; + } + } + return (Value_t) 0; +} + +template <typename Value_t> +Value_t +StringMap<Value_t>::get (const char *key, typename Map<const char*, + Value_t>::Relation rel) +{ + if (rel != Map<const char*, Value_t>::REL_EQ) + return (Value_t) 0; + return get (key); +} + +template <typename Value_t> +Value_t +StringMap<Value_t>::remove (const char*) +{ + // Not implemented + if (1) + assert (0); + return (Value_t) 0; +} + +template <typename Value_t> +Vector<Value_t> * +StringMap<Value_t>::values () +{ + Vector<Value_t> *vals = new Vector<Value_t>(entries); + for (int i = 0; i < entries; ++i) + { + Entry *entry = index->fetch (i); + vals->append (entry->val); + } + return vals; +} + +template <typename Value_t> +Vector<const char*> * +StringMap<Value_t>::keySet () +{ + Vector<const char*> *keys = new Vector<const char*>(entries); + for (int i = 0; i < entries; ++i) + { + Entry *entry = index->fetch (i); + keys->append (entry->key); + } + return keys; +} + +#endif diff --git a/gprofng/src/Table.cc b/gprofng/src/Table.cc new file mode 100644 index 0000000..afe44bd --- /dev/null +++ b/gprofng/src/Table.cc @@ -0,0 +1,1687 @@ +/* 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 <stdio.h> + +#include "IndexMap2D.h" +#include "DbeSession.h" +#include "FilterExp.h" +#include "Table.h" +#include "util.h" +#include "i18n.h" + +char * +get_prof_data_type_name (int t) +{ + switch (t) + { + case DATA_SAMPLE: return NTXT("PROFDATA_TYPE_SAMPLE"); + case DATA_GCEVENT: return NTXT("PROFDATA_TYPE_GCEVENT"); + case DATA_HEAPSZ: return NTXT("PROFDATA_TYPE_HEAPSZ"); + case DATA_CLOCK: return NTXT("PROFDATA_TYPE_CLOCK"); + case DATA_HWC: return NTXT("PROFDATA_TYPE_HWC"); + case DATA_SYNCH: return NTXT("PROFDATA_TYPE_SYNCH"); + case DATA_HEAP: return NTXT("PROFDATA_TYPE_HEAP"); + case DATA_OMP: return NTXT("PROFDATA_TYPE_OMP"); + case DATA_OMP2: return NTXT("PROFDATA_TYPE_OMP2"); + case DATA_OMP3: return NTXT("PROFDATA_TYPE_OMP3"); + case DATA_OMP4: return NTXT("PROFDATA_TYPE_OMP4"); + case DATA_OMP5: return NTXT("PROFDATA_TYPE_OMP5"); + case DATA_IOTRACE: return NTXT("PROFDATA_TYPE_IOTRACE"); + default: abort (); + return NTXT ("PROFDATA_TYPE_ERROR"); + } +} + +char * +get_prof_data_type_uname (int t) +{ + switch (t) + { + case DATA_SAMPLE: return GTXT("Process-wide Resource Utilization"); + case DATA_GCEVENT: return GTXT("Java Garbage Collection Events"); + case DATA_HEAPSZ: return GTXT("Heap Size"); + case DATA_CLOCK: return GTXT("Clock Profiling"); + case DATA_HWC: return GTXT("HW Counter Profiling"); + case DATA_SYNCH: return GTXT("Synchronization Tracing"); + case DATA_HEAP: return GTXT("Heap Tracing"); + case DATA_OMP: return GTXT("OpenMP Profiling"); + case DATA_OMP2: return GTXT("OpenMP Profiling"); + case DATA_OMP3: return GTXT("OpenMP Profiling"); + case DATA_OMP4: return GTXT("OpenMP Profiling"); + case DATA_OMP5: return GTXT("OpenMP Profiling"); + case DATA_IOTRACE: return GTXT("IO Tracing"); + default: abort (); + return NTXT ("PROFDATA_TYPE_ERROR"); + } +} + +int assert_level = 0; // set to 1 to bypass problematic asserts + +#define ASSERT_SKIP (assert_level) + +/* + * class PropDescr + */ + +PropDescr::PropDescr (int _propID, const char *_name) +{ + propID = _propID; + name = strdup (_name ? _name : NTXT ("")); + uname = NULL; + vtype = TYPE_NONE; + flags = 0; + stateNames = NULL; + stateUNames = NULL; +} + +PropDescr::~PropDescr () +{ + free (name); + free (uname); + if (stateNames) + { + stateNames->destroy (); + delete stateNames; + } + if (stateUNames) + { + stateUNames->destroy (); + delete stateUNames; + } +} + +void +PropDescr::addState (int value, const char *stname, const char *stuname) +{ + if (value < 0 || stname == NULL) + return; + if (stateNames == NULL) + stateNames = new Vector<char*>; + stateNames->store (value, strdup (stname)); + if (stateUNames == NULL) + stateUNames = new Vector<char*>; + stateUNames->store (value, strdup (stuname)); +} + +char * +PropDescr::getStateName (int value) +{ + if (stateNames && value >= 0 && value < stateNames->size ()) + return stateNames->fetch (value); + return NULL; +} + +char * +PropDescr::getStateUName (int value) +{ + if (stateUNames && value >= 0 && value < stateUNames->size ()) + return stateUNames->fetch (value); + return NULL; +} + +/* + * class FieldDescr + */ + +FieldDescr::FieldDescr (int _propID, const char *_name) +{ + propID = _propID; + name = _name ? strdup (_name) : NULL; + offset = 0; + vtype = TYPE_NONE; + format = NULL; +} + +FieldDescr::~FieldDescr () +{ + free (name); + free (format); +} + +/* + * class PacketDescriptor + */ + +PacketDescriptor::PacketDescriptor (DataDescriptor *_ddscr) +{ + ddscr = _ddscr; + fields = new Vector<FieldDescr*>; +} + +PacketDescriptor::~PacketDescriptor () +{ + fields->destroy (); + delete fields; +} + +void +PacketDescriptor::addField (FieldDescr *fldDscr) +{ + if (fldDscr == NULL) + return; + fields->append (fldDscr); +} + +/* + * class Data + */ + +/* Check compatibility between Datum and Data */ +static void +checkCompatibility (VType_type v1, VType_type v2) +{ + switch (v1) + { + case TYPE_NONE: + case TYPE_STRING: + case TYPE_DOUBLE: + case TYPE_OBJ: + case TYPE_DATE: + assert (v1 == v2); + break; + case TYPE_INT32: + case TYPE_UINT32: + assert (v2 == TYPE_INT32 || + v2 == TYPE_UINT32); + break; + case TYPE_INT64: + case TYPE_UINT64: + assert (v2 == TYPE_INT64 || + v2 == TYPE_UINT64); + break; + default: + assert (0); + } +} + +class DataINT32 : public Data +{ +public: + + DataINT32 () + { + data = new Vector<int32_t>; + } + + virtual + ~DataINT32 () + { + delete data; + } + + virtual VType_type + type () + { + return TYPE_INT32; + } + + virtual void + reset () + { + data->reset (); + } + + virtual long + getSize () + { + return data->size (); + } + + virtual int + fetchInt (long i) + { + return (int) data->fetch (i); + } + + virtual unsigned long long + fetchULong (long i) + { + return (unsigned long long) data->fetch (i); + } + + virtual long long + fetchLong (long i) + { + return (long long) data->fetch (i); + } + + virtual char * + fetchString (long i) + { + return dbe_sprintf (NTXT ("%d"), data->fetch (i)); + } + + virtual double + fetchDouble (long i) + { + return (double) data->fetch (i); + } + + virtual void * + fetchObject (long) + { + assert (ASSERT_SKIP); + return NULL; + } + + virtual void + setDatumValue (long idx, const Datum *val) + { + data->store (idx, val->i); + } + + virtual void + setValue (long idx, uint64_t val) + { + data->store (idx, (int32_t) val); + } + + virtual void + setObjValue (long, void*) + { + assert (ASSERT_SKIP); + return; + } + + virtual int + cmpValues (long idx1, long idx2) + { + int32_t i1 = data->fetch (idx1); + int32_t i2 = data->fetch (idx2); + return i1 < i2 ? -1 : i1 > i2 ? 1 : 0; + } + + virtual int + cmpDatumValue (long idx, const Datum *val) + { + int32_t i1 = data->fetch (idx); + int32_t i2 = val->i; + return i1 < i2 ? -1 : i1 > i2 ? 1 : 0; + } + +private: + Vector<int32_t> *data; +}; + +class DataUINT32 : public Data +{ +public: + + DataUINT32 () + { + data = new Vector<uint32_t>; + } + + virtual + ~DataUINT32 () + { + delete data; + } + + virtual VType_type + type () + { + return TYPE_UINT32; + } + + virtual void + reset () + { + data->reset (); + } + + virtual long + getSize () + { + return data->size (); + } + + virtual int + fetchInt (long i) + { + return (int) data->fetch (i); + } + + virtual unsigned long long + fetchULong (long i) + { + return (unsigned long long) data->fetch (i); + } + + virtual long long + fetchLong (long i) + { + return (long long) data->fetch (i); + } + + virtual char * + fetchString (long i) + { + return dbe_sprintf (NTXT ("%u"), data->fetch (i)); + } + + virtual double + fetchDouble (long i) + { + return (double) data->fetch (i); + } + + virtual void * + fetchObject (long) + { + assert (ASSERT_SKIP); + return NULL; + } + + virtual void + setDatumValue (long idx, const Datum *val) + { + data->store (idx, val->i); + } + + virtual void + setValue (long idx, uint64_t val) + { + data->store (idx, (uint32_t) val); + } + + virtual void + setObjValue (long, void*) + { + assert (ASSERT_SKIP); + return; + } + + virtual int + cmpValues (long idx1, long idx2) + { + uint32_t u1 = data->fetch (idx1); + uint32_t u2 = data->fetch (idx2); + return u1 < u2 ? -1 : u1 > u2 ? 1 : 0; + } + + virtual int + cmpDatumValue (long idx, const Datum *val) + { + uint32_t u1 = data->fetch (idx); + uint32_t u2 = (uint32_t) val->i; + return u1 < u2 ? -1 : u1 > u2 ? 1 : 0; + } + +private: + Vector<uint32_t> *data; +}; + +class DataINT64 : public Data +{ +public: + + DataINT64 () + { + data = new Vector<int64_t>; + } + + virtual + ~DataINT64 () + { + delete data; + } + + virtual VType_type + type () + { + return TYPE_INT64; + } + + virtual void + reset () + { + data->reset (); + } + + virtual long + getSize () + { + return data->size (); + } + + virtual int + fetchInt (long i) + { + return (int) data->fetch (i); + } + + virtual unsigned long long + fetchULong (long i) + { + return (unsigned long long) data->fetch (i); + } + + virtual long long + fetchLong (long i) + { + return (long long) data->fetch (i); + } + + virtual char * + fetchString (long i) + { + return dbe_sprintf (NTXT ("%lld"), (long long) data->fetch (i)); + } + + virtual double + fetchDouble (long i) + { + return (double) data->fetch (i); + } + + virtual void * + fetchObject (long) + { + assert (ASSERT_SKIP); + return NULL; + } + + virtual void + setDatumValue (long idx, const Datum *val) + { + data->store (idx, val->ll); + } + + virtual void + setValue (long idx, uint64_t val) + { + data->store (idx, (int64_t) val); + } + + virtual void + setObjValue (long, void*) + { + assert (ASSERT_SKIP); + return; + } + + virtual int + cmpValues (long idx1, long idx2) + { + int64_t i1 = data->fetch (idx1); + int64_t i2 = data->fetch (idx2); + return i1 < i2 ? -1 : i1 > i2 ? 1 : 0; + } + + virtual int + cmpDatumValue (long idx, const Datum *val) + { + int64_t i1 = data->fetch (idx); + int64_t i2 = val->ll; + return i1 < i2 ? -1 : i1 > i2 ? 1 : 0; + } + +private: + Vector<int64_t> *data; +}; + +class DataUINT64 : public Data +{ +public: + + DataUINT64 () + { + data = new Vector<uint64_t>; + } + + virtual + ~DataUINT64 () + { + delete data; + } + + virtual VType_type + type () + { + return TYPE_UINT64; + } + + virtual void + reset () + { + data->reset (); + } + + virtual long + getSize () + { + return data->size (); + } + + virtual int + fetchInt (long i) + { + return (int) data->fetch (i); + } + + virtual unsigned long long + fetchULong (long i) + { + return (unsigned long long) data->fetch (i); + } + + virtual long long + fetchLong (long i) + { + return (long long) data->fetch (i); + } + + virtual char * + fetchString (long i) + { + return dbe_sprintf (NTXT ("%llu"), (long long) data->fetch (i)); + } + + virtual double + fetchDouble (long i) + { + return (double) data->fetch (i); + } + + virtual void * + fetchObject (long) + { + assert (ASSERT_SKIP); + return NULL; + } + + virtual void + setDatumValue (long idx, const Datum *val) + { + data->store (idx, val->ll); + } + + virtual void + setValue (long idx, uint64_t val) + { + data->store (idx, val); + } + + virtual void + setObjValue (long, void*) + { + assert (ASSERT_SKIP); + return; + } + + virtual int + cmpValues (long idx1, long idx2) + { + uint64_t u1 = data->fetch (idx1); + uint64_t u2 = data->fetch (idx2); + return u1 < u2 ? -1 : u1 > u2 ? 1 : 0; + } + + virtual int + cmpDatumValue (long idx, const Datum *val) + { + uint64_t u1 = data->fetch (idx); + uint64_t u2 = (uint64_t) val->ll; + return u1 < u2 ? -1 : u1 > u2 ? 1 : 0; + } + +private: + Vector<uint64_t> *data; +}; + +class DataOBJECT : public Data +{ +public: + + DataOBJECT () + { + dtype = TYPE_OBJ; + data = new Vector<void*>; + } + + DataOBJECT (VType_type _dtype) + { + dtype = _dtype; + data = new Vector<void*>; + } + + virtual + ~DataOBJECT () + { + delete data; + } + + virtual VType_type + type () + { + return dtype; + } + + virtual void + reset () + { + data->reset (); + } + + virtual long + getSize () + { + return data->size (); + } + + virtual int + fetchInt (long) + { + assert (ASSERT_SKIP); + return 0; + } + + virtual unsigned long long + fetchULong (long) + { + assert (ASSERT_SKIP); + return 0LL; + } + + virtual long long + fetchLong (long) + { + assert (ASSERT_SKIP); + return 0LL; + } + + virtual char * + fetchString (long i) + { + return dbe_sprintf (NTXT ("%lu"), (unsigned long) data->fetch (i)); + } + + virtual double + fetchDouble (long) + { + assert (ASSERT_SKIP); + return 0.0; + } + + virtual void * + fetchObject (long i) + { + return data->fetch (i); + } + + virtual void + setDatumValue (long idx, const Datum *val) + { + data->store (idx, val->p); + } + + virtual void + setValue (long, uint64_t) + { + assert (ASSERT_SKIP); + return; + } + + virtual void + setObjValue (long idx, void *p) + { + data->store (idx, p); + } + + virtual int + cmpValues (long, long) + { + return 0; + } + + virtual int + cmpDatumValue (long, const Datum *) + { + return 0; + } + +private: + VType_type dtype; + Vector<void*> *data; +}; + +class DataSTRING : public Data +{ +public: + + DataSTRING () + { + data = new Vector<char*>; + } + + virtual + ~DataSTRING () + { + data->destroy (); + delete data; + } + + virtual VType_type + type () + { + return TYPE_STRING; + } + + virtual void + reset () + { + data->reset (); + } + + virtual long + getSize () + { + return data->size (); + } + + virtual int + fetchInt (long) + { + return 0; + } + + virtual unsigned long long + fetchULong (long) + { + return 0LL; + } + + virtual long long + fetchLong (long) + { + return 0LL; + } + + virtual char * + fetchString (long i) + { + return strdup (data->fetch (i)); + } + + virtual double + fetchDouble (long) + { + return 0.0; + } + + virtual void * + fetchObject (long i) + { + return data->fetch (i); + } + + virtual void + setDatumValue (long idx, const Datum *val) + { + data->store (idx, val->l); + } + + virtual void + setValue (long, uint64_t) + { + return; + } + + virtual void + setObjValue (long idx, void *p) + { + data->store (idx, (char*) p); + } + + virtual int + cmpValues (long, long) + { + return 0; + } + + virtual int + cmpDatumValue (long, const Datum *) + { + return 0; + } + +private: + Vector<char*> *data; +}; + +class DataDOUBLE : public Data +{ +public: + + DataDOUBLE () + { + data = new Vector<double>; + } + + virtual + ~DataDOUBLE () + { + delete data; + } + + virtual VType_type + type () + { + return TYPE_DOUBLE; + } + + virtual void + reset () + { + data->reset (); + } + + virtual long + getSize () + { + return data->size (); + } + + virtual int + fetchInt (long i) + { + return (int) data->fetch (i); + } + + virtual unsigned long long + fetchULong (long i) + { + return (unsigned long long) data->fetch (i); + } + + virtual long long + fetchLong (long i) + { + return (long long) data->fetch (i); + } + + virtual char * + fetchString (long i) + { + return dbe_sprintf (NTXT ("%f"), data->fetch (i)); + } + + virtual double + fetchDouble (long i) + { + return data->fetch (i); + } + + virtual void + setDatumValue (long idx, const Datum *val) + { + data->store (idx, val->d); + } + + virtual void + setValue (long idx, uint64_t val) + { + data->store (idx, (double) val); + } + + virtual void + setObjValue (long, void*) + { + return; + } + + virtual void * + fetchObject (long) + { + return NULL; + } + + virtual int + cmpValues (long idx1, long idx2) + { + double d1 = data->fetch (idx1); + double d2 = data->fetch (idx2); + return d1 < d2 ? -1 : d1 > d2 ? 1 : 0; + } + + virtual int + cmpDatumValue (long idx, const Datum *val) + { + double d1 = data->fetch (idx); + double d2 = val->d; + return d1 < d2 ? -1 : d1 > d2 ? 1 : 0; + } + +private: + Vector<double> *data; +}; + +Data * +Data::newData (VType_type vtype) +{ + switch (vtype) + { + case TYPE_INT32: + return new DataINT32; + case TYPE_UINT32: + return new DataUINT32; + case TYPE_INT64: + return new DataINT64; + case TYPE_UINT64: + return new DataUINT64; + case TYPE_OBJ: + return new DataOBJECT; + case TYPE_STRING: + return new DataSTRING; + case TYPE_DOUBLE: + return new DataDOUBLE; + default: + return NULL; + } +} + +/* + * class DataDescriptor + */ +DataDescriptor::DataDescriptor (int _id, const char *_name, const char *_uname, + int _flags) +{ + isMaster = true; + id = _id; + name = _name ? strdup (_name) : strdup (NTXT ("")); + uname = _uname ? strdup (_uname) : strdup (NTXT ("")); + flags = _flags; + + // master data, shared with reference copies: + master_size = 0; + master_resolveFrameInfoDone = false; + props = new Vector<PropDescr*>; + data = new Vector<Data*>; + setsTBR = new Vector<Vector<long long>*>; + + // master references point to self: + ref_size = &master_size; + ref_resolveFrameInfoDone = &master_resolveFrameInfoDone; +} + +DataDescriptor::DataDescriptor (int _id, const char *_name, const char *_uname, + DataDescriptor* dDscr) +{ + isMaster = false; + id = _id; + name = _name ? strdup (_name) : strdup (NTXT ("")); + uname = _uname ? strdup (_uname) : strdup (NTXT ("")); + flags = dDscr->flags; + + // references point to master DataDescriptor + ref_size = &dDscr->master_size; + ref_resolveFrameInfoDone = &dDscr->master_resolveFrameInfoDone; + props = dDscr->props; + data = dDscr->data; + setsTBR = dDscr->setsTBR; + + // data that should never be accessed in reference copy + master_size = -1; + master_resolveFrameInfoDone = false; +} + +DataDescriptor::~DataDescriptor () +{ + free (name); + free (uname); + if (!isMaster) + return; + props->destroy (); + delete props; + data->destroy (); + delete data; + setsTBR->destroy (); + delete setsTBR; +} + +void +DataDescriptor::reset () +{ + if (!isMaster) + return; + for (int i = 0; i < data->size (); i++) + { + Data *d = data->fetch (i); + if (d != NULL) + d->reset (); + Vector<long long> *set = setsTBR->fetch (i); + if (set != NULL) + set->reset (); + } + master_size = 0; +} + +PropDescr * +DataDescriptor::getProp (int prop_id) +{ + for (int i = 0; i < props->size (); i++) + { + PropDescr *propDscr = props->fetch (i); + if (propDscr->propID == prop_id) + return propDscr; + } + return NULL; +} + +Data * +DataDescriptor::getData (int prop_id) +{ + if (prop_id < 0 || prop_id >= data->size ()) + return NULL; + return data->fetch (prop_id); +} + +void +DataDescriptor::addProperty (PropDescr *propDscr) +{ + if (propDscr == NULL) + return; + if (propDscr->propID < 0) + return; + PropDescr *oldProp = getProp (propDscr->propID); + if (oldProp != NULL) + { + checkCompatibility (propDscr->vtype, oldProp->vtype); //YXXX depends on experiment correctness + delete propDscr; + return; + } + props->append (propDscr); + data->store (propDscr->propID, Data::newData (propDscr->vtype)); + setsTBR->store (propDscr->propID, NULL); +} + +long +DataDescriptor::addRecord () +{ + if (!isMaster) + return -1; + return master_size++; +} + +static void +checkEntity (Vector<long long> *set, long long val) +{ + // Binary search + int lo = 0; + int hi = set->size () - 1; + while (lo <= hi) + { + int md = (lo + hi) / 2; + long long ent = set->fetch (md); + if (ent < val) + lo = md + 1; + else if (ent > val) + hi = md - 1; + else + return; + } + set->insert (lo, val); +} + +void +DataDescriptor::setDatumValue (int prop_id, long idx, const Datum *val) +{ + if (idx >= *ref_size) + return; + Data *d = getData (prop_id); + if (d != NULL) + { + VType_type datum_type = val->type; + VType_type data_type = d->type (); + checkCompatibility (datum_type, data_type); + d->setDatumValue (idx, val); + Vector<long long> *set = setsTBR->fetch (prop_id); + if (set != NULL)// Sets are maintained + checkEntity (set, d->fetchLong (idx)); + } +} + +void +DataDescriptor::setValue (int prop_id, long idx, uint64_t val) +{ + if (idx >= *ref_size) + return; + Data *d = getData (prop_id); + if (d != NULL) + { + d->setValue (idx, val); + Vector<long long> *set = setsTBR->fetch (prop_id); + if (set != NULL)// Sets are maintained + checkEntity (set, d->fetchLong (idx)); + } +} + +void +DataDescriptor::setObjValue (int prop_id, long idx, void *val) +{ + if (idx >= *ref_size) + return; + Data *d = getData (prop_id); + if (d != NULL) + d->setObjValue (idx, val); +} + +DataView * +DataDescriptor::createView () +{ + return new DataView (this); +} + +DataView * +DataDescriptor::createImmutableView () +{ + return new DataView (this, DataView::DV_IMMUTABLE); +} + +DataView * +DataDescriptor::createExtManagedView () +{ + return new DataView (this, DataView::DV_EXT_MANAGED); +} + +int +DataDescriptor::getIntValue (int prop_id, long idx) +{ + Data *d = getData (prop_id); + if (d == NULL || idx >= d->getSize ()) + return 0; + return d->fetchInt (idx); +} + +unsigned long long +DataDescriptor::getULongValue (int prop_id, long idx) +{ + Data *d = getData (prop_id); + if (d == NULL || idx >= d->getSize ()) + return 0L; + return d->fetchULong (idx); +} + +long long +DataDescriptor::getLongValue (int prop_id, long idx) +{ + Data *d = getData (prop_id); + if (d == NULL || idx >= d->getSize ()) + return 0L; + return d->fetchLong (idx); +} + +void * +DataDescriptor::getObjValue (int prop_id, long idx) +{ + Data *d = getData (prop_id); + if (d == NULL || idx >= d->getSize ()) + return NULL; + return d->fetchObject (idx); +} + +static int +pcmp (const void *p1, const void *p2, const void *arg) +{ + long idx1 = *(long*) p1; // index1 into Data + long idx2 = *(long*) p2; // index2 into Data + for (Data **dsorted = (Data**) arg; *dsorted != DATA_SORT_EOL; dsorted++) + { + Data *data = *dsorted; + if (data == NULL)// sort property not in this data, skip this criteria + continue; + int res = data->cmpValues (idx1, idx2); + if (res) + return res; + } + // Provide stable sort + return idx1 < idx2 ? -1 : idx1 > idx2 ? 1 : 0; +} + +Vector<long long> * +DataDescriptor::getSet (int prop_id) +{ + if (prop_id < 0 || prop_id >= setsTBR->size ()) + return NULL; + Vector<long long> *set = setsTBR->fetch (prop_id); + if (set != NULL) + return set; + + Data *d = getData (prop_id); + if (d == NULL) + return NULL; + set = new Vector<long long>; + for (long i = 0; i<*ref_size; ++i) + checkEntity (set, d->fetchLong (i)); + setsTBR->store (prop_id, set); + + return set; +} + +/* + * class DataView + */ +DataView::DataView (DataDescriptor *_ddscr) +{ + init (_ddscr, DV_NORMAL); +} + +DataView::DataView (DataDescriptor *_ddscr, DataViewType _type) +{ + init (_ddscr, _type); +} + +void +DataView::init (DataDescriptor *_ddscr, DataViewType _type) +{ + ddscr = _ddscr; + type = _type; + switch (type) + { + case DV_IMMUTABLE: + ddsize = ddscr->getSize (); + index = NULL; + break; + case DV_NORMAL: + case DV_EXT_MANAGED: + ddsize = 0; + index = new Vector<long>; + break; + } + for (int ii = 0; ii < (MAX_SORT_DIMENSIONS + 1); ii++) + sortedBy[ii] = DATA_SORT_EOL; + filter = NULL; +} + +DataView::~DataView () +{ + delete filter; + delete index; +} + +void +DataView::appendDataDescriptorId (long pkt_id /* ddscr index */) +{ + if (type != DV_EXT_MANAGED) + return; // updates allowed only on externally managed DataViews + long curr_ddsize = ddscr->getSize (); + if (pkt_id < 0 || pkt_id >= curr_ddsize) + return; // error! + index->append (pkt_id); +} + +void +DataView::setDataDescriptorValue (int prop_id, long pkt_id, uint64_t val) +{ + ddscr->setValue (prop_id, pkt_id, val); +} + +long long +DataView::getDataDescriptorValue (int prop_id, long pkt_id) +{ + return ddscr->getLongValue (prop_id, pkt_id); +} + +Vector<PropDescr*>* +DataView::getProps () +{ + return ddscr->getProps (); +}; + +PropDescr* +DataView::getProp (int prop_id) +{ + return ddscr->getProp (prop_id); +}; + +void +DataView::filter_in_chunks (fltr_dbe_ctx *dctx) +{ + Expression::Context *e_ctx = new Expression::Context (dctx->fltr->ctx->dbev, dctx->fltr->ctx->exp); + Expression *n_expr = dctx->fltr->expr->copy (); + bool noParFilter = dctx->fltr->noParFilter; + FilterExp *nFilter = new FilterExp (n_expr, e_ctx, noParFilter); + long iter = dctx->begin; + long end = dctx->end; + long orig_ddsize = dctx->orig_ddsize; + while (iter < end) + { + nFilter->put (dctx->tmpView, iter); + if (nFilter->passes ()) + dctx->idxArr[iter - orig_ddsize] = 1; + iter += 1; + } + delete nFilter; +} + +bool +DataView::checkUpdate () +{ + long newSize = ddscr->getSize (); + if (ddsize == newSize) + return false; + if (index == NULL) + return false; + if (type == DV_EXT_MANAGED) + return false; + bool updated = false; + if (filter) + { + DataView *tmpView = ddscr->createImmutableView (); + assert (tmpView->getSize () == newSize); + while (ddsize < newSize) + { + filter->put (tmpView, ddsize); + if (filter->passes ()) + index->append (ddsize); + ddsize += 1; + } + delete tmpView; + return updated; + } + while (ddsize < newSize) + { + index->append (ddsize); + updated = true; + ddsize += 1; + } + return updated; +} + +long +DataView::getSize () +{ + if (checkUpdate () && sortedBy[0] != DATA_SORT_EOL) + // note: after new filter is set, getSize() incurs cost of + // sorting even if caller isn't interested in sort + index->sort ((CompareFunc) pcmp, sortedBy); + + if (index == NULL) + return ddscr->getSize (); + return index->size (); +} + +void +DataView::setDatumValue (int prop_id, long idx, const Datum *val) +{ + ddscr->setDatumValue (prop_id, getIdByIdx (idx), val); +} + +void +DataView::setValue (int prop_id, long idx, uint64_t val) +{ + ddscr->setValue (prop_id, getIdByIdx (idx), val); +} + +void +DataView::setObjValue (int prop_id, long idx, void *val) +{ + ddscr->setObjValue (prop_id, getIdByIdx (idx), val); +} + +int +DataView::getIntValue (int prop_id, long idx) +{ + return ddscr->getIntValue (prop_id, getIdByIdx (idx)); +} + +unsigned long long +DataView::getULongValue (int prop_id, long idx) +{ + return ddscr->getULongValue (prop_id, getIdByIdx (idx)); +} + +long long +DataView::getLongValue (int prop_id, long idx) +{ + return ddscr->getLongValue (prop_id, getIdByIdx (idx)); +} + +void * +DataView::getObjValue (int prop_id, long idx) +{ + return ddscr->getObjValue (prop_id, getIdByIdx (idx)); +} + +void +DataView::sort (const int props[], int prop_count) +{ + if (index == NULL) + { + assert (ASSERT_SKIP); + return; + } + assert (prop_count >= 0 && prop_count < MAX_SORT_DIMENSIONS); + bool sort_changed = false; // see if sort has changed... + for (int ii = 0; ii <= prop_count; ii++) + { // sortedBy size is prop_count+1 + Data *data; + if (ii == prop_count) + data = DATA_SORT_EOL; // special end of array marker + else + data = ddscr->getData (props[ii]); + if (sortedBy[ii] != data) + { + sortedBy[ii] = data; + sort_changed = true; + } + } + if (!checkUpdate () && !sort_changed) + return; + index->sort ((CompareFunc) pcmp, sortedBy); +} + +void +DataView::sort (int prop0) +{ + sort (&prop0, 1); +} + +void +DataView::sort (int prop0, int prop1) +{ + int props[2] = {prop0, prop1}; + sort (props, 2); +} + +void +DataView::sort (int prop0, int prop1, int prop2) +{ + int props[3] = {prop0, prop1, prop2}; + sort (props, 3); +} + +void +DataView::setFilter (FilterExp *f) +{ + if (index == NULL) + { + assert (ASSERT_SKIP); + return; + } + delete filter; + filter = f; + index->reset (); + ddsize = 0; + checkUpdate (); +} + +long +DataView::getIdByIdx (long idx) +{ + if (index == NULL) + return idx; + return index->fetch (idx); +} + +static int +tvalcmp (long data_id, const Datum valColumns[], Data *sortedBy[]) +{ + for (int ii = 0; ii < MAX_SORT_DIMENSIONS; ii++) + { + if (sortedBy[ii] == DATA_SORT_EOL) + break; + Data *d = sortedBy[ii]; + if (d == NULL)// property doesn't exist in data; compare always matches + continue; + const Datum *tvalue = &valColumns[ii]; + int res = d->cmpDatumValue (data_id, tvalue); + if (res) + return res; + } + return 0; +} + +static void +checkSortTypes (const Datum valColumns[], Data *sortedBy[]) +{ +#ifndef NDEBUG + for (int ii = 0; ii < MAX_SORT_DIMENSIONS; ii++) + { + if (sortedBy[ii] == DATA_SORT_EOL) + break; + Data *d = sortedBy[ii]; + if (d == NULL)// property doesn't exist in data; compare always matches + continue; + VType_type datum_type = valColumns[ii].type; + VType_type data_type = d->type (); + checkCompatibility (datum_type, data_type); + } +#endif +} + +bool +DataView::idxRootDimensionsMatch (long idx, const Datum valColumns[]) +{ + // compares idx vs. valColumns[] - If all dimensions match + // (except sort leaf), then the leaf value is valid => return true. + // Otherwise, return false. + checkSortTypes (valColumns, sortedBy); + if (idx < 0 || idx >= index->size ()) // fell off end of array + return false; + long data_id = index->fetch (idx); + + // we will check all dimensions for a match except the "leaf" dimension + for (int ii = 0; ii < (MAX_SORT_DIMENSIONS - 1); ii++) + { + if (sortedBy[ii + 1] == DATA_SORT_EOL) + break; // we are at leaf dimension, don't care about it's value + if (sortedBy[ii] == DATA_SORT_EOL) + break; // end of list + Data *d = sortedBy[ii]; + if (d == NULL) // property doesn't exist in data; compare always matches + continue; + const Datum *tvalue = &valColumns[ii]; + int res = d->cmpDatumValue (data_id, tvalue); + if (res) + return false; + } + return true; +} + +long +DataView::getIdxByVals (const Datum valColumns[], Relation rel) +{ + // checks sortedBy[] columns for match; relation only used on last column + return getIdxByVals (valColumns, rel, -1, -1); +} + +long +DataView::getIdxByVals (const Datum valColumns[], Relation rel, + long minIdx, long maxIdx) +{ + // checks sortedBy[] columns for match; relation only used on last column + checkSortTypes (valColumns, sortedBy); + if (index == NULL || sortedBy[0] == DATA_SORT_EOL) + return -1; + + long lo; + if (minIdx < 0) + lo = 0; + else + lo = minIdx; + + long hi; + if (maxIdx < 0 || maxIdx >= index->size ()) + hi = index->size () - 1; + else + hi = maxIdx; + + long md = -1; + while (lo <= hi) + { + md = (lo + hi) / 2; + int cmp = tvalcmp (index->fetch (md), valColumns, sortedBy); + if (cmp < 0) + { + lo = md + 1; + continue; + } + else if (cmp > 0) + { + hi = md - 1; + continue; + } + + // cmp == 0, we have an exact match + switch (rel) + { + case REL_LT: + hi = md - 1; // continue searching + break; + case REL_GT: + lo = md + 1; // continue searching + break; + case REL_LTEQ: + case REL_GTEQ: + case REL_EQ: + // note: "md" may not be deterministic if multiple matches exist + return md; // a match => done. + } + } + + // no exact match found + switch (rel) + { + case REL_LT: + case REL_LTEQ: + md = hi; + break; + case REL_GT: + case REL_GTEQ: + md = lo; + break; + case REL_EQ: + return -1; + } + if (idxRootDimensionsMatch (md, valColumns)) + return md; + return -1; +} + +void +DataView::removeDbeViewIdx (long idx) +{ + index->remove (idx); +} + diff --git a/gprofng/src/Table.h b/gprofng/src/Table.h new file mode 100644 index 0000000..48ce06a --- /dev/null +++ b/gprofng/src/Table.h @@ -0,0 +1,618 @@ +/* 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. */ + +#ifndef _TABLE_H +#define _TABLE_H + +#include "vec.h" +#include "Map2D.h" + +#include "dbe_structs.h" + +class FilterExp; +struct PropDescr; +struct FieldDescr; +class PacketDescriptor; +class DataDescriptor; +class DataView; + +// Note: order must match VTYPE_TYPE_NAMES, below + +enum VType_type +{ + TYPE_NONE, + TYPE_INT32, + TYPE_UINT32, + TYPE_INT64, + TYPE_UINT64, + TYPE_STRING, + TYPE_DOUBLE, + TYPE_OBJ, + TYPE_DATE, // Used in FieldDescr only, mapped to TYPE_UINT64 in PropDescr + TYPE_BOOL, // Used only to describe filter props + TYPE_ENUM, // Used only to describe filter props + + TYPE_LAST +}; + +#define VTYPE_TYPE_NAMES \ +{ \ + NTXT("NONE"), \ + NTXT("INT32"), \ + NTXT("UINT32"), \ + NTXT("INT64"), \ + NTXT("UINT64"), \ + NTXT("STRING"), \ + NTXT("DOUBLE"), \ + NTXT("OBJECT"), \ + NTXT("DATE"), \ + NTXT("BOOL"), \ + NTXT("ENUM") \ +} + +// Note: order must match PROFDATA_TYPE_NAMES and PROFDATA_TYPE_UNAMES, below + +enum ProfData_type +{ // a.k.a "data_id" (not the same as Pckt_type "kind") + DATA_SAMPLE, // Traditional collect "Samples" + DATA_GCEVENT, // Java Garbage Collection events + DATA_HEAPSZ, // heap size tracking based on heap tracing data + DATA_CLOCK, // clock profiling data + DATA_HWC, // hardware counter profiling data + DATA_SYNCH, // synchronization tracing data + DATA_HEAP, // heap tracing data + DATA_MPI, // MPI tracing data + DATA_RACE, // data race detection data + DATA_DLCK, // deadlock detection data + DATA_OMP, // OpenMP profiling data (fork events) + DATA_OMP2, // OpenMP profiling data (enter thread events) + DATA_OMP3, // OpenMP profiling data (enter task events) + DATA_OMP4, // OpenMP profiling data (parreg descriptions) + DATA_OMP5, // OpenMP profiling data (task descriptions) + DATA_IOTRACE, // IO tracing data + DATA_LAST +}; + +extern char *get_prof_data_type_name (int t); +extern char * +get_prof_data_type_uname (int t); + +enum Prop_type +{ + PROP_NONE, + // commonly used properties (libcollector modules, er_print) + PROP_ATSTAMP, // hrtime_t, Filter: system HRT timestamp; + // "Absolute TSTAMP" + PROP_ETSTAMP, // hrtime_t, Filter: nanoseconds from subexperiment start; + // "subExperiment TSTAMP" + PROP_TSTAMP, // hrtime_t, Packet: system HRT timestamp + // Filter: nanoseconds from founder start + PROP_THRID, // mapped to uint32_t by readPacket + PROP_LWPID, // mapped to uint32_t by readPacket + PROP_CPUID, // mapped to uint32_t by readPacket + PROP_FRINFO, // uint64_t frinfo + PROP_EVT_TIME, // hrtime_t Filter: Time delta + // If TSTAMP taken at end of event, EVT_TIME will be positive + // If TSTAMP taken at start of event, EVT_TIME will be negative + // Note: clock and hwc profile events set EVT_TIME=0 + // except Solaris Microstate events where NTICK>1: + // These will use EVT_TIME=(NTICK-1)*<tick duration> + + // DATA_SAMPLE + PROP_SAMPLE, // uint64_t sample number + PROP_SMPLOBJ, // Sample* + + // DATA_GCEVENT + PROP_GCEVENT, // uint64_t event id + PROP_GCEVENTOBJ, // GCEvent* + + // DATA_CLOCK + PROP_MSTATE, // unsigned ProfilePacket::mstate + PROP_NTICK, // unsigned ProfilePacket::value + PROP_OMPSTATE, // int ProfilePacket::ompstate + PROP_MPISTATE, // int ProfilePacket::mpistate + + // DATA_SAMPLE // see PrUsage class, see PROP_MSTATE - TBR? + PROP_UCPU, + PROP_SCPU, + PROP_TRAP, + PROP_TFLT, + PROP_DFLT, + PROP_KFLT, + PROP_ULCK, + PROP_TSLP, + PROP_WCPU, + PROP_TSTP, + + // DATA_SYNCH + PROP_SRQST, // hrtime_t SyncPacket::requested + PROP_SOBJ, // Vaddr SyncPacket::objp + + // DATA_HWC + PROP_HWCTAG, // uint32_t HWCntrPacket::tag; + PROP_HWCINT, // uint64_t HWCntrPacket::interval + PROP_VADDR, // Vaddr HWCntrPacket::dbeVA->eaddr + PROP_PADDR, // Vaddr HWCntrPacket::dbePA->eaddr + PROP_HWCDOBJ, // DataObject* HWCntrPacket::dobj + PROP_VIRTPC, // Vaddr HWCntrPacket::eventVPC + PROP_PHYSPC, // Vaddr HWCntrPacket::eventPPC + PROP_EA_PAGESIZE, // uint32_t HWCntrPacket::ea_pagesize + PROP_PC_PAGESIZE, // uint32_t HWCntrPacket::pc_pagesize + PROP_EA_LGRP, // uint32_t HWCntrPacket::ea_lgrp + PROP_PC_LGRP, // uint32_t HWCntrPacket::pc_lgrp + PROP_LWP_LGRP_HOME, // uint32_t HWCntrPacket::lwp_lgrp_home + PROP_PS_LGRP_HOME, // uint32_t HWCntrPacket::ps_lgrp_home + PROP_MEM_LAT, // uint64_t HWCntrPacket::latency + PROP_MEM_SRC, // uint64_t HWCntrPacket::data_source + + // DATA_HEAP + PROP_HTYPE, // Heap_type HeapPacket::mtype + PROP_HSIZE, // Size HeapPacket::size (bytes alloc'd by this event) + PROP_HVADDR, // Vaddr HeapPacket::vaddr + PROP_HOVADDR, // Vaddr HeapPacket::ovaddr + PROP_HLEAKED, // Size HeapPacket::leaked (net bytes leaked) + PROP_HMEM_USAGE, // Size heap memory usage + PROP_HFREED, // Size (bytes freed by this event) + PROP_HCUR_ALLOCS, // int64_t (net allocations running total. Recomputed after each filter) + PROP_HCUR_NET_ALLOC, // int64_t (net allocation for this packet. Recomputed after each filter) + PROP_HCUR_LEAKS, // Size (net leaks running total. Recomputed after each filter) + + // DATA_IOTRACE + PROP_IOTYPE, // IOTrace_type IOTracePacket::iotype + PROP_IOFD, // int32_t IOTracePacket::fd + PROP_IONBYTE, // Size_type IOTracePacket::nbyte + PROP_IORQST, // hrtime_t IOTracePacket::requested + PROP_IOOFD, // int32_t IOTracePacket::ofd + PROP_IOFSTYPE, // FileSystem_type IOTracePacket::fstype + PROP_IOFNAME, // char IOTracePacket::fname + PROP_IOVFD, // int32_t virtual file descriptor + + // DATA_MPI + PROP_MPITYPE, // MPI_type MPIPacket::mpitype + PROP_MPISCOUNT, // Size MPIPacket::scount + PROP_MPISBYTES, // Size MPIPacket::sbytes + PROP_MPIRCOUNT, // Size MPIPacket::rcount + PROP_MPIRBYTES, // Size MPIPacket::rbytes + + // DATA_OMP* + PROP_CPRID, // uint64_t (Note: not same as "PROP_CPRID" below) + PROP_PPRID, // uint64_t OMPPacket::omp_pprid + PROP_TSKID, // uint64_t (Note: not same as "PROP_CPRID" below) + PROP_PTSKID, // uint64_t OMPPacket::omp_ptskid + PROP_PRPC, // uint64_t OMPPacket::omp_prpc + + // DATA_RACE + PROP_RTYPE, // Race_type RacePacket::rtype + PROP_RID, // uint32_t RacePacket::id + PROP_RVADDR, // Vaddr RacePacket::vaddr + PROP_RCNT, // uint32_t RacePacket::count + PROP_LEAFPC, // Vaddr CommonPacket::leafpc + + // DATA_DLCK + PROP_DID, // uint32_t DeadlockPacket::id + PROP_DTYPE, // Deadlock_Lock_type DeadlockPacket::lock_type + PROP_DLTYPE, // Deadlock_type DeadlockPacket::dl_type + PROP_DVADDR, // Vaddr DeadlockPacket::lock_addr + + // Synthetic properties (queries only) + PROP_STACKID, + PROP_STACK, // void* Generic; mapped to M, U, or XSTACK + PROP_MSTACK, // void* machine stack + PROP_USTACK, // void* user_stack + PROP_XSTACK, // void* expert_stack + PROP_HSTACK, // void* hide_stack + //PROP_CPRID, // void* (Note: not same as "PROP_CPRID" above) + //PROP_TSKID, // void* (Note: not same as "PROP_TSKID" above) + PROP_JTHREAD, // JThread* CommonPacket::jthread + PROP_LEAF, // uint64_t stack leaf function + PROP_DOBJ, // "DOBJ" DataObject* + PROP_SAMPLE_MAP, // Map events to SAMPLE using sample's time range + PROP_GCEVENT_MAP, // Map events to GCEVENT using gcevent's time range + PROP_PID, // int unix getpid() + PROP_EXPID, // int Experiment->getUserExpId(), AKA process number, >=1. + PROP_EXPID_CMP, // int "Comparable PROP_EXPID". In compare mode, if this + // process has been matched to another groups' process, + // returns PROP_EXPID of the matching process with the + // lowest PROP_EXPGRID value. Otherwise returns PROP_EXPID. + PROP_EXPGRID, // int Comparison group number. >=0, 0 is Baseline. + PROP_PARREG, // "PARREG" uint64_t (see 6436500) TBR? + PROP_TSTAMP_LO, // hrtime_t Filter: Event's low TSTAMP + PROP_TSTAMP_HI, // hrtime_t Filter: Event's high TSTAMP + PROP_TSTAMP2, // hrtime_t Filter: End TSTAMP (TSTAMP<=TSTAMP2) + PROP_FREQ_MHZ, // int frequency in MHZ (for converting HWC profiling cycles to time) + PROP_NTICK_USEC, // hrtime_t Clock profiling interval, microseconds (PROP_NTICK * Experiment->ptimer_usec) + PROP_IOHEAPBYTES, // Size PROP_HSIZE or PROP_IONBYTE + PROP_STACKL, // void* Generic; mapped to M, U, or XSTACK for DbeLine + PROP_MSTACKL, // void* machine stack + PROP_USTACKL, // void* user_stack + PROP_XSTACKL, // void* expert_stack + PROP_STACKI, // void* Generic; mapped to M, U, or XSTACK for DbeInstr + PROP_MSTACKI, // void* machine stack + PROP_USTACKI, // void* user_stack + PROP_XSTACKI, // void* expert_stack + PROP_DDSCR_LNK, // long long index into DataDescriptor table for a related event + PROP_VOIDP_OBJ, // void* pointer to object containing metadata + PROP_LAST +}; + +enum Prop_flag +{ + PRFLAG_NOSHOW = 0x40 +}; + +struct PropDescr +{ + PropDescr (int propID, const char *name); + virtual ~PropDescr (); + + void addState (int value, const char *stname, const char *stuname); + char *getStateName (int value); + char *getStateUName (int value); + + int + getMaxState () + { + return stateNames ? stateNames->size () : 0; + } + + int propID; + char *name; + char *uname; + VType_type vtype; + int flags; + +private: + Vector<char*>*stateNames; + Vector<char*>*stateUNames; +}; + +struct FieldDescr +{ + FieldDescr (int propID, const char *name); + virtual ~FieldDescr (); + + int propID; + char *name; + int offset; + VType_type vtype; + char *format; +}; + +class PacketDescriptor +{ +public: + PacketDescriptor (DataDescriptor*); + virtual ~PacketDescriptor (); + + DataDescriptor * + getDataDescriptor () + { + return ddscr; + } + + Vector<FieldDescr*> * + getFields () + { + return fields; + } + + void addField (FieldDescr*); + +private: + DataDescriptor *ddscr; + Vector<FieldDescr*> *fields; +}; + +struct Datum +{ + + void + setUINT32 (uint32_t vv) + { + type = TYPE_UINT32; + i = vv; + } + + void + setUINT64 (uint64_t vv) + { + type = TYPE_UINT64; + ll = vv; + } + + void + setSTRING (char* vv) + { + type = TYPE_STRING; + l = vv; + } + + void + setDOUBLE (double vv) + { + type = TYPE_DOUBLE; + d = vv; + } + + void + setOBJ (void* vv) + { + type = TYPE_OBJ; + p = vv; + } + + VType_type type; + union + { + int i; + double d; + char *l; + void *p; + unsigned long long ll; + }; +}; + +class Data +{ +public: + static Data *newData (VType_type); + + virtual + ~Data () { } + + virtual VType_type + type () + { + return TYPE_NONE; + } + virtual void reset () = 0; + virtual long getSize () = 0; + virtual int fetchInt (long i) = 0; + virtual unsigned long long fetchULong (long i) = 0; + virtual long long fetchLong (long i) = 0; + virtual char *fetchString (long i) = 0; + virtual double fetchDouble (long i) = 0; + virtual void *fetchObject (long i) = 0; + virtual void setDatumValue (long, const Datum*) = 0; + virtual void setValue (long, uint64_t) = 0; + virtual void setObjValue (long, void*) = 0; + virtual int cmpValues (long idx1, long idx2) = 0; + virtual int cmpDatumValue (long idx, const Datum *val) = 0; +}; + +enum Data_flag +{ + DDFLAG_NOSHOW = 0x01 +}; + +class DataDescriptor +{ + /* + * An instance of this class stores the data packets for a specific + * type of profiling, for example, clock profiling. + * + * Each packet consists of values for various properties. + * For example, a timestamp is a property which is accessed with PROP_TSTAMP. + * + * Ideally, DataDescriptor contents are considered immutable after the + * data is read in. setValue() should only be used during creation. + * - The packets are in fixed order. This allows DataDescriptor <pkt_id> + * to be treated as a stable handle. + * - Sorting/filtering is handled by the DataView class + * - In the future, if we need to add the ability to append new packets, + * we might add a flag to show when the class is immutable and/or appendible + */ +public: + + DataDescriptor (int id, const char* name, const char* uname, int flags = 0); // master + DataDescriptor (int id, const char* name, const char* uname, DataDescriptor*); // reference copy + ~DataDescriptor (); + + // packets' descriptions + int + getId () + { + return id; + } + + char * + getName () + { + return name; + } + + char * + getUName () + { + return uname; + } + + Vector<PropDescr*> * + getProps () + { + return props; // packet properties + } + PropDescr *getProp (int prop_id); // packet property + + long + getSize () + { + return *ref_size; // number of packets + } + + long + getFlags () + { + return flags; + } + + // class to provide sorting and filtering + DataView *createView (); + DataView *createImmutableView (); + DataView *createExtManagedView (); + + // packet property values (<pkt_id> is stable packet handle) + int getIntValue (int prop_id, long pkt_id); + unsigned long long getULongValue (int prop_id, long pkt_id); + long long getLongValue (int prop_id, long pkt_id); + void *getObjValue (int prop_id, long pkt_id); + Vector<long long> *getSet (int prop_id); // list of sorted, unique values + + // table creation/reset + void addProperty (PropDescr*); // add property to all packets + long addRecord (); // add packet + Data *getData (int prop_id); // get all packets + void setDatumValue (int prop_id, long pkt_id, const Datum *val); + void setValue (int prop_id, long pkt_id, uint64_t val); + void setObjValue (int prop_id, long pkt_id, void *val); + void reset (); // remove all packets (ym: TBR?) + + void + setResolveFrInfoDone () + { + *ref_resolveFrameInfoDone = true; + } + + bool + isResolveFrInfoDone () + { + return *ref_resolveFrameInfoDone; + } + + +private: + bool isMaster; + int flags; // see Data_flag enum + int id; + char *name; + char *uname; + + // the following should only be accessed if parent==NULL + long master_size; + bool master_resolveFrameInfoDone; + + // the following point to the master DataDescriptor's fields + long *ref_size; + bool *ref_resolveFrameInfoDone; + Vector<PropDescr*> *props; + Vector<Data*> *data; + Vector<Vector<long long>*> *setsTBR; // Sets of unique values +}; + +typedef struct +{ + long begin; + long end; + long orig_ddsize; + DataView *tmpView; + long *idxArr; + FilterExp *fltr; +} fltr_dbe_ctx; + +class DataView +{ + /* + * Provides sorting and filtering of DataDescriptor packets + */ +public: + + enum Relation + { + REL_LT, + REL_LTEQ, + REL_EQ, + REL_GTEQ, + REL_GT + }; + + enum DataViewType + { + DV_NORMAL, // filterable, sortable + DV_IMMUTABLE, // reflects exact data in DataDescriptor + DV_EXT_MANAGED // sortable. index[] entries managed externally. + }; + + DataView (DataDescriptor*); + DataView (DataDescriptor*, DataViewType); + virtual ~DataView (); + + Vector<PropDescr*> *getProps (); + PropDescr *getProp (int prop_id); + long getSize (); // number of post-filter packets + + // packet property values accessed by sort index (not DataDescriptor pkt_id) + int getIntValue (int prop_id, long idx); + unsigned long long getULongValue (int prop_id, long idx); + long long getLongValue (int prop_id, long idx); + void *getObjValue (int prop_id, long idx); + long getIdByIdx (long idx); // returns DataDescriptor pkt_id + + // define sort/filter + void sort (const int props[], int prop_count); + void sort (int prop); + void sort (int prop1, int prop2); + void sort (int prop1, int prop2, int prop3); + void setFilter (FilterExp*); + + // search packets + // - sort must already be defined + // - requires the user to provide all properties used in current sort. + // - For a match, the all but the last sort property (the "leaf") + // must match exactly. + long getIdxByVals (const Datum valColumns[], Relation rel); + long getIdxByVals (const Datum valColumns[], Relation rel, + long minIdx, long maxIdx); //limit idx search range + bool idxRootDimensionsMatch (long idx, const Datum valColumns[]); + // packet at idx matches all non-leaf values in valColumns + + // use during table creation, updates underlying DataDescriptor + void setDatumValue (int prop_id, long idx, const Datum *val); + void setValue (int prop_id, long idx, uint64_t val); + void setObjValue (int prop_id, long idx, void *val); + + DataDescriptor * + getDataDescriptor () + { + return ddscr; + } + + void removeDbeViewIdx (long idx); + + // for use with DV_EXT_MANAGED DataViews: + void appendDataDescriptorId (long pkt_id); + void setDataDescriptorValue (int prop_id, long pkt_id, uint64_t val); + long long getDataDescriptorValue (int prop_id, long pkt_id); + +private: + bool checkUpdate (); + void init (DataDescriptor*, DataViewType); + + static void filter_in_chunks (fltr_dbe_ctx *dctx); + DataDescriptor *ddscr; + long ddsize; + Vector<long> *index; // sorted vector of data_id (index into dDscr) +#define MAX_SORT_DIMENSIONS 10 +#define DATA_SORT_EOL ((Data *) -1) /* marks end of sortedBy[] array */ + Data *sortedBy[MAX_SORT_DIMENSIONS + 1]; // columns for sort + FilterExp *filter; + DataViewType type; +}; + +#endif /* _TABLE_H */ diff --git a/gprofng/src/UserLabel.cc b/gprofng/src/UserLabel.cc new file mode 100644 index 0000000..bdb9922 --- /dev/null +++ b/gprofng/src/UserLabel.cc @@ -0,0 +1,177 @@ +/* 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 <time.h> + +#include "DbeSession.h" +#include "Expression.h" +#include "StringBuilder.h" +#include "util.h" +#include "UserLabel.h" +#include "debug.h" + +int UserLabel::last_id = 0; + +UserLabel::UserLabel (char *_name) +{ + name = dbe_strdup (_name); + comment = str_expr = all_times = hostname = NULL; + start_f = stop_f = false; + expr = NULL; + start_tv.tv_sec = 0; + start_tv.tv_usec = 0; + atime = timeStart = timeStop = start_sec = start_hrtime = 0; + relative = REL_TIME; + id = ++last_id; +} + +UserLabel::~UserLabel () +{ + free (name); + free (comment); + free (all_times); + free (hostname); + free (str_expr); + delete expr; +} + +void +UserLabel::gen_expr () +{ + if (!start_f && !stop_f) + return; + StringBuilder sb; + sb.append ('('); + if (str_expr) + { + sb.append (str_expr); + sb.append (NTXT (" || (")); + } + if (start_f) + { + sb.append (NTXT ("TSTAMP")); + sb.append (NTXT (">=")); + sb.append (timeStart); + if (stop_f) + { + sb.append (NTXT (" && ")); + } + } + if (stop_f) + { + sb.append (NTXT ("TSTAMP")); + sb.append ('<'); + sb.append (timeStop); + } + sb.append (')'); + if (str_expr) + { + sb.append (')'); + delete str_expr; + } + str_expr = sb.toString (); + start_f = stop_f = false; +} + +void +UserLabel::register_user_label (int groupId) +{ + gen_expr (); + if (str_expr) + { + char *old_str = str_expr; + str_expr = dbe_sprintf (NTXT ("(EXPGRID==%d && %s)"), groupId, old_str); + delete old_str; + UserLabel *ulbl = dbeSession->findUserLabel (name); + if (ulbl) + { + old_str = ulbl->str_expr; + ulbl->str_expr = dbe_sprintf (NTXT ("(%s || %s)"), old_str, str_expr); + delete old_str; + if (comment) + { + if (ulbl->comment) + { + old_str = ulbl->comment; + ulbl->comment = dbe_sprintf (NTXT ("%s; %s"), old_str, comment); + delete old_str; + } + else + ulbl->comment = dbe_strdup (comment); + } + delete ulbl->expr; + ulbl->expr = dbeSession->ql_parse (ulbl->str_expr); + } + else + { + expr = dbeSession->ql_parse (str_expr); + dbeSession->append (this); + } + } +} + +char * +UserLabel::dump () +{ + StringBuilder sb; + sb.append (name); + if (str_expr) + { + sb.append (NTXT (" str_expr='")); + sb.append (str_expr); + sb.append ('\''); + } + if (all_times) + { + sb.append (NTXT (" atime=")); + sb.append ((unsigned int) (atime / NANOSEC)); + sb.append ('.'); + char buf[128]; + snprintf (buf, sizeof (buf), NTXT ("%09llu"), (unsigned long long) (atime % NANOSEC)); + sb.append (buf); + sb.append (NTXT (" all_times='")); + sb.append (all_times); + sb.append ('\''); + } + if (comment) + { + sb.append (NTXT (" comment='")); + sb.append (comment); + sb.append ('\''); + } + return sb.toString (); +} + +void +UserLabel::dump (const char *msg, Vector<UserLabel*> *labels) +{ + if (!DUMP_USER_LABELS) + return; + if (msg) + fprintf (stderr, NTXT ("%s\n"), msg); + for (int i = 0, sz = labels ? labels->size () : 0; i < sz; i++) + { + UserLabel *lbl = labels->fetch (i); + char *s = lbl->dump (); + fprintf (stderr, NTXT ("%2d %s\n"), i, s); + delete s; + } +} diff --git a/gprofng/src/UserLabel.h b/gprofng/src/UserLabel.h new file mode 100644 index 0000000..0cb37e4 --- /dev/null +++ b/gprofng/src/UserLabel.h @@ -0,0 +1,61 @@ +/* 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. */ + +#ifndef _USER_LABEL_H +#define _USER_LABEL_H + +#include <time.h> +#include "vec.h" + +class Expression; +class StringBuilder; + +class UserLabel +{ +public: + + enum + { + REL_TIME = 0, + ABS_TIME = 1, + CUR_TIME = 2 + }; + + UserLabel (char *_name); + ~UserLabel (); + void register_user_label (int groupId); + void gen_expr (); + char *dump (); + static void dump (const char *msg, Vector<UserLabel*> *labels); + + char *name, *comment, *str_expr, *all_times, *hostname; + bool start_f, stop_f; + Expression *expr; + timeval start_tv; + long long atime, timeStart, timeStop, start_sec, start_hrtime; + int id, relative; + +private: + void gen_time_expr (StringBuilder *sb, long long hrtime, char *op); + + static int last_id; +}; + +#endif 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); +} diff --git a/gprofng/src/collctrl.cc b/gprofng/src/collctrl.cc new file mode 100644 index 0000000..48c65ff --- /dev/null +++ b/gprofng/src/collctrl.cc @@ -0,0 +1,3149 @@ +/* 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 <unistd.h> +#include <strings.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/statvfs.h> +#include <sys/param.h> +#include <signal.h> +#include <fcntl.h> +#include <errno.h> +#include <ctype.h> +#include <dirent.h> +#include <libgen.h> +#include <assert.h> +#include <regex.h> /* regcomp() */ + +#include "util.h" +#include "libiberty.h" +#include "collctrl.h" +#include "hwcdrv.h" +//#include "hwcfuncs.h" + +#define SP_GROUP_HEADER "#analyzer experiment group" +#define DD_MAXPATHLEN (MAXPATHLEN * 4) /* large, to build up data descriptor */ + +/* If the system doesn't provide strsignal, we get it defined in + libiberty but no declaration is supplied. */ +#if !defined (HAVE_STRSIGNAL) && !defined (strsignal) +extern const char *strsignal (int); +#endif + +// _SC_CPUID_MAX is not available on 2.6/2.7 +#ifndef _SC_CPUID_MAX +#define _SC_CPUID_MAX 517 +#endif + +const char *get_fstype (char *); + +Coll_Ctrl::Coll_Ctrl (int _interactive, bool _defHWC, bool _kernelHWC) +{ + char hostname[MAXPATHLEN]; + long ncpumax; + interactive = _interactive; + defHWC = _defHWC; + kernelHWC = _kernelHWC; + + /* set this host's parameters */ + gethostname (hostname, 1023); + node_name = strdup (hostname); + char *p = strchr (node_name, (int) '.'); + if (p != NULL) + *p = 0; + default_stem = strdup ("test"); + + /* get CPU count and processor clock rate */ + ncpumax = sysconf (_SC_CPUID_MAX); + if (ncpumax == -1) + { + ncpus = sysconf (_SC_NPROCESSORS_CONF); + /* add 2048 to count, since on some systems CPUID does not start at zero */ + ncpumax = ncpus + 2048; + } + ncpus = 0; + cpu_clk_freq = 0; + + // On Linux, read /proc/cpuinfo to get CPU count and clock rate + // Note that parsing is different on SPARC and x86 +#if defined(sparc) + FILE *procf = fopen ("/proc/cpuinfo", "r"); + if (procf != NULL) + { + char temp[1024]; + while (fgets (temp, (int) sizeof (temp), procf) != NULL) + { + if (strncmp (temp, "Cpu", 3) == 0 && temp[3] != '\0' + && strncmp ((strchr (temp + 1, 'C')) ? strchr (temp + 1, 'C') + : (temp + 4), "ClkTck", 6) == 0) + { + ncpus++; + char *val = strchr (temp, ':'); + if (val) + { + unsigned long long freq; + sscanf (val + 2, "%llx", &freq); + cpu_clk_freq = (unsigned int) (((double) freq) / 1000000.0 + 0.5); + } + else + cpu_clk_freq = 0; + } + } + fclose (procf); + } + +#elif defined(__aarch64__) + asm volatile("mrs %0, cntfrq_el0" : "=r" (cpu_clk_freq)); + dbe_write (2, GTXT ("CPU clock frequency: %d\n"), cpu_clk_freq); + +#else + FILE *procf = fopen ("/proc/cpuinfo", "r"); + if (procf != NULL) + { + char temp[1024]; + while (fgets (temp, (int) sizeof (temp), procf) != NULL) + { + // x86 Linux + if (strncmp (temp, "processor", 9) == 0) + ncpus++; + else if (strncmp (temp, "cpu MHz", 7) == 0) + { + char *val = strchr (temp, ':'); + cpu_clk_freq = val ? atoi (val + 1) : 0; + } + } + fclose (procf); + } +#endif + + /* check resolution of system clock */ + sys_resolution = sysconf (_SC_CLK_TCK); + if (sys_resolution == 0) + sys_period = 10000; + else + sys_period = MICROSEC / (int) sys_resolution; + + /* determine memory page size and number of pages */ + npages = sysconf (_SC_PHYS_PAGES); + page_size = sysconf (_SC_PAGE_SIZE); + + /* set default clock parameters */ + hwcprof_enabled_cnt = 0; // must be set before calling determine_profile_params(); + determine_profile_params (); // inits clk_params which is used by clock profiling AND HWCs + cpc_cpuver = CPUVER_UNDEFINED; + + /* set default control values */ + debug_mode = 0; +#if defined(GPROFNG_JAVA_PROFILING) + java_mode = 1; +#else + java_mode = 0; +#endif + java_default = 1; + java_path = NULL; + java_args = NULL; + njava_args = 0; + follow_mode = FOLLOW_ON; + follow_default = 1; + follow_spec_usr = NULL; + follow_spec_cmp = NULL; + prof_idle = 1; + archive_mode = strdup ("on"); + pauseresume_sig = 0; + sample_sig = 0; + uinterrupt = 0; + attach_pid = 0; + time_run = 0; + start_delay = 0; + + /* clear the string pointers */ + uexpt_name = NULL; + expt_name = NULL; + expt_dir = NULL; + base_name = NULL; + udir_name = NULL; + store_dir = NULL; + prev_store_dir = strdup (""); + store_ptr = NULL; + expt_group = NULL; + target_name = NULL; + data_desc = NULL; + lockname = NULL; + hwc_string = NULL; + project_home = NULL; + lockfd = -1; + + /* set default data collection values */ + enabled = 0; + opened = 0; + clkprof_enabled = 1; + clkprof_default = 1; + for (unsigned ii = 0; ii < MAX_PICS; ii++) + { + memset (&hwctr[ii], 0, sizeof (Hwcentry)); + hwctr[ii].reg_num = -1; + } + hwcprof_default = 0; + if (defHWC == true) + { + setup_hwc (); + hwcprof_default = 1; + } + else // disable the default, and reset the counters + hwcprof_enabled_cnt = 0; + synctrace_enabled = 0; + synctrace_thresh = -1; + synctrace_scope = 0; + heaptrace_enabled = 0; + heaptrace_checkenabled = 0; + iotrace_enabled = 0; + count_enabled = 0; + Iflag = 0; + Nflag = 0; + sample_period = 1; + sample_default = 1; + size_limit = 0; + nofswarn = 0; + expno = 1; + + // ensure that the default name is updated + // but don't print any message + (void) preprocess_names (); + (void) update_expt_name (false, false); +} + +/* Copy constructor */ +Coll_Ctrl::Coll_Ctrl (Coll_Ctrl * cc) +{ + uinterrupt = 0; + interactive = cc->interactive; + defHWC = cc->defHWC; + kernelHWC = cc->kernelHWC; + node_name = strdup (cc->node_name); + default_stem = strdup (cc->default_stem); + ncpus = cc->ncpus; + cpu_clk_freq = cc->cpu_clk_freq; + npages = cc->npages; + page_size = cc->page_size; + cpc_cpuver = cc->cpc_cpuver; + debug_mode = cc->debug_mode; + java_mode = cc->java_mode; + java_default = cc->java_default; + java_path = NULL; + java_args = NULL; + njava_args = 0; + follow_mode = cc->follow_mode; + follow_default = cc->follow_default; + if (cc->follow_spec_usr) + { + follow_spec_usr = strdup (cc->follow_spec_usr); + follow_spec_cmp = strdup (cc->follow_spec_cmp); + } + else + { + follow_spec_usr = NULL; + follow_spec_cmp = NULL; + } + archive_mode = strdup (cc->archive_mode); + pauseresume_sig = cc->pauseresume_sig; + sample_sig = cc->sample_sig; + time_run = cc->time_run; + start_delay = cc->start_delay; + clk_params = cc->clk_params; + clkprof_enabled = cc->clkprof_enabled; + clkprof_default = cc->clkprof_default; + clkprof_timer = cc->clkprof_timer; + clkprof_timer_target = cc->clkprof_timer_target; + + // copy HW counter information + hwcprof_default = cc->hwcprof_default; + hwcprof_enabled_cnt = cc->hwcprof_enabled_cnt; + if (cc->hwc_string != NULL) + hwc_string = strdup (cc->hwc_string); + else + hwc_string = NULL; + for (int i = 0; i < hwcprof_enabled_cnt; i++) + hwcentry_dup (&hwctr[i], &(cc->hwctr[i])); + project_home = cc->project_home ? strdup (cc->project_home) : NULL; + synctrace_enabled = cc->synctrace_enabled; + synctrace_thresh = cc->synctrace_thresh; + synctrace_scope = cc->synctrace_scope; + heaptrace_enabled = cc->heaptrace_enabled; + heaptrace_checkenabled = cc->heaptrace_checkenabled; + iotrace_enabled = cc->iotrace_enabled; + count_enabled = cc->count_enabled; + Iflag = cc->Iflag; + Nflag = cc->Nflag; + sample_period = cc->sample_period; + sample_default = cc->sample_default; + size_limit = cc->size_limit; + nofswarn = cc->nofswarn; + + // these will get reset during preprocess_names() + expt_name = NULL; + expt_dir = NULL; + store_dir = NULL; + base_name = NULL; + expno = 1; + + // these represent user settings + expt_group = NULL; + if (cc->expt_group != NULL) + expt_group = strdup (cc->expt_group); + uexpt_name = NULL; + if (cc->uexpt_name != NULL) + uexpt_name = strdup (cc->uexpt_name); + udir_name = NULL; + if (cc->udir_name != NULL) + udir_name = strdup (cc->udir_name); + + /* clear the string pointers */ + prev_store_dir = strdup (""); + store_ptr = NULL; + target_name = NULL; + data_desc = NULL; + lockname = NULL; + lockfd = -1; + + /* set default data collection values */ + enabled = cc->enabled; + opened = 0; + nofswarn = cc->nofswarn; + sys_resolution = cc->sys_resolution; + sys_period = cc->sys_period; + + // ensure that the default name is updated + (void) preprocess_names (); + (void) update_expt_name (false, false); + build_data_desc (); +} + +Coll_Ctrl::~Coll_Ctrl () +{ + free (node_name); + free (expt_name); + free (expt_dir); + free (base_name); + free (udir_name); + free (store_dir); + free (store_ptr); + free (expt_group); + free (target_name); + free (data_desc); + free (lockname); + free (hwc_string); + free (project_home); + free (java_path); + hwcprof_enabled_cnt = 0; +} + +/* set up the experiment */ +char * +Coll_Ctrl::setup_experiment () +{ + char *ret; + if (enabled == 0) + return NULL; + build_data_desc (); + + /* create the experiment directory */ + ret = create_exp_dir (); + if (ret != NULL) + return ret; + + /* if an experiment-group, join it */ + ret = join_group (); + if (ret != NULL) + { + remove_exp_dir (); + return ret; + } + /* all is OK, return 0 */ + opened = 1; + return NULL; +} + +void +Coll_Ctrl::interrupt () +{ + uinterrupt = 1; +} + +char * +Coll_Ctrl::enable_expt () +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (cpu_clk_freq == 0) + return strdup (GTXT ("Can not determine CPU clock frequency.\n")); + if (sys_resolution == 0) + return strdup (GTXT ("System clock profile resolution can not be determined.\n")); + enabled = 1; + return NULL; +} + +/* close the experiment */ +void +Coll_Ctrl::close_expt () +{ + opened = 0; + (void) update_expt_name (false, false); +} + +/* close and delete the experiment */ +void +Coll_Ctrl::delete_expt () +{ + if (opened == 0) + return; + remove_exp_dir (); + + /* The order of removing the directory and closing + * the experiment may seem unnatural, but it's not. + * We do need to update names when we close the experiment + * (actually Coll_Ctrl object) and we can't remove anything + * after that. + */ + close_expt (); +} + +// Check the experiment settings for consistency. Returns NULL if OK, +// or an error message if there are invalid combinations of settings +char * +Coll_Ctrl::check_consistency () +{ + /* check for Java arguments, but not Java profiling */ + if (java_args != NULL && java_mode == 0) + return strdup (GTXT ("Java arguments can not be set if Java profiling is not enabled.\n")); + + /* if count data, no other data is allowed */ + if (count_enabled != 0 + && ((clkprof_default != 1 && clkprof_enabled != 0) + || hwcprof_enabled_cnt != 0 || synctrace_enabled != 0 + || heaptrace_enabled != 0 || iotrace_enabled != 0)) + return strdup (GTXT ("Count data cannot be collected along with any other data.\n")); + + /* if count data, various other options are not allowed */ + if (count_enabled != 0 + && ((java_mode != 0 && java_default != 1) + || java_args != NULL || debug_mode != 0 + || (follow_mode != 0 && follow_default != 1) + || pauseresume_sig != 0 || sample_sig != 0 + || (sample_default != 1 && sample_period != 0) || time_run != 0)) + return strdup (GTXT ("Count data cannot be collected with any of -F -S -y -l -j -J -x -t .\n")); + /* if not count data, I and N options are not allowed */ + if (count_enabled == 0 && (Iflag != 0 || Nflag != 0)) + return strdup (GTXT ("-I or -N can only be specified with count data.\n")); + return NULL; +} + +char * +Coll_Ctrl::check_expt (char **warn) +{ + char *ret; + *warn = NULL; + ret = check_consistency (); + if (ret != NULL) /* something is wrong, return the error */ + return ret; + /* check for heaptrace and java -- warn that it covers native allocations only */ + if (heaptrace_enabled == 1 && java_mode == 1 && java_default == 0) + *warn = strdup (GTXT ("Note: Heap profiling will only trace native allocations, not Java allocations.\n")); + + /* if no profiling data selected, warn the user */ + if (clkprof_enabled == 0 && hwcprof_enabled_cnt == 0 && synctrace_enabled == 0 + && heaptrace_enabled == 0 && iotrace_enabled == 0 && count_enabled == 0) + *warn = strdup (GTXT ("Warning: No function level data requested; only statistics will be collected.\n\n")); + build_data_desc (); + + /* verify that the directory exists */ + struct stat statbuf; + if (stat (store_dir, &statbuf) != 0) + return dbe_sprintf (GTXT ("Store directory %s is not accessible: %s\n"), + store_dir, strerror (errno)); + if (access (store_dir, W_OK) != 0) + return dbe_sprintf (GTXT ("Store directory %s is not writeable: %s\n"), + store_dir, strerror (errno)); + + /* if an experiment-group, verify that it can be written */ + ret = check_group (); + if (ret != NULL) + return ret; + return NULL; +} + +char * +Coll_Ctrl::show (int i) +{ + char UEbuf[4096]; + UEbuf[0] = 0; + if (i == 0) + { + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("Collection parameters:\n")); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT (" experiment enabled\n")); + } + if (target_name != NULL) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\ttarget = %s\n"), target_name); + if (uexpt_name != NULL) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tuser_expt_name = %s\n"), uexpt_name); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\texpt_name = %s\n"), + ((expt_name != NULL) ? expt_name : NTXT ("<NULL>"))); + if (udir_name != NULL) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdir_name = %s\n"), udir_name); + if (expt_group != NULL) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\texpt_group = %s\n"), expt_group); + if (debug_mode == 1) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdebug_mode enabled\n")); + if (clkprof_enabled != 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tclock profiling enabled, %.3f millisec.\n"), + (double) (clkprof_timer) / 1000.); + if (synctrace_enabled != 0) + { + if (synctrace_thresh < 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tsynchronization tracing enabled, threshold: calibrate; ")); + else if (synctrace_thresh == 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tsynchronization tracing enabled, threshold: all; ")); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tsynchronization tracing enabled, threshold: %d micros.; "), synctrace_thresh); + switch (synctrace_scope) + { + case SYNCSCOPE_NATIVE: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("Native-APIs\n")); + break; + case SYNCSCOPE_JAVA: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("Java-APIs\n")); + break; + case SYNCSCOPE_NATIVE | SYNCSCOPE_JAVA: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("Native- and Java-APIs\n")); + break; + default: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("ERR -- unexpected synctrace_scope %d\n"), synctrace_scope); + break; + } + } + if (hwcprof_enabled_cnt != 0) + { + char ctrbuf[MAXPATHLEN]; + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\thardware counter profiling%s enabled:\n"), + (hwcprof_default == 1 ? GTXT (" (default)") : "")); + for (int ii = 0; ii < hwcprof_enabled_cnt; ii++) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\t %u. %s\n"), ii + 1, + hwc_hwcentry_specd_string (ctrbuf, MAXPATHLEN, &hwctr[ii])); + } + if (heaptrace_enabled != 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\theap tracing enabled, %s\n"), + (heaptrace_checkenabled == 0 ? GTXT ("no checking") : + (heaptrace_checkenabled == 1 ? GTXT ("over/underrun checking") : + GTXT ("over/underrun checking and pattern storing")))); + if (iotrace_enabled != 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tI/O tracing enabled\n")); + switch (count_enabled) + { + case 0: + break; + case 1: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tcount data enabled\n")); + break; + case -1: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tstatic count data will be generated (for a.out only)\n")); + break; + } + switch (follow_mode) + { + case FOLLOW_ON: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdescendant processes will be followed\n")); + break; + case FOLLOW_ALL: + if (follow_spec_usr && follow_spec_cmp) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\texperiments will be recorded for descendant processes that match pattern '%s'\n"), + follow_spec_usr); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdescendant processes will all be followed\n")); + break; + case FOLLOW_NONE: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdescendant processes will not be followed\n")); + break; + default: + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tfollowing descendant processes: <UNKNOWN>\n")); + break; + } + if (java_mode == 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tjava profiling disabled\n")); + if (pauseresume_sig != 0) + { + const char *buf = strsignal (pauseresume_sig); + if (buf != NULL) + { + if (pauseresume_pause == 1) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tpause-resume (delayed initialization) signal %s (%d) -- paused\n"), buf, pauseresume_sig); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tpause-resume (delayed initialization) signal %s (%d)\n"), buf, pauseresume_sig); + } + else + { + if (pauseresume_pause == 1) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tpause-resume (delayed initialization) signal %d -- paused\n"), pauseresume_sig); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tpause-resume (delayed initialization) signal %d\n"), pauseresume_sig); + } + } + if (sample_sig != 0) + { + const char *buf = strsignal (sample_sig); + if (buf != NULL) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tsample signal %s (%d)\n"), buf, sample_sig); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tsample signal %d\n"), sample_sig); + } + if (time_run != 0 || start_delay != 0) + { + if (start_delay != 0) + { + if (time_run != 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdata-collection duration, %d-%d secs.\n"), start_delay, time_run); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdata-collection duration, %d- secs.\n"), start_delay); + } + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdata-collection duration, %d secs.\n"), time_run); + } + if (sample_period != 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tperiodic sampling, %d secs.\n"), sample_period); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tno periodic sampling\n")); + if (size_limit != 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\texperiment size limit %d MB.\n"), size_limit); + else + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tno experiment size limit set\n")); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\texperiment archiving: -a %s\n"), archive_mode); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\tdata descriptor: \"%s\"\n"), + ((data_desc != NULL) ? data_desc : NTXT ("<NULL>"))); +#if 0 + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\t expt_dir: %s\n"), + ((expt_dir != NULL) ? expt_dir : NTXT ("<NULL>"))); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\t base_name: %s\n"), + ((base_name != NULL) ? base_name : NTXT ("<NULL>"))); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\t store_dir: %s\n"), + ((store_dir != NULL) ? store_dir : NTXT ("<NULL>"))); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\t store_ptr: %s\n"), + ((store_ptr != NULL) ? store_ptr : NTXT ("<NULL>"))); +#endif + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\t\thost: `%s', ncpus = %d, clock frequency %d MHz.\n"), + ((node_name != NULL) ? node_name : NTXT ("<NULL>")), + (int) ncpus, (int) cpu_clk_freq); + if (npages > 0) + { + long long memsize = ((long long) npages * (long long) page_size) / (1024 * 1024); + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("\t\tmemory: %ld pages @ %ld bytes = %lld MB.\n"), + npages, page_size, memsize); + } + return strdup (UEbuf); +} + +#define MAX_COLLECT_ARGS 100 + +char ** +Coll_Ctrl::get_collect_args () +{ + char buf[DD_MAXPATHLEN]; + char **p; + char **argv = (char **) calloc (MAX_COLLECT_ARGS, sizeof (char *)); + if (argv == NULL) // poor way of dealing with calloc failure + abort (); + p = argv; + *p++ = strdup ("collect"); + if (debug_mode == 1) + *p++ = strdup ("-x"); + if (clkprof_enabled != 0) + { + *p++ = strdup ("-p"); + snprintf (buf, sizeof (buf), "%du", clkprof_timer); + *p++ = strdup (buf); + } + if (hwcprof_enabled_cnt > 0) + { + *buf = 0; + *p++ = strdup ("-h"); + for (int ii = 0; ii < hwcprof_enabled_cnt; ii++) + { + char*rateString = hwc_rate_string (&hwctr[ii], 1); //"1" is for temporary goldfile compatibility. TBR YXXX!! + snprintf (buf + strlen (buf), sizeof (buf) - strlen (buf), + "%s%s,%s%s", ii ? "," : "", hwctr[ii].name, + rateString ? rateString : "", + (ii + 1 < hwcprof_enabled_cnt) ? "," : ""); + free (rateString); + } + if (strlen (buf) + 1 >= sizeof (buf)) + abort (); + *p++ = strdup (buf); + } + if (heaptrace_enabled != 0) + { + *p++ = strdup ("-H"); + *p++ = strdup ("on"); + } + if (iotrace_enabled != 0) + { + *p++ = strdup ("-i"); + *p++ = strdup ("on"); + } + if (synctrace_enabled != 0) + { + *p++ = strdup ("-s"); + if (synctrace_thresh < 0) + *p++ = strdup ("calibrate"); + else if (synctrace_thresh < 0) + *p++ = strdup ("all"); + else + *p++ = dbe_sprintf ("%d", synctrace_thresh); + *p++ = dbe_sprintf (",%d", synctrace_scope); + } + if (follow_mode != 0) + { + *p++ = strdup ("-F"); + char * fs = get_follow_usr_spec (); + if (fs) + *p++ = strdup (fs); + else + { + switch (get_follow_mode ()) + { + case FOLLOW_ON: + *p++ = strdup ("on"); + break; + case FOLLOW_ALL: + *p++ = strdup ("all"); + break; + case FOLLOW_NONE: + default: + *p++ = strdup ("off"); + break; + } + } + } + *p++ = strdup ("-a"); + *p++ = strdup (get_archive_mode ()); + if (java_mode != 0) + { + *p++ = strdup ("-j"); + *p++ = strdup ("on"); + } + if (pauseresume_sig != 0) + { + *p++ = strdup ("-y"); + *p++ = dbe_sprintf ("%d%s", pauseresume_sig, + (pauseresume_pause == 0 ? ",r" : "")); + } + if (sample_sig != 0) + { + *p++ = strdup ("-l"); + *p++ = dbe_sprintf ("%d", sample_sig); + } + if (sample_period != 0) + { + *p++ = strdup ("-S"); + *p++ = dbe_sprintf ("%d", sample_period); + } + if (size_limit != 0) + { + *p++ = strdup ("-L"); + *p++ = dbe_sprintf ("%d", size_limit); + } + if (expt_group != NULL) + { + *p++ = strdup ("-g"); + *p++ = strdup (expt_group); + } + if (udir_name != 0) + { + *p++ = strdup ("-d"); + *p++ = strdup (udir_name); + } + if (expt_name != 0) + { + *p++ = strdup ("-o"); + *p++ = strdup (expt_name); + } + if (p - argv >= MAX_COLLECT_ARGS) // argument list too small -- fatal error + abort (); + return argv; +} + +char * +Coll_Ctrl::show_expt () +{ + if (enabled == 0) + return NULL; + char UEbuf[4096]; + UEbuf[0] = 0; + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("Creating experiment directory %s (Process ID: %ld) ...\n"), + ((store_ptr != NULL) ? store_ptr : NTXT ("<NULL>")), (long) getpid ()); + char *caller = getenv ("SP_COLLECTOR_FROM_GUI"); // Collector from GUI + if (caller != NULL) // Print non-localized message + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + NTXT ("\nCreating experiment directory %s (Process ID: %ld) ...\n"), + ((store_ptr != NULL) ? store_ptr : NTXT ("<NULL>")), (long) getpid ()); +#if 0 + char *fstype = get_fstype (store_dir); + if ((fstype != NULL) && (nofswarn == 0)) + { + // only warn if clock or hwc profiling is turned on + if (clkprof_enabled || hwcprof_enabled_cnt != 0) + snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), + GTXT ("this experiment is being recorded to a file system \nof type \"%s\", which may distort the measured performance."), + fstype); + } +#endif + return strdup (UEbuf); +} + +void +Coll_Ctrl::set_clk_params (int min, int res, int max, int hi, int norm, int lo) +{ + clk_params.min = min; + clk_params.res = res; + clk_params.max = max; + clk_params.hival = hi; + clk_params.normval = norm; + clk_params.lowval = lo; + set_clkprof_timer_target (clk_params.normval); // note: requires clk_params to be initialized! +} + +char * +Coll_Ctrl::reset_clkprof (int val) +{ + if (val != clkprof_timer) + { + // profiler has had to reset to a different value; warn user + char *msg = dbe_sprintf ( + GTXT ("Warning: Clock profiling timer reset from %.3f millisec. to %.3f millisec. as required by profiling driver\n\n"), + (double) (clkprof_timer) / 1000., (double) (val) / 1000.); + adjust_clkprof_timer (val); + return msg; + } + return NULL; +} + +char * +Coll_Ctrl::set_clkprof (const char *string, char** warn) +{ + int ticks; + int nclkprof_timer; + int prevclkprof_enabled; + int prevclkprof_default; + *warn = NULL; + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + /* if the first character is a +, warn user that it is no longer supported */ + if (string[0] == '+') + return strdup (GTXT ("Warning: clock-based memoryspace and dataspace profiling is no longer supported\n")); + if (strcmp (string, "off") == 0) + { + clkprof_enabled = 0; + clkprof_default = 0; + return NULL; + } + else if (string == NULL || strcmp (string, "on") == 0) + nclkprof_timer = clk_params.normval; + else if (strcmp (string, "lo") == 0 || strcmp (string, "low") == 0) + nclkprof_timer = clk_params.lowval; + else if (strcmp (string, "hi") == 0 || strcmp (string, "high") == 0 + || strcmp (string, "h") == 0) + nclkprof_timer = clk_params.hival; + else + { + /* the remaining string should be a number > 0 */ + char *endchar = NULL; + double dval = strtod (string, &endchar); + if (*endchar == 'm' || *endchar == 0) /* user specified milliseconds */ + dval = dval * 1000.; + else if (*endchar == 'u') /* user specified microseconds */ + dval = dval; + else + return dbe_sprintf (GTXT ("Unrecognized clock-profiling interval `%s'\n"), string); + nclkprof_timer = (int) (dval + 0.5); + } + // we now have the proposed value; ensure it's within limits + if (nclkprof_timer <= 0) + return dbe_sprintf (GTXT ("Unrecognized clock-profiling interval `%s'\n"), string); + + // Check consistency with experiment + prevclkprof_enabled = clkprof_enabled; + prevclkprof_default = clkprof_default; + clkprof_enabled = 1; + clkprof_default = 0; + char *ret = check_consistency (); + if (ret != NULL) + { + clkprof_default = prevclkprof_default; + clkprof_enabled = prevclkprof_enabled; + return ret; + } + int ref_nclkprof_timer = nclkprof_timer; + + // check for minimum value + if (nclkprof_timer < clk_params.min) + { + /* value too small, use minimum value, with warning */ + *warn = dbe_sprintf ( + GTXT ("Warning: Clock profiling at %.3f millisec. interval is not supported on this system; minimum %.3f millisec. used\n"), + (double) (nclkprof_timer) / 1000., (double) (clk_params.min) / 1000.); + nclkprof_timer = clk_params.min; + } + + // check for maximum value + if (nclkprof_timer > clk_params.max) + { + *warn = dbe_sprintf ( + GTXT ("Clock profiling at %.3f millisec. interval is not supported on this system; maximum %.3f millisec. used\n"), + (double) (nclkprof_timer) / 1000., (double) (clk_params.max) / 1000.); + nclkprof_timer = clk_params.max; + } + + /* see if setting is a multiple of the period */ + if (nclkprof_timer > clk_params.res) + { + ticks = ((nclkprof_timer / clk_params.res) * clk_params.res); + if (ticks != nclkprof_timer) + { + /* no, we need to reset to a multiple */ + *warn = dbe_sprintf ( + GTXT ("Clock profile interval rounded from %.3f to %.3f (system resolution = %.3f) millisec."), + (double) (nclkprof_timer) / 1000., (double) (ticks) / 1000., + (double) (clk_params.res) / 1000.); + nclkprof_timer = ticks; + } + } + + // limit reference "target" rate. Target rate is also used for HWCS. + if (ref_nclkprof_timer > PROFINT_MAX) + ref_nclkprof_timer = PROFINT_MAX; + if (ref_nclkprof_timer < PROFINT_MIN) + ref_nclkprof_timer = PROFINT_MIN; + set_clkprof_timer_target (ref_nclkprof_timer); + adjust_clkprof_timer (nclkprof_timer); + return NULL; +} + +char * +Coll_Ctrl::set_synctrace (const char *string) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + char *comma_p = NULL; + if (string == NULL) + { + /* no argument provided, use default: calibrate and native */ + synctrace_enabled = 1; + synctrace_thresh = -1; + synctrace_scope = SYNCSCOPE_NATIVE; + char *ret = check_consistency (); + if (ret != NULL) + { + synctrace_enabled = 0; + return ret; + } + return NULL; + } + char *val = strdup (string); + /* see if there's a comma in the string */ + char *next = strchr (val, (int) ','); + if (next != NULL) + { + /* remember where the comma was */ + comma_p = next; + + /* set the scope based on the characters following the comma */ + synctrace_scope = 0; + next++; + while (*next != 0) + { + if (*next == 'n') + synctrace_scope |= SYNCSCOPE_NATIVE; + else if (*next == 'j') + synctrace_scope |= SYNCSCOPE_JAVA; + else + return dbe_sprintf (GTXT ("Unrecognized synchronization tracing threshold `%s'\n"), string); + next++; + } + if (synctrace_scope == 0) + synctrace_scope = SYNCSCOPE_NATIVE; + /* clear the comma for the threshold determination */ + *comma_p = 0; + } + else /* no ",<scope>" -- default to native and Java */ + synctrace_scope = SYNCSCOPE_NATIVE | SYNCSCOPE_JAVA; + if (!strlen (val) || !strcmp (val, "calibrate") || !strcmp (val, "on")) + { + /* use default: calibrate and native */ + synctrace_enabled = 1; + synctrace_thresh = -1; + free (val); + char *ret = check_consistency (); + if (ret != NULL) + { + synctrace_enabled = 0; + return ret; + } + return NULL; + } + if (strcmp (val, "off") == 0) + { + synctrace_enabled = 0; + free (val); + return NULL; + } + if (strcmp (val, "all") == 0) + { + /* set to record all events */ + synctrace_thresh = 0; + synctrace_enabled = 1; + char *ret = check_consistency (); + free (val); + if (ret != NULL) + { + synctrace_enabled = 0; + return ret; + } + return NULL; + } + /* the remaining string should be a number >= 0 */ + char *endchar = NULL; + int tval = (int) strtol (val, &endchar, 0); + free (val); + if (*endchar != 0 || tval < 0) + { + /* invalid setting */ + /* restore the comma, if it was zeroed out */ + if (comma_p != NULL) + *comma_p = ','; + return dbe_sprintf (GTXT ("Unrecognized synchronization tracing threshold `%s'\n"), string); + } + synctrace_thresh = tval; + synctrace_enabled = 1; + return NULL; +} + +char * +Coll_Ctrl::set_heaptrace (const char *string) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (string == NULL || strlen (string) == 0 || strcmp (string, "on") == 0) + { + heaptrace_enabled = 1; + char *ret = check_consistency (); + if (ret != NULL) + { + heaptrace_enabled = 0; + return ret; + } + return NULL; + } + if (strcmp (string, "off") == 0) + { + heaptrace_enabled = 0; + return NULL; + } +#if 0 + if (strcmp (string, "check") == 0) + { + /* set to check for over/underruns */ + heaptrace_checkenabled = 1; + heaptrace_enabled = 1; + return NULL; + } + if (strcmp (string, "clear") == 0) + { + /* set to check for over/underruns, and store patterns */ + heaptrace_checkenabled = 2; + heaptrace_enabled = 1; + return NULL; + } +#endif + return dbe_sprintf (GTXT ("Unrecognized heap tracing parameter `%s'\n"), string); +} + +char * +Coll_Ctrl::set_iotrace (const char *string) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (string == NULL || strlen (string) == 0 || strcmp (string, "on") == 0) + { + iotrace_enabled = 1; + char *ret = check_consistency (); + if (ret != NULL) + { + iotrace_enabled = 0; + return ret; + } + return NULL; + } + if (strcmp (string, "off") == 0) + { + iotrace_enabled = 0; + return NULL; + } + return dbe_sprintf (GTXT ("Unrecognized I/O tracing parameter `%s'\n"), string); +} + +char * +Coll_Ctrl::set_count (const char *string) +{ + int ret = -1; + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (string == NULL || strlen (string) == 0 || strcmp (string, "off") == 0) + { + count_enabled = 0; + ret = 0; + } + if (strcmp (string, "on") == 0) + { + count_enabled = 1; + char *cret = check_consistency (); + if (cret != NULL) + { + count_enabled = 0; + return cret; + } + ret = 0; + } + if (strcmp (string, "static") == 0) + { + count_enabled = -1; + char *cret = check_consistency (); + if (cret != NULL) + { + count_enabled = 0; + return cret; + } + ret = 0; + } + if (ret == 0) + { + if (count_enabled != 0) + { + /* ensure that sample period is 0, if set by default */ + if (sample_default == 1) + sample_period = 0; + /* ensure that clock profiling is off, if set by default */ + if (clkprof_default == 1) + { + clkprof_default = 0; + clkprof_enabled = 0; + } + if (hwcprof_default == 1) + hwcprof_default = 0; + } + return NULL; + } + return dbe_sprintf (GTXT ("Unrecognized count parameter `%s'\n"), string); +} + +char * +Coll_Ctrl::set_time_run (const char *valarg) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (valarg == NULL) /* invalid setting */ + return strdup (GTXT ("time parameter can not be NULL\n")); + /* the string should be a number >= 0 */ + int prev_start_delay = start_delay; + int prev_time_run = time_run; + const char *endchar = valarg; + char *newchar = NULL; + int val = 0; + if (*endchar != '-') + { + val = (int) strtol (endchar, &newchar, 0); + endchar = newchar; + if (val < 0) + return dbe_sprintf (GTXT ("Unrecognized time parameter `%s'\n"), valarg); + if (*endchar == 'm') + { + val = val * 60; /* convert to seconds */ + endchar++; + } + else if (*endchar == 's') /* no conversion needed */ + endchar++; + if (*endchar == 0) + { + time_run = val; + return NULL; + } + else if (*endchar != '-') + return dbe_sprintf (GTXT ("Unrecognized time parameter `%s'\n"), valarg); + } + /* a second number is provided */ + start_delay = val; + endchar++; + val = (int) strtol (endchar, &newchar, 0); + endchar = newchar; + if (val < 0) + { + start_delay = prev_start_delay; + return dbe_sprintf (GTXT ("Unrecognized time parameter `%s'\n"), valarg); + } + if (*endchar == 'm') + { + val = val * 60; /* convert to seconds */ + endchar++; + } + else if (*endchar == 's') /* no conversion needed */ + endchar++; + if (*endchar != 0) + { + start_delay = prev_start_delay; + return dbe_sprintf (GTXT ("Unrecognized time parameter `%s'\n"), valarg); + } + time_run = val; + if (time_run != 0 && start_delay >= time_run) + { + start_delay = prev_start_delay; + return dbe_sprintf (GTXT ("Invalid time parameter `%s': start time must be earlier than end time\n"), valarg); + } + char *ret = check_consistency (); + if (ret != NULL) + { + start_delay = prev_start_delay; + time_run = prev_time_run; + return ret; + } + return NULL; +} + +char * +Coll_Ctrl::set_attach_pid (char *valarg) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (valarg == NULL) + return strdup (GTXT ("Specified PID can not be NULL\n")); + + /* the string should be a number corresponding to an active process' pid */ + char *endchar = NULL; + int val = (int) strtol (valarg, &endchar, 0); + if (*endchar != 0 || val < 0) + return dbe_sprintf (GTXT ("Invalid process pid `%s'\n"), valarg); + int prev_attach_pid = attach_pid; + attach_pid = val; + char *ret = check_consistency (); + if (ret != NULL) + { + attach_pid = prev_attach_pid; + return ret; + } + return NULL; +} + +void +Coll_Ctrl::free_hwc_fields (Hwcentry * tmpctr) +{ + if (tmpctr->name != NULL) + free (tmpctr->name); + if (tmpctr->int_name != NULL) + free (tmpctr->int_name); + memset (tmpctr, 0, sizeof (Hwcentry)); + tmpctr->reg_num = -1; +} + +void +Coll_Ctrl::hwcentry_dup (Hwcentry *hnew, Hwcentry *_hwc) +{ + *hnew = *_hwc; + if (_hwc->name != NULL) + hnew->name = strdup (_hwc->name); + else + hnew->name = NULL; + if (_hwc->int_name != NULL) + hnew->int_name = strdup (_hwc->int_name); + else + hnew->int_name = NULL; + if (_hwc->metric != NULL) + hnew->metric = strdup (_hwc->metric); + else + hnew->metric = NULL; + if (_hwc->short_desc != NULL) + hnew->short_desc = strdup (_hwc->short_desc); + else + hnew->short_desc = NULL; + if (_hwc->reg_list != NULL) + { + hnew->reg_list = (regno_t*) malloc (sizeof (regno_t*) * MAX_PICS); + // poor way of dealing with malloc failure + if (hnew->reg_list) + { + for (int i = 0; i < MAX_PICS; i++) + { + hnew->reg_list[i] = _hwc->reg_list[i]; + if (hnew->reg_list[i] == REGNO_ANY) + break; + } + } + } +} + +// Routine to initialize the HWC tables, set up the default experiment, etc. +void +Coll_Ctrl::setup_hwc () +{ + static bool is_hwc_setup = false; + if (is_hwc_setup == true) + return; + // try to set the default counters + is_hwc_setup = true; + set_hwcdefault (); +} + +hrtime_t +Coll_Ctrl::clkprof_timer_2_hwcentry_min_time (int target_clkprof_usec) +{ + hrtime_t hwc_nanosec; + if (target_clkprof_usec == clk_params.normval) + hwc_nanosec = HWCTIME_ON; + else if (target_clkprof_usec == clk_params.lowval) + hwc_nanosec = HWCTIME_LO; + else if (target_clkprof_usec == clk_params.hival) + hwc_nanosec = HWCTIME_HI; + else + hwc_nanosec = 1000LL * target_clkprof_usec; // nanoseconds + return hwc_nanosec; +} + +void +Coll_Ctrl::set_clkprof_timer_target (int microseconds) +{ + clkprof_timer = microseconds; + clkprof_timer_target = microseconds; + hrtime_t hwc_min_time_nanosec = clkprof_timer_2_hwcentry_min_time (microseconds); + for (int ii = 0; ii < hwcprof_enabled_cnt; ii++) + { + hwctr[ii].min_time_default = hwc_min_time_nanosec; + hwc_update_val (&hwctr[ii]); + } +} + +void +Coll_Ctrl::adjust_clkprof_timer (int use) +{ + clkprof_timer = use; +} + +/* set HWC counter set from a string */ +char * /* return an error string */ +Coll_Ctrl::set_hwcstring (const char *string, char **warnmsg) +{ + *warnmsg = NULL; + if (string == NULL || strcmp (string, "off") == 0) + { + hwcprof_enabled_cnt = 0; + return NULL; + } + setup_hwc (); + int old_cnt = hwcprof_enabled_cnt; + int old_hwcprof_default = hwcprof_default; + + /* reset any previous count to zero */ + hwcprof_enabled_cnt = 0; + char *ret = add_hwcstring (string, warnmsg); + if (ret != NULL) + { + // restore previous setting + hwcprof_enabled_cnt = old_cnt; + hwcprof_default = old_hwcprof_default; + } + return ret; +} + +/* add additional HWC counters to counter set from string */ +char * /* return an error string */ +Coll_Ctrl::add_hwcstring (const char *string, char **warnmsg) +{ + *warnmsg = NULL; + if (string == NULL || strcmp (string, "off") == 0) + { + hwcprof_enabled_cnt = 0; + return NULL; + } + setup_hwc (); + int rc = 0; + int old_cnt = hwcprof_enabled_cnt; + int prev_cnt = hwcprof_enabled_cnt; + // int old_hwcprof_default = hwcprof_default; + char UEbuf[MAXPATHLEN * 5]; + int UEsz; + Hwcentry tmpctr[MAX_PICS]; + Hwcentry * ctrtable[MAX_PICS]; + char *emsg; + char *wmsg; + UEbuf[0] = 0; + UEsz = sizeof (UEbuf); + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (hwcprof_default == 0) + { + /* Copy the counters already defined */ + for (int ii = 0; ii < prev_cnt; ii++) + tmpctr[ii] = hwctr[ii]; + } + else /* the previously-defined counters were defaulted; don't copy them */ + prev_cnt = 0; + + /* look up the CPU version */ + cpc_cpuver = hwc_get_cpc_cpuver (); + if (string && *string) + { + /* lookup counters */ + /* set up a pointer array */ + for (unsigned ii = 0; ii < MAX_PICS; ii++) + ctrtable[ii] = &tmpctr[ii]; + hrtime_t global_min_time = clkprof_timer_2_hwcentry_min_time (clkprof_timer_target); + rc = hwc_lookup (kernelHWC, global_min_time, string, &ctrtable[prev_cnt], MAX_PICS - prev_cnt, &emsg, &wmsg); + if (wmsg != NULL) + *warnmsg = wmsg; + if (rc < 0) + return emsg; + /* set count for sum of old and new counters */ + rc = rc + prev_cnt; + } + + /* even though the actual hwctr[] array is not updated, we can check consistency */ + char *ret = check_consistency (); + if (ret != NULL) + { + hwcprof_enabled_cnt = old_cnt; + return ret; + } + + /* finally, validate the full counter set */ + emsg = hwc_validate_ctrs (kernelHWC, ctrtable, rc); + if (emsg != NULL) + { + hwcprof_enabled_cnt = old_cnt; + return emsg; + } + + /* success, update real counters and the string for them */ + /* turn off the default */ + hwcprof_default = 0; + hwcprof_enabled_cnt = rc; + free (hwc_string); + for (int ii = 0; ii < hwcprof_enabled_cnt; ii++) + { + /* shallow copy of new counters */ + hwctr[ii] = tmpctr[ii]; + char *rateString = hwc_rate_string (&hwctr[ii], 0); + snprintf (UEbuf + strlen (UEbuf), UEsz - strlen (UEbuf), + NTXT (",%s,%s"), hwctr[ii].name, + rateString ? rateString : ""); + free (rateString); + } + /* now duplicate that string, skipping the leading comma */ + hwc_string = strdup (&UEbuf[1]); + return NULL; +} + +/* add default HWC counters to counter set with resolution (on, hi, or lo) */ +/* Note that the resultion will also be used to set the clock-profiling default */ +char * /* return an error string */ +Coll_Ctrl::add_default_hwcstring (const char *resolution, char **warnmsg, bool add, bool forKernel) +{ + setup_hwc (); + *warnmsg = NULL; + char *def_string = hwc_get_default_cntrs2 (forKernel, 1); + if (def_string == NULL) + { + /* no string defined, format and return an error message */ + char cpuname[128]; + hwc_get_cpuname (cpuname, sizeof (cpuname)); + return dbe_sprintf (GTXT ("No default HW counter set is defined for %s\n"), cpuname); + } + int len = strlen (def_string); + if (len == 0) + { + /* string zero-length, meaning default counters can't be used */ + char cpuname[128]; + hwc_get_cpuname (cpuname, sizeof (cpuname)); + return dbe_sprintf (GTXT ("HW counter set for %s cannot be loaded on this system\n"), cpuname); + } + /* allocate return string */ + int retsize = 2 * len + 10; + char *ret = (char *) malloc (retsize); + if (ret == NULL) + return strdup (GTXT ("internal error formating HW counter set; malloc failed\n")); + *ret = 0; + char *retp = ret; + char *stringp = def_string; + int first = 1; + char *hwc_defaultx = strdup (def_string); + + /* now massage the string in order to insert resolution for each counter */ + for (;;) + { + /* find the next comma */ + char * next; + char *nextp; + if (first == 1) + nextp = stringp; + else + nextp = stringp + 1; + first = 0; + if ((next = strchr (nextp, (int) ',')) != NULL) + { + if (next == nextp) + { + /* next counter is zero-length -- invalid string */ + char cpuname[128]; + hwc_get_cpuname (cpuname, sizeof (cpuname)); + free (ret); + ret = dbe_sprintf (GTXT ("HW counter set for %s, \"%s\", format error\n"), cpuname, hwc_defaultx); + free (hwc_defaultx); + return ret; + } + /* another field found */ + *next = 0; + char nextc = *(next + 1); + if ((nextc == 0) || (nextc == ',')) + { + /* either ,, between fields, or string ends in comma */ + /* append the string */ + strncat (retp, stringp, (retsize - strlen (retp) - 1)); + strncat (retp, ",", (retsize - strlen (retp) - 1)); + strncat (retp, resolution, (retsize - strlen (retp) - 1)); + if (nextc == 0) /* string ended in comma; we're done */ + break; + } + else + { + /* string had only one comma between counter names; that's not valid */ + char cpuname[128]; + hwc_get_cpuname (cpuname, sizeof (cpuname)); + free (ret); + ret = dbe_sprintf (GTXT ("HW counter set for %s, \"%s\", format error\n"), cpuname, hwc_defaultx); + free (hwc_defaultx); + return ret; + } + /* string had ,, between fields; move to next field */ + stringp = next + 1; + if (* (stringp + 1) == 0) /* name ended in ,, -- we're done */ + break; + continue; + } + else + { + /* no comma found, add the last counter and the comma and resolution */ + strncat (retp, stringp, (retsize - strlen (retp) - 1)); + strncat (retp, ",", (retsize - strlen (retp) - 1)); + strncat (retp, resolution, (retsize - strlen (retp) - 1)); + break; + } + } + + /* we have now formatted the new string, with resolution inserted */ + char *ccret; + if (add == true) + ccret = add_hwcstring (ret, warnmsg); + else + ccret = set_hwcstring (ret, warnmsg); + free (hwc_defaultx); + free (ret); + + /* now set the clock-profiling timer, if on by default */ + if (clkprof_default == 1) + { + if (strcmp (resolution, NTXT ("on")) == 0) + set_clkprof_timer_target (clk_params.normval); + else if (strcmp (resolution, NTXT ("lo")) == 0) + set_clkprof_timer_target (clk_params.lowval); + else if (strcmp (resolution, NTXT ("hi")) == 0) + set_clkprof_timer_target (clk_params.hival); + } + return ccret; +} + +void +Coll_Ctrl::set_hwcdefault () +{ + char *string = hwc_get_default_cntrs2 (kernelHWC, 1); + if (string != NULL) + { + if (strlen (string) == 0) + hwcprof_default = 0; + else + { + char * warnmsg = NULL; + char *ccret = add_hwcstring (string, &warnmsg); + if (ccret != NULL) + { +#if 0 + /* set string to zero-length so that it won't be used again */ + hwc_set_default_cntrs (kernelHWC, NTXT ("")); +#endif + hwcprof_default = 0; + } + else + hwcprof_default = 1; + } + free (string); + } + else + hwcprof_default = 0; +} + +void +Coll_Ctrl::disable_hwc () +{ + hwcprof_enabled_cnt = 0; + hwcprof_default = 0; + free (hwc_string); + hwc_string = NULL; +} + +char * +Coll_Ctrl::set_sample_period (const char *string) +{ + int val; + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (string == NULL || strcmp (string, "on") == 0) + val = 1; + else if (strcmp (string, "off") == 0) + val = 0; + else + { + /* string should be a number > 0 */ + char *endchar = NULL; + val = (int) strtol (string, &endchar, 0); + if (*endchar != 0 || val <= 0) + return dbe_sprintf (GTXT ("Unrecognized sample period `%s'\n"), string); + } + /* set that value */ + int prev_sample_period = sample_period; + sample_period = val; + char *ret = check_consistency (); + if (ret != NULL) + { + sample_period = prev_sample_period; + return ret; + } + sample_default = 0; + return NULL; +} + +char * +Coll_Ctrl::set_size_limit (const char *string) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (string == NULL || strlen (string) == 0 + || strcmp (string, "unlimited") == 0 || strcmp (string, "none") == 0) + { + size_limit = 0; + return NULL; + } + /* string should be a number >0; 0 is an error */ + char *endchar = NULL; + int val = (int) strtol (string, &endchar, 0); + if (*endchar != 0 || val <= 0) + return dbe_sprintf (GTXT ("Unrecognized size limit `%s'\n"), string); + size_limit = val; + return 0; +} + +void +Coll_Ctrl::build_data_desc () +{ + char spec[DD_MAXPATHLEN]; + spec[0] = 0; + + // Put sample sig before clock profiling. Dbx uses PROF + // for that purpose and we want it to be processed first. + if (project_home) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "P:%s;", project_home); + if (sample_sig != 0) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "g:%d;", sample_sig); + if (pauseresume_sig != 0) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "d:%d%s;", pauseresume_sig, + (pauseresume_pause == 1 ? "p" : "")); + if (clkprof_enabled == 1) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "p:%d;", clkprof_timer); + if (synctrace_enabled == 1) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "s:%d,%d;", synctrace_thresh, synctrace_scope); + if (heaptrace_enabled == 1) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "H:%d;", heaptrace_checkenabled); + if (iotrace_enabled == 1) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "i:;"); + if (hwcprof_enabled_cnt > 0) + { + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "h:%s", + (hwcprof_default == true) ? "*" : ""); + for (int ii = 0; ii < hwcprof_enabled_cnt; ii++) + { + /* min_time is a "new" field. + * + * To help process_data_descriptor() in hwcfuncs.c parse + * the HWC portion of this string -- specifically, to + * recognize min_time when it's present and skip over + * when it's not -- we prepend 'm' to the min_time value. + * + * When we no longer worry about, say, an old dbx + * writing this string and a new libcollector looking for + * the min_time field, the 'm' character can be + * removed and process_data_descriptor() simplified. + */ + hrtime_t min_time = hwctr[ii].min_time; + if (min_time == HWCTIME_TBD) + // user did not specify any value for overflow rate + min_time = hwctr[ii].min_time_default; + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), + "%s%s:%s:%d:%d:m%lld:%d:%d:0x%x", ii ? "," : "", + strcmp (hwctr[ii].name, hwctr[ii].int_name) ? hwctr[ii].name : "", + hwctr[ii].int_name, hwctr[ii].reg_num, hwctr[ii].val, + min_time, ii, /*tag*/ hwctr[ii].timecvt, hwctr[ii].memop); + } + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), ";"); + } + if ((time_run != 0) || (start_delay != 0)) + { + if (start_delay != 0) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "t:%d:%d;", start_delay, time_run); + else + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "t:%d;", time_run); + } + if (sample_period != 0) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "S:%d;", + sample_period); + if (size_limit != 0) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "L:%d;", + size_limit); + if (java_mode != 0) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "j:%d;", (int) java_mode); + if (follow_mode != FOLLOW_NONE) + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "F:%d;", (int) follow_mode); + snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "a:%s;", archive_mode); + if (strlen (spec) + 1 >= sizeof (spec)) + abort (); + free (data_desc); + data_desc = strdup (spec); +} + +char * +Coll_Ctrl::check_group () +{ + char group_file[MAXPATHLEN]; + if (expt_group == NULL) + return NULL; + // Is the group an relative path, with a store directory set? + if ((expt_group[0] == '/') || ((udir_name == NULL) || (udir_name[0] == '0'))) + snprintf (group_file, sizeof (group_file), "%s", expt_group); + else // relative path, store directory; make group_file in that directory + snprintf (group_file, sizeof (group_file), "%s/%s", udir_name, expt_group); + // See if we can write the group file + int ret = access (group_file, W_OK); + if (ret != 0) + { + if (errno == ENOENT) + { + char *stmp = group_file; + char *dir = dirname (stmp); + ret = access (dir, W_OK); + if (ret != 0) // group file does not exist; + return dbe_sprintf (GTXT ("Directory (%s) for group file %s is not writeable: %s\n"), + dir, group_file, strerror (errno)); + } + else + return dbe_sprintf (GTXT ("Group file %s is not writeable: %s\n"), + group_file, strerror (errno)); + } + return NULL; +} + +char * +Coll_Ctrl::join_group () +{ + int tries = 0; + int groupfd; + FILE *file; + char group_file[MAXPATHLEN]; + struct stat statbuf; + struct flock flockbuf; + flockbuf.l_type = F_WRLCK; + flockbuf.l_whence = SEEK_SET; + flockbuf.l_start = 0; + flockbuf.l_len = 0; + if (expt_group == NULL) + return NULL; + // Is the group an relative path, with a store directory set? + if (expt_group[0] == '/' || udir_name == NULL || udir_name[0] == '0') + snprintf (group_file, sizeof (group_file), "%s", expt_group); + else // relative path, store directory; make group_file in that directory + snprintf (group_file, sizeof (group_file), "%s/%s", udir_name, expt_group); + for (;;) + { + tries++; + // try to open the group file + while ((groupfd = open (group_file, O_RDWR)) >= 0) + { + if (uinterrupt == 1) + { + close (groupfd); + return strdup (GTXT ("user interrupt\n")); + } + // it's opened, now lock it + if (fcntl (groupfd, F_SETLK, &flockbuf) != -1) + { + // we got the lock; check the file size + if (fstat (groupfd, &statbuf) != 0) + { + // can't stat the file -- give up + close (groupfd); + return dbe_sprintf (GTXT ("Can't fstat group file %s\n"), group_file); + } + if (statbuf.st_size == 0) + { + // size is zero: we got the lock just as someone + // else created the group file + // close the file and release the lock; try again + close (groupfd); + continue; + } + else + { + // size is non-zero, add our record + file = fdopen (groupfd, "a"); + if (file == NULL) + { + close (groupfd); + return dbe_sprintf (GTXT ("Can't access group file %s\n"), group_file); + } + if (fprintf (file, "%s\n", store_ptr) <= 0) + { + fclose (file); + return dbe_sprintf (GTXT ("Can't update group file %s\n"), group_file); + } + // close the file, releasing our lock + fclose (file); + return NULL; + } + } + else + { + // can't get the lock, close the file and try again + close (groupfd); + if (uinterrupt == 1) + return strdup (GTXT ("user interrupt\n")); + if (tries == 11900) + return dbe_sprintf (GTXT ("Timed out: waiting for group file %s\n"), group_file); +#if 0 + if (tries % 500 == 0) + USR_WARN (GTXT ("Waiting for group file %s . . ."), group_file); +#endif + usleep (10000U); + continue; + } + } + // If the error was not that the file did not exist, report it + if (errno != ENOENT) + return dbe_sprintf (GTXT ("Can't open group file %s: %s\n"), + group_file, strerror (errno)); + // the file did not exist, try to create it + groupfd = open (group_file, O_CREAT | O_EXCL | O_RDWR, 0666); + if (groupfd < 0) + { + // we could not create the file + if (errno == EEXIST) + continue; + return dbe_sprintf (GTXT ("Can't create group file %s: %s\n"), + group_file, strerror (errno)); + } + // we created the group file, now lock it, waiting for the lock + while (fcntl (groupfd, F_SETLKW, &flockbuf) == -1) + { + // we created the file, but couldn't lock it + if (errno != EINTR) + return dbe_sprintf (GTXT ("Unable to lock group file %s\n"), group_file); + } + // we created and locked the file, write to it + file = fdopen (groupfd, "a"); + if (file == NULL) + { + close (groupfd); + return dbe_sprintf (GTXT ("Can't access group file %s\n"), group_file); + } + // write the header line + if (fprintf (file, "%s\n", SP_GROUP_HEADER) <= 0) + { + fclose (file); + return dbe_sprintf (GTXT ("Can't initialize group file %s\n"), group_file); + } + if (fprintf (file, "%s\n", store_ptr) <= 0) + { + fclose (file); + return dbe_sprintf (GTXT ("Can't update group file %s\n"), group_file); + } + // finally, close the file, releasing the lock + fclose (file); + return NULL; + } + // never reached +} + +char * +Coll_Ctrl::set_directory (char *dir, char **warn) +{ + struct stat statbuf; + *warn = NULL; + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (stat (dir, &statbuf) != 0) + return dbe_sprintf (GTXT ("Can't set directory `%s': %s\n"), + dir, strerror (errno)); + if (!S_ISDIR (statbuf.st_mode)) + return dbe_sprintf (GTXT ("Can't set directory `%s': %s\n"), + dir, strerror (ENOTDIR)); + free (udir_name); + udir_name = strdup (dir); + + // Process new setting + *warn = preprocess_names (); + if ((uexpt_name != NULL) || (interactive != 0)) + { + char *ret = update_expt_name (true, true); + if (ret != NULL) + { + if (*warn != NULL) + { + char *msg = dbe_sprintf ("%s%s", *warn, ret); + free (*warn); + free (ret); + *warn = msg; + } + else + *warn = ret; + } + } + else + (void) update_expt_name (false, false); + return NULL; // All is OK +} + +int +Coll_Ctrl::set_target (char* targetname) +{ + free (target_name); + target_name = NULL; + if (targetname != NULL) + target_name = strdup (targetname); + return 0; +} + +void +Coll_Ctrl::set_default_stem (const char* stem) +{ + default_stem = strdup (stem); + preprocess_names (); + (void) update_expt_name (false, false); // no warnings +} + +char * +Coll_Ctrl::set_expt (const char *ename, char **warn, bool overwriteExp) +{ + *warn = NULL; + if (ename == NULL) + { + free (uexpt_name); + uexpt_name = NULL; + return NULL; + } + char *exptname = canonical_path(strdup(ename)); + size_t i = strlen (exptname); + if (i < 4 || strcmp (&exptname[i - 3], ".er") != 0) + { + free (exptname); + return dbe_sprintf (GTXT ("Experiment name `%s' must end in `.er'\n"), + ename); + } + // Name is OK + free (uexpt_name); + uexpt_name = exptname; + preprocess_names (); + char *err = update_expt_name (true, true, overwriteExp); + if (err != NULL) + return err; + if (overwriteExp) + { + char *nm = dbe_sprintf ("%s/%s", store_dir, base_name); + struct stat statbuf; + char *cmd = dbe_sprintf ("/bin/rm -rf %s >/dev/null 2>&1", nm); + system (cmd); + free (cmd); + if (stat (nm, &statbuf) == 0) + return dbe_sprintf (GTXT ("Cannot remove experiment `%s'\n"), nm); + if (errno != ENOENT) + return dbe_sprintf (GTXT ("Cannot remove experiment `%s'\n"), nm); + free (nm); + } + *warn = update_expt_name (true, false); + return NULL; +} + +char * +Coll_Ctrl::set_group (char *groupname) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (expt_group != NULL) + { + free (expt_group); + expt_group = NULL; + } + if (groupname == NULL) + { + // reset the name + preprocess_names (); + (void) update_expt_name (true, false); + return NULL; + } + int i = (int) strlen (groupname); + if (i < 5 || strcmp (&groupname[i - 4], ".erg") != 0) + return dbe_sprintf (GTXT ("Experiment group name `%s'must end in `.erg'\n"), groupname); + expt_group = strdup (groupname); + preprocess_names (); + (void) update_expt_name (true, false); + return NULL; +} + +char * +Coll_Ctrl::set_java_mode (const char *string) +{ + struct stat statbuf; + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (string == NULL || strlen (string) == 0 || strcmp (string, "on") == 0) + { +#if defined(GPROFNG_JAVA_PROFILING) + int prev_java_mode = java_mode; + int prev_java_default = java_default; + java_mode = 1; + java_default = 0; + char *ret = check_consistency (); + if (ret != NULL) + { + java_mode = prev_java_mode; + java_default = prev_java_default; + return ret; + } + return NULL; +#else + return strdup (GTXT ("gprofng was built without support for profiling Java applications\n")); +#endif + } + if (strcmp (string, "off") == 0) + { + int prev_java_mode = java_mode; + int prev_java_default = java_default; + java_mode = 0; + java_default = 0; + char *ret = check_consistency (); + if (ret != NULL) + { + java_mode = prev_java_mode; + java_default = prev_java_default; + return ret; + } + free (java_path); + java_path = NULL; + return NULL; + } + /* any other value should be a path to Java installation directory */ + if (stat (string, &statbuf) == 0) + { + if ((statbuf.st_mode & S_IFMT) == S_IFDIR) + { + // it's a directory -- set the Java path to it + int prev_java_mode = java_mode; + int prev_java_default = java_default; + java_mode = 1; + java_default = 0; + char *ret = check_consistency (); + if (ret != NULL) + { + java_mode = prev_java_mode; + java_default = prev_java_default; + return ret; + } + return set_java_path (string); + } + } + return dbe_sprintf (GTXT ("Java-profiling parameter is neither \"on\", nor \"off\", nor is it a directory: `%s'\n"), string); +} + +char * +Coll_Ctrl::set_java_path (const char *string) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + free (java_path); + java_path = strdup (string); + return NULL; +} + +char * +Coll_Ctrl::set_java_args (char *string) +{ + char *next; + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + char *prev_java_args = java_args; + if (string == NULL || strlen (string) == 0) + java_args = strdup (""); + else + java_args = strdup (string); + // now count the number of Java arguments + for (next = java_args; *next; next++) + { + if (*next == ' ' || *next == '\t') + continue; + njava_args++; + for (++next; *next; next++) + if (*next == ' ' || *next == '\t') + break; + if (!*next) + break; + } + if (njava_args == 0) + java_args = NULL; + char *ret = check_consistency (); + if (ret != NULL) + { + java_args = prev_java_args; + return ret; + } + free (prev_java_args); + return NULL; +} + +char * +Coll_Ctrl::set_follow_mode (const char *string) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + free (follow_spec_usr); + free (follow_spec_cmp); + follow_spec_usr = NULL; + follow_spec_cmp = NULL; + if (string == NULL || strlen (string) == 0 || strcmp (string, "all") == 0 + || strcmp (string, "on") == 0) + { + follow_mode = FOLLOW_ON; + follow_default = 0; + return NULL; + } + if (strcmp (string, "off") == 0) + { + follow_mode = FOLLOW_NONE; + follow_default = 0; + return NULL; + } + + /* compile regular expression if string starts with "=" */ + if (string[0] == '=' && string[1] != 0) + { + // user has specified a string matching specification + regex_t regex_desc; + int ercode; + const char *userspec = &string[1]; + size_t newstrlen = strlen (userspec) + 3; + char * str = (char *) malloc (newstrlen); + if (str) + { + snprintf (str, newstrlen, "^%s$", userspec); + assert (strlen (str) == newstrlen - 1); + ercode = regcomp (®ex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); + } + else + ercode = 1; + if (!ercode) + { + follow_spec_usr = strdup (string); + /* Ideally, follow_spec_cmp = [serialized regex_desc], */ + /* so that libcollector wouldn't have to recompile it. */ + /* For now, just copy the regular expression into follow_spec_cmp */ + follow_spec_cmp = str; + follow_mode = FOLLOW_ALL; + follow_default = 0; + return NULL; + } + // syntax error in parsing string +#if 0 + char errbuf[256]; + regerror (ercode, ®ex_desc, errbuf, sizeof (errbuf)); + fprintf (stderr, "Coll_Ctrl::set_follow_mode: regerror()=%s\n", errbuf); +#endif + free (str); + } + return dbe_sprintf (GTXT ("Unrecognized follow-mode parameter `%s'\n"), string); +} + +char * +Coll_Ctrl::set_prof_idle (const char *string) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (string == NULL || strlen (string) == 0 || strcmp (string, "on") == 0) + { + prof_idle = 1; + return NULL; + } + if (strcmp (string, "off") == 0) + { + prof_idle = 0; + return NULL; + } + return dbe_sprintf (GTXT ("Unrecognized profiling idle cpus parameter `%s'\n"), string); +} + +char * +Coll_Ctrl::set_archive_mode (const char *string) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (string == NULL || strlen (string) == 0) + string = "on"; + if (strcasecmp (string, "on") == 0 || strcasecmp (string, "off") == 0 + || strcasecmp (string, "ldobjects") == 0 + || strcasecmp (string, "usedldobjects") == 0 + || strcasecmp (string, "src") == 0 || strcasecmp (string, "usedsrc") == 0 + || strcasecmp (string, "all") == 0) + { + free (archive_mode); + archive_mode = strdup (string); + return NULL; + } + return dbe_sprintf (GTXT ("Unrecognized archive-mode parameter `%s'\n"), string); +} + +char * +Coll_Ctrl::set_sample_signal (int value) +{ + const char *buf; + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (value == 0) + { + sample_sig = 0; + return NULL; + } + if (value == pauseresume_sig) + return report_signal_conflict (value); + if ((buf = strsignal (value)) != NULL) + sample_sig = value; + else + return dbe_sprintf (GTXT ("Invalid sample signal %d\n"), value); + return NULL; +} + +/* find a signal by name */ +int +Coll_Ctrl::find_sig (const char *string) +{ + int val; + char *signame_alloc = NULL; + const char *signame; + val = -1; + if (strcmp (string, "off") == 0) + return 0; + // see if the name begins with SIG + if (strncmp (string, "SIG", 3) != 0) + { + // no: add it + signame_alloc = (char *) malloc (strlen (string) + 3 + 1); + if (signame_alloc == NULL) + return -1; + strcpy (signame_alloc, "SIG"); + strcpy (&signame_alloc[3], string); + signame = signame_alloc; + } + else + signame = string; + + /* see if the string is a number */ + char *endchar = NULL; + val = (int) strtol (signame, &endchar, 0); + if (*endchar != 0) + val = strtosigno (signame); + free (signame_alloc); + if (val == SIGKILL) + return -1; + return val; +} + +char * +Coll_Ctrl::set_pauseresume_signal (int value, int resume) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + if (value == 0) + { + pauseresume_sig = 0; + return NULL; + } + if (value == sample_sig) + return report_signal_conflict (value); + if (strsignal (value) != NULL) + { + pauseresume_sig = value; + pauseresume_pause = resume; + } + else + return dbe_sprintf (GTXT ("Invalid pause-resume (delayed initialization) signal %d\n"), value); + return NULL; +} + +char * +Coll_Ctrl::report_signal_conflict (int value) +{ + const char *xbuf = strsignal (value); + if (xbuf != NULL) + return dbe_sprintf (GTXT ("Signal %s (%d) can not be used for both sample and pause-resume (delayed initialization)\n"), + xbuf, value); + return dbe_sprintf (GTXT ("Signal %d can not be used for both sample and pause-resume (delayed initialization)\n"), + value); +} + +char * +Coll_Ctrl::set_debug_mode (int value) +{ + if (opened == 1) + return strdup (GTXT ("Experiment is active; command ignored.\n")); + debug_mode = value; + return NULL; +} + +char * +Coll_Ctrl::create_exp_dir () +{ + int max = 4095; // 0xFFF - can be increased if it seems too low + for (int i = 0; i < max; i++) + { + if (mkdir (store_ptr, + S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) + { + int err = errno; + if (err == EACCES) + return dbe_sprintf (GTXT ("Store directory %s is not writeable: %s\n"), + store_dir, strerror (err)); + if (i + 1 >= max) // no more attempts + return dbe_sprintf (GTXT ("Unable to create directory `%s' -- %s\n%s: %d\n"), + store_ptr, strerror (err), + GTXT ("collect: Internal error: loop count achieved"), + max); + char *ermsg = update_expt_name (false, false, true); + if (ermsg != NULL) + { + char *msg = dbe_sprintf (GTXT ("Unable to create directory `%s' -- %s\n"), + store_ptr, ermsg); + free (ermsg); + return msg; + } + continue; + } + return NULL; // All is OK + } + return dbe_sprintf (GTXT ("Unable to create directory `%s'\n"), store_ptr); +} + +char * +Coll_Ctrl::get_exp_name (const char *stembase) +{ + expno = 1; + return dbe_sprintf ("%s.%d.er", stembase, expno); +} + +char * +Coll_Ctrl::preprocess_names () +{ + char buf[MAXPATHLEN]; + char msgbuf[MAXPATHLEN]; + char *ret = NULL; + + /* convert the experiment name and directory into store name/dir */ + /* free the old strings */ + if (store_dir != NULL) + { + free (store_dir); + store_dir = NULL; + } + if (expt_dir != NULL) + { + free (expt_dir); + expt_dir = NULL; + } + if (base_name != NULL) + { + free (base_name); + base_name = NULL; + } + if (expt_name != NULL) + { + free (expt_name); + expt_name = NULL; + } + expno = 1; + if (uexpt_name != NULL) + expt_name = strdup (uexpt_name); + else + { + // no user name -- pick a default + char *c; + char *stem; + char *stembase; + if (expt_group == NULL) + { + stem = strdup (default_stem); + stembase = stem; + } + else + { + stem = strdup (expt_group); + stem[strlen (stem) - 4] = 0; + stembase = stem; + // now remove any leading directory + for (int i = 0;; i++) + { + if (stem[i] == 0) + break; + if (stem[i] == '/') + stembase = &stem[i + 1]; + } + if (strlen (stembase) == 0) + { + free (stem); + stem = strdup (default_stem); + stembase = stem; + } + } + c = get_exp_name (stembase); + expt_name = c; + free (stem); + } + snprintf (buf, sizeof (buf), NTXT ("%s"), expt_name); + if (buf[0] == '/') + { + // it's a full path name + if (udir_name != NULL) + { + snprintf (msgbuf, sizeof (msgbuf), + GTXT ("Warning: Experiment name is an absolute path; directory name %s ignored.\n"), + udir_name); + ret = strdup (msgbuf); + } + } + + // now extract the directory and basename + int lastslash = 0; + for (int i = 0;; i++) + { + if (buf[i] == 0) + break; + if (buf[i] == '/') + lastslash = i; + } + expt_dir = strdup (buf); + if (lastslash != 0) + base_name = strdup (&buf[lastslash + 1]); + else + base_name = strdup (buf); + expt_dir[lastslash] = 0; + if (expt_dir[0] == '/') + store_dir = strdup (expt_dir); + else if ((udir_name == NULL) || (udir_name[0] == 0)) + { + if (expt_dir[0] == 0) + store_dir = strdup ("."); + else + store_dir = strdup (expt_dir); + } + else + { + /* udir_name is a non-empty string */ + if (expt_dir[0] == 0) + store_dir = strdup (udir_name); + else + { + snprintf (buf, sizeof (buf), "%s/%s", udir_name, expt_dir); + store_dir = strdup (buf); + } + } + free (store_ptr); + if (strcmp (store_dir, ".") == 0) + store_ptr = strdup (base_name); + else + { + snprintf (buf, sizeof (buf), "%s/%s", store_dir, base_name); + store_ptr = strdup (buf); + } + + // determine the file system type + if (strcmp (store_dir, prev_store_dir) != 0) + { + free (prev_store_dir); + prev_store_dir = strdup (store_dir); + const char *fstype = get_fstype (store_dir); + if (interactive && enabled && (fstype != NULL) && (nofswarn == 0)) + { + snprintf (msgbuf, sizeof (msgbuf), + GTXT ("%sExperiment directory is set to a file system of type \"%s\",\n which may distort the measured performance;\n it is preferable to record to a local disk.\n"), + (ret == NULL ? "" : ret), fstype); + free (ret); + ret = strdup (msgbuf); + } + } + return ret; +} + +char * +Coll_Ctrl::update_expt_name (bool chgmsg, bool chkonly, bool newname) +{ + char *ret = NULL; + struct stat statbuf; + // make sure the name ends in .er + // set count to the length of the name + int count = (int) strlen (base_name); + + // this should have been checked already, so we can abort + if (count < 4 || strcmp (&base_name[count - 3], ".er") != 0) + abort (); + int pcount = count - 4; + if (!newname) + { // check if old name can be used + char fullname[MAXPATHLEN]; + snprintf (fullname, sizeof (fullname), "%s/%s", store_dir, base_name); + if (stat (fullname, &statbuf) != 0) + if (errno == ENOENT) // name does not exist, we can use it + return NULL; + } + else if (chkonly) + return NULL; + + // current name will not work, update the name + DIR *dir; + struct dirent *dir_entry; + + // see if there's a numeric field in front of the .er of the name + int digits = 0; + while (isdigit ((int) (base_name[pcount])) != 0) + { + pcount--; + if (pcount == 0) // name is of the form 12345.er; don't update it + return dbe_sprintf (GTXT ("name %s is in use and cannot be updated\n"), + base_name); + digits++; + } + if (digits == 0) // name is of form xyz.er (or xyz..er); don't update it + return dbe_sprintf (GTXT ("name %s is in use and cannot be updated\n"), + base_name); + if (base_name[pcount] != '.') // name is of form xyz123.er; don't update it + return dbe_sprintf (GTXT ("name %s is in use and cannot be updated\n"), + base_name); + if (chkonly) + return NULL; + + // save the name for a changed message + char *oldbase = strdup (base_name); + + // the name is of the from prefix.nnn.er; extract the value of nnn + int version = atoi (&base_name[pcount + 1]); + if (newname) // do not try to use old name + version++; + int max_version = version - 1; + + // terminate the base_name string after that . yielding "prefix." + base_name[pcount + 1] = 0; + if ((dir = opendir (store_dir)) == NULL) + { + // ignore error -- we'll hit it again later + free (oldbase); + return NULL; + } + + // find the maximum version in the directory + // count is the number of characters before the number + // + while ((dir_entry = readdir (dir)) != NULL) + { + count = (int) strlen (dir_entry->d_name); + if ((count < 4) || (strcmp (&dir_entry->d_name[count - 3], ".er") != 0)) + continue; + // check that the name is of the form prefix.nnn.er; if not, skip it + if (strncmp (base_name, dir_entry->d_name, pcount + 1) == 0) + { + // the "prefix." part matches, terminate the entry name before the .er + dir_entry->d_name[count - 3] = 0; + char *lastchar; + int dversion = (int) strtol (&dir_entry->d_name[pcount + 1], &lastchar, 10); + + // if it did not end where the .er was, skip it + if (*lastchar != 0) + continue; + if (dversion > max_version) + max_version = dversion; + } + } + + // we now have the maximum version determined + char newbase[MAXPATHLEN]; + base_name[pcount + 1] = 0; + version = max_version + 1; + snprintf (newbase, sizeof (newbase), "%s%d.er", base_name, version); + if ((strcmp (oldbase, newbase) != 0) && chgmsg) + { + ret = dbe_sprintf (GTXT ("name %s is in use; changed to %s\n"), + oldbase, newbase); + free (oldbase); + } + else + free (oldbase); + free (base_name); + base_name = strdup (newbase); + + // now, reset expt_name to reflect new setting + free (expt_name); + if (expt_dir[0] == 0) + expt_name = strdup (base_name); + else + expt_name = dbe_sprintf ("%s/%s", expt_dir, base_name); + free (store_ptr); + if (strcmp (store_dir, ".") == 0) + store_ptr = strdup (base_name); + else + store_ptr = dbe_sprintf ("%s/%s", store_dir, base_name); + closedir (dir); + return ret; +} + +void +Coll_Ctrl::remove_exp_dir () +{ + if (store_ptr == NULL) + return; + rmdir (store_ptr); + free (store_ptr); + store_ptr = NULL; + return; +} + +void +Coll_Ctrl::determine_profile_params () +{ + struct itimerval itimer; + struct itimerval otimer; + int period; + long nperiod; + struct sigaction act; + struct sigaction old_handler; + memset (&act, 0, sizeof (struct sigaction)); + period = 997; + + // set SIGPROF handler to SIG_IGN + sigemptyset (&act.sa_mask); + act.sa_handler = SIG_IGN; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (SIGPROF, &act, &old_handler) == -1) + { + /* couldn't set signal */ + fprintf (stderr, GTXT ("Can't set SIGPROF: %s\n"), strerror (errno)); + exit (1); + } + + // set the timer to arbitrary resolution + itimer.it_interval.tv_sec = period / MICROSEC; + itimer.it_interval.tv_usec = period % MICROSEC; + itimer.it_value = itimer.it_interval; + setitimer (ITIMER_REALPROF, &itimer, &otimer); + + // now reset the timer to turn it off + itimer.it_value.tv_sec = 0; + itimer.it_value.tv_usec = 0; + if (setitimer (ITIMER_REALPROF, &itimer, &otimer) == -1) // call failed + nperiod = -1; + else + nperiod = otimer.it_interval.tv_sec * MICROSEC + otimer.it_interval.tv_usec; + + // check the returned value: is the what we asked for? + if (period == nperiod) // arbitrary precision is OK + set_clk_params (PROFINT_MIN, 1, PROFINT_MAX, PROFINT_HIGH, PROFINT_NORM, PROFINT_LOW); + else if (nperiod < 10000) // hi resolution allowed, but not arbitrary precision + set_clk_params ((int) nperiod, 1000, PROFINT_MAX, 1000, 10000, 100000); + else // low resolution only allowed + set_clk_params (10000, 10000, PROFINT_MAX, 1000, 10000, 100000); + + // If old handler was default, ignore it; otherwise restore it + if (old_handler.sa_handler != SIG_DFL) + { + act.sa_handler = old_handler.sa_handler; + if (sigaction (SIGPROF, &act, &old_handler) == -1) + { + /* couldn't reset signal */ + fprintf (stderr, GTXT ("Can't reset SIGPROF: %s\n"), strerror (errno)); + exit (1); + } + } +} + +const char * +get_fstype (char *) +{ + /* On Linux, statvfs() doesn't return any information that seems to indicate + the filetype. The structure statvfs does not have any field/flag that + gives this information. Comparing the fields from + /usr/include/bits/statvfs.h: + unsigned long int f_fsid; + int __f_unused; + ^^^^ On Solaris, this is where f_basetype is + unsigned long int f_flag; + unsigned long int f_namemax; + XXX Need to revisit this XXX + */ + return NULL; // no NFS warning on Linux for now +} + +//========== Special functions to communicate with the Collector GUI ==========// + +/* Interface strings GUI <-> CLI */ +const char *ipc_str_exp_limit = "exp_limit"; +const char *ipc_str_time_limit = "time_limit"; +const char *ipc_str_arch_exp = "arch_exp"; +const char *ipc_str_descendant = "descendant"; +const char *ipc_str_clkprof = "clkprof"; +const char *ipc_str_hwcprof = "hwcprof"; +const char *ipc_str_hwc2_prof = "hwc2_prof"; +const char *ipc_str_javaprof = "javaprof"; +const char *ipc_str_sample = "sample"; +const char *ipc_str_sample_sig = "sample_sig"; +const char *ipc_str_pause_resume_sig = "pause_resume_sig"; +const char *ipc_str_synctrace = "synctrace"; +const char *ipc_str_heaptrace = "heaptrace"; +const char *ipc_str_iotrace = "iotrace"; +const char *ipc_str_count = "count"; +const char *ipc_str_prof_idle = "prof_idle"; // -x option +// Standard answers +const char *ipc_str_empty = ""; +const char *ipc_str_on = "on"; +const char *ipc_str_off = "off"; +const char *ipc_str_src = "src"; +const char *ipc_str_usedsrc = "usedsrc"; +const char *ipc_str_usedldobjects = "usedldobjects"; +const char *ipc_str_unlimited = "unlimited"; +const char *ipc_str_unknown_control = "Unknown control"; +const char *ipc_str_internal_error = "Internal error"; + +/** + * Finds signal name + * @param signal + * @return NULL or signal name (pointer to allocated memory) + */ +char * +Coll_Ctrl::find_signal_name (int signal) +{ + char *str_signal = NULL; + const char *buf = strsignal (signal); + if (buf != NULL) + str_signal = strdup (buf); + return str_signal; +} + +/** + * Gets control's value + * @param control + * @return value + */ +char * +Coll_Ctrl::get (char * control) +{ + int len = strlen (control); + if (!strncmp (control, ipc_str_exp_limit, len)) + { + if ((size_limit > 0)) + return dbe_sprintf ("%d", size_limit); + return strdup (ipc_str_unlimited); + } + if (!strncmp (control, ipc_str_time_limit, len)) + { + if ((time_run != 0) || (start_delay != 0)) + { + if (start_delay != 0) + { + if (time_run != 0) + return dbe_sprintf ("%ds-%ds", start_delay, start_delay + time_run); + return dbe_sprintf ("%ds-0s", start_delay); + } + return dbe_sprintf ("0s-%ds", time_run); + } + return strdup (ipc_str_unlimited); + } + if (strncmp (control, ipc_str_arch_exp, len) == 0) + return strdup (get_archive_mode ()); + if (!strncmp (control, ipc_str_descendant, len)) + { + switch (get_follow_mode ()) + { + case FOLLOW_ON: + return strdup (ipc_str_on); + case FOLLOW_ALL: + return strdup (ipc_str_on); + case FOLLOW_NONE: + default: + return strdup (ipc_str_off); + } + } + if (!strncmp (control, ipc_str_prof_idle, len)) + { + if (prof_idle == 0) + return strdup (ipc_str_off); + return strdup (ipc_str_on); + } + if (!strncmp (control, ipc_str_clkprof, len)) + { + if (clkprof_default == 1 && clkprof_enabled == 1) // Default value + return strdup (ipc_str_empty); + if (clkprof_enabled == 0) + return strdup (ipc_str_off); + if ((clkprof_timer > 0)) + return dbe_sprintf ("%d", clkprof_timer / 1000); + return strdup (ipc_str_internal_error); + } + if (!strncmp (control, ipc_str_hwcprof, len)) + { + if (hwcprof_enabled_cnt == 0) + return strdup (ipc_str_off); + if (hwc_string != NULL) + return dbe_sprintf ("on\n%s", hwc_string); + return strdup (ipc_str_on); // XXX need more details? + } + if (!strncmp (control, ipc_str_javaprof, len)) + { + if ((java_mode == 0)) + return strdup (ipc_str_off); + return strdup (ipc_str_on); + } + if (!strncmp (control, ipc_str_sample, len)) + { + if (sample_default == 1 && sample_period == 1) // Default value + return strdup (ipc_str_empty); + if (sample_period == 0) + return strdup (ipc_str_off); + if (sample_period > 0) + return dbe_sprintf ("%d", sample_period); + return strdup (ipc_str_internal_error); + } + if (!strncmp (control, ipc_str_sample_sig, len)) + { + if ((sample_sig == 0)) + return strdup (ipc_str_off); + char *str_signal = find_signal_name (sample_sig); + if (str_signal != NULL) + return str_signal; + return dbe_sprintf (GTXT ("Invalid sample signal %d\n"), sample_sig); + } + if (!strncmp (control, ipc_str_pause_resume_sig, len)) + { + if (pauseresume_sig == 0) + return strdup (ipc_str_off); + char *str_signal = find_signal_name (pauseresume_sig); + if (str_signal != NULL) + return str_signal; + return dbe_sprintf (GTXT ("Invalid pause/resume signal %d\n"), pauseresume_sig); + } + if (!strncmp (control, ipc_str_synctrace, len)) + { + if (synctrace_enabled == 0) + return strdup (ipc_str_off); + if (synctrace_thresh < 0) + return strdup ("on\nthreshold: calibrate"); + if (synctrace_thresh == 0) + return strdup ("on\nthreshold: all"); + return dbe_sprintf ("on\nthreshold: %d", synctrace_thresh); + } + if (!strncmp (control, ipc_str_heaptrace, len)) + { + if ((heaptrace_enabled == 0)) + return strdup (ipc_str_off); + return strdup (ipc_str_on); + } + if (!strncmp (control, ipc_str_iotrace, len)) + { + if ((iotrace_enabled == 0)) + return strdup (ipc_str_off); + return strdup (ipc_str_on); + } + if (!strncmp (control, ipc_str_count, len)) + { + if ((count_enabled == 0)) + return strdup (ipc_str_off); + if ((count_enabled < 0)) + return strdup ("on\nstatic"); + return strdup (ipc_str_on); + } + return strdup (ipc_str_unknown_control); +} + +/** + * Resets control's value (restores the default value) + * @param control + * @param value + * @return error or warning or NULL (done) + */ +char * +Coll_Ctrl::set (char * control, const char * value) +{ + char * ret; + char * warn = NULL; + int len = strlen (control); + if (!strncmp (control, ipc_str_exp_limit, len)) + return set_size_limit (value); + if (!strncmp (control, ipc_str_time_limit, len)) + return set_time_run (value); + if (!strncmp (control, ipc_str_arch_exp, len)) + return set_archive_mode (value); + if (!strncmp (control, ipc_str_descendant, len)) + return set_follow_mode (value); + if (!strncmp (control, ipc_str_prof_idle, len)) + return set_prof_idle (value); + if (!strncmp (control, ipc_str_clkprof, len)) + { + ret = set_clkprof (value, &warn); + if (ret == NULL) + { + if (warn != NULL) + return warn; // Warning + return NULL; // Done + } + return ret; // Error + } + if (!strncmp (control, ipc_str_hwcprof, len)) + { + ret = set_hwcstring (value, &warn); + if (ret == NULL) + { + if (warn != NULL) + return warn; // Warning + return NULL; // Done + } + return ret; // Error + } + if (!strncmp (control, ipc_str_hwc2_prof, len)) + { + ret = set_hwcstring (value, &warn); + if (ret == NULL) + { + if (warn != NULL) + return warn; // Warning + return NULL; // Done + } + return ret; // Error + } + if (!strncmp (control, ipc_str_javaprof, len)) + return set_java_mode (value); + if (!strncmp (control, ipc_str_sample, len)) + return set_sample_period (value); + if (!strncmp (control, ipc_str_sample_sig, len)) + return set_sample_signal (find_sig (value)); + if (!strncmp (control, ipc_str_pause_resume_sig, len)) + { + char *str_signal = strdup (value); + char *str_state = strchr (str_signal, (int) '\n'); + if (str_state != NULL) + { + *str_state = 0; + str_state++; + } + int signal = atoi (str_signal); + int state = 0; + if (str_state != NULL) + state = atoi (str_state); + free (str_signal); + return set_pauseresume_signal (signal, state); + } + if (!strncmp (control, ipc_str_synctrace, len)) + return set_synctrace (value); + if (!strncmp (control, ipc_str_heaptrace, len)) + return set_heaptrace (value); + if (!strncmp (control, ipc_str_iotrace, len)) + return set_iotrace (value); + if (!strncmp (control, ipc_str_count, len)) + return set_count (value); + return strdup (ipc_str_unknown_control); +} + +/** + * Resets control's value (restores the default value) + * @param control + * @return error or NULL (done) + */ +char * +Coll_Ctrl::unset (char * control) +{ + int len = strlen (control); + if (!strncmp (control, ipc_str_exp_limit, len)) + size_limit = 0; + if (!strncmp (control, ipc_str_time_limit, len)) + { + time_run = 0; + start_delay = 0; + } + if (!strncmp (control, ipc_str_arch_exp, len)) + { + archive_mode = strdup ("on"); + return NULL; + } + if (!strncmp (control, ipc_str_descendant, len)) + { + follow_mode = FOLLOW_NONE; + return NULL; + } + if (!strncmp (control, ipc_str_prof_idle, len)) + { + prof_idle = 1; + return NULL; + } + if (!strncmp (control, ipc_str_clkprof, len)) + { + clkprof_default = 1; + clkprof_enabled = 1; + return NULL; + } + if (!strncmp (control, ipc_str_hwcprof, len)) + { + setup_hwc (); + set_hwcdefault (); + return NULL; + } + if (!strncmp (control, ipc_str_javaprof, len)) + { + java_mode = 0; + java_default = 0; + free (java_path); + java_path = NULL; + free (java_args); + java_args = NULL; + } + if (!strncmp (control, ipc_str_sample, len)) + { + sample_period = 1; + sample_default = 1; + return NULL; + } + if (!strncmp (control, ipc_str_sample_sig, len)) + { + sample_sig = 0; + return NULL; + } + if (!strncmp (control, ipc_str_pause_resume_sig, len)) + { + pauseresume_sig = 0; + return NULL; + } + if (!strncmp (control, ipc_str_synctrace, len)) + { + synctrace_enabled = 0; + synctrace_thresh = -1; + return NULL; + } + if (!strncmp (control, ipc_str_heaptrace, len)) + { + heaptrace_enabled = 0; + return NULL; + } + if (!strncmp (control, ipc_str_iotrace, len)) + { + iotrace_enabled = 0; + return NULL; + } + if (!strncmp (control, ipc_str_count, len)) + { + count_enabled = 0; + Iflag = 0; + Nflag = 0; + return NULL; + } + return strdup (ipc_str_unknown_control); +} + +void +Coll_Ctrl::set_project_home (char *s) +{ + if (s) + project_home = strdup (s); +} diff --git a/gprofng/src/collctrl.h b/gprofng/src/collctrl.h new file mode 100644 index 0000000..6555df7 --- /dev/null +++ b/gprofng/src/collctrl.h @@ -0,0 +1,405 @@ +/* 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. */ + +/* This file describes the data structures used to control + * data collection; it is used by various commands in the MPMT + * tree, and is also shared with dbx. Care should be taken + * to ensure that both the mpmt and dbx builds continue. + + * To remove any APIs or change any enum cases: + * + * 1. Make the changes in mpmt, and preserve the old APIs + * as scaffolding and any old enum values as #defines. + * + * 2. Add the new APIs and new cases to dbx, remove the + * old ones. + * + * 3. Remove the old API scaffolding and enum values here + * + */ + +#ifndef _COLLCTRL_H +#define _COLLCTRL_H + +#include "hwcentry.h" +#include "cc_libcollector.h" + +/*---------------------------------------------------------------------------*/ + +/* class */ + +typedef struct { + int min; + int res; + int max; + int hival; + int normval; + int lowval; +} clk_params_t; + +#define PROFINT_HIGH 997 +#define PROFINT_NORM 10007 +#define PROFINT_LOW 100003 + +#define PROFINT_MIN 500 +#define PROFINT_MAX 1000000 + +class Coll_Ctrl { +public: + + /* _interactive is 1 for dbx, 0 for collect */ + Coll_Ctrl(int _interactive = 0, bool _defHWC = false, bool _kernelHWC = false); + ~Coll_Ctrl(); + + Coll_Ctrl(Coll_Ctrl *cc); /* constructor for duplicate */ + char *check_expt(char **); /* check the experiment directory */ + char *setup_experiment(); /* set up the experiment directory, etc. */ + void close_expt(); + void interrupt(); /* the user interrupts experiment */ + void delete_expt(); + + /* enable/disable the experiment */ + char *enable_expt(); + void disable_expt() { enabled = 0; }; + int isenabled() { return enabled; }; + + /* check for active experiment */ + int isopened() { return opened; }; + + /* set the parameters for clock-profiling */ + void set_clk_params(int min, int res, int max, int hi, int norm, int lo); + char *set_clkprof(const char *valptr, char **warn); + char *reset_clkprof(int val); /* called if profiler must reset value */ + int get_sys_period() { return clk_params.min; }; + int get_clk_min() { return clk_params.min; }; + int get_clk_max() { return clk_params.max; }; + int get_clk_res() { return clk_params.res; }; + int get_clkprof_mode() { return clkprof_enabled; }; + int get_clkprof_timer() { return clkprof_timer; }; + + /* set the parameters for synchronization tracing */ + char *set_synctrace(const char *valptr); + int get_synctrace_mode() { return synctrace_enabled; }; + int get_synctrace_thresh() { return synctrace_thresh; }; + int get_synctrace_scope() { return synctrace_scope; }; + + /* set the parameters for heap tracing */ + char *set_heaptrace(const char *); + int get_heaptrace_mode() { return heaptrace_enabled; }; + int get_heaptrace_checkmode() { return heaptrace_checkenabled; }; + + /* set the parameters for I/O tracing */ + char *set_iotrace(const char *); + int get_iotrace_mode() { return iotrace_enabled; }; + + /* set the parameters for HW counting */ + void setup_hwc(); + char *set_hwcstring(const char *str, char **warn); + char *add_hwcstring(const char *str, char **warn); + char *add_default_hwcstring(const char *str, char **warn, bool add, bool forKernel = false); + void set_hwcdefault(); + void disable_hwc(); + int get_hwc_cnt() { return hwcprof_enabled_cnt; }; + int get_hwc_mode() { return hwcprof_enabled_cnt ? 1 : 0; }; + char *get_hwc_string() { return hwc_string; }; + + Hwcentry * + get_hwc_entry (int n) + { + if (n < 0 || n >= hwcprof_enabled_cnt) + return 0; + return &hwctr[n]; + }; + + void hwcentry_dup (Hwcentry *, Hwcentry *); + char *get_hwc_counter (int n) { return get_hwc_entry (n)->name; }; + + /* set the parameters for count data */ + char *set_count (const char *); + int get_count () { return count_enabled; }; + void set_Iflag () { Iflag = 1; }; + void set_Nflag () { Nflag = 1; }; + + /* set time interval for attach with dbx timed collection */ + /* also used for er_kernel */ + char *set_time_run (const char *); + int get_time_run (void) { return time_run; }; + int get_start_delay (void) { return start_delay; }; + + /* set pid for attach with dbx to collect data */ + char *set_attach_pid (char *); + int get_attach_pid (void) { return attach_pid; }; + + /* set java mode, "on" = yes; "off" = no; anthing else implies + * yes, and is the path to the java to use + * java_mode is returned as zero for off, one for on + * java_default is returned as zero for explicitly set, one for defaulted on + */ + char *set_java_mode (const char *); + int get_java_mode () { return java_mode; }; + int get_java_default () { return java_default; }; + + /* setting Java path explicitly */ + char *set_java_path (const char *); + char *get_java_path () { return java_path; }; + + /* set additional arguments for Java invocation */ + char *set_java_args (char *); + char *get_java_args () { return java_args; }; + int get_java_arg_cnt () { return njava_args; }; + + /* set load-object archive mode, 0 = no; other = yes */ + char *set_archive_mode (const char *); + char *get_archive_mode () { return archive_mode; }; + + /* set follow-descendants mode, 0 = no; other = yes */ + char *set_follow_mode (const char *); + Follow_type get_follow_mode () { return follow_mode; }; + int get_follow_default () { return follow_default; }; + char *get_follow_usr_spec () { return follow_spec_usr; }; + char *get_follow_cmp_spec () { return follow_spec_cmp; }; + + /* set profile idle cpus mode, 1 = no; 0 = yes */ + char *set_prof_idle (const char *); + int get_prof_idle () { return prof_idle; }; + + /* set debug more, 1 = yes; other = no */ + /* if set, target will be set to halt at exit from exec */ + char *set_debug_mode (int); + int get_debug_mode () { return debug_mode; }; + + /* find a signal from a string */ + int find_sig (const char *); + /* find a signal name from a signal value */ + char *find_signal_name (int signal); + + /* set the pauseresume (delayed initialization) signal */ + char *set_pauseresume_signal (int, int); + int get_pauseresume_signal () { return pauseresume_sig; }; + int get_pauseresume_pause () { return pauseresume_pause; }; + + /* set the sample signal */ + char *set_sample_signal (int); + int get_sample_signal () { return sample_sig; }; + + /* set the periodic sampling */ + char *set_sample_period (const char *); + int get_sample_period (void) { return sample_period; }; + + /* set experiment size limit */ + char *set_size_limit (const char *); + int get_size_limit (void) { return size_limit; }; + + /* naming methods */ + /* set the target executable name */ + int set_target (char *); + char *get_target () { return target_name; }; + + /* set the experiment name */ + void set_default_stem (const char *); + char *set_expt (const char *, char **, bool); + char *get_expt () { return expt_name; }; + + /* set the experiment directory */ + char *set_directory (char *, char **); + + char *get_directory () { return udir_name ? udir_name : store_dir; }; + + /* return the real experiment ptr file name */ + char *get_experiment () { return store_ptr; }; + char *update_expt_name (bool verbose = true, bool ckonly = false, bool newname = false); + + /* remove the experiment */ + void remove_exp_dir (); + + /* return the data descriptor */ + char * + get_data_desc () + { + return data_desc; + }; + + /* set the experiment group */ + char *set_group (char *); + char *get_group () { return expt_group; }; + + /* return the experiment settings as a string */ + char *show (int); /* full show */ + char *show_expt (); /* short form */ + + /* return an argv array to compose a "collect" command from settings */ + char **get_collect_args (); + + /* determine characteristics of system */ + char *get_node_name () { return node_name; }; + long get_ncpus () { return ncpus; }; + int get_cpu_clk_freq () { return cpu_clk_freq; }; + int get_cpc_cpuver () { return cpc_cpuver; }; + + /* disable warning about non-local filesystems */ + void set_nofswarn () { nofswarn = 1; }; + + //========== Special functions to communicate with the Collector GUI ==========// + char *get (char *); /* get control's value */ + char *set (char *, const char *); /* set control's value */ + char *unset (char *); /* reset control's value to its default */ + void set_project_home (char *); + +private: + int interactive; /* 1 - dbx, 0 - collect */ + bool defHWC; /* true if default HWC experiment should be run */ + bool kernelHWC; /* T if default HWC counters are for kernel profiling */ + int opened; /* T if an experiment is opened */ + int enabled; /* T if an experiment is enabled */ + volatile int uinterrupt; /* set if interrupt from user */ + + /* experiment/machine characteristics */ + char *node_name; /* name of machine on which experiment is run */ + long ncpus; /* number of online CPUs */ + int cpu_clk_freq; /* chip clock (MHz.), as reported from processor_info */ + int cpc_cpuver; /* chip version, as reported from libcpc */ + long sys_resolution; /* system clock resolution */ + int sys_period; /* profiling clock resolution on the system */ + int sample_period; /* period for sampling, seconds */ + int sample_default; /* if period for sampling set by default */ + int size_limit; /* experiment size limit, MB */ + long npages; /* number of pages configured */ + long page_size; /* size of system page */ + clk_params_t clk_params; + + /* user specification of name */ + /* user may specify both uexpt_name and udir_name + * if uexpt_name is absolute path, udir_name is ignored, with warning + * otherwise, udir_name is prepended to uexpt_name + * + * if uexpt_name is of the form: <dir>/zzzz.nnn.er, where nnn is numeric, + * nnn will be reset to one greater than the the highest experiment + * with a name of the same form. + */ + char *default_stem; /* default stem for experiment name */ + char *uexpt_name; /* suggested experiment name */ + char *expt_name; /* experiment name, after defaulting */ + char *expt_dir; /* directory part of suggested experiment name */ + char *base_name; /* basename of suggested experiment name */ + char *udir_name; /* user name of directory for data */ + + char *store_dir; /* directory to contain experiment dir. */ + char *prev_store_dir; /* previously set store directory */ + char *store_ptr; /* experiment pointer file */ + char *expt_group; /* name of experiment group, if any */ + char *project_home; /* argv[0] */ + + char *target_name; /* target executable name */ + char *data_desc; /* string describing the data to be collected */ + char *lockname; /* name of directory lock file */ + int lockfd; /* fd of open lock file */ + + int nofswarn; /* if 1, don't warn of filesystem */ + int expno; /* number in <stem>.<expno>.er */ + + /* T if an target is to be left for debugger attach */ + int debug_mode; + + /* clock-profiling controls */ + /* T if clock-based profiling */ + int clkprof_enabled; + + /* T if on by default, rather than explicit setting */ + int clkprof_default; + + /* value for timer, microseconds. */ + int clkprof_timer; // adjusted clock profiling interval + int clkprof_timer_target; // desired clock profiling interval + + /* HW counter-profiling controls */ + /* >0 if HW counter-based profiling */ + int hwcprof_default; + int hwcprof_enabled_cnt; + char *hwc_string; + Hwcentry hwctr[MAX_PICS]; + + int synctrace_enabled; /* T if synchronization tracing */ + /* sync trace threshold value, microsec. */ + /* if 0, record all */ + /* if <0, calibrate */ + int synctrace_thresh; + + /* sync trace scope -- a bit mask */ + /* definitions in data_pckts.h */ + int synctrace_scope; + + int heaptrace_enabled; /* T if heap tracing */ + /* if 0 no checking; + * if 1, check for over- and under-write + * if 2, also set patterns in malloc'd and free'd areas + */ + int heaptrace_checkenabled; + int iotrace_enabled; /* T if I/O tracing */ + + /* count controls */ + /* 1 if counting is enabled; -1 if count static is enabled */ + int count_enabled; + int Iflag; /* specify bit output directory -- only with count_enabled */ + int Nflag; /* specify bit library to ignore -- only with count_enabled */ + + /* pid, if -P <pid> is invoked for attach with dbx */ + int attach_pid; + + /* time_run -- non-zero if timed execution request (dbx/er_kernel) */ + int time_run; + int start_delay; + + /* T if Java profiling is requested */ + int java_mode; + int java_default; + char *java_path; + char *java_args; + int njava_args; + + /* whether/how following-descendants is requested */ + Follow_type follow_mode; + int follow_default; + char *follow_spec_usr; // user's selective follow spec + char *follow_spec_cmp; // compiled selective follow spec + int prof_idle; // whether profiling idle cpus is requested + char *archive_mode; // how load-objects archiving is requested + + // signals to control pause-resume (delayed initialization) and samples + int pauseresume_sig; // for pause-resume signal -- delayed initialization + int pauseresume_pause; // 1 if pauseresume and start paused; 0 if not + int sample_sig; // to trigger sample + char *report_signal_conflict (int); + char *check_consistency (); // check the experiment settings for consistency + void determine_profile_params (); + char *preprocess_names (); + char *get_exp_name (const char *); + char *create_exp_dir (); + void build_data_desc (); + char *check_group (); + char *join_group (); + void free_hwc_fields (Hwcentry *tmpctr); + + // propagates clkprof_timer change to Hwcentry hwctr[] + void set_clkprof_timer_target (int microseconds); + void adjust_clkprof_timer (int microseconds); + hrtime_t clkprof_timer_2_hwcentry_min_time (int clkprof_microseconds); +}; + +#endif /* !_COLLCTRL_H */ diff --git a/gprofng/src/collect.h b/gprofng/src/collect.h new file mode 100644 index 0000000..c500dee --- /dev/null +++ b/gprofng/src/collect.h @@ -0,0 +1,156 @@ +/* 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. */ + +#ifndef _COLLECT_H +#define _COLLECT_H + +#include <Application.h> + +extern "C" +{ + typedef void (*SignalHandler)(int); +} + +class Coll_Ctrl; +class Elf; + +#define MAXLABELS 10 /* maximum number of -C arguments */ +#define STDEBUFSIZE 24000 + +enum { MAX_LD_PRELOAD_TYPES = 3 }; + +struct Process +{ + Process (pid_t _pid) : pid (_pid) { } + pid_t pid; +}; + +struct DtraceTool +{ + DtraceTool (char*); + ~DtraceTool (); + + char *name; // Tool name as specified by -D flag + char *params; // Tool parameters + char *dfile; // Extracted d-script + char *mfile; // Extracted metadata file + char *ofile; // Output file + pid_t pid; +}; + +// collect object +class collect : Application +{ +public: + collect (int argc, char *argv[], char **envp); + virtual ~collect (); + void start (int argc, char *argv[]); + void writeStr (int f, const char *buf); + + // the collector control class + Coll_Ctrl *cc; + +private: + enum Exec_status + { + EXEC_OK = 0, // found as runnable executable + EXEC_ELF_NOSHARE, // found, but built unshared + EXEC_IS_JAR, // found as a .jar file + EXEC_IS_CLASS, // found as a .class file but name missing .class + EXEC_IS_CLASSCLASS, // found as a .class file with explicit .class + EXEC_OPEN_FAIL, // could not be opened + EXEC_ELF_LIB, // internal error: bad elf library + EXEC_ELF_HEADER, // executable, with bad ELF header + EXEC_ELF_ARCH, // executable, but unrunnable architecture + EXEC_ISDIR, // a directory, not a file + EXEC_NOT_EXEC, // a file, but not executable + EXEC_NOT_FOUND // a directory, not a file + }; + + // override methods in base class + void usage (); + void short_usage (); + void show_hwc_usage (); + int check_args (int argc, char *argv[]); + void check_target (int, char **); + Exec_status check_executable (char *); + Exec_status check_executable_arch (Elf *); + char *status_str (Exec_status, char *); + int do_flag (const char *); + char *find_java (void); + char *java_path; + char *java_how; + int putenv_libcollector (); + int putenv_libcollector_ld_audits (); + int putenv_libcollector_ld_preloads (); + int putenv_libcollector_ld_misc (); + void add_ld_preload (const char *lib); + int putenv_ld_preloads (); + int putenv_memso (); + int env_strip (char *env, const char *str); + int putenv_purged_ld_preloads (const char *var); + int putenv_append (const char *var, const char *val); + void get_count_data (); + void prepare_dbx (); + int traceme (const char *file, char *const argv[]); + int checkflagterm (const char *); + void dupflagseen (char); + void dupflagseen (const char *); + void validate_config (int); + void validate_java (const char *, const char *, int); + int set_output (); + void reset_output (); + + /* Logging warning messages */ + char **collect_warnings; + int collect_warnings_idx; + void warn_open (); + void warn_close (); + void warn_write (const char *format, ...); + void warn_comment (const char *kind, int num, char *s = NULL, int len = 0); + char *warnfilename; + FILE *warn_file; + + /* MPI experiment handling */ + void setup_MPI_expt (); /* the founder experiment */ + void write_MPI_founder_log (); + void close_MPI_founder_log (int, int); + void spawn_MPI_job (); /* run the MPI job */ + void copy_collect_args (char ***); /* copy collect args for an MPI target */ + int disabled; + int jseen_global; /* if -j flag was seen */ + int verbose; + bool mem_so_me; /* if T, preload mem.so, not libcollector */ + int origargc; + char **arglist; + char **origargv; + char **origenvp; + int targ_index; // index of name of target in origargv + bool is_64; + int nargs; + int njargs; + char *jargs; + int nlabels; + char *label[MAXLABELS]; + char *sp_preload_list[MAX_LD_PRELOAD_TYPES + 1]; // +1 for NULL termination + char *sp_libpath_list[MAX_LD_PRELOAD_TYPES + 1]; // +1 for NULL termination +}; + +#endif /* ! _COLLECT_H */ diff --git a/gprofng/src/collector_module.h b/gprofng/src/collector_module.h new file mode 100644 index 0000000..512af7a --- /dev/null +++ b/gprofng/src/collector_module.h @@ -0,0 +1,223 @@ +/* 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. */ + +#ifndef _COLLECTOR_MODULE_H +#define _COLLECTOR_MODULE_H + +#include <sys/types.h> +#include <stdarg.h> +#include <stdio.h> +#include <unistd.h> +#include <dirent.h> + +#include "gp-defs.h" + +struct stat; +struct tm; + +#define COLLECTOR_MODULE_ERR ((CollectorModule)-1) + +/* ------- libc interface ----------------- */ +/* the fields in this structure are in alphabetical order. + * If you add any, please put it in the right place */ +typedef struct CollectorUtilFuncs +{ + int (*access)(); + int (*atoi)(const char *nptr); + void *(*calloc)(size_t nelem, size_t elsize); + int (*clearenv)(void); + int (*close)(int); + int (*closedir)(); + int (*execv)(const char *path, char *const argv[]); + void (*exit)(int status); + int (*fclose)(FILE *stream); + int (*fcntl)(int fd, int cmd, ...); + char *(*fgets)(char *s, int n, FILE *stream); + FILE *(*fopen)(const char *filename, const char *mode); + pid_t (*vfork)(); + int (*fprintf)(FILE *stream, const char *format, ...); + void (*free)(void *ptr); + int (*fstat)(int fd, struct stat *buf); + int (*getcpuid)(); + char *(*getcwd)(char *buf, size_t size); + char *(*getenv)(const char *name); + struct tm *(*gmtime_r)(const time_t *clock, struct tm *res); + int (*ioctl)(int d, int request, ...); + off_t (*lseek)(int fd, off_t offset, int whence); + void *(*malloc)(size_t size); + void *(*memset)(void *s1, int c, size_t n); + int (*mkdir)(); + time_t (*mktime)(struct tm *timeptr); + void *(*mmap)(void *, size_t, int, int, int, off_t); + void *(*mmap64)(); + int (*munmap)(); + int (*open)(const char *, int, ...); + int (*open_bare)(const char *, int, ...); + DIR *(*opendir)(); + int (*pclose)(FILE *stream); + FILE *(*popen)(const char *command, const char *mode); + int (*putenv)(char *string); + ssize_t (*pwrite)(); + ssize_t (*pwrite64)(); + ssize_t (*read)(); + int (*setenv)(const char *name, const char *value, int overwrite); + int (*sigfillset)(sigset_t *set); + int (*sigprocmask)(int how, const sigset_t *set, sigset_t *oldset); + int (*snprintf)(char *str, size_t size, const char *format, ...); + int (*stack_getbounds)(); + char *(*strchr)(const char *name, int c); + int (*strcmp)(const char *s1, const char *s2); + int (*strcpy)(const char *s1, const char *s2); + char *(*libc_strdup)(const char *s1); // Don't use "strdup" because it is a macro in gcc + char *(*strerror)(int errnum); + int (*strerror_r)(int errnum, char *strerrbuf, size_t buflen); + size_t (*strlcat)(char *dest, const char *src, size_t dstsize); + size_t (*strlcpy)(char *dest, const char *src, size_t dstsize); + size_t (*strlen)(const char *string); + int (*strncmp)(const char *s1, const char *s2, size_t n); + size_t (*strncpy)(char *dst, const char *src, size_t dstsize); + size_t (*strspn)(const char *s1, const char *s2); + char *(*strrchr)(const char *name, int c); + char *(*strstr)(const char *s1, const char *s2); + long int (*strtol)(const char *nptr, char **endptr, int base); + long long int (*strtoll)(const char *nptr, char **endptr, int base); + unsigned long int (*strtoul)(const char *nptr, char **endptr, int base); + unsigned long long int (*strtoull)(const char *nptr, char **endptr, int base); + int (*symlink)(const char *s1, const char *s2); + int (*syscall)(int number, ...); + long (*sysconf)(int name); + long (*sysinfo)(int command, char *buf, long count); + time_t (*time)(time_t *tloc); + int (*unsetenv)(const char *name); + int (*vsnprintf)(char *str, size_t size, const char *format, va_list ap); + pid_t (*waitpid)(pid_t pid, int *stat_loc, int options); + ssize_t (*write)(); + double (*atof)(); + void *n_a; +} CollectorUtilFuncs; + +extern CollectorUtilFuncs __collector_util_funcs; +extern int __collector_dlsym_guard; + +#define CALL_UTIL(x) __collector_util_funcs.x + +/* The following constants define the meaning of the "void *arg" + * argument of getFrameInfo(). + */ +/* arg is a pointer to ucontext_t, walk the stack described by it */ +#define FRINFO_FROM_UC 1 +/* walk the current stack starting from the frame containing arg */ +#define FRINFO_FROM_STACK 2 +/* walk the current stack starting from the caller of the frame containing arg */ +#define FRINFO_FROM_STACK_ARG 3 +/* arg is a pc, process a stack containing just that pc */ +#define FRINFO_FROM_PC 4 +/* arg is of type CM_Array describing a stack image */ +#define FRINFO_FROM_ARRAY 5 +#define FRINFO_NO_OMP_INFO 0x80000000 +#define FRINFO_NO_WALK 0x40000000 + +typedef struct CM_Array +{ + unsigned int length; /* in bytes, not including length */ + void *bytes; +} CM_Array; + +// Interface with libcollector.so: +typedef enum +{ + SP_ORIGIN_FORK = -1, + SP_ORIGIN_LIBCOL_INIT = 0, + SP_ORIGIN_DBX_ATTACH = 1, + SP_ORIGIN_GENEXP = 2, + SP_ORIGIN_KERNEL = 3, + SP_ORIGIN_DTRACE = 4, + SP_ORIGIN_COLLECT = 5 +} sp_origin_t; + +struct Heap; +struct Common_packet; +struct CM_Packet; +struct ModuleInterface; + +typedef long long HiResTime; +typedef int CollectorModule; +typedef unsigned long long FrameInfo; +typedef struct CollectorInterface +{ + /* General services */ + CollectorModule (*registerModule)(struct ModuleInterface*); + const char *(*getParams)(); + const char *(*getExpDir)(); + int (*writeLog)(char *format, ...); + FrameInfo (*getFrameInfo)(CollectorModule modl, HiResTime ts, int mode, void *arg); + FrameInfo (*getUID)(CM_Array *arg); + FrameInfo (*getUID2)(CM_Array *arg, FrameInfo uid); + int (*getStackTrace)(void *buf, int size, void *bptr, void *eptr, void *arg); + int (*writeMetaData)(CollectorModule modl, char *format, ...); + + /* writeDataRecord ensures that the header is filled in, and then calls writeDataPacket */ + int (*writeDataRecord)(CollectorModule modl, struct Common_packet *pckt); + int (*writeDataPacket)(CollectorModule modl, struct CM_Packet *pckt); + void (*write_sample)(char *name); + void (*get_progspec)(char *retstr, int tmp_sz, char *namestr, int name_sz); + int (*open_experiment)(const char *exp, const char *params, sp_origin_t origin); + HiResTime (*getHiResTime)(); + + /* Dynamic memory allocation service */ + struct Heap *(*newHeap)(); + void (*deleteHeap)(struct Heap *heap); + void *(*allocCSize)(struct Heap *heap, unsigned sz, int log); + void (*freeCSize)(struct Heap *heap, void *ptr, unsigned sz); + void *(*allocVSize)(struct Heap *heap, unsigned sz); + void *(*reallocVSize)(struct Heap *heap, void *ptr, unsigned newsz); + + /* Thread specific data service */ + unsigned (*createKey)(size_t sz, void (*init)(void*), void (*fini)(void*)); + void *(*getKey)(unsigned key); + + /* Debugging services */ + void (*writeDebugInfo)(int, int, char *, ...) __attribute__ ((format (printf, 3, 4))); +} CollectorInterface; + +typedef struct ModuleInterface +{ + char *description; + int (*initInterface)(CollectorInterface*); + int (*openExperiment)(const char *); + int (*startDataCollection)(); + int (*stopDataCollection)(); + int (*closeExperiment)(); + int (*detachExperiment)(); /* called from fork-child before openExperiment() */ +} ModuleInterface; + +typedef CollectorModule (*RegModuleFunc)(ModuleInterface*); +typedef void (*ModuleInitFunc)(CollectorInterface*); + +#ifdef __cplusplus +extern "C" +{ +#endif + CollectorModule __collector_register_module (ModuleInterface *modint); +#ifdef __cplusplus +} +#endif + +#endif /* _COLLECTOR_MODULE_H */ diff --git a/gprofng/src/comp_com.c b/gprofng/src/comp_com.c new file mode 100644 index 0000000..486bd1a --- /dev/null +++ b/gprofng/src/comp_com.c @@ -0,0 +1,3481 @@ +/* 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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <locale.h> +#include <values.h> +#include <assert.h> + +#include "comp_com.h" + +/* + * To add a new message _FORMAT_ please perform the following tasks: + * 1) Insert it into the list below, with the matching comment. + * The table is sorted by parameter type. In increasing order + * they are: String, Procedure, Variable, Loop, Region, Integer. + * 2) Insert the corresponding information into the following + * procedures in this file: ccm_num_params(), ccm_paramlist_index(), + * ccm_param_primtype(), and ccm_param_hightype(). + * 3) If you are also creating a new high-type or primitive-type, + * extend the corresponding enum, update this comment and make sure + * to update any code in the analyzer, iropt, cg or ube that depends + * on knowing the limited set of types. + */ + +typedef enum ccm_fmt { + CCMFMT_NONE, /* none */ + CCMFMT_S1, /* s1 */ + CCMFMT_S1S2, /* s1, s2 */ + CCMFMT_S1L2, /* s1, l2 */ + CCMFMT_S1L2VV3, /* s1, l2, v3, v4, ... */ + CCMFMT_S1R2VV3, /* s1, r2, v3, v4, ... */ + CCMFMT_S1X2, /* s1, x2 */ + CCMFMT_P1, /* p1 */ + CCMFMT_P1S2, /* p1, s2 */ + CCMFMT_P1S2P3, /* p1, s2, p3 */ + CCMFMT_P1S2P3I4, /* p1, s2, p3, i4 */ + CCMFMT_P1S2I3, /* p1, s2, i3 */ + CCMFMT_P1P2, /* p1, p2 */ + CCMFMT_P1L2, /* p1, l2 */ + CCMFMT_P1I2, /* p1, i2 */ + CCMFMT_P1I2L3, /* p1, i2, l3 */ + CCMFMT_P1I2LL3, /* p1, i2, l3, l4 ... */ + CCMFMT_P1I2I3, /* p1, i2, i3 */ + CCMFMT_PP1, /* p1, p2, ... */ + CCMFMT_V1, /* v1 */ + CCMFMT_V1V2, /* v1, v2 */ + CCMFMT_V1L2, /* v1, l2 */ + CCMFMT_VV1, /* v1, v2, ... */ + CCMFMT_L1, /* l1 */ + CCMFMT_L1S2, /* l1, s2 */ + CCMFMT_L1S2L3, /* l1, s2, l3 */ + CCMFMT_L1P2, /* l1, p2 */ + CCMFMT_L1P2I3, /* l1, p2, i3 */ + CCMFMT_L1PP2, /* l1, p2, p3, ... */ + CCMFMT_L1VV2, /* l1, v2, v3, ... */ + CCMFMT_L1L2, /* l1, l2 */ + CCMFMT_L1L2L3, /* l1, l2, l3 */ + CCMFMT_LL1, /* l1, l2, ... */ + CCMFMT_L1R2, /* l1, r2 */ + CCMFMT_L1I2, /* l1, i2 */ + CCMFMT_L1I2L3, /* l1, i2, l3 */ + CCMFMT_L1I2LL3, /* l1, i2, l3, l4, ... */ + CCMFMT_L1I2I3L4, /* l1, i2, i3, l4 */ + CCMFMT_L1I2I3I4I5, /* l1, i2, ..., i5 */ + CCMFMT_L1I2I3I4I5I6I7, /* l1, i2, ..., i7 */ + CCMFMT_L1I2I3I4I5I6I7I8I9, /* l1, i2, ..., i9 */ + CCMFMT_L1II2, /* l1, i2, i3, ... */ + CCMFMT_R1, /* r1 */ + CCMFMT_R1VV2, /* r1, v2, v3, ... */ + CCMFMT_I1, /* i1 */ + CCMFMT_I1P2I3, /* i1, p2, i3 */ + CCMFMT_I1V2, /* i1, v2 */ + CCMFMT_I1V2V3, /* i1, v2, v3 */ + CCMFMT_I1L2, /* i1, l2 */ + CCMFMT_I1LL2, /* i1, l2, l3, ... */ + CCMFMT_I1I2I3I4, /* i1, i2, i3, i4 */ + CCMFMT_I1I2I3I4I5I6, /* i1, i2, ..., i6 */ + CCMFMT_I1I2I3I4I5I6I7I8, /* i1, i2, ..., i8 */ + CCMFMT_LAST +} Ccm_Fmttype_t; + +/* + * Low- and high-level types for commentary parameters. + */ + +typedef enum ccm_primtype +{ + CCM_PRIMTYPE_NONE, + CCM_PRIMTYPE_STRING, + CCM_PRIMTYPE_INTEGER, + CCM_PRIMTYPE_HEXSTRING +} Ccm_Primtype_t; + +typedef enum ccm_hightype +{ + CCM_HITYPE_NONE, + CCM_HITYPE_STRING, + CCM_HITYPE_PROCEDURE, + CCM_HITYPE_VARIABLE, + CCM_HITYPE_LOOPTAG, + CCM_HITYPE_REGIONTAG, + CCM_HITYPE_HEXSTRING, + CCM_HITYPE_INTEGER +} Ccm_Hitype_t; + +typedef struct ccm_attrs +{ + char *msg; /* I18N msg string */ + const char *name; /* Print name for this message ID */ + int32_t vis; /* Visibility bits */ + Ccm_Fmttype_t fmt; /* Format type */ +} Ccm_Attr_t; + +static Ccm_Attr_t *ccm_attrs; /* Table of per-msg attributes */ +static nl_catd ccm_catd = (nl_catd) - 1; /* messages id */ + +/* + * map COMPMSG_ID to table indices + */ +static int +ccm_vis_index (COMPMSG_ID m) +{ + int32_t high = m >> 8; + int32_t low = m & 0xFF; + for (int i = 0; i < 24; i++, high >>= 1) + if (high <= 1) + return (i << 8) + low + 1; + return 0; +} + +/* + * Return # parameters for this message; MAXINT for messages with + * parameter lists. + */ +static int +ccm_num_params (COMPMSG_ID m) +{ + int vindex; + int res; + vindex = ccm_vis_index (m); + switch (ccm_attrs[vindex].fmt) + { + case CCMFMT_NONE: + res = 0; + break; + case CCMFMT_S1: + case CCMFMT_P1: + case CCMFMT_V1: + case CCMFMT_L1: + case CCMFMT_R1: + case CCMFMT_I1: + res = 1; + break; + case CCMFMT_S1S2: + case CCMFMT_S1L2: + case CCMFMT_S1X2: + case CCMFMT_P1S2: + case CCMFMT_P1P2: + case CCMFMT_P1L2: + case CCMFMT_P1I2: + case CCMFMT_V1V2: + case CCMFMT_V1L2: + case CCMFMT_L1S2: + case CCMFMT_L1P2: + case CCMFMT_L1L2: + case CCMFMT_L1R2: + case CCMFMT_L1I2: + case CCMFMT_I1V2: + case CCMFMT_I1L2: + res = 2; + break; + case CCMFMT_P1S2P3: + case CCMFMT_P1S2I3: + case CCMFMT_P1I2L3: + case CCMFMT_P1I2I3: + case CCMFMT_L1S2L3: + case CCMFMT_L1P2I3: + case CCMFMT_L1L2L3: + case CCMFMT_L1I2L3: + case CCMFMT_I1P2I3: + case CCMFMT_I1V2V3: + res = 3; + break; + case CCMFMT_P1S2P3I4: + case CCMFMT_L1I2I3L4: + case CCMFMT_I1I2I3I4: + res = 4; + break; + case CCMFMT_L1I2I3I4I5: + res = 5; + break; + case CCMFMT_I1I2I3I4I5I6: + res = 6; + break; + case CCMFMT_L1I2I3I4I5I6I7: + res = 7; + break; + case CCMFMT_I1I2I3I4I5I6I7I8: + res = 8; + break; + case CCMFMT_L1I2I3I4I5I6I7I8I9: + res = 9; + break; + case CCMFMT_S1L2VV3: + case CCMFMT_S1R2VV3: + case CCMFMT_PP1: + case CCMFMT_P1I2LL3: + case CCMFMT_VV1: + case CCMFMT_L1PP2: + case CCMFMT_L1VV2: + case CCMFMT_LL1: + case CCMFMT_L1I2LL3: + case CCMFMT_L1II2: + case CCMFMT_R1VV2: + case CCMFMT_I1LL2: + res = MAXINT; + break; + case CCMFMT_LAST: + default: + /* programming failure */ + /* if(1) is hack to get around warning from C++ compiler */ + if (1) assert (0); + break; + } + return res; +} + +static int +ccm_paramlist_index (COMPMSG_ID m) +{ + int res; + int vindex = ccm_vis_index (m); + switch (ccm_attrs[vindex].fmt) + { + case CCMFMT_NONE: + case CCMFMT_S1: + case CCMFMT_S1S2: + case CCMFMT_S1L2: + case CCMFMT_S1X2: + case CCMFMT_P1: + case CCMFMT_P1S2: + case CCMFMT_P1S2P3: + case CCMFMT_P1S2P3I4: + case CCMFMT_P1S2I3: + case CCMFMT_P1P2: + case CCMFMT_P1L2: + case CCMFMT_P1I2: + case CCMFMT_P1I2L3: + case CCMFMT_P1I2I3: + case CCMFMT_V1: + case CCMFMT_V1V2: + case CCMFMT_V1L2: + case CCMFMT_L1: + case CCMFMT_L1S2: + case CCMFMT_L1S2L3: + case CCMFMT_L1P2: + case CCMFMT_L1P2I3: + case CCMFMT_L1L2: + case CCMFMT_L1L2L3: + case CCMFMT_L1R2: + case CCMFMT_L1I2: + case CCMFMT_L1I2L3: + case CCMFMT_L1I2I3L4: + case CCMFMT_L1I2I3I4I5: + case CCMFMT_L1I2I3I4I5I6I7: + case CCMFMT_L1I2I3I4I5I6I7I8I9: + case CCMFMT_R1: + case CCMFMT_I1: + case CCMFMT_I1P2I3: + case CCMFMT_I1V2: + case CCMFMT_I1V2V3: + case CCMFMT_I1L2: + case CCMFMT_I1I2I3I4: + case CCMFMT_I1I2I3I4I5I6: + case CCMFMT_I1I2I3I4I5I6I7I8: + res = 0; + break; + case CCMFMT_PP1: + case CCMFMT_VV1: + case CCMFMT_LL1: + res = 1; + break; + case CCMFMT_L1PP2: + case CCMFMT_L1VV2: + case CCMFMT_L1II2: + case CCMFMT_R1VV2: + case CCMFMT_I1LL2: + res = 2; + break; + case CCMFMT_S1L2VV3: + case CCMFMT_S1R2VV3: + case CCMFMT_P1I2LL3: + case CCMFMT_L1I2LL3: + res = 3; + break; + case CCMFMT_LAST: + default: + /* programming failure */ + /* if(1) is hack to get around warning from C++ compiler */ + if (1) assert (0); + break; + } + return res; +} + +static Ccm_Primtype_t +ccm_param_primtype (COMPMSG_ID m, int param_idx) +{ + int vindex; + Ccm_Primtype_t res; + if (param_idx <= 0 || param_idx > ccm_num_params (m)) + return CCM_PRIMTYPE_NONE; + + res = CCM_PRIMTYPE_NONE; /* should always be updated */ + vindex = ccm_vis_index (m); + switch (ccm_attrs[vindex].fmt) + { + /* + * Sort cases by: + * 1) # parameters + * 2) Strings before Integers + * 3) Enum tags + */ + case CCMFMT_NONE: + /* programming failure */ + /* if(1) is hack to get around warning from C++ compiler */ + if (1) + assert (0); + break; + case CCMFMT_S1: + case CCMFMT_P1: + case CCMFMT_V1: + case CCMFMT_L1: + case CCMFMT_R1: + if (param_idx == 1) + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_I1: + if (param_idx == 1) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_S1S2: + case CCMFMT_S1L2: + case CCMFMT_P1S2: + case CCMFMT_P1P2: + case CCMFMT_P1L2: + case CCMFMT_V1V2: + case CCMFMT_V1L2: + case CCMFMT_L1S2: + case CCMFMT_L1P2: + case CCMFMT_L1L2: + case CCMFMT_L1R2: + if (param_idx == 1 || param_idx == 2) + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_S1X2: + if (param_idx == 1) + res = CCM_PRIMTYPE_STRING; + else if (param_idx == 2) + res = CCM_PRIMTYPE_HEXSTRING; + break; + case CCMFMT_P1I2: + case CCMFMT_L1I2: + if (param_idx == 1) + res = CCM_PRIMTYPE_STRING; + else if (param_idx == 2) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_I1V2: + case CCMFMT_I1L2: + if (param_idx == 1) + res = CCM_PRIMTYPE_INTEGER; + else if (param_idx == 2) + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_P1S2P3: + case CCMFMT_L1S2L3: + case CCMFMT_L1L2L3: + if (param_idx >= 1 && param_idx <= 3) + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_P1S2I3: + case CCMFMT_L1P2I3: + if (param_idx == 1 || param_idx == 2) + res = CCM_PRIMTYPE_STRING; + else if (param_idx == 3) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_P1I2L3: + case CCMFMT_L1I2L3: + if (param_idx == 1 || param_idx == 3) + res = CCM_PRIMTYPE_STRING; + else if (param_idx == 2) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_P1I2I3: + if (param_idx == 1) + res = CCM_PRIMTYPE_STRING; + else if (param_idx == 2 || param_idx == 3) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_I1V2V3: + if (param_idx == 1) + res = CCM_PRIMTYPE_INTEGER; + else if (param_idx == 2 || param_idx == 3) + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_I1P2I3: + if (param_idx == 1 || param_idx == 3) + res = CCM_PRIMTYPE_INTEGER; + else if (param_idx == 2) + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_L1I2I3L4: + if (param_idx == 1 || param_idx == 4) + res = CCM_PRIMTYPE_STRING; + else if (param_idx == 2 || param_idx == 3) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_P1S2P3I4: + if (param_idx >= 1 && param_idx <= 3) + res = CCM_PRIMTYPE_STRING; + else if (param_idx == 4) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_I1I2I3I4: + if (param_idx >= 1 && param_idx <= 4) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_L1I2I3I4I5: + if (param_idx == 1) + res = CCM_PRIMTYPE_STRING; + else if (param_idx >= 2 && param_idx <= 5) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_I1I2I3I4I5I6: + if (param_idx >= 1 && param_idx <= 6) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_L1I2I3I4I5I6I7: + if (param_idx == 1) + res = CCM_PRIMTYPE_STRING; + else if (param_idx >= 2 && param_idx <= 7) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_I1I2I3I4I5I6I7I8: + if (param_idx >= 1 && param_idx <= 8) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_L1I2I3I4I5I6I7I8I9: + if (param_idx == 1) + res = CCM_PRIMTYPE_STRING; + else if (param_idx >= 2 && param_idx <= 9) + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_S1L2VV3: + case CCMFMT_S1R2VV3: + case CCMFMT_PP1: + case CCMFMT_VV1: + case CCMFMT_L1PP2: + case CCMFMT_L1VV2: + case CCMFMT_LL1: + case CCMFMT_R1VV2: + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_P1I2LL3: + case CCMFMT_L1I2LL3: + if (param_idx == 2) + res = CCM_PRIMTYPE_INTEGER; + else + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_L1II2: + if (param_idx == 1) + res = CCM_PRIMTYPE_STRING; + else + res = CCM_PRIMTYPE_INTEGER; + break; + case CCMFMT_I1LL2: + if (param_idx == 1) + res = CCM_PRIMTYPE_INTEGER; + else + res = CCM_PRIMTYPE_STRING; + break; + case CCMFMT_LAST: + default: + /* programming failure */ + /* if(1) is hack to get around warning from C++ compiler */ + if (1) + assert (0); + break; + } + return res; +} + +static Ccm_Hitype_t +ccm_param_hightype (COMPMSG_ID m, int param_idx) +{ + int vindex; + Ccm_Hitype_t res; + + if (param_idx <= 0 || param_idx > ccm_num_params (m)) + return CCM_HITYPE_NONE; + res = CCM_HITYPE_NONE; /* should always be updated */ + vindex = ccm_vis_index (m); + switch (ccm_attrs[vindex].fmt) + { + case CCMFMT_NONE: + /* programming failure */ + /* if(1) is hack to get around warning from C++ compiler */ + if (1) + assert (0); + break; + case CCMFMT_S1: + if (param_idx == 1) + res = CCM_HITYPE_STRING; + break; + case CCMFMT_S1S2: + if (param_idx == 1 || param_idx == 2) + res = CCM_HITYPE_STRING; + break; + case CCMFMT_S1L2: + if (param_idx == 1) + res = CCM_HITYPE_STRING; + else if (param_idx == 2) + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_S1L2VV3: + if (param_idx == 1) + res = CCM_HITYPE_STRING; + else if (param_idx == 2) + res = CCM_HITYPE_LOOPTAG; + else + res = CCM_HITYPE_STRING; + break; + case CCMFMT_S1R2VV3: + if (param_idx == 1) + res = CCM_HITYPE_STRING; + else if (param_idx == 2) + res = CCM_HITYPE_REGIONTAG; + else + res = CCM_HITYPE_VARIABLE; + break; + case CCMFMT_S1X2: + if (param_idx == 1) + res = CCM_HITYPE_STRING; + else if (param_idx == 2) + res = CCM_HITYPE_HEXSTRING; + break; + case CCMFMT_P1: + if (param_idx == 1) + res = CCM_HITYPE_PROCEDURE; + break; + case CCMFMT_P1S2: + if (param_idx == 1) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2) + res = CCM_HITYPE_STRING; + break; + case CCMFMT_P1S2P3: + if (param_idx == 1 || param_idx == 3) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2) + res = CCM_HITYPE_STRING; + break; + case CCMFMT_P1S2P3I4: + if (param_idx == 1 || param_idx == 3) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2) + res = CCM_HITYPE_STRING; + else if (param_idx == 4) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_P1S2I3: + if (param_idx == 1) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2) + res = CCM_HITYPE_STRING; + else if (param_idx == 3) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_P1P2: + if (param_idx == 1 || param_idx == 2) + res = CCM_HITYPE_PROCEDURE; + break; + case CCMFMT_P1L2: + if (param_idx == 1) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2) + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_P1I2: + if (param_idx == 1) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_P1I2L3: + if (param_idx == 1) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2) + res = CCM_HITYPE_INTEGER; + else if (param_idx == 3) + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_P1I2I3: + if (param_idx == 1) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2 || param_idx == 3) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_P1I2LL3: + if (param_idx == 1) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 2) + res = CCM_HITYPE_INTEGER; + else + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_PP1: + res = CCM_HITYPE_PROCEDURE; + break; + case CCMFMT_V1: + if (param_idx == 1) + res = CCM_HITYPE_VARIABLE; + break; + case CCMFMT_V1V2: + if (param_idx == 1 || param_idx == 2) + res = CCM_HITYPE_VARIABLE; + break; + case CCMFMT_V1L2: + if (param_idx == 1) + res = CCM_HITYPE_VARIABLE; + else if (param_idx == 2) + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_VV1: + res = CCM_HITYPE_VARIABLE; + break; + case CCMFMT_L1: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_L1S2: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx == 2) + res = CCM_HITYPE_STRING; + break; + case CCMFMT_L1S2L3: + if (param_idx == 1 || param_idx == 3) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx == 2) + res = CCM_HITYPE_STRING; + break; + case CCMFMT_L1P2: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx == 2) + res = CCM_HITYPE_PROCEDURE; + break; + case CCMFMT_L1P2I3: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx == 2) + res = CCM_HITYPE_PROCEDURE; + else if (param_idx == 3) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_L1PP2: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else + res = CCM_HITYPE_PROCEDURE; + break; + case CCMFMT_L1VV2: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else + res = CCM_HITYPE_VARIABLE; + break; + case CCMFMT_L1L2: + if (param_idx == 1 || param_idx == 2) + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_L1L2L3: + if (param_idx >= 1 && param_idx <= 3) + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_LL1: + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_L1R2: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx == 2) + res = CCM_HITYPE_REGIONTAG; + break; + case CCMFMT_L1I2: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx == 2) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_L1I2L3: + if (param_idx == 1 || param_idx == 3) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx == 2) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_L1I2LL3: + if (param_idx == 2) + res = CCM_HITYPE_INTEGER; + else + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_L1I2I3L4: + if (param_idx == 1 || param_idx == 4) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx == 2 || param_idx == 3) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_L1I2I3I4I5: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx >= 2 && param_idx <= 5) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_L1I2I3I4I5I6I7: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx >= 2 && param_idx <= 7) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_L1I2I3I4I5I6I7I8I9: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else if (param_idx >= 2 && param_idx <= 9) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_L1II2: + if (param_idx == 1) + res = CCM_HITYPE_LOOPTAG; + else + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_R1: + if (param_idx == 1) + res = CCM_HITYPE_REGIONTAG; + break; + case CCMFMT_R1VV2: + if (param_idx == 1) + res = CCM_HITYPE_REGIONTAG; + else + res = CCM_HITYPE_VARIABLE; + break; + case CCMFMT_I1: + if (param_idx == 1) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_I1P2I3: + if (param_idx == 1 || param_idx == 3) + res = CCM_HITYPE_INTEGER; + else if (param_idx == 2) + res = CCM_HITYPE_PROCEDURE; + break; + case CCMFMT_I1V2: + if (param_idx == 1) + res = CCM_HITYPE_INTEGER; + else if (param_idx == 2) + res = CCM_HITYPE_VARIABLE; + break; + case CCMFMT_I1V2V3: + if (param_idx == 1) + res = CCM_HITYPE_INTEGER; + else if (param_idx == 2 || param_idx == 3) + res = CCM_HITYPE_VARIABLE; + break; + case CCMFMT_I1L2: + if (param_idx == 1) + res = CCM_HITYPE_INTEGER; + else if (param_idx == 2) + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_I1LL2: + if (param_idx == 1) + res = CCM_HITYPE_INTEGER; + else + res = CCM_HITYPE_LOOPTAG; + break; + case CCMFMT_I1I2I3I4: + if (param_idx >= 1 && param_idx <= 4) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_I1I2I3I4I5I6: + if (param_idx >= 1 && param_idx <= 6) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_I1I2I3I4I5I6I7I8: + if (param_idx >= 1 && param_idx <= 8) + res = CCM_HITYPE_INTEGER; + break; + case CCMFMT_LAST: + default: + /* programming failure */ + /* if(1) is hack to get around warning from C++ compiler */ + if (1) + assert (0); + break; + } + return res; +} + +static void +ccm_vis_init () +{ + int size, vindex; + static int done = 0; + if (done) + return; + done = 1; + size = ccm_vis_index ((COMPMSG_ID) (CCMV_BASIC << 8)); + ccm_attrs = (Ccm_Attr_t *) calloc (size, sizeof (Ccm_Attr_t)); + if (ccm_attrs == NULL) + exit (1); + vindex = ccm_vis_index (CCM_MODDATE); + ccm_attrs[vindex].vis = CCMV_VER | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MODDATE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Source file %s, last modified on date %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1S2; + + vindex = ccm_vis_index (CCM_COMPVER); + ccm_attrs[vindex].vis = CCMV_VER | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_COMPVER"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Component %s, version %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1S2; + + vindex = ccm_vis_index (CCM_COMPDATE); + ccm_attrs[vindex].vis = CCMV_VER | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_COMPDATE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Compilation date %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_COMPOPT); + ccm_attrs[vindex].vis = CCMV_VER | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_COMPOPT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Compilation options %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_ACOMPOPT); + ccm_attrs[vindex].vis = CCMV_VER | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_ACOMPOPT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Actual Compilation options %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_VAR_ALIAS); + ccm_attrs[vindex].vis = CCMV_WARN | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_VAR_ALIAS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variable %s aliased to %s"); + ccm_attrs[vindex].fmt = CCMFMT_V1V2; + + vindex = ccm_vis_index (CCM_FBIRDIFF); + ccm_attrs[vindex].vis = CCMV_WARN | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_FBIRDIFF"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Profile feedback data inconsistent with" + " intermediate representation file; check compiler" + " version, flags and source file"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_OPTRED_SWAP); + ccm_attrs[vindex].vis = CCMV_WARN | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_OPTRED_SWAP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Optimization level for %s reduced from %d to" + " %d due to insufficient swap space"); + ccm_attrs[vindex].fmt = CCMFMT_P1I2I3; + + vindex = ccm_vis_index (CCM_OPTRED_CPLX); + ccm_attrs[vindex].vis = CCMV_WARN | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_OPTRED_CPLX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Optimization level for %s reduced from %d to" + " %d due to program complexity"); + ccm_attrs[vindex].fmt = CCMFMT_P1I2I3; + + vindex = ccm_vis_index (CCM_UNKNOWN); + ccm_attrs[vindex].vis = CCMV_WARN | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNKNOWN"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Unexpected compiler comment %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_UNPAR_CALL); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_CALL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because it contains a" + " call to %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_PAR_SER); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PAR_SER"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Both serial and parallel versions generated for" + " loop below"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_PAR_SER_VER); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PAR_SER_VER"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Both serial and parallel versions generated for" + " loop below; with parallel version used if %s," + " serial otherwise"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_PAR_DRECTV); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PAR_DRECTV"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below parallelized by explicit user" + " directive"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_APAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_APAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below autoparallelized"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_AUTOPAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_AUTOPAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below autoparallelized; equivalent" + " explict directive is %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_UNPAR_DD); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_DD"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below could not be parallelized because of a" + " data dependency on %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_UNPAR_DDA); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_DDA"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below could not be parallelized because of a" + " data dependency or aliasing of %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_UNPAR_ANONDD); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_ANONDD"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below could not be parallelized because of" + " an anonymous data dependency"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_ANONDDA); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_ANONDDA"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below could not be parallelized because of" + " an anonymous data dependency or aliasing"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_PAR_WORK); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PAR_WORK"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below parallelized, but might not contain" + " enough work to be efficiently run in parallel"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_EXIT); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_EXIT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because it contains" + " multiple exit points"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_STRNG); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_STRNG"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because it contains a" + " strange flow of control"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_IO); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_IO"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because it contains" + " I/O or other MT-unsafe calls"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_PAR_BODY_NAME); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PAR_BODY_NAME"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Parallel loop-body code is in function %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_UNPAR_NLOOPIDX); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_NLOOPIDX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because loop index" + " not found"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_DRECTV); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_DRECTV"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because of explicit" + " user directive"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_NOTPROFIT); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_NOTPROFIT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because it was not" + " profitable to do so"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_NEST); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_NEST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because it was" + " nested in a parallel loop"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_NOAUTO); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_NOAUTO"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because" + " autoparallelization is not enabled"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_PR_L_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PR_L_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Private variables in loop below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_SH_L_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_SH_L_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Shared variables in loop below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_TP_L_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_TP_L_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Threadprivate variables in loop below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_RV_L_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_RV_L_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Reduction variables in loop below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_IM_L_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_IM_L_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Implicit variables in loop below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_PR_O_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PR_O_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Private variables in OpenMP construct below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_SH_O_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_SH_O_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Shared variables in OpenMP construct below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_TP_O_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_TP_O_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Threadprivate variables in OpenMP construct" + " below: %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_RV_O_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_RV_O_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Reduction variables in OpenMP construct below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_IM_O_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_IM_O_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Implicit variables in OpenMP construct below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_UNPAR_IN_OMP); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_IN_OMP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below not parallelized because it is inside" + " an OpenMP region"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_FP_O_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_FP_O_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Firstprivate variables in OpenMP construct below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_LP_O_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LP_O_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Lastprivate variables in OpenMP construct below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_CP_O_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_CP_O_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Copyprivate variables in OpenMP construct below:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_PR_OAS_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PR_OAS_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as PRIVATE in OpenMP" + " construct below: %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_SH_OAS_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_SH_OAS_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as SHARED in OpenMP" + " construct below: %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_FP_OAS_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_FP_OAS_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as FIRSTPRIVATE in OpenMP" + " construct below: %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_LP_OAS_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LP_OAS_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as LASTPRIVATE in OpenMP" + " construct below: %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_RV_OAS_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_RV_OAS_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as REDUCTION in OpenMP" + " construct below: %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_FAIL_OAS_VAR); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_WARN | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_FAIL_OAS_VAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables cannot be autoscoped in OpenMP" + " construct below: %s"); + ccm_attrs[vindex].fmt = CCMFMT_VV1; + + vindex = ccm_vis_index (CCM_SERIALIZE_OAS); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_WARN | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_SERIALIZE_OAS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "OpenMP parallel region below is serialized" + " because autoscoping has failed"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_UNPAR_CALL_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_CALL_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because it contains calls" + " to: %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1PP2; + + vindex = ccm_vis_index (CCM_PAR_DRECTV_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PAR_DRECTV_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s parallelized by explicit user directive"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_APAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_APAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s autoparallelized"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_AUTOPAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_AUTOPAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s autoparallelized; equivalent" + " explict directive is %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1S2; + + vindex = ccm_vis_index (CCM_UNPAR_DD_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_DD_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be parallelized because of" + " data dependences on: %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1VV2; + + vindex = ccm_vis_index (CCM_UNPAR_DDA_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_DDA_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be parallelized because of a" + " data dependence or aliasing of: %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1VV2; + + vindex = ccm_vis_index (CCM_UNPAR_ANONDD_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_ANONDD_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be parallelized because of an" + " anonymous data dependence"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_ANONDDA_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_ANONDDA_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be parallelized because of an" + " anonymous data dependence or aliasing"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_PAR_WORK_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PAR_WORK_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s parallelized, but might not contain" + " enough work to run efficiently in parallel"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_EXIT_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_EXIT_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because it contains" + " multiple exit points"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_STRANGE_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_STRANGE_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because it contains a" + " strange flow of control"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_IO_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_IO_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because it contains" + " I/O or other MT-unsafe calls"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_PAR_BODY_NAME_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP; + ccm_attrs[vindex].name = "CCM_PAR_BODY_NAME_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s parallel loop-body code placed in" + " function %s along with %d inner loops"); + ccm_attrs[vindex].fmt = CCMFMT_L1P2I3; + + vindex = ccm_vis_index (CCM_UNPAR_NLOOPIDX_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_NLOOPIDX_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because loop index not" + " found"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_DRECTV_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_DRECTV_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because of explicit" + " user directive"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_NOTPROFIT_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_NOTPROFIT_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because it was not" + " profitable to do so"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_NEST_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_NEST_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because it was" + " nested within a parallel loop"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_UNPAR_NOAUTO_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNPAR_NOAUTO_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because" + " autoparallelization is not enabled"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_PR_L_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PR_L_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Private variables in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1VV2; + + vindex = ccm_vis_index (CCM_SH_L_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_SH_L_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Shared variables in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1VV2; + + vindex = ccm_vis_index (CCM_TP_L_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_TP_L_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Threadprivate variables in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1VV2; + + vindex = ccm_vis_index (CCM_RV_L_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_RV_L_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Reduction variables of operator %s in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1L2VV3; + + vindex = ccm_vis_index (CCM_IM_L_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_IM_L_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Implicit variables in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1VV2; + + vindex = ccm_vis_index (CCM_PR_O_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PR_O_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Private variables in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_SH_O_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_SH_O_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Shared variables in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_TP_O_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_TP_O_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Threadprivate variables in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_RV_O_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_RV_O_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Reduction variables of operator %s in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1R2VV3; + + vindex = ccm_vis_index (CCM_IM_O_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_IM_O_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Implicit variables in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_UNPAR_IN_OMP_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNPAR_IN_OMP_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s not parallelized because it is inside" + " OpenMP region %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1R2; + + vindex = ccm_vis_index (CCM_FP_O_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_FP_O_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Firstprivate variables in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_LP_O_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LP_O_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Lastprivate variables in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_CP_O_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_CP_O_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Copyprivate variables in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_PR_OAS_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PR_OAS_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as PRIVATE in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_SH_OAS_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_SH_OAS_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as SHARED in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_FP_OAS_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_FP_OAS_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as FIRSTPRIVATE in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_LP_OAS_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LP_OAS_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as LASTPRIVATE in %s:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_RV_OAS_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_RV_OAS_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables autoscoped as REDUCTION of operator" + " %s in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1R2VV3; + + vindex = ccm_vis_index (CCM_FAIL_OAS_VAR_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_WARN; + ccm_attrs[vindex].name = "CCM_FAIL_OAS_VAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variables treated as shared because they cannot" + " be autoscoped in %s: %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1VV2; + + vindex = ccm_vis_index (CCM_SERIALIZE_OAS_2); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC | CCMV_WARN; + ccm_attrs[vindex].name = "CCM_SERIALIZE_OAS_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s will be executed by a single thread because" + " autoscoping for some variables was not successful"); + ccm_attrs[vindex].fmt = CCMFMT_R1; + + vindex = ccm_vis_index (CCM_QPERMVEC); + ccm_attrs[vindex].vis = CCMV_QUERY | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_QPERMVEC"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Is %s a permutation vector during execution of" + " %s?"); + ccm_attrs[vindex].fmt = CCMFMT_V1L2; + + vindex = ccm_vis_index (CCM_QEXPR); + ccm_attrs[vindex].vis = CCMV_QUERY | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_QEXPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Is expression %s true for %s?"); + ccm_attrs[vindex].fmt = CCMFMT_S1L2; + + vindex = ccm_vis_index (CCM_QSAFECALL); + ccm_attrs[vindex].vis = CCMV_QUERY | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_QSAFECALL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Is subroutine %s MP-safe as used in %s?"); + ccm_attrs[vindex].fmt = CCMFMT_P1L2; + + vindex = ccm_vis_index (CCM_LCOST); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LCOST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below estimated to cost %d cycles per" + " iteration"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_UNROLL); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_UNROLL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below unrolled %d times"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_IMIX); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_IMIX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below has %d loads, %d stores," + " %d prefetches, %d FPadds, %d FPmuls, and" + " %d FPdivs per iteration"); + ccm_attrs[vindex].fmt = CCMFMT_I1I2I3I4I5I6; + + vindex = ccm_vis_index (CCM_SPILLS); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_UNIMPL | CCMV_WANT | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_SPILLS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below required %d integer register spills," + " %d FP register spills, and used" + " %d integer registers and %d FP registers"); + ccm_attrs[vindex].fmt = CCMFMT_I1I2I3I4; + + vindex = ccm_vis_index (CCM_LFISSION); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LFISSION"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below fissioned into %d loops"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_LPEEL); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LPEEL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below had iterations peeled off for better" + " unrolling and/or parallelization"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_LBLOCKED); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LBLOCKED"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below blocked by %d for improved cache" + " performance"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_LTILED); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LTILED"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below tiled for better performance"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_LUNRJAM); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LUNRJAM"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below unrolled and jammed"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_LWHILE2DO); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LWHILE2DO"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Bounds test for loop below moved to top of loop"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_L2CALL); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_L2CALL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below replaced by a call to %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_LDEAD); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LDEAD"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below deleted as dead code"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_LINTRCHNG); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LINTRCHNG"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below interchanged with loop on line %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_FUSEDTO); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_FUSEDTO"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below fused with loop on line %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_FUSEDFROM); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_FUSEDFROM"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop from line %d fused with loop below"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_VECINTRNSC); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_VECINTRNSC"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below transformed to use calls to vector" + " intrinsic %s"); + ccm_attrs[vindex].fmt = CCMFMT_PP1; + + vindex = ccm_vis_index (CCM_LSTRIPMINE); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LSTRIPMINE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below strip-mined"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_LNEST2LOOPS); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LNEST2LOOPS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below collapsed with loop on line %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_LREVERSE); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LREVERSE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below has had its iteration direction" + " reversed"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_IMIX2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_IMIX2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below has %d loads, %d stores," + " %d prefetches, %d FPadds, %d FPmuls," + " %d FPdivs, %d FPsubs, and %d FPsqrts per" + " iteration"); + ccm_attrs[vindex].fmt = CCMFMT_I1I2I3I4I5I6I7I8; + + vindex = ccm_vis_index (CCM_LUNRFULL); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_LUNRFULL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below fully unrolled"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_ELIM_NOAMORTINST); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_ELIM_NOAMORTINST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below was eliminated as it contains no" + " non-amortizable instructions"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_COMP_DALIGN); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_COMP_DALIGN"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Performance of loop below could be improved" + " by compiling with -dalign"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_INTIMIX); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_INTIMIX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below has %d int-loads, %d int-stores," + " %d alu-ops, %d muls, %d int-divs and" + " %d shifts per iteration"); + ccm_attrs[vindex].fmt = CCMFMT_I1I2I3I4I5I6; + + vindex = ccm_vis_index (CCM_LMULTI_VERSION); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LMULTI_VERSION"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s multi-versioned. Specialized version" + " is %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1L2; + + vindex = ccm_vis_index (CCM_LCOST_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LCOST_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s estimated to cost %d cycles per iteration"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_UNROLL_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_UNROLL_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s unrolled %d times"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_IMIX_B); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_IMIX_B"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s has %d loads, %d stores," + " %d prefetches, %d FPadds, %d FPmuls, and" + " %d FPdivs per iteration"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2I3I4I5I6I7; + + vindex = ccm_vis_index (CCM_SPILLS_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_SPILLS_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s required %d integer register spills," + " %d FP register spills, and used" + " %d integer registers and %d FP registers"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2I3I4I5; + + vindex = ccm_vis_index (CCM_LFISSION_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LFISSION_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s fissioned into %d loops, generating:" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2LL3; + + vindex = ccm_vis_index (CCM_LFISSION_FRAG); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LFISSION_FRAG"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s contains code from lines: %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1II2; + + vindex = ccm_vis_index (CCM_LPEEL_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LPEEL_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s had iterations peeled off for better" + " unrolling and/or parallelization"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_LBLOCKED_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LBLOCKED_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s blocked by %d for improved memory" + " hierarchy performance, new inner loop %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2L3; + + vindex = ccm_vis_index (CCM_LOUTER_UNROLL); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LOUTER_UNROLL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s is outer-unrolled %d times as part" + " of unroll and jam"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_LJAMMED); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LJAMMED"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "All %d copies of %s are fused together" + " as part of unroll and jam"); + ccm_attrs[vindex].fmt = CCMFMT_I1L2; + + vindex = ccm_vis_index (CCM_LWHILE2DO_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LWHILE2DO_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Bounds test for %s moved to top of loop"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_L2CALL_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_L2CALL_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s replaced by a call to %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1P2; + + vindex = ccm_vis_index (CCM_LDEAD_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LDEAD_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s deleted as dead code"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_LINTRCHNG_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LINTRCHNG_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s interchanged with %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1L2; + + vindex = ccm_vis_index (CCM_LINTRCHNG_ORDER); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LINTRCHNG_ORDER"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "For loop nest below, the final order of loops" + " after interchanging and subsequent" + " transformations is: %s"); + ccm_attrs[vindex].fmt = CCMFMT_LL1; + + vindex = ccm_vis_index (CCM_FUSED_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_FUSED_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s fused with %s, new loop %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1L2L3; + + vindex = ccm_vis_index (CCM_VECINTRNSC_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_VECINTRNSC_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s transformed to use calls to vector" + " intrinsics: %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1PP2; + + vindex = ccm_vis_index (CCM_LSTRIPMINE_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LSTRIPMINE_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s strip-mined by %d, new inner loop %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2L3; + + vindex = ccm_vis_index (CCM_LNEST2LOOPS_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LNEST2LOOPS_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s collapsed with %s, new loop %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1L2L3; + + vindex = ccm_vis_index (CCM_LREVERSE_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LREVERSE_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s has had its iteration direction reversed"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_IMIX2_B); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_IMIX2_B"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s has %d loads, %d stores," + " %d prefetches, %d FPadds, %d FPmuls," + " %d FPdivs, %d FPsubs, and %d FPsqrts per" + " iteration"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2I3I4I5I6I7I8I9; + + vindex = ccm_vis_index (CCM_LUNRFULL_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LUNRFULL_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s fully unrolled"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_ELIM_NOAMORTINST_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_ELIM_NOAMORTINST_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s was eliminated as it contains no" + " non-amortizable instructions"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_COMP_DALIGN_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_COMP_DALIGN_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Performance of %s could be improved by" + " compiling with -dalign"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_INTIMIX_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_INTIMIX_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s has %d int-loads, %d int-stores," + " %d alu-ops, %d muls, %d int-divs and" + " %d shifts per iteration"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2I3I4I5I6I7; + + vindex = ccm_vis_index (CCM_OMP_REGION); + ccm_attrs[vindex].vis = CCMV_PAR | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_OMP_REGION"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Source OpenMP region below has tag %s"); + ccm_attrs[vindex].fmt = CCMFMT_R1; + + vindex = ccm_vis_index (CCM_LMICROVECTORIZE); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LMICROVECTORIZE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s is micro-vectorized"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_LMULTI_VERSION_2); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LMULTI_VERSION_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s multi-versioned for %s." + " Specialized version is %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1S2L3; + + vindex = ccm_vis_index (CCM_LCLONED); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LCLONED"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s cloned for %s. Clone is %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1S2L3; + + vindex = ccm_vis_index (CCM_LUNSWITCHED); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LUNSWITCHED"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s is unswitched. New loops" + " are %s and %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1L2L3; + + vindex = ccm_vis_index (CCM_LRESWITCHED); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LRESWITCHED"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loops %s and %s and their surrounding" + " conditional code have been merged to" + " form loop %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1L2L3; + + vindex = ccm_vis_index (CCM_LSKEWBLOCKED); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LSKEWBLOCKED"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s skew-blocked by %d with slope" + " %d for improved memory hierarchy" + " performance, new inner loop %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2I3L4; + + vindex = ccm_vis_index (CCM_IVSUB); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_IVSUB"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Induction variable substitution performed on %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_ONEITER_REPLACED); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_ONEITER_REPLACED"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s determined to have a trip count of 1;" + " converted to straight-line code"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_IMIX3_B); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_IMIX3_B"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s has %d loads, %d stores," + " %d prefetches, %d FPadds, %d FPmuls," + " %d FPmuladds, %d FPdivs, and %d FPsqrts per" + " iteration"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2I3I4I5I6I7I8I9; + + vindex = ccm_vis_index (CCM_PIPELINE); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PIPELINE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below pipelined"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_PIPESTATS); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PIPESTATS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below scheduled with steady-state cycle" + " count = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_NOPIPE_CALL); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_CALL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it contains" + " calls"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_INTCC); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_INTCC"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it sets" + " multiple integer condition codes."); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_MBAR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_MBAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it contains a" + " memory barrier instruction"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_MNMX); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_MNMX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it contains" + " a minimum or a maximum operation"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_U2FLT); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_U2FLT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it contains" + " an unsigned to float conversion"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_GOT); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_GOT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it sets the" + " Global Offset Table pointer"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_IDIV); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_IDIV"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it contains" + " an integer divide"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_PRFTCH); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_PRFTCH"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it contains" + " a prefetch operation"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_EXIT); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_EXIT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it contains" + " an exit operation"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_REG); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_REG"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it contains" + " instructions that set the %%gsr or %%fsr register"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_UNS); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_UNS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it has an" + " unsigned loop counter"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_UNSUIT); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_UNSUIT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop was unsuitable for pipelining"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_INTRINSIC); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_INTRINSIC"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined because it has an" + " intrinsic call to %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NOPIPE_BIG); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_BIG"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined as it is too big"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NOPIPE_INVINTPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_INVINTPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined as it contains too" + " many loop invariant integers = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_NOPIPE_INVFLTPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_INVFLTPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined as it contains too" + " many loop invariant floats = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_NOPIPE_INVDBLPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_INVDBLPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined as it contains too" + " many loop invariant doubles = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_PIPE_SCHEDAFIPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PIPE_SCHEDAFIPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below was adversely affected by high" + " integer register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_PIPE_SCHEDAFDPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PIPE_SCHEDAFDPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below was adversely affected by high" + " double register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_PIPE_SCHEDAFFPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_PIPE_SCHEDAFFPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop below was adversely affected by high" + " float register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_NOPIPE_INTPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_INTPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined due to high" + " integer register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_NOPIPE_DBLPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_DBLPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined due to high" + " double register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_NOPIPE_FLTPR); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_NOPIPE_FLTPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop could not be pipelined due to high" + " float register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_PIPELINE_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PIPELINE_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s pipelined"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_PIPESTATS_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PIPESTATS_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s scheduled with steady-state cycle" + " count = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_NOPIPE_CALL_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_CALL_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " calls"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_INTCC_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_INTCC_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it sets" + " multiple integer condition codes."); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_MBAR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_MBAR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " a memory barrier instruction"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_MNMX_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_MNMX_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " a minimum or a maximum operation"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_U2FLT_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_U2FLT_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " an unsigned to float conversion"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_GOT_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP; + ccm_attrs[vindex].name = "CCM_NOPIPE_GOT_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it sets the" + " Global Offset Table pointer"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_IDIV_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_IDIV_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " an integer divide"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_PRFTCH_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_PRFTCH_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " a prefetch operation"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_EXIT_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_EXIT_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " an exit operation"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_REG_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP; + ccm_attrs[vindex].name = "CCM_NOPIPE_REG_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " instructions that set the %%gsr or %%fsr register"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_UNS_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_UNS_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it has an" + " unsigned loop counter"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_UNSUIT_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_UNSUIT_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s is unsuitable for pipelining"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_INTRINSIC_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_INTRINSIC_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined because it contains" + " a call to intrinsic %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1P2; + + vindex = ccm_vis_index (CCM_NOPIPE_BIG_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_BIG_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined as it is too big"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_NOPIPE_INVINTPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_INVINTPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined as it contains too" + " many loop invariant integers = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_NOPIPE_INVFLTPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_INVFLTPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined as it contains too" + " many loop invariant floats = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_NOPIPE_INVDBLPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_INVDBLPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined as it contains too" + " many loop invariant doubles = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_PIPE_SCHEDAFIPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PIPE_SCHEDAFIPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s was adversely affected by high" + " integer register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_PIPE_SCHEDAFDPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PIPE_SCHEDAFDPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s was adversely affected by high" + " double register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_PIPE_SCHEDAFFPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PIPE_SCHEDAFFPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s was adversely affected by high" + " float register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_NOPIPE_INTPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_INTPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined due to high" + " integer register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_NOPIPE_DBLPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_DBLPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined due to high" + " double register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_NOPIPE_FLTPR_2); + ccm_attrs[vindex].vis = CCMV_PIPE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NOPIPE_FLTPR_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "%s could not be pipelined due to high" + " float register pressure = %d"); + ccm_attrs[vindex].fmt = CCMFMT_L1I2; + + vindex = ccm_vis_index (CCM_INLINE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_INLINE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s inlined from source file %s into" + " the code for the following line"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2; + + vindex = ccm_vis_index (CCM_INLINE2); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_INLINE2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s inlined from source file %s into" + " inline copy of function %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2P3; + + vindex = ccm_vis_index (CCM_INLINE_TMPLT); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_INLINE_TMPLT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s inlined from template file %s" + " into the code for the following line"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2; + + vindex = ccm_vis_index (CCM_INLINE_TMPLT2); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_INLINE_TMPLT2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s inlined from template file %s" + " into inline copy of function %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2P3; + + vindex = ccm_vis_index (CCM_INLINE_OUT_COPY); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_INLINE_OUT_COPY"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Out-of-line copy of inlined function %s from" + " source file %s generated"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2; + + vindex = ccm_vis_index (CCM_NINLINE_REC); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_REC"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Recursive function %s inlined only up to" + " depth %d"); + ccm_attrs[vindex].fmt = CCMFMT_P1I2; + + vindex = ccm_vis_index (CCM_NINLINE_NEST); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_NEST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because inlining is" + " already nested too deeply"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CMPLX); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CMPLX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it contains" + " too many operations"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_FB); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_FB"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because the" + " profile-feedback execution count is too low"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_PAR); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_PAR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it contains" + " explicit parallel pragmas"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_OPT); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_OPT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it is" + " compiled with optimization level <= 2"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_USR); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_USR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because either command" + " line option or source code pragma prohibited it," + " or it's not safe to inline it"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_AUTO); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_AUTO"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because doing so" + " would make automatic storage for %s too large"); + ccm_attrs[vindex].fmt = CCMFMT_P1P2; + + vindex = ccm_vis_index (CCM_NINLINE_CALLS); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CALLS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it contains" + " too many calls"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_ACTUAL); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_ACTUAL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it has more" + " actual parameters than formal parameters"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_FORMAL); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_FORMAL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it has more" + " formal parameters than actual parameters"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_TYPE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_TYPE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because formal" + " argument type does not match actual type"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_ATYPE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_ATYPE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because array formal" + " argument does not match reshaped array actual" + " argument type"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_RETTYPE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_RETTYPE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because return type" + " does not match"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_EXCPT); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_EXCPT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it" + " guarded by an exception handler"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_UNSAFE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_UNSAFE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it might be" + " unsafe (call alloca(), etc)"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_ALIAS); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_ALIAS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because inlining it" + " will make the alias analysis in the calling" + " function more conservative"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_FEMARK); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_FEMARK"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it contains" + " setjmp/longjmp, or indirect goto, etc"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_RAREX); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_RAREX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it is known" + " to be rarely executed"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_CLONING); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_CLONING"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s from source file %s cloned," + " creating cloned function %s; constant" + " parameters propagated to clone"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2P3; + + vindex = ccm_vis_index (CCM_INLINE_B); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_INLINE_B"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s inlined from source file %s into" + " the code for the following line. %d loops" + " inlined"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2I3; + + vindex = ccm_vis_index (CCM_INLINE2_B); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_INLINE2_B"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s inlined from source file %s into" + " inline copy of function %s. %d loops inlined"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2P3I4; + + vindex = ccm_vis_index (CCM_INLINE_LOOP); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_LOOP | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_INLINE_LOOP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Loop in function %s, line %d has" + " tag %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1I2L3; + + vindex = ccm_vis_index (CCM_NINLINE_MULTIENTRY); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_MULTIENTRY"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it" + " contains an ENTRY statement"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_VARARGS); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_VARARGS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because variable" + " argument routines cannot be inlined"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_UNSEEN_BODY); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_UNSEEN_BODY"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because the compiler" + " has not seen the body of the function. Use" + " -xcrossfile or -xipo in order to inline it"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_UPLEVEL); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_UPLEVEL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it is a" + " nested routine containing references to" + " variables defined in an outer function"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CMDLINE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CMDLINE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because either" + " -xinline or source code pragma prohibited it"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CALL_CMPLX); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CALL_CMPLX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s not inlined because of the" + " complexity of the calling routine"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_LANG_MISMATCH); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_LANG_MISMATCH"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s not inlined because it is in" + " a different language"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_RTN_WEAK); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_RTN_WEAK"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it" + " is marked weak"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CALL_WEAKFILE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CALL_WEAKFILE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s not inlined because it is" + " in a different file and it contains a" + " call to a weak routine"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CALL_TRYCATCH); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CALL_TRYCATCH"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s not inlined because it is" + " in a different file and contains an" + " explicit try/catch"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CALL_REGP); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CALL_REGP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s not inlined because it would" + " cause excessive register pressure"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_RTN_REGP); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_RTN_REGP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it would" + " cause excessive register pressure"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CALL_XPENSV); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CALL_XPENSV"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s not inlined because analysis" + " exceeds the compilation time limit"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_READONLYIR); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_READONLYIR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it is in a file" + " specified as read-only by -xipo_archive=readonly" + " and it contains calls to static functions"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CALL_THUNK); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CALL_THUNK"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s not inlined because it is in a" + " compiler-generated function that does not" + " permit inlining"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CALL_XTARGETS); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CALL_XTARGETS"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Indirect callsite has too many targets;" + " callsite marked do not inline"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NINLINE_SELFTAIL_RECURSIVE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_SELFTAIL_RECURSIVE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because" + " of a recursive tail-call to itself"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_PRAGMA); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_PRAGMA"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it contains" + " explicit parallel or alias pragmas"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CMPLX2); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CMPLX2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it contains too" + " many operations. Increase max_inst_hard in order" + " to inline it: -xinline_param=max_inst_hard:n"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_RARE); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_RARE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because the call" + " is rarely executed"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_PAR2); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_PAR2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it is called" + " within a region guarded by an explicit" + " parallel pragmas"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_G_LIMIT); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_G_LIMIT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it would exceed" + " the permitted global code size growth limit. Try" + " to increase max_growth in order to inline it:" + " -xinline_param=max_growth:n"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_L_LIMIT); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_L_LIMIT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it would exceed" + " the maximum function size growth limit. Increase" + " max_function_inst in order to inline it:" + " -xinline_param=max_function_inst:n"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_REC2); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_REC2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Recursive function %s is inlined only up to" + " %d levels and up to %d size. Increase" + " max_recursive_deptha or max_recursive_inst in" + " order to inline it:" + " -xinline_param=max_recursive_depth:n," + " -xinline_param=max_recursive_inst:n"); + ccm_attrs[vindex].fmt = CCMFMT_P1I2I3; + + vindex = ccm_vis_index (CCM_NINLINE_FB2); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_FB2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because the" + " profile-feedback execution count is too" + " low. Decrease min_counter in order to inline it:" + " -xinline_param:min_counter:n"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_CS_CMPLX); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_CS_CMPLX"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because called" + " function's size is too big. Increase" + " max_inst_soft in order to inline it:" + " -xinline_param=max_inst_soft:n"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_R_EXCPT); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_R_EXCPT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it contains" + " an exception handler"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_ASM); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_ASM"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because" + " it contains asm statements"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_R_READONLYIR); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_R_READONLYIR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it is in a file" + " specified as read-only by -xipo_archive=readonly" + " and it is a static function"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_C_READONLYIR); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_C_READONLYIR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s not inlined because the calling" + " function is in a file specified as read-only" + " by -xipo_archive=readonly"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NINLINE_NEVERRETURN); + ccm_attrs[vindex].vis = CCMV_INLINE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NINLINE_NEVERRETURN"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it" + " never returns"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_MPREFETCH); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_MPREFETCH"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Prefetch of %s inserted"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_MPREFETCH_LD); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_MPREFETCH_LD"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Prefetch of %s inserted for load at %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1X2; + + vindex = ccm_vis_index (CCM_MPREFETCH_ST); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_MPREFETCH_ST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Prefetch of %s inserted for store at %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1X2; + + vindex = ccm_vis_index (CCM_MPREFETCH_FB); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_MPREFETCH_FB"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Prefetch of %s inserted based on feedback data"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_MPREFETCH_FB_LD); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_MPREFETCH_FB_LD"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Prefetch of %s inserted for load at %s based" + " on feedback data"); + ccm_attrs[vindex].fmt = CCMFMT_S1X2; + + vindex = ccm_vis_index (CCM_MPREFETCH_FB_ST); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_MPREFETCH_FB_ST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Prefetch of %s inserted for store at %s based" + " on feedback data"); + ccm_attrs[vindex].fmt = CCMFMT_S1X2; + + vindex = ccm_vis_index (CCM_MLOAD); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MLOAD"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Load below refers to %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_MSTORE); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MSTORE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Store below refers to %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_MLOAD_P); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MLOAD_P"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Load below refers to %s, and was prefetched" + " at %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1X2; + + vindex = ccm_vis_index (CCM_MSTORE_P); + ccm_attrs[vindex].vis = CCMV_MEMOPS | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MSTORE_P"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Store below refers to %s, and was prefetched" + " at %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1X2; + + vindex = ccm_vis_index (CCM_COPYIN); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_COPYIN"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Parameter %d caused a copyin in the following" + " call"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_COPYOUT); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_COPYOUT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Parameter %d caused a copyout in the following" + " call"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_COPYINOUT); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_COPYINOUT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Parameter %d caused both a copyin and copyout" + " in the following call"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_PADDING); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_PADDING"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Padding of %d bytes inserted before" + " array %s"); + ccm_attrs[vindex].fmt = CCMFMT_I1V2; + + vindex = ccm_vis_index (CCM_PADCOMMON); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_PADCOMMON"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Padding of %d bytes inserted before" + " array %s in common block %s"); + ccm_attrs[vindex].fmt = CCMFMT_I1V2V3; + + vindex = ccm_vis_index (CCM_ALIGN_EQ); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_ALIGN_EQ"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Variable/array %s can not be double-aligned," + " because it is equivalenced"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_ALIGN_PERF); + ccm_attrs[vindex].vis = CCMV_FE; + ccm_attrs[vindex].name = "CCM_ALIGN_PERF"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Alignment of variables in common block may cause" + " performance degradation"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_ALIGN_STRUCT); + ccm_attrs[vindex].vis = CCMV_FE; + ccm_attrs[vindex].name = "CCM_ALIGN_STRUCT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Alignment of component %s in numeric sequence" + " structure %s may cause performance degradation"); + ccm_attrs[vindex].fmt = CCMFMT_S1S2; + + vindex = ccm_vis_index (CCM_TMP_COPY); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_TMP_COPY"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Argument %s copied to a temporary"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_TMP_COPYM); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_TMP_COPYM"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Argument %s might be copied to a temporary;" + " runtime decision made"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_PROC_MISMATCH); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_PROC_MISMATCH"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Argument %d to subprogram %s differs from" + " reference on line %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1P2I3; + + vindex = ccm_vis_index (CCM_PROC_MISMATCH2); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_PROC_MISMATCH2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Scalar argument %d to subprogram %s is" + " referred to as an array on line %d"); + ccm_attrs[vindex].fmt = CCMFMT_I1P2I3; + + vindex = ccm_vis_index (CCM_PROC_MISMATCH3); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_PROC_MISMATCH3"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Return type/rank from subprogram %s differs" + " from return on line %d"); + ccm_attrs[vindex].fmt = CCMFMT_P1I2; + + vindex = ccm_vis_index (CCM_DO_EXPR); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_DO_EXPR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "DO statement bounds lead to no executions of the" + " loop"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_AUTO_BND); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_AUTO_BND"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "The bounds for automatic variable %s are not" + " available at all entry points; zero-length" + " variable might be allocated"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_LIT_PAD); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_LIT_PAD"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "The character string literal %s padded" + " to the length specified for the dummy argument"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_ARRAY_LOOP); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_ARRAY_LOOP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Array statement below generated a loop"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_ARRAY_LOOPNEST); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_LOOP | CCMV_BASIC | CCMV_OBS; + ccm_attrs[vindex].name = "CCM_ARRAY_LOOPNEST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Array statement below generated %d nested loops"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_ALIGN_PERF2); + ccm_attrs[vindex].vis = CCMV_FE; + ccm_attrs[vindex].name = "CCM_ALIGN_PERF2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Alignment of variable %s in common block %s" + " may cause a performance degradation"); + ccm_attrs[vindex].fmt = CCMFMT_V1V2; + + vindex = ccm_vis_index (CCM_ALIGN_PERF3); + ccm_attrs[vindex].vis = CCMV_FE; + ccm_attrs[vindex].name = "CCM_ALIGN_PERF3"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Alignment of variable %s in blank common may" + " cause a performance degradation"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_IO_LOOP_ARRAY); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_LOOP | CCMV_BASIC | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_IO_LOOP_ARRAY"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "I/O implied do item below generated an array" + " section"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_TMPCONST); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_TMPCONST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Implicit invocation of class %s constructor for" + " temporary"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_TMPDEST); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_TMPDEST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Implicit invocation of class %s destructor for" + " temporary"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_DBL_CONST); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_DBL_CONST"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Double constant %s used in float expression"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_MINLINE); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MINLINE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s inlined from source file %s by" + " front-end"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2; + + vindex = ccm_vis_index (CCM_MINLINE2); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MINLINE2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s from source file %s inlined into" + " inline copy of method %s by front-end"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2P3; + + vindex = ccm_vis_index (CCM_MINLINE3); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MINLINE3"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it uses keyword" + " %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1S2; + + vindex = ccm_vis_index (CCM_MINLINE4); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC | CCMV_UNIMPL; + ccm_attrs[vindex].name = "CCM_MINLINE4"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s not inlined because it is too" + " complex"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_TMP_COPYOUT); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_TMP_COPYOUT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Argument %s copied from a temporary"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_TMP_COPYOUTM); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_TMP_COPYOUTM"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Argument %s might be copied from a temporary;" + " runtime decision made"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_TMP_COPYINOUT); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_TMP_COPYINOUT"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Argument %s copied in and out of a temporary"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_TMP_COPYINOUTM); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_TMP_COPYINOUTM"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Argument %s might be copied in and out of" + " a temporary; runtime decision made"); + ccm_attrs[vindex].fmt = CCMFMT_V1; + + vindex = ccm_vis_index (CCM_ARRAY_LOOP_2); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_LOOP | CCMV_BASIC | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_ARRAY_LOOP_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Array statement below generated loop %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_ARRAY_LOOPNEST_2); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_LOOP | CCMV_BASIC | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_ARRAY_LOOPNEST_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Array statement below generated %d nested" + " loops: %s"); + ccm_attrs[vindex].fmt = CCMFMT_I1LL2; + + vindex = ccm_vis_index (CCM_IO_LOOP_ARRAY_2); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_LOOP | CCMV_BASIC | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_IO_LOOP_ARRAY_2"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "I/O implied do item below generated an array" + " section: %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_USER_LOOP); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_LOOP | CCMV_BASIC | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_USER_LOOP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Source loop below has tag %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_FOUND_LOOP); + ccm_attrs[vindex].vis = CCMV_FE | CCMV_LOOP | CCMV_BASIC | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_FOUND_LOOP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Discovered loop below has tag %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_MFUNCTION_LOOP); + ccm_attrs[vindex].vis = CCMV_LOOP | CCMV_BASIC | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_MFUNCTION_LOOP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Copy in M-function of loop below has tag %s"); + ccm_attrs[vindex].fmt = CCMFMT_L1; + + vindex = ccm_vis_index (CCM_FSIMPLE); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_FSIMPLE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Transformations for fsimple=%d applied"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_STACK); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_STACK"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Function %s requires %d Mbytes of stack" + " storage"); + ccm_attrs[vindex].fmt = CCMFMT_P1I2; + + vindex = ccm_vis_index (CCM_TAILRECUR); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_TAILRECUR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Recursive tail call in %s optimized to jump to" + " entry point"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_TAILCALL); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC | CCMV_UNIMPL | CCMV_WANT; + ccm_attrs[vindex].name = "CCM_TAILCALL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to function %s was tail-call optimized"); + ccm_attrs[vindex].fmt = CCMFMT_P1; + + vindex = ccm_vis_index (CCM_NI_EXIT_OR_PSEUDO); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_EXIT_OR_PSEUDO"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " contains the pseudo instruction %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_NI_BAD_UNARY_OPC); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_BAD_UNARY_OPC"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " contains the instruction opcode %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_NI_INT_LDD_ON_V9); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_INT_LDD_ON_V9"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " contains integer ldd instructions, which are" + " deprecated in the v9 architecture"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NI_LATE_INL_OPC); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_LATE_INL_OPC"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " contains the instruction opcode %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_NI_BAD_IMM_OP); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_BAD_IMM_OP"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because the" + " relocation or immediate operand %s is not well" + " understood by the optimizer"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_NI_BAD_STATELEAF); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_BAD_STATELEAF"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " references the state register %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_NI_BAD_ASR_19); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_BAD_ASR_19"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because" + " %%asr19 is not supported in pre v8plus code"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NI_BAD_FSR_USE); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_BAD_FSR_USE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because" + " references to %%fsr can only be optimized when the" + " -iaopts flag is used"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NI_BAD_REGISTER); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_BAD_REGISTER"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " references the register %s"); + ccm_attrs[vindex].fmt = CCMFMT_S1; + + vindex = ccm_vis_index (CCM_NI_NO_RET_VAL); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_NO_RET_VAL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " does not return the value declared"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NI_DELAY); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_DELAY"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " contains a non nop delay slot"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NI_SCALL); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_SCALL"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " calls a function which returns a structure"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_CASE_POSITION); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_CASE_POSITION"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Case block below was placed at position %d" + " based on execution frequency"); + ccm_attrs[vindex].fmt = CCMFMT_I1; + + vindex = ccm_vis_index (CCM_CALL_WITH_CODE); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_CALL_WITH_CODE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Call to %s replaced with inline code. %d" + " loops created: %s"); + ccm_attrs[vindex].fmt = CCMFMT_P1I2LL3; + + vindex = ccm_vis_index (CCM_NI_BAD_SP_ADDR); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_BAD_SP_ADDR"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " contains a %%sp+reg address"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NI_BAD_SP_USAGE); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_BAD_SP_USAGE"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " uses/defines the stack pointer in a non-load/store instruction"); + ccm_attrs[vindex].fmt = CCMFMT_NONE; + + vindex = ccm_vis_index (CCM_NI_MIXED_REG_TYPES); + ccm_attrs[vindex].vis = CCMV_CG | CCMV_BASIC; + ccm_attrs[vindex].name = "CCM_NI_MIXED_REG_TYPES"; + ccm_attrs[vindex].msg = catgets (ccm_catd, 99, vindex, + "Template could not be early inlined because it" + " contains register %s used as both x-register and register pair"); + ccm_attrs[vindex].fmt = CCMFMT_S1; +} diff --git a/gprofng/src/comp_com.h b/gprofng/src/comp_com.h new file mode 100644 index 0000000..e410924 --- /dev/null +++ b/gprofng/src/comp_com.h @@ -0,0 +1,903 @@ +/* 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. */ + +#ifndef _COMP_COM_H +#define _COMP_COM_H + +#include <sys/types.h> +#include <nl_types.h> + +/* + * This file describes format for the compiler-commentary + * section to be added to .o's and propagated to the a.out. It reflects + * information the compiler can expose to the user about his or her + * program. The section should be generated for all compiles where + * the user has specified -g on the compile line. + * + * In the analyzer, display of the messages will be governed by a user UI + * that sets a vis_bits bitmap, and matches it against a show_bits + * bitmap table, which is maintained separately from the producer + * code. For any message, if (vis_bits&show_bits) is non-zero, the + * message is shown. If zero, the message is not shown. A similar + * mechanism would be used for a stand-alone source or disassembly browser. + * + * + * The .compcom Section + * -------------------- + * The section will be named ".compcom"; it is generated for each + * .o, and aggregated into a single section in the a.out. In that + * section, each .o's data is separate, and the tools will loop + * over the data for each .o in order to find the subsection for + * the particular .o being annotated. + * + * + * Since the header is fixed-length, and the total size of the section + * can be easily determined as: + * + * sizeof(stuct compcomhdr) + * + msgcount * sizeof(struct compmsg) + * + paramcount * sizeof(int32_t) + * + stringlen + * + * there is no need to have the size in the header. + */ + +typedef struct +{ /* Header describing the section */ + int32_t srcname; /* index into strings of source file path */ + int32_t version; /* a version number for the .compcom format */ + int32_t msgcount; /* count of messages in the section */ + int32_t paramcount; /* count of parameters in the section */ + int32_t stringcount; /* count of strings in the section */ + int32_t stringlen; /* count of total bytes in strings */ +} compcomhdr; + +/* + * The data for the .o after the header as: + * + * compmsg msgs[msgcount]; the array of messages + * int32_t param[paramcount]; the parameters used in the messages + * parameters are either integers or + * string-indices + * char msgstrings[stringlen]; the strings used in the messages + */ + +/* + * Message Classes and Visualization Bits + * -------------------------------------- + * Each of the messages above may belong to zero or more visualization + * classes, governed by a table using zero or more of the following symbolic + * names for the classes: + */ +typedef enum { +CCMV_WANT = 0x000, /* High-priority RFE -- used only for human */ + /* reading of message list */ +CCMV_UNIMPL = 0x000, /* Unimplemented -- used only for human */ + /* reading of message list */ +CCMV_OBS = 0x000, /* Obsolete -- to be replaced by a different */ + /* message with different parameters -- */ + /* used only for human reading of message */ + /* list */ +CCMV_VER = 0x001, /* Versioning messages */ +CCMV_WARN = 0x002, /* Warning messages */ +CCMV_PAR = 0x004, /* Parallelization messages */ +CCMV_QUERY = 0x008, /* Compiler queries */ +CCMV_LOOP = 0x010, /* Loop detail messages */ +CCMV_PIPE = 0x020, /* Pipelining messages */ +CCMV_INLINE = 0x040, /* Inlining information */ +CCMV_MEMOPS = 0x080, /* Messages concerning memory operations */ +CCMV_FE = 0x100, /* Front-end messages (all compilers) */ +CCMV_CG = 0x200, /* Code-generator messages (all compilers) */ +CCMV_BASIC = 0x400, /* for default messages */ +CCMV_ALL = 0x7FFFFFFF /* for all messages */ +} COMPCLASS_ID; + +typedef enum ccm_msgid +{ + /* Group: Versioning Messages */ + /* All of these are global to the .o, and will */ + /* have lineno = pcoffset = 0 */ + +CCM_MODDATE=0x00100, /* Source file <s1>, last modified on date <s2> */ +CCM_COMPVER, /* Component <s1>, version <s2> */ + /* [Emitted for each component of the compiler.] */ +CCM_COMPDATE, /* Compilation date <s1> */ + /* [<s1> is an I18n string with the date and time] */ +CCM_COMPOPT, /* Compilation options <s1> */ + /* [As specified by the user] */ +CCM_ACOMPOPT, /* Actual Compilation options <s1> */ + /* [As expanded by the driver] */ + + /* Group: Warning Messages */ +CCM_VAR_ALIAS=0x00200, /* Variable <v1> aliased to <v2> */ +CCM_FBIRDIFF, /* Profile feedback data inconsistent with */ + /* intermediate representation file; check compiler */ + /* version, flags and source file */ +CCM_OPTRED_SWAP, /* Optimization level for <p1> reduced from <i2> to */ + /* <i3> due to insufficient swap space */ +CCM_OPTRED_CPLX, /* Optimization level for <p1> reduced from <i2> to */ + /* <i3> due to program complexity */ +CCM_UNKNOWN, /* Unexpected compiler comment <i1> */ + + /* Group: Parallelization Messages */ +CCM_UNPAR_CALL=0x00400, /* Loop below not parallelized because it contains a */ + /* call to <p1> */ + + /* CCMV_WANT: Don't generate CCM_PAR_SER; always use CCM_PAR_SER_VER */ +CCM_PAR_SER, /* Both serial and parallel versions generated for */ + /* loop below */ +CCM_PAR_SER_VER, /* Both serial and parallel versions generated for */ + /* loop below; with parallel version used if <s1>, */ + /* serial otherwise */ +CCM_PAR_DRECTV, /* Loop below parallelized by explicit user */ + /* directive */ +CCM_APAR, /* Loop below autoparallelized */ +CCM_AUTOPAR, /* Loop below autoparallelized; equivalent */ + /* explict directive is <s1> */ +CCM_UNPAR_DD, /* Loop below could not be parallelized because of a */ + /* data dependency on <v1>, <v2>, ... */ + /* [The number of parameters will determine how many */ + /* names appear, and the formatter will get the */ + /* commas right.] */ +CCM_UNPAR_DDA, /* Loop below could not be parallelized because of a */ + /* data dependency or aliasing of <v1>, <v2>, ... */ +CCM_UNPAR_ANONDD, /* Loop below could not be parallelized because of */ + /* an anonymous data dependency */ +CCM_UNPAR_ANONDDA, /* Loop below could not be parallelized because of */ + /* an anonymous data dependency or aliasing */ +CCM_PAR_WORK, /* Loop below parallelized, but might not contain */ + /* enough work to be efficiently run in parallel */ +CCM_UNPAR_EXIT, /* Loop below not parallelized because it contains */ + /* multiple exit points */ +CCM_UNPAR_STRNG, /* Loop below not parallelized because it contains a */ + /* strange flow of control */ +CCM_UNPAR_IO, /* Loop below not parallelized because it contains */ + /* I/O or other MT-unsafe calls */ +CCM_PAR_BODY_NAME, /* Parallel loop-body code is in function <p1> */ +CCM_UNPAR_NLOOPIDX, /* Loop below not parallelized because loop index */ + /* not found */ +CCM_UNPAR_DRECTV, /* Loop below not parallelized because of explicit */ + /* user directive */ +CCM_UNPAR_NOTPROFIT, /* Loop below not parallelized because it was not */ + /* profitable to do so */ +CCM_UNPAR_NEST, /* Loop below not parallelized because it was */ + /* nested in a parallel loop */ +CCM_UNPAR, /* Loop below not parallelized */ +CCM_UNPAR_NOAUTO, /* Loop below not parallelized because */ + /* autoparallelization is not enabled */ +CCM_PR_L_VAR, /* Private variables in loop below: */ + /* <v1>, <v2>, ... */ + /* [The number of parameters will determine how many */ + /* names appear, and the formatter will get the */ + /* commas right.] */ +CCM_SH_L_VAR, /* Shared variables in loop below: */ + /* <v1>, <v2>, ... */ +CCM_TP_L_VAR, /* Threadprivate variables in loop below: */ + /* <v1>, <v2>, ... */ +CCM_RV_L_VAR, /* Reduction variables in loop below: */ + /* <v1>, <v2>, ... */ +CCM_IM_L_VAR, /* Implicit variables in loop below: */ + /* <v1>, <v2>, ... */ +CCM_PR_O_VAR, /* Private variables in OpenMP construct below: */ + /* <v1>, <v2>, ... */ +CCM_SH_O_VAR, /* Shared variables in OpenMP construct below: */ + /* <v1>, <v2>, ... */ +CCM_TP_O_VAR, /* Threadprivate variables in OpenMP construct */ + /* below: <v1>, <v2>, ... */ +CCM_RV_O_VAR, /* Reduction variables in OpenMP construct below: */ + /* <v1>, <v2>, ... */ +CCM_IM_O_VAR, /* Implicit variables in OpenMP construct below: */ + /* <v1>, <v2>, ... */ +CCM_UNPAR_IN_OMP, /* Loop below not parallelized because it is inside */ + /* an OpenMP region */ +CCM_FP_O_VAR, /* Firstprivate variables in OpenMP construct below: */ + /* <v1>, <v2>, ... */ +CCM_LP_O_VAR, /* Lastprivate variables in OpenMP construct below: */ + /* <v1>, <v2>, ... */ +CCM_CP_O_VAR, /* Copyprivate variables in OpenMP construct below: */ + /* <v1>, <v2>, ... */ +CCM_PR_OAS_VAR, /* Variables autoscoped as PRIVATE in OpenMP */ + /* construct below: <v1>, <v2>, ... */ +CCM_SH_OAS_VAR, /* Variables autoscoped as SHARED in OpenMP */ + /* construct below: <v1>, <v2>, ... */ +CCM_FP_OAS_VAR, /* Variables autoscoped as FIRSTPRIVATE in OpenMP */ + /* construct below: <v1>, <v2>, ... */ +CCM_LP_OAS_VAR, /* Variables autoscoped as LASTPRIVATE in OpenMP */ + /* construct below: <v1>, <v2>, ... */ +CCM_RV_OAS_VAR, /* Variables autoscoped as REDUCTION in OpenMP */ + /* construct below: <v1>, <v2>, ... */ +CCM_FAIL_OAS_VAR, /* Variables cannot be autoscoped in OpenMP */ + /* construct below: <v1>, <v2>, ... */ +CCM_SERIALIZE_OAS, /* OpenMP parallel region below is serialized */ + /* because autoscoping has failed */ +CCM_UNPAR_CALL_2, /* <l1> not parallelized because it contains calls */ + /* to: <p2>, <p3>, ... */ +CCM_PAR_DRECTV_2, /* <l1> parallelized by explicit user directive */ +CCM_APAR_2, /* <l1> autoparallelized */ +CCM_AUTOPAR_2, /* <l1> autoparallelized; equivalent */ + /* explict directive is <s2> */ +CCM_UNPAR_DD_2, /* <l1> could not be parallelized because of */ + /* data dependences on: <v2>, <v3>, ... */ + /* [The number of parameters will determine how many */ + /* names appear, and the formatter will get the */ + /* commas right.] */ +CCM_UNPAR_DDA_2, /* <l1> could not be parallelized because of a */ + /* data dependence or aliasing of: <v2>, <v3>, ... */ +CCM_UNPAR_ANONDD_2, /* <l1> could not be parallelized because of an */ + /* anonymous data dependence */ +CCM_UNPAR_ANONDDA_2, /* <l1> could not be parallelized because of an */ + /* anonymous data dependence or aliasing */ +CCM_PAR_WORK_2, /* <l1> parallelized, but might not contain */ + /* enough work to run efficiently in parallel */ +CCM_UNPAR_EXIT_2, /* <l1> not parallelized because it contains */ + /* multiple exit points */ +CCM_UNPAR_STRANGE_2, /* <l1> not parallelized because it contains a */ + /* strange flow of control */ +CCM_UNPAR_IO_2, /* <l1> not parallelized because it contains */ + /* I/O or other MT-unsafe calls */ +CCM_PAR_BODY_NAME_2, /* <l1> parallel loop-body code placed in */ + /* function <p2> along with <i3> inner loops */ +CCM_UNPAR_NLOOPIDX_2, /* <l1> not parallelized because loop index not */ + /* found */ +CCM_UNPAR_DRECTV_2, /* <l1> not parallelized because of explicit */ + /* user directive */ +CCM_UNPAR_NOTPROFIT_2, /* <l1> not parallelized because it was not */ + /* profitable to do so */ +CCM_UNPAR_NEST_2, /* <l1> not parallelized because it was */ + /* nested within a parallel loop */ +CCM_UNPAR_2, /* <l1> not parallelized */ +CCM_UNPAR_NOAUTO_2, /* <l1> not parallelized because */ + /* autoparallelization is not enabled */ +CCM_PR_L_VAR_2, /* Private variables in <l1>: */ + /* <v2>, <v3>, ... */ + /* [The number of parameters will determine how many */ + /* names appear, and the formatter will get the */ + /* commas right.] */ +CCM_SH_L_VAR_2, /* Shared variables in <l1>: */ + /* <v2>, <v3>, ... */ +CCM_TP_L_VAR_2, /* Threadprivate variables in <l1>: */ + /* <v2>, <v3>, ... */ +CCM_RV_L_VAR_2, /* Reduction variables of operator <s1> in <l2>: */ + /* <v3>, <v4>, ... */ +CCM_IM_L_VAR_2, /* Implicit variables in <l1>: */ + /* <v2>, <v3>, ... */ +CCM_PR_O_VAR_2, /* Private variables in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_SH_O_VAR_2, /* Shared variables in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_TP_O_VAR_2, /* Threadprivate variables in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_RV_O_VAR_2, /* Reduction variables of operator <s1> in <r2>: */ + /* <v3>, <v4>, ... */ +CCM_IM_O_VAR_2, /* Implicit variables in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_UNPAR_IN_OMP_2, /* <l1> not parallelized because it is inside */ + /* OpenMP region <r2> */ +CCM_FP_O_VAR_2, /* Firstprivate variables in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_LP_O_VAR_2, /* Lastprivate variables in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_CP_O_VAR_2, /* Copyprivate variables in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_PR_OAS_VAR_2, /* Variables autoscoped as PRIVATE in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_SH_OAS_VAR_2, /* Variables autoscoped as SHARED in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_FP_OAS_VAR_2, /* Variables autoscoped as FIRSTPRIVATE in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_LP_OAS_VAR_2, /* Variables autoscoped as LASTPRIVATE in <r1>: */ + /* <v2>, <v3>, ... */ +CCM_RV_OAS_VAR_2, /* Variables autoscoped as REDUCTION of operator */ + /* <s1> in <r2>: <v3>, <v4>, ... */ +CCM_FAIL_OAS_VAR_2, /* Variables treated as shared because they cannot */ + /* be autoscoped in <r1>: <v2>, <v3>, ... */ +CCM_SERIALIZE_OAS_2, /* <r1> will be executed by a single thread because */ + /* autoscoping for some variables was not successful */ + + /* Group: Parallelization Questions asked of the user */ + /* How will the user answer these questions? */ +CCM_QPERMVEC=0x00800, /* Is <v1> a permutation vector during execution of */ + /* <l2>? */ +CCM_QEXPR, /* Is expression <s1> true for <l2>? */ +CCM_QSAFECALL, /* Is subroutine <p1> MP-safe as used in <l2>? */ + + /* Group: Loop Optimization Messages */ +CCM_LCOST=0x01000, /* Loop below estimated to cost <i1> cycles per */ + /* iteration */ +CCM_UNROLL, /* Loop below unrolled <i1> times */ + /* CCMV_WANT: the next one should be replaced by CCM_IMIX2 */ +CCM_IMIX, /* Loop below has <i1> loads, <i2> stores, */ + /* <i3> prefetches, <i4> FPadds, <i5> FPmuls, and */ + /* <i6> FPdivs per iteration */ +CCM_SPILLS, /* Loop below required <i1> integer register spills, */ + /* <i2> FP register spills, and used */ + /* <i3> integer registers and <i4> FP registers */ +CCM_LFISSION, /* Loop below fissioned into <i1> loops */ +CCM_LPEEL, /* Loop below had iterations peeled off for better */ + /* unrolling and/or parallelization */ +CCM_LBLOCKED, /* Loop below blocked by <i1> for improved cache */ + /* performance */ +CCM_LTILED, /* Loop below tiled for better performance */ +CCM_LUNRJAM, /* Loop below unrolled and jammed */ +CCM_LWHILE2DO, /* Bounds test for loop below moved to top of loop */ +CCM_L2CALL, /* Loop below replaced by a call to <p1> */ +CCM_LDEAD, /* Loop below deleted as dead code */ +CCM_LINTRCHNG, /* Loop below interchanged with loop on line <i1> */ +CCM_FUSEDTO, /* Loop below fused with loop on line <i1> */ +CCM_FUSEDFROM, /* Loop from line <i1> fused with loop below */ +CCM_VECINTRNSC, /* Loop below transformed to use calls to vector */ + /* intrinsic <p1>, <p2>, ... */ + /* [The number of parameters will determine how many */ + /* names appear, and the formatter will get the */ + /* commas right.] */ +CCM_LSTRIPMINE, /* Loop below strip-mined */ +CCM_LNEST2LOOPS, /* Loop below collapsed with loop on line <i1> */ +CCM_LREVERSE, /* Loop below has had its iteration direction */ + /* reversed */ +CCM_IMIX2, /* Loop below has <i1> loads, <i2> stores, */ + /* <i3> prefetches, <i4> FPadds, <i5> FPmuls, */ + /* <i6> FPdivs, <i7> FPsubs, and <i8> FPsqrts per */ + /* iteration */ +CCM_LUNRFULL, /* Loop below fully unrolled */ +CCM_ELIM_NOAMORTINST, /* Loop below was eliminated as it contains no */ + /* non-amortizable instructions */ +CCM_COMP_DALIGN, /* Performance of loop below could be improved */ + /* by compiling with -dalign */ +CCM_INTIMIX, /* Loop below has <i1> int-loads, <i2> int-stores, */ + /* <i3> alu-ops, <i4> muls, <i5> int-divs and */ + /* <i6> shifts per iteration */ +CCM_LMULTI_VERSION, /* <l1> multi-versioned. Specialized version */ + /* is <l2> */ +CCM_LCOST_2, /* <l1> estimated to cost <i2> cycles per iteration */ +CCM_UNROLL_2, /* <l1> unrolled <i2> times */ + + /* CCMV_WANT: the next one should be replaced by CCM_IMIX2_B or CCM_IMIX3_B */ +CCM_IMIX_B, /* <l1> has <i2> loads, <i3> stores, */ + /* <i4> prefetches, <i5> FPadds, <i6> FPmuls, and */ + /* <i7> FPdivs per iteration */ +CCM_SPILLS_2, /* <l1> required <i2> integer register spills, */ + /* <i3> FP register spills, and used */ + /* <i4> integer registers and <i5> FP registers */ +CCM_LFISSION_2, /* <l1> fissioned into <i2> loops, generating: */ + /* <l3>, <l4>, ... */ + /* [The number of parameters will determine how many */ + /* names appear, and the formatter will get the */ + /* commas right.] */ +CCM_LFISSION_FRAG, /* <l1> contains code from lines: <i2>, <i3>, ... */ +CCM_LPEEL_2, /* <l1> had iterations peeled off for better */ + /* unrolling and/or parallelization */ +CCM_LBLOCKED_2, /* <l1> blocked by <i2> for improved memory */ + /* hierarchy performance, new inner loop <l3> */ +CCM_LOUTER_UNROLL, /* <l1> is outer-unrolled <i2> times as part */ + /* of unroll and jam */ +CCM_LJAMMED, /* All <i1> copies of <l2> are fused together */ + /* as part of unroll and jam */ +CCM_LWHILE2DO_2, /* Bounds test for <l1> moved to top of loop */ +CCM_L2CALL_2, /* <l1> replaced by a call to <p2> */ +CCM_LDEAD_2, /* <l1> deleted as dead code */ +CCM_LINTRCHNG_2, /* <l1> interchanged with <l2> */ +CCM_LINTRCHNG_ORDER, /* For loop nest below, the final order of loops */ + /* after interchanging and subsequent */ + /* transformations is: <l1>, <l2>, ... */ + /* [The number of parameters will determine how many */ + /* names appear, and the formatter will get the */ + /* commas right.] */ +CCM_FUSED_2, /* <l1> fused with <l2>, new loop <l3> */ +CCM_VECINTRNSC_2, /* <l1> transformed to use calls to vector */ + /* intrinsics: <p2>, <p3>, ... */ +CCM_LSTRIPMINE_2, /* <l1> strip-mined by <i2>, new inner loop <l3> */ +CCM_LNEST2LOOPS_2, /* <l1> collapsed with <l2>, new loop <l3> */ +CCM_LREVERSE_2, /* <l1> has had its iteration direction reversed */ +CCM_IMIX2_B, /* <l1> has <i2> loads, <i3> stores, */ + /* <i4> prefetches, <i5> FPadds, <i6> FPmuls, */ + /* <i7> FPdivs, <i8> FPsubs, and <i9> FPsqrts per */ + /* iteration */ +CCM_LUNRFULL_2, /* <l1> fully unrolled */ +CCM_ELIM_NOAMORTINST_2, /* <l1> was eliminated as it contains no */ + /* non-amortizable instructions */ +CCM_COMP_DALIGN_2, /* Performance of <l1> could be improved by */ + /* compiling with -dalign */ +CCM_INTIMIX_2, /* <l1> has <i2> int-loads, <i3> int-stores, */ + /* <i4> alu-ops, <i5> muls, <i6> int-divs and */ + /* <i7> shifts per iteration */ +CCM_OMP_REGION, /* Source OpenMP region below has tag <r1> */ +CCM_LMICROVECTORIZE, /* <l1> is micro-vectorized */ +CCM_LMULTI_VERSION_2, /* <l1> multi-versioned for <s2>. */ + /* Specialized version is <l3> */ +CCM_LCLONED, /* <l1> cloned for <s2>. Clone is <l3> */ +CCM_LUNSWITCHED, /* <l1> is unswitched. New loops */ + /* are <l2> and <l3> */ +CCM_LRESWITCHED, /* Loops <l1> and <l2> and their surrounding */ + /* conditional code have been merged to */ + /* form loop <l3> */ +CCM_LSKEWBLOCKED, /* <l1> skew-blocked by <i2> with slope */ + /* <i3> for improved memory hierarchy */ + /* performance, new inner loop <l4> */ +CCM_IVSUB, /* Induction variable substitution performed on <l1> */ +CCM_ONEITER_REPLACED, /* <l1> determined to have a trip count of 1; */ + /* converted to straight-line code */ +CCM_IMIX3_B, /* <l1> has <i2> loads, <i3> stores, */ + /* <i4> prefetches, <i5> FPadds, <i6> FPmuls, */ + /* <i7> FPmuladds, <i8> FPdivs, and <i9> FPsqrts per */ + /* iteration */ + + /* Group: Pipelining Messages */ +CCM_PIPELINE=0x02000, /* Loop below pipelined */ +CCM_PIPESTATS, /* Loop below scheduled with steady-state cycle */ + /* count = <i1> */ +CCM_NOPIPE_CALL, /* Loop could not be pipelined because it contains */ + /* calls */ +CCM_NOPIPE_INTCC, /* Loop could not be pipelined because it sets */ + /* multiple integer condition codes. */ +CCM_NOPIPE_MBAR, /* Loop could not be pipelined because it contains a */ + /* memory barrier instruction */ +CCM_NOPIPE_MNMX, /* Loop could not be pipelined because it contains */ + /* a minimum or a maximum operation */ +CCM_NOPIPE_U2FLT, /* Loop could not be pipelined because it contains */ + /* an unsigned to float conversion */ +CCM_NOPIPE_GOT, /* Loop could not be pipelined because it sets the */ + /* Global Offset Table pointer */ +CCM_NOPIPE_IDIV, /* Loop could not be pipelined because it contains */ + /* an integer divide */ +CCM_NOPIPE_PRFTCH, /* Loop could not be pipelined because it contains */ + /* a prefetch operation */ +CCM_NOPIPE_EXIT, /* Loop could not be pipelined because it contains */ + /* an exit operation */ +CCM_NOPIPE_REG, /* Loop could not be pipelined because it contains */ + /* instructions that set the %gsr or %fsr register */ +CCM_NOPIPE_UNS, /* Loop could not be pipelined because it has an */ + /* unsigned loop counter */ +CCM_NOPIPE_UNSUIT, /* Loop was unsuitable for pipelining */ +CCM_NOPIPE_INTRINSIC, /* Loop could not be pipelined because it has an */ + /* intrinsic call to <p1> */ +CCM_NOPIPE_BIG, /* Loop could not be pipelined as it is too big */ +CCM_NOPIPE_INVINTPR, /* Loop could not be pipelined as it contains too */ + /* many loop invariant integers = <i1> */ +CCM_NOPIPE_INVFLTPR, /* Loop could not be pipelined as it contains too */ + /* many loop invariant floats = <i1> */ +CCM_NOPIPE_INVDBLPR, /* Loop could not be pipelined as it contains too */ + /* many loop invariant doubles = <i1> */ +CCM_PIPE_SCHEDAFIPR, /* Loop below was adversely affected by high */ + /* integer register pressure = <i1> */ +CCM_PIPE_SCHEDAFDPR, /* Loop below was adversely affected by high */ + /* double register pressure = <i1> */ +CCM_PIPE_SCHEDAFFPR, /* Loop below was adversely affected by high */ + /* float register pressure = <i1> */ +CCM_NOPIPE_INTPR, /* Loop could not be pipelined due to high */ + /* integer register pressure = <i1> */ +CCM_NOPIPE_DBLPR, /* Loop could not be pipelined due to high */ + /* double register pressure = <i1> */ +CCM_NOPIPE_FLTPR, /* Loop could not be pipelined due to high */ + /* float register pressure = <i1> */ +CCM_PIPELINE_2, /* <l1> pipelined */ +CCM_PIPESTATS_2, /* <l1> scheduled with steady-state cycle */ + /* count = <i2> */ +CCM_NOPIPE_CALL_2, /* <l1> could not be pipelined because it contains */ + /* calls */ +CCM_NOPIPE_INTCC_2, /* <l1> could not be pipelined because it sets */ + /* multiple integer condition codes. */ +CCM_NOPIPE_MBAR_2, /* <l1> could not be pipelined because it contains */ + /* a memory barrier instruction */ +CCM_NOPIPE_MNMX_2, /* <l1> could not be pipelined because it contains */ + /* a minimum or a maximum operation */ +CCM_NOPIPE_U2FLT_2, /* <l1> could not be pipelined because it contains */ + /* an unsigned to float conversion */ +CCM_NOPIPE_GOT_2, /* <l1> could not be pipelined because it sets the */ + /* Global Offset Table pointer */ +CCM_NOPIPE_IDIV_2, /* <l1> could not be pipelined because it contains */ + /* an integer divide */ +CCM_NOPIPE_PRFTCH_2, /* <l1> could not be pipelined because it contains */ + /* a prefetch operation */ +CCM_NOPIPE_EXIT_2, /* <l1> could not be pipelined because it contains */ + /* an exit operation */ +CCM_NOPIPE_REG_2, /* <l1> could not be pipelined because it contains */ + /* instructions that set the %gsr or %fsr register */ +CCM_NOPIPE_UNS_2, /* <l1> could not be pipelined because it has an */ + /* unsigned loop counter */ +CCM_NOPIPE_UNSUIT_2, /* <l1> is unsuitable for pipelining */ +CCM_NOPIPE_INTRINSIC_2, /* <l1> could not be pipelined because it contains */ + /* a call to intrinsic <p2> */ +CCM_NOPIPE_BIG_2, /* <l1> could not be pipelined as it is too big */ +CCM_NOPIPE_INVINTPR_2, /* <l1> could not be pipelined as it contains too */ + /* many loop invariant integers = <i2> */ +CCM_NOPIPE_INVFLTPR_2, /* <l1> could not be pipelined as it contains too */ + /* many loop invariant floats = <i2> */ +CCM_NOPIPE_INVDBLPR_2, /* <l1> could not be pipelined as it contains too */ + /* many loop invariant doubles = <i2> */ +CCM_PIPE_SCHEDAFIPR_2, /* <l1> was adversely affected by high */ + /* integer register pressure = <i2> */ +CCM_PIPE_SCHEDAFDPR_2, /* <l1> was adversely affected by high */ + /* double register pressure = <i2> */ +CCM_PIPE_SCHEDAFFPR_2, /* <l1> was adversely affected by high */ + /* float register pressure = <i2> */ +CCM_NOPIPE_INTPR_2, /* <l1> could not be pipelined due to high */ + /* integer register pressure = <i2> */ +CCM_NOPIPE_DBLPR_2, /* <l1> could not be pipelined due to high */ + /* double register pressure = <i2> */ +CCM_NOPIPE_FLTPR_2, /* <l1> could not be pipelined due to high */ + /* float register pressure = <i2> */ + + /* Group: Inlining Messages */ +CCM_INLINE=0x04000, /* Function <p1> inlined from source file <s2> into */ + /* the code for the following line */ +CCM_INLINE2, /* Function <p1> inlined from source file <s2> into */ + /* inline copy of function <p3> */ +CCM_INLINE_TMPLT, /* Function <p1> inlined from template file <s2> */ + /* into the code for the following line */ +CCM_INLINE_TMPLT2, /* Function <p1> inlined from template file <s2> */ + /* into inline copy of function <p3> */ +CCM_INLINE_OUT_COPY, /* Out-of-line copy of inlined function <p1> from */ + /* source file <s2> generated */ +CCM_NINLINE_REC, /* Recursive function <p1> inlined only up to */ + /* depth <i2> */ +CCM_NINLINE_NEST, /* Function <p1> not inlined because inlining is */ + /* already nested too deeply */ +CCM_NINLINE_CMPLX, /* Function <p1> not inlined because it contains */ + /* too many operations */ +CCM_NINLINE_FB, /* Function <p1> not inlined because the */ + /* profile-feedback execution count is too low */ +CCM_NINLINE_PAR, /* Function <p1> not inlined because it contains */ + /* explicit parallel pragmas */ +CCM_NINLINE_OPT, /* Function <p1> not inlined because it is */ + /* compiled with optimization level <= 2 */ +CCM_NINLINE_USR, /* Function <p1> not inlined because either command */ + /* line option or source code pragma prohibited it, */ + /* or it's not safe to inline it */ +CCM_NINLINE_AUTO, /* Function <p1> not inlined because doing so */ + /* would make automatic storage for <p2> too large */ +CCM_NINLINE_CALLS, /* Function <p1> not inlined because it contains */ + /* too many calls */ +CCM_NINLINE_ACTUAL, /* Function <p1> not inlined because it has more */ + /* actual parameters than formal parameters */ +CCM_NINLINE_FORMAL, /* Function <p1> not inlined because it has more */ + /* formal parameters than actual parameters */ +CCM_NINLINE_TYPE, /* Function <p1> not inlined because formal */ + /* argument type does not match actual type */ +CCM_NINLINE_ATYPE, /* Function <p1> not inlined because array formal */ + /* argument does not match reshaped array actual */ + /* argument type */ +CCM_NINLINE_RETTYPE, /* Function <p1> not inlined because return type */ + /* does not match */ +CCM_NINLINE_EXCPT, /* Function <p1> not inlined because it */ + /* guarded by an exception handler */ +CCM_NINLINE_UNSAFE, /* Function <p1> not inlined because it might be */ + /* unsafe (call alloca(), etc) */ +CCM_NINLINE_ALIAS, /* Function <p1> not inlined because inlining it */ + /* will make the alias analysis in the calling */ + /* function more conservative */ +CCM_NINLINE_FEMARK, /* Function <p1> not inlined because it contains */ + /* setjmp/longjmp, or indirect goto, etc */ +CCM_NINLINE_RAREX, /* Function <p1> not inlined because it is known */ + /* to be rarely executed */ +CCM_CLONING, /* Function <p1> from source file <s2> cloned, */ + /* creating cloned function <p3>; constant */ + /* parameters propagated to clone */ +CCM_INLINE_B, /* Function <p1> inlined from source file <s2> into */ + /* the code for the following line. <i3> loops */ + /* inlined */ +CCM_INLINE2_B, /* Function <p1> inlined from source file <s2> into */ + /* inline copy of function <p3>. <i4> loops inlined */ +CCM_INLINE_LOOP, /* Loop in function <p1>, line <i2> has */ + /* tag <l3> */ +CCM_NINLINE_MULTIENTRY, /* Function <p1> not inlined because it */ + /* contains an ENTRY statement */ +CCM_NINLINE_VARARGS, /* Function <p1> not inlined because variable */ + /* argument routines cannot be inlined */ +CCM_NINLINE_UNSEEN_BODY, /* Function <p1> not inlined because the compiler */ + /* has not seen the body of the function. Use */ + /* -xcrossfile or -xipo in order to inline it */ +CCM_NINLINE_UPLEVEL, /* Function <p1> not inlined because it is a */ + /* nested routine containing references to */ + /* variables defined in an outer function */ +CCM_NINLINE_CMDLINE, /* Function <p1> not inlined because either */ + /* -xinline or source code pragma prohibited it */ +CCM_NINLINE_CALL_CMPLX, /* Call to <p1> not inlined because of the */ + /* complexity of the calling routine */ +CCM_NINLINE_LANG_MISMATCH, /* Call to <p1> not inlined because it is in */ + /* a different language */ +CCM_NINLINE_RTN_WEAK, /* Function <p1> not inlined because it */ + /* is marked weak */ +CCM_NINLINE_CALL_WEAKFILE, /* Call to <p1> not inlined because it is */ + /* in a different file and it contains a */ + /* call to a weak routine */ +CCM_NINLINE_CALL_TRYCATCH, /* Call to <p1> not inlined because it is */ + /* in a different file and contains an */ + /* explicit try/catch */ +CCM_NINLINE_CALL_REGP, /* Call to <p1> not inlined because it would */ + /* cause excessive register pressure */ +CCM_NINLINE_RTN_REGP, /* Function <p1> not inlined because it would */ + /* cause excessive register pressure */ +CCM_NINLINE_CALL_XPENSV, /* Call to <p1> not inlined because analysis */ + /* exceeds the compilation time limit */ +CCM_NINLINE_READONLYIR, /* Function <p1> not inlined because it is in a file */ + /* specified as read-only by -xipo_archive=readonly */ + /* and it contains calls to static functions */ +CCM_NINLINE_CALL_THUNK, /* Call to <p1> not inlined because it is in a */ + /* compiler-generated function that does not */ + /* permit inlining */ +CCM_NINLINE_CALL_XTARGETS, /* Indirect callsite has too many targets; */ + /* callsite marked do not inline */ +CCM_NINLINE_SELFTAIL_RECURSIVE, /* Function <p1> not inlined because */ + /* of a recursive tail-call to itself */ +CCM_NINLINE_PRAGMA, /* Function <p1> not inlined because it contains */ + /* explicit parallel or alias pragmas */ +CCM_NINLINE_CMPLX2, /* Function <p1> not inlined because it contains too */ + /* many operations. Increase max_inst_hard in order */ + /* to inline it: -xinline_param=max_inst_hard:n */ +CCM_NINLINE_RARE, /* Function <p1> not inlined because the call */ + /* is rarely executed */ +CCM_NINLINE_PAR2, /* Function <p1> not inlined because it is called */ + /* within a region guarded by an explicit */ + /* parallel pragmas */ +CCM_NINLINE_G_LIMIT, /* Function <p1> not inlined because it would exceed */ + /* the permitted global code size growth limit. Try */ + /* to increase max_growth in order to inline it: */ + /* -xinline_param=max_growth:n */ +CCM_NINLINE_L_LIMIT, /* Function <p1> not inlined because it would exceed */ + /* the maximum function size growth limit. Increase */ + /* max_function_inst in order to inline it: */ + /* -xinline_param=max_function_inst:n */ +CCM_NINLINE_REC2, /* Recursive function <p1> is inlined only up to */ + /* <i2> levels and up to <i3> size. Increase */ + /* max_recursive_deptha or max_recursive_inst in */ + /* order to inline it: */ + /* -xinline_param=max_recursive_depth:n, */ + /* -xinline_param=max_recursive_inst:n */ +CCM_NINLINE_FB2, /* Function <p1> not inlined because the */ + /* profile-feedback execution count is too */ + /* low. Decrease min_counter in order to inline it: */ + /* -xinline_param:min_counter:n */ +CCM_NINLINE_CS_CMPLX, /* Function <p1> not inlined because called */ + /* function's size is too big. Increase */ + /* max_inst_soft in order to inline it: */ + /* -xinline_param=max_inst_soft:n */ +CCM_NINLINE_R_EXCPT, /* Function <p1> not inlined because it contains */ + /* an exception handler */ +CCM_NINLINE_ASM, /* Function <p1> not inlined because */ + /* it contains asm statements */ +CCM_NINLINE_R_READONLYIR, /* Function <p1> not inlined because it is in a file */ + /* specified as read-only by -xipo_archive=readonly */ + /* and it is a static function */ +CCM_NINLINE_C_READONLYIR, /* Call to <p1> not inlined because the calling */ + /* function is in a file specified as read-only */ + /* by -xipo_archive=readonly */ +CCM_NINLINE_NEVERRETURN, /* Function <p1> not inlined because it */ + /* never returns */ + + /* Group: Messages Concerning Memory Operations */ + /* Notes: */ + /* a. In all of these, <s1> is a string that is something like */ + /* "A(i+5*k)" or "structure.field", giving the high-level */ + /* construct that is being loaded or stored. */ + /* */ + /* b. In all of these, <x2> refers to an instruction offset, */ + /* expressed as a 32-bit signed integer. It is assumed */ + /* that any prefetches will be within this range of the */ + /* load/store they are prefetching for. */ +CCM_MPREFETCH=0x08000, /* Prefetch of <s1> inserted */ + /* [This message has a lineno for the source, */ + /* but no instaddr for the disassembly.] */ +CCM_MPREFETCH_LD, /* Prefetch of <s1> inserted for load at <x2> */ + /* [This message has lineno = -1, */ + /* and is for disassembly only] */ +CCM_MPREFETCH_ST, /* Prefetch of <s1> inserted for store at <x2> */ + /* [This message has lineno = -1, */ + /* and is for disassembly only] */ +CCM_MPREFETCH_FB, /* Prefetch of <s1> inserted based on feedback data */ + /* [This message has a lineno for the source, */ + /* but no instaddr for the disassembly.] */ +CCM_MPREFETCH_FB_LD, /* Prefetch of <s1> inserted for load at <x2> based */ + /* on feedback data */ + /* [This message has lineno = -1, */ + /* and is for disassembly only] */ +CCM_MPREFETCH_FB_ST, /* Prefetch of <s1> inserted for store at <x2> based */ + /* on feedback data */ + /* [This message has lineno = -1, */ + /* and is for disassembly only] */ +CCM_MLOAD, /* Load below refers to <s1> */ + /* [This message has lineno = -1, */ + /* and is for disassembly only] */ +CCM_MSTORE, /* Store below refers to <s1> */ + /* [This message has lineno = -1, */ + /* and is for disassembly only] */ +CCM_MLOAD_P, /* Load below refers to <s1>, and was prefetched */ + /* at <x2> */ + /* [This message has lineno = -1, */ + /* and is for disassembly only] */ +CCM_MSTORE_P, /* Store below refers to <s1>, and was prefetched */ + /* at <x2> */ + /* [This message has lineno = -1, */ + /* and is for disassembly only] */ + + /* Group: Front-end messages [all compilers] */ + /* Group: F95 Front-end Messages */ +CCM_COPYIN=0x10000, /* Parameter <i1> caused a copyin in the following */ + /* call */ +CCM_COPYOUT, /* Parameter <i1> caused a copyout in the following */ + /* call */ +CCM_COPYINOUT, /* Parameter <i1> caused both a copyin and copyout */ + /* in the following call */ +CCM_PADDING, /* Padding of <i1> bytes inserted before */ + /* array <v2> */ +CCM_PADCOMMON, /* Padding of <i1> bytes inserted before */ + /* array <v2> in common block <v3> */ +CCM_ALIGN_EQ, /* Variable/array <v1> can not be double-aligned, */ + /* because it is equivalenced */ +CCM_ALIGN_PERF, /* Alignment of variables in common block may cause */ + /* performance degradation */ +CCM_ALIGN_STRUCT, /* Alignment of component <s1> in numeric sequence */ + /* structure <s2> may cause performance degradation */ +CCM_TMP_COPY, /* Argument <v1> copied to a temporary */ +CCM_TMP_COPYM, /* Argument <v1> might be copied to a temporary; */ + /* runtime decision made */ +CCM_PROC_MISMATCH, /* Argument <i1> to subprogram <p2> differs from */ + /* reference on line <i3> */ +CCM_PROC_MISMATCH2, /* Scalar argument <i1> to subprogram <p2> is */ + /* referred to as an array on line <i3> */ +CCM_PROC_MISMATCH3, /* Return type/rank from subprogram <p1> differs */ + /* from return on line <i2> */ +CCM_DO_EXPR, /* DO statement bounds lead to no executions of the */ + /* loop */ +CCM_AUTO_BND, /* The bounds for automatic variable <v1> are not */ + /* available at all entry points; zero-length */ + /* variable might be allocated */ +CCM_LIT_PAD, /* The character string literal <s1> padded */ + /* to the length specified for the dummy argument */ +CCM_ARRAY_LOOP, /* Array statement below generated a loop */ +CCM_ARRAY_LOOPNEST, /* Array statement below generated <i1> nested loops */ +CCM_ALIGN_PERF2, /* Alignment of variable <v1> in common block <v2> */ + /* may cause a performance degradation */ +CCM_ALIGN_PERF3, /* Alignment of variable <v1> in blank common may */ + /* cause a performance degradation */ +CCM_IO_LOOP_ARRAY, /* I/O implied do item below generated an array */ + /* section */ + + /* Group: C++ Front-end Messages */ +CCM_TMPCONST, /* Implicit invocation of class <s1> constructor for */ + /* temporary */ +CCM_TMPDEST, /* Implicit invocation of class <s1> destructor for */ + /* temporary */ +CCM_DBL_CONST, /* Double constant <s1> used in float expression */ +CCM_MINLINE, /* Function <p1> inlined from source file <s2> by */ + /* front-end */ + /* [This refers to front-end inlining, */ + /* not the backend inlining above.] */ +CCM_MINLINE2, /* Function <p1> from source file <s2> inlined into */ + /* inline copy of method <p3> by front-end */ + /* [This refers to front-end inlining, */ + /* not the backend inlining above.] */ +CCM_MINLINE3, /* Function <p1> not inlined because it uses keyword */ + /* <s2> */ +CCM_MINLINE4, /* Function <p1> not inlined because it is too */ + /* complex */ +CCM_TMP_COPYOUT, /* Argument <v1> copied from a temporary */ +CCM_TMP_COPYOUTM, /* Argument <v1> might be copied from a temporary; */ + /* runtime decision made */ +CCM_TMP_COPYINOUT, /* Argument <v1> copied in and out of a temporary */ +CCM_TMP_COPYINOUTM, /* Argument <v1> might be copied in and out of */ + /* a temporary; runtime decision made */ + + /* Group: C Front-end Messages */ + /* Group: NJC Front-end Messages */ + /* Group: Updated F95 Front-end Messages */ +CCM_ARRAY_LOOP_2, /* Array statement below generated loop <l1> */ +CCM_ARRAY_LOOPNEST_2, /* Array statement below generated <i1> nested */ + /* loops: <l2>, <l3>, ... */ + /* [The number of parameters will determine how many */ + /* names appear, and the formatter will get the */ + /* commas right.] */ +CCM_IO_LOOP_ARRAY_2, /* I/O implied do item below generated an array */ + /* section: <l1> */ +CCM_USER_LOOP, /* Source loop below has tag <l1> */ +CCM_FOUND_LOOP, /* Discovered loop below has tag <l1> */ +CCM_MFUNCTION_LOOP, /* Copy in M-function of loop below has tag <l1> */ + + /* Group: Code-generator Messages */ +CCM_FSIMPLE=0x20000, /* Transformations for fsimple=<i1> applied */ +CCM_STACK, /* Function <p1> requires <i2> Mbytes of stack */ + /* storage */ +CCM_TAILRECUR, /* Recursive tail call in <p1> optimized to jump to */ + /* entry point */ +CCM_TAILCALL, /* Call to function <p1> was tail-call optimized */ +CCM_NI_EXIT_OR_PSEUDO, /* Template could not be early inlined because it */ + /* contains the pseudo instruction <s1> */ +CCM_NI_BAD_UNARY_OPC, /* Template could not be early inlined because it */ + /* contains the instruction opcode <s1> */ +CCM_NI_INT_LDD_ON_V9, /* Template could not be early inlined because it */ + /* contains integer ldd instructions, which are */ + /* deprecated in the v9 architecture */ +CCM_NI_LATE_INL_OPC, /* Template could not be early inlined because it */ + /* contains the instruction opcode <s1> */ +CCM_NI_BAD_IMM_OP, /* Template could not be early inlined because the */ + /* relocation or immediate operand <s1> is not well */ + /* understood by the optimizer */ +CCM_NI_BAD_STATELEAF, /* Template could not be early inlined because it */ + /* references the state register <s1> */ +CCM_NI_BAD_ASR_19, /* Template could not be early inlined because */ + /* %asr19 is not supported in pre v8plus code */ +CCM_NI_BAD_FSR_USE, /* Template could not be early inlined because */ + /* references to %fsr can only be optimized when the */ + /* -iaopts flag is used */ +CCM_NI_BAD_REGISTER, /* Template could not be early inlined because it */ + /* references the register <s1> */ +CCM_NI_NO_RET_VAL, /* Template could not be early inlined because it */ + /* does not return the value declared */ +CCM_NI_DELAY, /* Template could not be early inlined because it */ + /* contains a non nop delay slot */ +CCM_NI_SCALL, /* Template could not be early inlined because it */ + /* calls a function which returns a structure */ +CCM_CASE_POSITION, /* Case block below was placed at position <i1> */ + /* based on execution frequency */ +CCM_CALL_WITH_CODE, /* Call to <p1> replaced with inline code. <i2> */ + /* loops created: <l3>, <l4>, ... */ +CCM_NI_BAD_SP_ADDR, /* Template could not be early inlined because it */ + /* contains a %sp+reg address */ +CCM_NI_BAD_SP_USAGE, /* Template could not be early inlined because it */ + /* uses/defines the stack pointer in a non-load/store instruction */ +CCM_NI_MIXED_REG_TYPES, /* Template could not be early inlined because it */ + /* contains register <s1> used as both x-register and register pair */ +CCM_LAST +} COMPMSG_ID; +/* + * The Message Structure + * Each message is a fixed-length structure as follows: + */ +typedef struct +{ + int64_t instaddr; /* the PC offset, relative to the .o .text section */ + int32_t lineno; /* the source line to which it refers */ + COMPMSG_ID msg_type; /* the specific message index */ + int32_t nparam; /* number of parameters to this message */ + int32_t param_index; /* the index of the first parameter */ +} compmsg; + +#if defined(__cplusplus) +extern "C" +{ +#endif + /* + * Initializes the data structures, converts the source name to a string, + * and fills in srcname and version in the header + */ + void compcom_p_open (char *srcname, int32_t version); + + /* + * Finds or enters the string s into the string table, and returns the index + * of the string + */ + int32_t compcom_p_string (char *s); + + /* + * Enter the single message. Any string parameters should have been converted + * to int32's by calling compcom_p_string() + */ + void compcom_p_putmsg (int32_t show_bits, int64_t pcoffset, int32_t lineno, + COMPMSG_ID m, int32_t nparams); + + /* + * Whatever is needed to close the section and write it out to the .o + */ + void compcom_p_finalize (); + +#if defined(__cplusplus) +} +#endif + +#endif /* _COMP_COM_H */ diff --git a/gprofng/src/count.cc b/gprofng/src/count.cc new file mode 100644 index 0000000..6005a49 --- /dev/null +++ b/gprofng/src/count.cc @@ -0,0 +1,237 @@ +/* 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <strings.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <i18n.h> +#include <Elf.h> +#include <collctrl.h> +#include <StringBuilder.h> +#include "collect.h" + +/* get_count_data -- format exec of bit to do the real work */ +void +collect::get_count_data () +{ + char command[8192]; + char *s; + struct stat statbuf; + + // reserve space for original args, plus 30 arguments to bit + nargs = origargc + 30; + char **narglist = (char **) calloc (nargs, sizeof (char *)); + arglist = narglist; + + // construct the command for bit + snprintf (command, sizeof (command), NTXT ("%s"), run_dir); + s = strstr_r (command, NTXT ("/bin")); + if (s != NULL) + { + // build command line for launching it + snprintf (s, sizeof (command) - (s - command), NTXT ("/lib/compilers/bit")); + if (stat (command, &statbuf) == -1) + { + // if bit command does not exist there + char *first_look = strdup (command); + snprintf (command, sizeof (command), NTXT ("%s"), run_dir); + s = strstr (command, NTXT ("/bin")); + snprintf (s, sizeof (command) - (s - command), NTXT ("/prod/bin/bit")); + if (stat (command, &statbuf) == -1) + { + // if bit command does not exist + dbe_write (2, GTXT ("bit is not installed as `%s' or `%s'\nNo experiment is possible\n"), first_look, command); + exit (2); + } + free (first_look); + } + *arglist++ = strdup (command); + } + else + { + dbe_write (2, GTXT ("collect can't find install bin directory\n")); + exit (1); + } + + // Tell it to collect data + *arglist++ = NTXT ("collect"); + + // add the flag for real-data vs. static data + switch (cc->get_count ()) + { + case -1: + *arglist++ = NTXT ("-i"); + *arglist++ = NTXT ("static"); + *arglist++ = NTXT ("-M"); + break; + case 1: + *arglist++ = NTXT ("-M"); + *arglist++ = NTXT ("-u"); + break; + default: + abort (); + } + + // tell bit to produce an experiment + *arglist++ = NTXT ("-e"); + + // now copy an edited list of collect options to the arglist + char **oargv = origargv; + + // skip the "collect" + oargv++; + int argc = 1; + while (argc != targ_index) + { + char *p = *oargv; + switch (p[1]) + { + // pass these arguments along, with parameter + case 'o': + case 'd': + case 'g': + case 'A': + case 'C': + case 'O': + case 'N': + *arglist++ = *oargv++; + *arglist++ = *oargv++; + argc = argc + 2; + break; + case 'I': + *arglist++ = *oargv++; // set the -I flag + *arglist++ = *oargv; // and the directory name + *arglist++ = NTXT ("-d"); // and the -d flag + *arglist++ = *oargv++; // to the same directory name + argc = argc + 2; + break; + case 'n': + case 'v': + // pass these arguments along as is + *arglist++ = *oargv++; + argc = argc + 1; + break; + case 'x': + // skip one argument + oargv++; + argc++; + break; + case 'c': + case 'L': + case 'y': + case 'l': + case 'F': + case 'j': + case 'J': + case 'p': + case 's': + case 'h': + case 'S': + case 'm': + case 'M': + case 'H': + case 'r': + case 'i': + // skip two arguments + oargv++; + oargv++; + argc = argc + 2; + break; + case 'R': + case 'Z': + default: + // these should never get this far + dbe_write (2, GTXT ("unexpected argument %s\n"), p); + abort (); + } + } + + // now copy the target and its arguments + if (access (prog_name, X_OK) != 0) // not found + *arglist++ = *oargv++; + else + { + oargv++; + *arglist++ = prog_name; + } + while (*oargv != NULL) + *arglist++ = *oargv++; + + /* now we have the full argument list composed; if verbose, print it */ + if ((verbose == 1) || (disabled)) + { + /* describe the experiment */ + char *ccret = cc->show (0); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + } + ccret = cc->show_expt (); + if (ccret != NULL) + { + /* write this to stdout */ + writeStr (1, ccret); + free (ccret); + } + /* print the arguments to bit */ + arglist = narglist; + StringBuilder sb; + sb.append (NTXT ("Exec argv[] = ")); + for (int ret = 0; ret < nargs; ret++) + { + if (narglist[ret] == NULL) + break; + if (ret > 0) + sb.append (NTXT (" ")); + sb.append (narglist[ret]); + } + sb.append (NTXT ("\n\n")); + write (2, sb.toString (), sb.length ()); + } + + /* check for dry run */ + if (disabled) + exit (0); + + /* ensure original outputs restored for target */ + reset_output (); + + /* now exec the bit to instrument and run the target ... */ + // (void) execve( *narglist, narglist, origenvp); + (void) execvp (*narglist, narglist); + + /* exec failed; no experiment to delete */ + /* restore output for collector */ + set_output (); + char *em = strerror (errno); + if (em == NULL) + dbe_write (2, GTXT ("execve of %s failed: errno = %d\n"), narglist[0], errno); + else + dbe_write (2, GTXT ("execve of %s failed: %s\n"), narglist[0], em); + exit (1); +} diff --git a/gprofng/src/data_pckts.h b/gprofng/src/data_pckts.h new file mode 100644 index 0000000..93d0307 --- /dev/null +++ b/gprofng/src/data_pckts.h @@ -0,0 +1,595 @@ +/* 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. */ + +#ifndef _DATA_PCKTS_H +#define _DATA_PCKTS_H + +/* + * This file contains structure definitions for the binary file formats + * used in the experiment. It is implemented as C header file so that + * it can be processed by both ANSI-C and C++. + */ + +#include <pthread.h> +#include <stdint.h> + +#include "gp-defs.h" +#include "gp-time.h" + +#if WSIZE(64) +typedef uint64_t Vaddr_type; /* process address for 64 bit apps */ +typedef uint64_t Size_type; /* size_t for 64 bit apps */ +#else +typedef uint32_t Vaddr_type; /* process address */ +typedef uint32_t Size_type; /* size_t for 32 bit apps */ +#endif + +/* marker to indicate dump of O7 register on stack (support for leaf routines) */ +#define SP_LEAF_CHECK_MARKER ((uint64_t)(-1)) + +/* marker to indicate truncated stack */ +#define SP_TRUNC_STACK_MARKER ((uint64_t)(-2)) + +/* marker to indicate failed stack unwind */ +#define SP_FAILED_UNWIND_MARKER ((uint64_t)(-3)) + +#define PROFILE_BUFFER_CHUNK 16384 + +typedef enum +{ + MASTER_SMPL = 0, + PROGRAM_SMPL, + PERIOD_SMPL, + MANUAL_SMPL +} Smpl_type; + +typedef enum +{ /* values for "profpckt kind" stored in log.xml */ + EMPTY_PCKT = 0, + PROF_PCKT, + SYNC_PCKT, + HW_PCKT, + XHWC_PCKT, + HEAP_PCKT, + MPI_PCKT, + MHWC_PCKT, + OPROF_PCKT, + OMP_PCKT, + RACE_PCKT, + FRAME_PCKT, + OMP2_PCKT, + DEADLOCK_PCKT, + OMP3_PCKT, + OMP4_PCKT, + OMP5_PCKT, + UID_PCKT, + FRAME2_PCKT, + IOTRACE_PCKT, + LAST_PCKT, /* last data packet type */ + CLOSED_PCKT = 65535 /* -1, this packet closes a block */ +} Pckt_type; + +typedef enum +{ + EMPTY_INFO = 0, + STACK_INFO, + JAVA_INFO, + OMP_INFO, + MPI_INFO, + OMP2_INFO, + LAST_INFO /* keep this one last */ +} Info_type; + +#define COMPRESSED_INFO 0x80000000 + +#define JAVA_PCKT 0x80 +#define OMPS_PCKT 0x40 /* packet contains OMP state info */ +#define PCKT_TYPE(x) ((x) & 0x1f) + +typedef struct CommonHead_packet +{ + unsigned int tsize : 16; + unsigned int type : 16; +} CommonHead_packet; + +// All collector modules record their packets as extensions of CM_Packet +typedef struct CM_Packet +{ + unsigned int tsize : 16; + unsigned int type : 16; +} CM_Packet; + +typedef struct Common_packet +{ + unsigned int tsize : 16; /* packet size */ + unsigned int type : 16; + pthread_t lwp_id; + pthread_t thr_id; + uint32_t cpu_id; + hrtime_t tstamp; + uint64_t frinfo; +} Common_packet; + +/* Definition of values stored in the experiment PROP_MSTATE field */ +/* They include: + * LWP microstates (copied from msacct.h). Also see PrUsage class. + * Linux's CPU time + * er_kernel time + */ +/* Can be used with LMS_STATE_STRINGS (below) */ +#define LMS_USER 0 /* running in user mode */ +#define LMS_SYSTEM 1 /* running in sys call or page fault */ +#define LMS_TRAP 2 /* running in other trap */ +#define LMS_TFAULT 3 /* asleep in user text page fault */ +#define LMS_DFAULT 4 /* asleep in user data page fault */ +#define LMS_KFAULT 5 /* asleep in kernel page fault */ +#define LMS_USER_LOCK 6 /* asleep waiting for user-mode lock */ +#define LMS_SLEEP 7 /* asleep for any other reason */ +#define LMS_WAIT_CPU 8 /* waiting for CPU (latency) */ +#define LMS_STOPPED 9 /* stopped (/proc, jobcontrol, or lwp_stop) */ +#define LMS_LINUX_CPU 10 /* LINUX timer_create(CLOCK_THREAD_CPUTIME_ID) */ +#define LMS_KERNEL_CPU 11 /* LINUX timer_create(CLOCK_THREAD_CPUTIME_ID) */ +#define LMS_NUM_STATES 12 /* total number of above states */ +#define LMS_NUM_SOLARIS_MSTATES 10 /* LMS microstates thru LMS_STOPPED */ + +// Magic value stored in experiments that identifies which LMS states are valid +#define LMS_MAGIC_ID_SOLARIS 10 // Solaris: LMS_USER thru LMS_STOPPED +#define LMS_MAGIC_ID_ERKERNEL_USER 2 // er_kernel user: LMS_USER, LMS_SYSTEM +#define LMS_MAGIC_ID_ERKERNEL_KERNEL 3 // er_kernel kernel: LMS_KERNEL_CPU +#define LMS_MAGIC_ID_LINUX 1 // Linux: LMS_LINUX_CPU + +#define LMS_STATE_STRINGS \ +{ \ + NTXT("USER"), /* LMS_USER */ \ + NTXT("SYSTEM"), /* LMS_SYSTEM */ \ + NTXT("TRAP"), /* LMS_TRAP */ \ + NTXT("TFAULT"), /* LMS_TFAULT */ \ + NTXT("DFAULT"), /* LMS_DFAULT */ \ + NTXT("KFAULT"), /* LMS_KFAULT */ \ + NTXT("USER_LOCK"), /* LMS_USER_LOCK */ \ + NTXT("SLEEP"), /* LMS_SLEEP */ \ + NTXT("WAIT_CPU"), /* LMS_WAIT_CPU */ \ + NTXT("STOPPED"), /* LMS_STOPPED */ \ + NTXT("LINUX_CPU"), /* LMS_LINUX_CPU */ \ + NTXT("KERNEL_CPU") /* LMS_KERNEL_CPU */ \ +} +#define LMS_STATE_USTRINGS \ +{ \ + GTXT("User CPU"), /* LMS_USER */ \ + GTXT("System CPU"), /* LMS_SYSTEM */ \ + GTXT("Trap CPU"), /* LMS_TRAP */ \ + GTXT("Text Page Fault"), /* LMS_TFAULT */ \ + GTXT("Data Page Fault"), /* LMS_DFAULT */ \ + GTXT("Kernel Page Fault"), /* LMS_KFAULT */ \ + GTXT("User Lock"), /* LMS_USER_LOCK */ \ + GTXT("Sleep"), /* LMS_SLEEP */ \ + GTXT("Wait CPU"), /* LMS_WAIT_CPU */ \ + GTXT("Stopped"), /* LMS_STOPPED */ \ + GTXT("User+System CPU"), /* LMS_LINUX_CPU */ \ + GTXT("Kernel CPU") /* LMS_KERNEL_CPU */ \ +} + +typedef enum +{ + MALLOC_TRACE = 0, + FREE_TRACE, + REALLOC_TRACE, + MMAP_TRACE, + MUNMAP_TRACE, + HEAPTYPE_LAST +} Heap_type; + +#define HEAPTYPE_STATE_STRINGS \ +{ \ + NTXT("MALLOC"), \ + NTXT("FREE"), \ + NTXT("REALLOC"), \ + NTXT("MMAP"), \ + NTXT("MUNMAP") \ +} +#define HEAPTYPE_STATE_USTRINGS \ +{ \ + GTXT("malloc"), \ + GTXT("free"), \ + GTXT("realloc"), \ + GTXT("mmap"), \ + GTXT("munmap") \ +} + +typedef enum +{ + ZFS_TYPE = 0, + NFS_TYPE, + UFS_TYPE, + UDFS_TYPE, + LOFS_TYPE, + VXFS_TYPE, + TMPFS_TYPE, + PCFS_TYPE, + HSFS_TYPE, + PROCFS_TYPE, + FIFOFS_TYPE, + SWAPFS_TYPE, + CACHEFS_TYPE, + AUTOFS_TYPE, + SPECFS_TYPE, + SOCKFS_TYPE, + FDFS_TYPE, + MNTFS_TYPE, + NAMEFS_TYPE, + OBJFS_TYPE, + SHAREFS_TYPE, + EXT2FS_TYPE, + EXT3FS_TYPE, + EXT4FS_TYPE, + UNKNOWNFS_TYPE, + FSTYPE_LAST +} FileSystem_type; + +typedef enum +{ + READ_TRACE = 0, + WRITE_TRACE, + OPEN_TRACE, + CLOSE_TRACE, + OTHERIO_TRACE, + READ_TRACE_ERROR, + WRITE_TRACE_ERROR, + OPEN_TRACE_ERROR, + CLOSE_TRACE_ERROR, + OTHERIO_TRACE_ERROR, + IOTRACETYPE_LAST +} IOTrace_type; + +#define IOTRACETYPE_STATE_STRINGS \ +{ \ + NTXT("READ"), \ + NTXT("WRITE"), \ + NTXT("OPEN"), \ + NTXT("CLOSE"), \ + NTXT("OTHERIO"), \ + NTXT("READERROR"), \ + NTXT("WRITEERROR"), \ + NTXT("OPENERROR"), \ + NTXT("CLOSEERROR"), \ + NTXT("OTHERIOERROR") \ +} +#define IOTRACETYPE_STATE_USTRINGS \ +{ \ + GTXT("Read"), \ + GTXT("Write"), \ + GTXT("Open"), \ + GTXT("Close"), \ + GTXT("Other I/O"), \ + GTXT("Read error"), \ + GTXT("Write error"), \ + GTXT("Open error"), \ + GTXT("Close error"), \ + GTXT("Other I/O error") \ +} + +// the type of racing memory access with redundance flag +typedef enum +{ + WRITE_RACE = 0, + WRITE_RACE_RED, + READ_RACE, + READ_RACE_RED, + RACETYPE_LAST +} Race_type; + +typedef struct Frame_packet +{ + unsigned int tsize : 16; /* packet size */ + unsigned int type : 16; + uint32_t hsize; /* header size */ + uint64_t uid; /* unique id (experiment wide) */ +} Frame_packet; + +typedef struct Uid_packet +{ + unsigned int tsize : 16; /* packet size */ + unsigned int type : 16; + uint32_t flags; + uint64_t uid; /* unique id (experiment wide) */ +} Uid_packet; + +/* + * Components of the variable part of Frame_packet + */ +typedef struct Common_info +{ + unsigned int hsize; /* size of this info */ + unsigned int kind; + uint64_t uid; /* unique id of this info if any */ +} Common_info; + +typedef struct Stack_info +{ /* Native call stack */ + unsigned int hsize; + unsigned int kind; + uint64_t uid; +} Stack_info; + +typedef struct Java_info +{ /* Java call stack */ + unsigned int hsize; + unsigned int kind; + uint64_t uid; +} Java_info; + +typedef struct OMP_info +{ /* OMP thread state */ + unsigned int hsize; + unsigned int kind; + uint32_t omp_state; + uint32_t pad; +} OMP_info; + +typedef struct OMP2_info +{ /* OpenMP user call stack */ + unsigned int hsize; + unsigned int kind; + uint32_t omp_state; + uint32_t pad; + uint64_t uid; +} OMP2_info; + +/* OMP thread states as recorded in the experiment */ +/* Definition of values stored in the experiment PROP_OMPSTATE field */ + +/* Can be used with OMP_THR_STATE_STRINGS (below) */ +typedef enum +{ + OMP_NO_STATE = 0, /* Not initialized */ + OMP_OVHD_STATE, /* Overhead */ + OMP_WORK_STATE, /* Useful work, excluding reduction, master, single, critical */ + OMP_IBAR_STATE, /* In an implicit barrier */ + OMP_EBAR_STATE, /* In an explicit barrier */ + OMP_IDLE_STATE, /* Slave waiting */ + OMP_SERL_STATE, /* User OMPead not in any OMP parallel region */ + OMP_RDUC_STATE, /* Reduction */ + OMP_LKWT_STATE, /* Waiting for lock */ + OMP_CTWT_STATE, /* Waiting to enter critical section */ + OMP_ODWT_STATE, /* Waiting to execute an ordered section */ + OMP_ATWT_STATE, /* Wait for atomic */ + OMP_TSKWT_STATE, /* Task wait */ + OMP_LAST_STATE +} OMP_THR_STATE; +#define OMP_THR_STATE_STRINGS \ +{ \ + NTXT("NO"), /* OMP_NO_STATE */ \ + NTXT("OVHD"), /* OMP_OVHD_STATE */ \ + NTXT("WORK"), /* OMP_WORK_STATE */ \ + NTXT("IBAR"), /* OMP_IBAR_STATE */ \ + NTXT("EBAR"), /* OMP_EBAR_STATE */ \ + NTXT("IDLE"), /* OMP_IDLE_STATE */ \ + NTXT("SERL"), /* OMP_SERL_STATE */ \ + NTXT("RDUC"), /* OMP_RDUC_STATE */ \ + NTXT("LKWT"), /* OMP_LKWT_STATE */ \ + NTXT("CTWT"), /* OMP_CTWT_STATE */ \ + NTXT("ODWT"), /* OMP_ODWT_STATE */ \ + NTXT("ATWT"), /* OMP_ATWT_STATE */ \ + NTXT("TSKWT") /* OMP_TSKWT_STATE */ \ +} +#define OMP_THR_STATE_USTRINGS \ +{ \ + GTXT("None"), /* OMP_NO_STATE */ \ + GTXT("Overhead"), /* OMP_OVHD_STATE */ \ + GTXT("Work"), /* OMP_WORK_STATE */ \ + GTXT("Implicit Barrier"), /* OMP_IBAR_STATE */ \ + GTXT("Explicit Barrier"), /* OMP_EBAR_STATE */ \ + GTXT("Idle"), /* OMP_IDLE_STATE */ \ + GTXT("Serial"), /* OMP_SERL_STATE */ \ + GTXT("Reduction"), /* OMP_RDUC_STATE */ \ + GTXT("Lock Wait"), /* OMP_LKWT_STATE */ \ + GTXT("Critical Section Wait"), /* OMP_CTWT_STATE */ \ + GTXT("Ordered Section Wait"), /* OMP_ODWT_STATE */ \ + GTXT("Atomic Wait"), /* OMP_ATWT_STATE */ \ + GTXT("Task Wait") /* OMP_TSKWT_STATE */ \ +} + +/* sub-packet for MPI state information */ +typedef struct MPI_info +{ /* MPI thread state */ + unsigned int hsize; + unsigned int kind; + uint32_t mpi_state; + uint32_t pad; +} MPI_info; + +/* MPI thread states, as recorded in the experiment */ +typedef enum +{ + MPI_NO_STATE = 0, /* Not initialized */ + MPI_USER, /* Executing user code, not in MPI */ + MPI_PROG, /* Executing in the MPI library (progressing) */ + MPI_WAIT /* Waiting in the MPI library */ +} MPI_THR_STATE; + +/* + * Dyntext file structure + */ +typedef enum +{ + DT_HEADER = 1, + DT_CODE, + DT_LTABLE, + DT_SRCFILE +} DT_type; + +typedef struct DT_common +{ + DT_type type; + unsigned int size; +} DT_common; + +typedef struct DT_header +{ + DT_type type; + unsigned int size; + hrtime_t time; /* time of loading */ + uint64_t vaddr; +} DT_header; + +typedef struct DT_code +{ + DT_type type; + unsigned int size; +} DT_code; + +typedef struct DT_ltable +{ + DT_type type; + unsigned int size; +} DT_ltable; + +typedef struct DT_lineno +{ + unsigned int offset; + unsigned int lineno; +} DT_lineno; + +typedef struct DT_srcfile +{ + DT_type type; + unsigned int size; +} DT_srcfile; + +/* + * Archive file structure + */ +#define ARCH_VERSION 0x100 /* version 1.0 */ + +/* For compatibility with older archives append new types only */ +typedef enum +{ + ARCH_SEGMENT_TYPE = 1, + ARCH_MSG_TYPE, + ARCH_PLT_TYPE, + ARCH_MODULE_TYPE, + ARCH_FUNCTION_TYPE, + ARCH_LDINSTR_TYPE, + ARCH_STINSTR_TYPE, + ARCH_PREFETCH_TYPE, + ARCH_BRTARGET_TYPE, + ARCH_JCLASS_TYPE, + ARCH_JMETHOD_TYPE, + ARCH_JUNLOAD_TYPE, + ARCH_INF_TYPE, + ARCH_JCLASS_LOCATION_TYPE +} ARCH_type; + +#define ARCH_TYPE(x,y) ((ARCH_##x##_TYPE<<8)|y) + +typedef struct +{ + unsigned int type : 16; + unsigned int size : 16; +} ARCH_common; + +/* The maximum value that fits into ARCH_common.size */ +#define ARCH_MAX_SIZE 0xffff + +#define ARCH_SEGMENT ARCH_TYPE(SEGMENT, 0) + +typedef struct +{ + ARCH_common common; + int version; + uint32_t inode; + uint32_t textsz; /* text segment size */ + uint32_t platform; /* sparc, intel, etc. */ +} ARCH_segment; + +#define ARCH_MSG ARCH_TYPE(MSG, 0) + +typedef struct +{ + ARCH_common common; + uint32_t errcode; +} ARCH_message; + +#define ARCH_INF ARCH_TYPE(INF, 0) + +typedef struct +{ + ARCH_common common; +} ARCH_info; + +#define ARCH_MODULE ARCH_TYPE(MODULE, 0) + +typedef struct +{ + ARCH_common common; + unsigned int lang_code; + unsigned int fragmented; +} ARCH_module; + +#define ARCH_FUNCTION ARCH_TYPE(FUNCTION, 0) + +typedef struct +{ + ARCH_common common; + uint32_t offset; + uint32_t size; + uint32_t save_addr; +} ARCH_function; + +#define ARCH_LDINSTR ARCH_TYPE(LDINSTR, 0) +#define ARCH_STINSTR ARCH_TYPE(STINSTR, 0) +#define ARCH_PREFETCH ARCH_TYPE(PREFETCH, 0) +#define ARCH_BRTARGET ARCH_TYPE(BRTARGET, 0) + +typedef struct +{ + ARCH_common common; +} ARCH_aninfo; + +#define ARCH_JCLASS_LOCATION ARCH_TYPE(JCLASS_LOCATION, 3) + +typedef struct +{ + CM_Packet comm; + uint32_t pad; + uint64_t class_id; +} ARCH_jclass_location; + +#define ARCH_JCLASS ARCH_TYPE(JCLASS, 3) + +typedef struct +{ + CM_Packet comm; + uint32_t pad; + uint64_t class_id; + hrtime_t tstamp; +} ARCH_jclass; + +#define ARCH_JMETHOD ARCH_TYPE(JMETHOD, 3) + +typedef struct +{ + CM_Packet comm; + uint32_t pad; + uint64_t class_id; + uint64_t method_id; +} ARCH_jmethod; + +#endif /* _DATA_PCKTS_H */ diff --git a/gprofng/src/dbe_collctrl.cc b/gprofng/src/dbe_collctrl.cc new file mode 100644 index 0000000..9219a8e --- /dev/null +++ b/gprofng/src/dbe_collctrl.cc @@ -0,0 +1,28 @@ +/* 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 "config.h" +#include <stdio.h> +#include <stdarg.h> + +#include "i18n.h" + +#include "collctrl.cc" diff --git a/gprofng/src/dbe_hwc.h b/gprofng/src/dbe_hwc.h new file mode 100644 index 0000000..74b6838 --- /dev/null +++ b/gprofng/src/dbe_hwc.h @@ -0,0 +1,38 @@ +/* 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. */ + +#ifndef dbe_hwc_h +#define dbe_hwc_h + +#include <stdio.h> +#include <stdarg.h> + +#include "i18n.h" + +#define HWC_TRACELEVEL -1 +#if HWC_TRACELEVEL < 0 +#define TprintfT(x1,...) +#define Tprintf(x1,...) +#else +#define TprintfT(x1,...) if( x1<=HWC_TRACELEVEL ) fprintf(stderr,__VA_ARGS__) +#define Tprintf(x1,...) if( x1<=HWC_TRACELEVEL ) fprintf(stderr,__VA_ARGS__) +#endif + +#endif diff --git a/gprofng/src/dbe_hwcdrv.c b/gprofng/src/dbe_hwcdrv.c new file mode 100644 index 0000000..a471c3e --- /dev/null +++ b/gprofng/src/dbe_hwcdrv.c @@ -0,0 +1,23 @@ +/* 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 "dbe_hwc.h" +#include "hwcdrv.c" diff --git a/gprofng/src/dbe_hwcfuncs.c b/gprofng/src/dbe_hwcfuncs.c new file mode 100644 index 0000000..66d371a --- /dev/null +++ b/gprofng/src/dbe_hwcfuncs.c @@ -0,0 +1,23 @@ +/* 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 "dbe_hwc.h" +#include "hwcfuncs.c" diff --git a/gprofng/src/dbe_hwctable.c b/gprofng/src/dbe_hwctable.c new file mode 100644 index 0000000..ce077a1 --- /dev/null +++ b/gprofng/src/dbe_hwctable.c @@ -0,0 +1,23 @@ +/* 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 "dbe_hwc.h" +#include "hwctable.c" diff --git a/gprofng/src/dbe_memmgr.c b/gprofng/src/dbe_memmgr.c new file mode 100644 index 0000000..2c2e2b4 --- /dev/null +++ b/gprofng/src/dbe_memmgr.c @@ -0,0 +1,118 @@ +/* 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 <dlfcn.h> +#include "util.h" + +#define CHECK_OUT_OF_MEM(ptr, size) if (ptr == NULL) err_out_of_memory(size) + +/* Report Out of Memory error and exit */ +static void +err_out_of_memory (unsigned nbytes) +{ + char *nm = get_prog_name (1); + if (nm) + fprintf (stderr, GTXT ("%s: Error: Memory capacity exceeded.\n"), nm); + else + fprintf (stderr, GTXT ("Error: Memory capacity exceeded.\n")); + fprintf (stderr, GTXT (" Requested %u bytes.\n"), nbytes); + exit (16); +} + +#define CALL_REAL(x) (__real_##x) +#define NULL_PTR(x) ( __real_##x == NULL ) + +static void *(*__real_malloc)(size_t) = NULL; +static void (*__real_free)(void *) = NULL; +static void *(*__real_realloc)(void *, size_t) = NULL; +static void *(*__real_calloc)(size_t, size_t) = NULL; +static char *(*__real_strdup)(const char*) = NULL; +static volatile int in_init = 0; + +static int +init_heap_intf () +{ + in_init = 1; + __real_malloc = (void*(*)(size_t))dlsym (RTLD_NEXT, "malloc"); + __real_free = (void(*)(void *))dlsym (RTLD_NEXT, "free"); + __real_realloc = (void*(*)(void *, size_t))dlsym (RTLD_NEXT, "realloc"); + __real_calloc = (void*(*)(size_t, size_t))dlsym (RTLD_NEXT, "calloc"); + __real_strdup = (char*(*)(const char*))dlsym (RTLD_NEXT, "strdup"); + in_init = 0; + return 0; +} + +/* --------------------------------------------------------------------------- */ +/* libc's memory management functions substitutions */ + +/* Allocate memory and make sure we got some */ +void * +malloc (size_t size) +{ + if (NULL_PTR (malloc)) + init_heap_intf (); + void *ptr = CALL_REAL (malloc)(size); + CHECK_OUT_OF_MEM (ptr, size); + return ptr; +} + + +/* Implement a workaround for a libdl recursion problem */ +void * +calloc (size_t nelem, size_t size) +{ + if (NULL_PTR (calloc)) + { + /* If a program is linked with libpthread then the following + * calling sequence occurs: + * init_heap_intf -> dlsym -> calloc -> malloc -> init_heap_intf + * We break some performance improvement in libdl by returning + * NULL but preserve functionality. + */ + if (in_init) + return NULL; + init_heap_intf (); + } + return CALL_REAL (calloc)(nelem, size); +} + +/* Free the storage associated with data */ +void +free (void *ptr) +{ + if (ptr == NULL) + return; + if (NULL_PTR (free)) + init_heap_intf (); + CALL_REAL (free)(ptr); + return; +} + +/* Reallocate buffer */ +void * +realloc (void *ptr, size_t size) +{ + if (NULL_PTR (realloc)) + init_heap_intf (); + ptr = CALL_REAL (realloc)(ptr, size); + CHECK_OUT_OF_MEM (ptr, size); + return ptr; +} diff --git a/gprofng/src/dbe_structs.h b/gprofng/src/dbe_structs.h new file mode 100644 index 0000000..e6eaed6 --- /dev/null +++ b/gprofng/src/dbe_structs.h @@ -0,0 +1,219 @@ +/* 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. */ + +#ifndef _DBE_STRUCTS_H +#define _DBE_STRUCTS_H + +#include "dbe_types.h" +#include "enums.h" + +typedef enum +{ + Sp_lang_unknown = 0, + Sp_lang_asm = 1, + Sp_lang_c = 2, + Sp_lang_ansic = 3, + Sp_lang_cplusplus = 4, + Sp_lang_fortran = 5, + Sp_lang_pascal = 6, + Sp_lang_fortran90 = 7, + Sp_lang_java = 8, + Sp_lang_c99 = 9, + Sp_lang_gcc = 16, + Sp_lang_KAI_KPTS = 32, + Sp_lang_KAI_KCC = 33, + Sp_lang_KAI_Kcc = 34 +} Sp_lang_code; + +struct Value +{ + union + { + short s; + int i; + float f; + double d; + timestruc_t t; + char *l; // Label + unsigned long long ll; // address + }; +}; + +// sync enum changes with both AnMetric.java and AnVariable.java +enum ValueTag +{ + VT_SHORT = 1, + VT_INT, + VT_LLONG, + VT_FLOAT, + VT_DOUBLE, + VT_HRTIME, + VT_LABEL, + VT_ADDRESS, + VT_OFFSET, + VT_ULLONG +}; + +// Tagged numeric value +struct TValue +{ + ValueTag tag; + bool sign; // The print result will always begin with a sign (+ or -). + union + { + short s; + int i; + float f; + double d; + char *l; + void *p; + long long ll; + unsigned long long ull; + }; + double to_double (); + int to_int (); + char *to_str (char *str, size_t strsz); + size_t get_len (); + void make_delta (TValue *v1, TValue *v2); + void make_ratio (TValue *v1, TValue *v2); + int compare (TValue *v); +}; + +// XXX MAX_HWCOUNT may need to be managed dynamically, not #defined +#define MAX_HWCOUNT 64 + +// Experiment collection parameters +struct Collection_params +{ + int profile_mode; // if clock-profiling is on + long long ptimer_usec; // Clock profile timer interval (microseconds) + int lms_magic_id; // identifies which LMS_* states are live + int sync_mode; // if synctrace is on + int sync_threshold; // value of synctrace threshold, in microseconds + int sync_scope; // value of synctrace scope: Java and/or native + + int heap_mode; // if heaptrace is on + int io_mode; // if iotrace is on + int race_mode; // if race-detection is on + int race_stack; // setting for stack data collection + int deadlock_mode; // if deadlock-detection is on + int omp_mode; // if omptrace is on + + int hw_mode; // if hw-counter profiling is on + int xhw_mode; // if extended (true-PC) HW counter profiling for any counter + + char *hw_aux_name[MAX_HWCOUNT]; + char *hw_username[MAX_HWCOUNT]; + int hw_interval[MAX_HWCOUNT]; // nominal interval for count + int hw_tpc[MAX_HWCOUNT]; // non-zero, if aggressive TPC/VA requested + int hw_metric_tag[MAX_HWCOUNT]; // tag as used for finding metrics + int hw_cpu_ver[MAX_HWCOUNT]; // Chip version number for this metric + + int sample_periodic; // if periodic sampling is on + int sample_timer; // Sample timer (sec) + int limit; // experiment size limit + const char *pause_sig; // Pause/resume signal string + const char *sample_sig; // Sampling signal string + const char *start_delay; // Data collect start delay string + const char *terminate; // Data collection termination time string + char *linetrace; +}; + +const hrtime_t ZERO_TIME = (hrtime_t) 0; +const hrtime_t MAX_TIME = (hrtime_t) 0x7fffffffffffffffLL; + +#define PCInvlFlag ((int) 0x8LL) +#define PCLineFlag ((int) 0x4LL) +#define PCTrgtFlag ((int) 0x2LL) +#define MAKE_ADDRESS(idx, off) (((unsigned long long)(idx)<<32) | off) +#define ADDRESS_SEG(x) ((unsigned int)(((x)>>32) & 0xffffffff)) +#define ADDRESS_OFF(x) ((unsigned int)((x) & 0xffffffff)) + +// +// Analyzer info +#define AnalyzerInfoVersion 2 + +typedef struct +{ + uint64_t text_labelref; + int32_t entries; + uint32_t version; +} AnalyzerInfoHdr; // => header from .__analyzer_info + +typedef struct +{ + uint32_t offset; // offset relative to text_labelref + uint32_t id; // profiled instruction identifier + uint32_t signature; // signature of profiled instruction + uint32_t datatype_id; // referenced datatype identifier +} memop_info_t; // => used for table_type=0,1,2 + +typedef struct +{ + uint32_t offset; // offset relative to text_labelref +} target_info_t; // => used for table_type=3 + +typedef struct +{ + uint32_t type; + uint32_t offset; + union + { + memop_info_t *memop; + target_info_t *target; + }; +} inst_info_t; + +class DataObject; + +typedef struct +{ + uint32_t datatype_id; // datatype identifier (local) + uint32_t memop_refs; // count of referencing memops + uint32_t event_data; // count of event data + DataObject *dobj; // corresponding dataobject (unique) +} datatype_t; + +typedef struct +{ + uint32_t offset; // entry offset in compilation unit + uint32_t extent; // sibling offset + void *parent; // container symbol + void *object; // resolved object +} symbol_t; + +typedef struct +{ + char *old_prefix; + char *new_prefix; +} pathmap_t; + +typedef struct +{ + char *libname; + enum LibExpand expand; +} lo_expand_t; + +typedef struct +{ + int index1; + int index2; +} int_pair_t; +#endif /* _DBE_STRUCTS_H */ diff --git a/gprofng/src/dbe_types.h b/gprofng/src/dbe_types.h new file mode 100644 index 0000000..79fd6c7 --- /dev/null +++ b/gprofng/src/dbe_types.h @@ -0,0 +1,62 @@ +/* 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. */ + +#ifndef _DBE_TYPES_H +#define _DBE_TYPES_H + +#include <stdint.h> +#include "gp-time.h" + +typedef unsigned long long Size; /* object sizes in 64 bit apps */ +typedef unsigned long long Vaddr; /* process address for 64 bit apps */ + +typedef unsigned long long ull_t; +typedef long long ll_t; +typedef unsigned long ul_t; + +// Note: these values are stored in archive files; changing them +// may cause old archives to become incompatible. +enum Platform_t +{ + Unknown = 0, + Sparc, + Sparcv9, + Intel, + Sparcv8plus, + Java, + Amd64, + Aarch64 +}; + +enum WSize_t +{ + Wnone, + W32, + W64 +}; + +enum VMode +{ + VMODE_MACHINE = 0, + VMODE_USER, + VMODE_EXPERT +}; + +#endif /* _DBE_TYPES_H */ diff --git a/gprofng/src/debug.h b/gprofng/src/debug.h new file mode 100644 index 0000000..9761f2a --- /dev/null +++ b/gprofng/src/debug.h @@ -0,0 +1,89 @@ +/* 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. */ + +#ifndef _PERFAN_DEBUG_H +#define _PERFAN_DEBUG_H + +extern unsigned int mpmt_debug_opt; +// To set mpmt_debug_opt use: +// MPMT_DEBUG=4095 ; export MPMT_DEBUG +#define DEBUG_FLAG (mpmt_debug_opt & 1) +#define DUMP_ELF_SEC (mpmt_debug_opt & 2) +#define DUMP_ELF_SYM (mpmt_debug_opt & 4) +#define DUMP_RELA_SEC (mpmt_debug_opt & 8) +#define DUMP_ELF_RELOC DUMP_RELA_SEC +#define DUMP_DWARFLIB (mpmt_debug_opt & 16) +#define DUMP_DWR_LINE_REGS (mpmt_debug_opt & 32) +#define DUMP_USER_LABELS (mpmt_debug_opt & 64) +#define DEBUG_MAPS (mpmt_debug_opt & 128) +#define DEBUG_DBE_FILE (mpmt_debug_opt & 256) +#define DEBUG_DATA_WINDOW (mpmt_debug_opt & 512) +#define DEBUG_STABS (mpmt_debug_opt & 1024) +#define DEBUG_DATAOBJ (mpmt_debug_opt & 2048) +#define DEBUG_LOADOBJ (mpmt_debug_opt & 4096) +#define DEBUG_SAXPARSER (mpmt_debug_opt & 8192) +#define DUMP_JAVA_CLASS (mpmt_debug_opt & 16384) +#define DEBUG_COMPARISON (mpmt_debug_opt & 32768) +#define DEBUG_READ_AR (mpmt_debug_opt & 65536) +#define DEBUG_ERR_MSG (mpmt_debug_opt & 131072) +#define DUMP_JCLASS_READER (mpmt_debug_opt & 262144) +#define DEBUG_DBE (mpmt_debug_opt & 524288) +#define DEBUG_ARCHIVE (mpmt_debug_opt & 1048576) +#define DEBUG_IO (mpmt_debug_opt & 2097152) +#define DUMP_DYN_FILE (mpmt_debug_opt & 4194304) +#define DUMP_JAR_FILE (mpmt_debug_opt & 8388608) +#define DUMP_CALL_STACK (mpmt_debug_opt & 16777216) +#define DEBUG_THREADS (mpmt_debug_opt & 33554432) +#define DBE_USE_MMAP (mpmt_debug_opt & 67108864) + +#ifdef DEBUG + +// Turn on assertion checking whenever debugging +#define ASSERTS 1 + +// debug macro - provides a clean way of inserting debugging code without +// having the distracting #ifdef DEBUG ... #else ... #endif directives +// interspersed throughout the code. It also provides an easy way +// to turn them off with no loss of efficiency. It is not limited +// to printf() commands; any code may be inserted. Variables +// needed only by the debugging code can be declared inside a +// debug { ... } statement. +// +// usage: +// debug <statement> +// or, debug { <statements> } +// If DEBUG is on, map "DEBUG_CODE" to nothing! +// This results in the <statement> being executed normally + +#define DEBUG_CODE + +#else +// If DEBUG is off, map "DEBUG_CODE" to something harmless. +// The clever hack used here is to use a conditional with a +// constant condition, which is optimized out by the compiler, +// so that <statement> is not present in the compiled code! + +#define DEBUG_CODE if (0) + +#endif /*DEBUG*/ + +#define Dprintf(x, ...) DEBUG_CODE if(x) fprintf(stderr, __VA_ARGS__) + +#endif /* ! _DEBUG_H */ diff --git a/gprofng/src/enums.h b/gprofng/src/enums.h new file mode 100644 index 0000000..a2c9500 --- /dev/null +++ b/gprofng/src/enums.h @@ -0,0 +1,195 @@ +/* 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. */ + +#ifndef _PERFAN_ENUMS_H +#define _PERFAN_ENUMS_H + +#include "comp_com.h" + +enum Cmd_status +{ + CMD_OK = 0, + CMD_BAD, + CMD_AMBIGUOUS, + CMD_BAD_ARG, + CMD_OUTRANGE, + CMD_INVALID +}; + +enum LibExpand +{ + LIBEX_SHOW = 0, + LIBEX_HIDE = 1, + LIBEX_API = 2 +}; + +enum SrcVisible +{ + SRC_NA = 0, + SRC_CODE = 1, + SRC_METRIC = 2 +}; + +enum MetricType +{ // sync enum changes with Settings.java + MET_NORMAL = 0, // functions, lines, pcs; src & disasm (non-compare) + MET_CALL, // callers-callees + MET_DATA, // dataspace + MET_INDX, // index objects + MET_CALL_AGR, // call tree + MET_COMMON, // Analyzer uses for DSP_DISASM, DSP_SOURCE, ... + MET_IO, // IO activity + MET_SRCDIS, // src & disasm (non comparison mode) + MET_HEAP // Heap leaked list +}; + +enum ValueType +{ // Bitmask (!) sync enum changes with AnMetric.java + VAL_NA = 0, // nothing specified (use this enum instead of 0) + VAL_TIMEVAL = 1, + VAL_VALUE = 2, + VAL_PERCENT = 4, + VAL_DELTA = 8, + VAL_RATIO = 16, + VAL_INTERNAL = 32, + VAL_HIDE_ALL = 64 // hide all, but allows settings to be remembered +}; + +enum CompCom +{ // no value here can be the same as CCMV_ + COMP_SRC = CCMV_BASIC + 1, + COMP_SRC_METRIC, + COMP_NOSRC, + COMP_HEX, + COMP_NOHEX, + COMP_THRESHOLD, + COMP_CMPLINE, + COMP_FUNCLINE +}; + +enum TLStack_align +{ + TLSTACK_ALIGN_ROOT = 1, + TLSTACK_ALIGN_LEAF +}; + +enum Reorder_status +{ + REORDER_SUCCESS, + REORDER_FAIL, + REORDER_ZERO, + REORDER_ONE_FUNC, + REORDER_FILE_OPEN, + REORDER_FILE_WRITE, + REORDER_COMP, + REORDER_NO_LOAD_OBJ, + REORDER_NO_OBJECT, + REORDER_INVALID +}; + +enum AnUtility_state +{ + EXP_SUCCESS = 0, + EXP_FAILURE = 1, + EXP_INCOMPLETE = 2, + EXP_BROKEN = 4, + EXP_OBSOLETE = 8 +}; + +enum Presentation_align_type +{ + TEXT_LEFT = 1, + TEXT_CENTER = 2, + TEXT_RIGHT = 3 +}; + +enum Message_type +{ + ERROR_MSG = 1, + WARNING_MSG = 2, + PSTAT_MSG = 3, + PWARN_MSG = 4 +}; + +enum Presentation_clock_unit +{ + CUNIT_NULL = -1, + CUNIT_BYTES = -2, + CUNIT_TIME = -3 +}; + +enum FuncListDisp_type +{ + DSP_FUNCTION = 1, + DSP_LINE = 2, + DSP_PC = 3, + DSP_SOURCE = 4, + DSP_DISASM = 5, + DSP_SELF = 6, // not a tab; ID for Callers-Callees fragment data + DSP_CALLER = 7, + DSP_CALLEE = 8, // not a tab; ID for Callers-Callees callees data + DSP_CALLTREE = 9, + DSP_TIMELINE = 10, + DSP_STATIS = 11, + DSP_EXP = 12, + DSP_LEAKLIST = 13, + DSP_MEMOBJ = 14, // requires a specific subtype to define a tab + DSP_DATAOBJ = 15, + DSP_DLAYOUT = 16, + DSP_SRC_FILE = 17, // not a tab; Details information (?) + DSP_IFREQ = 18, + DSP_RACES = 19, + DSP_INDXOBJ = 20, // requires a specific subtype to define a tab + DSP_DUALSOURCE = 21, + DSP_SOURCE_DISASM = 22, + DSP_DEADLOCKS = 23, + DSP_MPI_TL = 24, + DSP_MPI_CHART = 25, + //DSP_TIMELINE_CLASSIC_TBR = 26, + DSP_SOURCE_V2 = 27, // comparison + DSP_DISASM_V2 = 28, // comparison + //DSP_THREADS_TL = 29; + //DSP_THREADS_CHART = 30; + DSP_IOACTIVITY = 31, + DSP_OVERVIEW = 32, + DSP_IOVFD = 33, + DSP_IOCALLSTACK = 34, + DSP_MINICALLER = 37, + DSP_HEAPCALLSTACK = 39, + DSP_CALLFLAME = 40, + DSP_SAMPLE = 99 +}; + +enum CmpMode +{ + CMP_DISABLE = 0, + CMP_ENABLE = 1, + CMP_RATIO = 2, + CMP_DELTA = 4 +}; + +enum PrintMode +{ + PM_TEXT = 0, + PM_HTML = 1, + PM_DELIM_SEP_LIST = 2 +}; + +#endif // _ENUMS_H diff --git a/gprofng/src/envsets.cc b/gprofng/src/envsets.cc new file mode 100644 index 0000000..de06fbf --- /dev/null +++ b/gprofng/src/envsets.cc @@ -0,0 +1,420 @@ +/* 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 <assert.h> +#include <ctype.h> +#include <sys/param.h> +#include <unistd.h> + +#include "gp-defs.h" +#include "util.h" +#include "collctrl.h" +#include "collect.h" +#include "StringBuilder.h" +#include "Settings.h" + +#define STDEBUFSIZE 24000 + +#define LIBGP_COLLECTOR "libgp-collector.so" +#define GPROFNG_PRELOAD_LIBDIRS "GPROFNG_PRELOAD_LIBDIRS" +#define SP_COLLECTOR_EXPNAME "SP_COLLECTOR_EXPNAME" +#define SP_COLLECTOR_FOLLOW_SPEC "SP_COLLECTOR_FOLLOW_SPEC" +#define SP_COLLECTOR_PARAMS "SP_COLLECTOR_PARAMS" +#define SP_COLLECTOR_FOUNDER "SP_COLLECTOR_FOUNDER" +#define SP_COLLECTOR_ORIGIN_COLLECT "SP_COLLECTOR_ORIGIN_COLLECT" + +static const char *LD_AUDIT[] = { + // "LD_AUDIT", Do not set LD_AUDIT on Linux + NULL +}; + +static const char *LD_PRELOAD[] = { + "LD_PRELOAD", + NULL +}; + +static const char *SP_PRELOAD[] = { + "SP_COLLECTOR_PRELOAD", + NULL +}; + +static const char *LD_LIBRARY_PATH[] = { + "LD_LIBRARY_PATH", + NULL, +}; + +static int +add_env (char *ev) +{ + int r = putenv (ev); + if (r != 0) + { + dbe_write (2, GTXT ("Can't putenv of %s: run aborted\n"), ev); + free (ev); + } + return r; +} + +int +collect::putenv_libcollector_ld_audits () +{ + StringBuilder sb; + for (unsigned int ii = 0; ii < ARR_SIZE (LD_AUDIT) && LD_AUDIT[ii]; ++ii) + { + sb.sprintf ("%s=%s", LD_AUDIT[ii], SP_LIBAUDIT_NAME); + // Append the current value. Check if already set + char *old_val = getenv (LD_AUDIT[ii]); + if (old_val != NULL) + { + while (isspace (*old_val)) + ++old_val; + if (*old_val != (char) 0) + { + int fromIdx = sb.length (); + sb.append (" "); + sb.append (old_val); + if (sb.indexOf (SP_LIBAUDIT_NAME, fromIdx) >= 0) + continue; // Already set. Do nothing. + } + } + if (add_env (sb.toString ())) + return 1; + } + return 0; +} + +int +collect::putenv_libcollector_ld_preloads () +{ + // for those data types that get extra libs LD_PRELOAD'd, add them + if (cc->get_synctrace_mode () != 0) + add_ld_preload ("libgp-sync.so"); + if (cc->get_heaptrace_mode () != 0) + add_ld_preload ("libgp-heap.so"); + if (cc->get_iotrace_mode () != 0) + add_ld_preload ("libgp-iotrace.so"); + add_ld_preload (SP_LIBCOLLECTOR_NAME); + + // --- putenv SP_COLLECTOR_PRELOAD* + int ii; + for (ii = 0; SP_PRELOAD[ii]; ii++) + { + // construct the SP_PRELOAD_* environment variables + // and put them into the environment + if (add_env (dbe_sprintf ("%s=%s", SP_PRELOAD[ii], sp_preload_list[ii]))) + return 1; + } + // --- putenv LD_PRELOADS + /* purge LD_PRELOAD* of values containing contents of SP_LIBCOLLECTOR_NAME */ + if (putenv_purged_ld_preloads (SP_LIBCOLLECTOR_NAME)) + dbe_write (2, GTXT ("Warning: %s is already defined in one or more LD_PRELOAD environment variables\n"), + SP_LIBCOLLECTOR_NAME); + if (putenv_ld_preloads ()) + return 1; + return 0; +} + +int +collect::putenv_libcollector_ld_misc () +{ +#if 0 // XXX 1 turns on LD_DEBUG + putenv (strdup ("LD_DEBUG=audit,bindings,detail")); +#endif + // workaround to have the dynamic linker use absolute names + if (add_env (dbe_strdup ("LD_ORIGIN=yes"))) + return 1; + + // On Linux we have to provide SP_COLLECTOR_LIBRARY_PATH and LD_LIBRARY_PATH + // so that -agentlib:gp-collector works + // and so that collect -F works with 32/64-bit mix of processes + + // Set GPROFNG_PRELOAD_LIBDIRS + char *ev = getenv (GPROFNG_PRELOAD_LIBDIRS); + char *libpath_list = NULL; + if (ev == NULL && settings->preload_libdirs == NULL) + { + settings->read_rc (false); + ev = settings->preload_libdirs; + } + ev = dbe_strdup (ev); + StringBuilder sb; + sb.appendf ("%s=", "SP_COLLECTOR_LIBRARY_PATH"); + int len = sb.length (); + int cnt = 0; + for (char *s = ev; s;) + { + char *s1 = strchr (s, ':'); + if (s1) + *(s1++) = 0; + char *fname; + if (*s == '/') + { + fname = dbe_sprintf ("%s/%s", s, LIBGP_COLLECTOR); + if (access (fname, R_OK | F_OK) == 0) + { + if (++cnt != 1) + sb.append (':'); + sb.appendf ("%s", s); + } + } + else + { + fname = dbe_sprintf ("%s/%s/%s", run_dir, s, LIBGP_COLLECTOR); + if (access (fname, R_OK | F_OK) == 0) + { + if (++cnt != 1) + sb.append (':'); + sb.appendf ("%s/%s", run_dir, s); + } + } + free (fname); + s = s1; + } + free (ev); + if (cnt == 0) + { + dbe_write (2, GTXT ("configuration error: can not find %s. run aborted\n"), + LIBGP_COLLECTOR); + return 1; + } + libpath_list = sb.toString (); + if (add_env (libpath_list)) + return 1; + libpath_list += len; + + // --- set LD_LIBRARY_PATH using libpath_list + char *old = getenv (LD_LIBRARY_PATH[0]); + if (old) + ev = dbe_sprintf ("%s=%s:%s", LD_LIBRARY_PATH[0], libpath_list, old); + else + ev = dbe_sprintf ("%s=%s", LD_LIBRARY_PATH[0], libpath_list); + if (add_env (ev)) + return 1; + return 0; +} + +void +collect::add_ld_preload (const char *lib) +{ + for (int ii = 0; SP_PRELOAD[ii]; ii++) + { + char *old_sp = sp_preload_list[ii]; + if (old_sp == NULL) + sp_preload_list[ii] = strdup (lib); + else + { + sp_preload_list[ii] = dbe_sprintf ("%s %s", old_sp, lib); + free (old_sp); + } + } +} + +int +collect::putenv_memso () +{ + // Set environment variable "MEM_USE_LOG" to 1, to keep it out of stderr + if (add_env (dbe_strdup ("MEM_USE_LOG=1"))) + return 1; + // Set environment variable "MEM_ABORT_ON_ERROR", to force a core dump + if (add_env (dbe_strdup ("MEM_ABORT_ON_ERROR=1"))) + return 1; + add_ld_preload ("mem.so"); + return putenv_ld_preloads (); +} + +// set LD_PRELOAD and friends to prepend the given library or libraries + +int +collect::putenv_ld_preloads () +{ + for (int ii = 0; LD_PRELOAD[ii]; ii++) + { + char *old_val = getenv (LD_PRELOAD[ii]); + int sp_num = ii; + assert (SP_PRELOAD[sp_num]); + char *preload_def; + if (old_val) + preload_def = dbe_sprintf ("%s=%s %s", LD_PRELOAD[ii], sp_preload_list[sp_num], old_val); + else + preload_def = dbe_sprintf ("%s=%s", LD_PRELOAD[ii], sp_preload_list[sp_num]); + if (add_env (preload_def)) + return 1; + } + return 0; +} + +/* copied from linetrace.c */ +/* + function: env_strip() + Finds str in env; Removes + all characters from previous ':' or ' ' + up to and including any trailing ':' or ' '. + params: + env: environment variable + str: substring to find + return: count of instances removed from env + */ +int +collect::env_strip (char *env, const char *str) +{ + int removed = 0; + char *p, *q; + if (env == NULL || str == NULL || *str == 0) + return 0; + size_t maxlen = strlen (env); + size_t len = strlen (str); + q = env; + while ((p = strstr (q, str)) != NULL) + { + q = p; + p += len; + if (*p) + { + while ((*p) && (*p != ':') && (*p != ' ')) + p++; /* skip the rest of the name*/ + while ((*p == ':') || (*p == ' ')) + p++; /* strip trailing separator */ + } + while (*q != ':' && *q != ' ' && *q != '=' && q != env) + q--; /* strip path */ + if (*p) + { /* copy the rest of the string */ + if (q != env) + q++; /* restore leading separator (if any) */ + size_t n = (maxlen - (q - env)); + strncpy (q, p, n); + } + else + *q = 0; + removed++; + } + return removed; +} +/* + function: putenv_purged_ld_preloads() + Remove selected preload strings from all LD_PRELOAD* env vars. + params: + var: executable name (leading characters don't have to match) + return: number of instances removed from all PRELOAD vars. + */ +int +collect::putenv_purged_ld_preloads (const char *var) +{ + int total_removed = 0; + if (!var || *var == 0) + return 0; + for (int ii = 0; LD_PRELOAD[ii]; ii++) + { + char *ev = getenv (LD_PRELOAD[ii]); + int removed = 0; + if (!ev) + continue; + removed = env_strip (ev, var); + if (!removed) + continue; + if (putenv (ev) != 0) + dbe_write (2, GTXT ("Can't putenv of %s\n"), ev); + total_removed += removed; + } + return total_removed; +} +/* + function: putenv_append() + append string to current enviroment variable setting and then do a putenv() + params: + var: environment variable name + val: string to append + */ +int +collect::putenv_append (const char *var, const char *val) +{ + char *ev; + if (!var || !val) + return 1; + const char *old_val = getenv (var); + if (old_val == NULL || *old_val == 0) + ev = dbe_sprintf ("%s=%s", var, val); + else + ev = dbe_sprintf ("%s=%s %s", var, old_val, val); + + // now put the new variable into the environment + if (add_env (ev)) + return 1; + return 0; +} + +int +collect::putenv_libcollector (void) +{ + char buf[MAXPATHLEN + 1]; + // --- set SP_COLLECTOR_EXPNAME + // fetch the experiment name and CWD + char *exp = cc->get_experiment (); + char *cwd = getcwd (buf, MAXPATHLEN); + char *ev; + + // format the environment variable for the experiment directory name + if (cwd != NULL && exp[0] != '/') // experiment is a relative path + ev = dbe_sprintf ("%s=%s/%s", SP_COLLECTOR_EXPNAME, cwd, exp); + else // getcwd failed or experiment is a fullpath + ev = dbe_sprintf ("%s=%s", SP_COLLECTOR_EXPNAME, exp); + + // set the experiment directory name + if (add_env (ev)) + return 1; + + // --- set SP_COLLECTOR_PARAMS + // set the data descriptor + exp = cc->get_data_desc (); + if (add_env (dbe_sprintf ("%s=%s", SP_COLLECTOR_PARAMS, exp))) + return 1; + + // --- set SP_COLLECTOR_FOLLOW_SPEC + const char *follow_spec = cc->get_follow_cmp_spec (); + if (follow_spec) + // selective following has been enabled + if (add_env (dbe_sprintf ("%s=%s", SP_COLLECTOR_FOLLOW_SPEC, follow_spec))) + return 1; + + if (add_env (dbe_sprintf ("%s=%d", SP_COLLECTOR_FOUNDER, getpid ()))) + return 1; + if (add_env (dbe_sprintf ("%s=1", SP_COLLECTOR_ORIGIN_COLLECT))) + return 1; + + // --- set LD_* + if (putenv_libcollector_ld_misc ()) + return 1; + + // --- set LD_PRELOAD* + if (putenv_libcollector_ld_preloads () != 0) + return 1; + + // --- set JAVA_TOOL_OPTIONS + if (cc->get_java_mode () == 1) + if (putenv_append ("JAVA_TOOL_OPTIONS", "-agentlib:gp-collector")) + exit (1); +#if 0 + // --- set LD_AUDIT* + if (putenv_libcollector_ld_audits () != 0) + return 1; +#endif + return 0; +} diff --git a/gprofng/src/gethrtime.c b/gprofng/src/gethrtime.c new file mode 100644 index 0000000..8ba7295 --- /dev/null +++ b/gprofng/src/gethrtime.c @@ -0,0 +1,166 @@ +/* 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 <time.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/resource.h> + +#include "gp-defs.h" +#include "gp-time.h" + +/* =============================================================== */ +/* + * Below this are the get_clock_rate() and get_ncpus() for all architectures + */ + +static int clock_rate = 0; +static int ncpus = 0; +static char msgbuf[1024]; + +int +get_clock_rate (void) +{ + /* Linux version -- read /proc/cpuinfo + * Note the parsing is different on intel-Linux and sparc-Linux + */ + FILE *fp = fopen ("/proc/cpuinfo", "r"); + if (fp != NULL) + { + + char temp[1024]; + while (fgets (temp, sizeof (temp), fp) != NULL) + { +#if ARCH(SPARC) + /* cpu count for SPARC linux -- read from /proc/cpuinfo */ + if (strncmp (temp, "ncpus active", 12) == 0) + { + char *val = strchr (temp, ':'); + ncpus = val ? atol (val + 1) : 0; + } +#endif /* ARCH(SPARC) */ + + if (clock_rate == 0) + { + /* pick the first line that gives a CPU clock rate */ +#if ARCH(SPARC) + long long clk; + if (strncmp (temp, "Cpu0ClkTck", 10) == 0) + { + char *val = strchr (temp, ':'); + clk = val ? strtoll (val + 1, NULL, 16) : 0; + clock_rate = (int) (clk / 1000000); + } +#else + if (strncmp (temp, "cpu MHz", 7) == 0) + { + char *val = strchr (temp, ':'); + clock_rate = val ? atoi (val + 1) : 0; + } +#endif /* ARCH() */ + } + + /* did we get a clock rate? */ + if (clock_rate != 0) + { +#if ARCH(SPARC) + /* since we got a cpu count, we can break from the look */ + break; +#endif /* ARCH(SPARC) */ + } +#if ARCH(Intel) + /* On intel-Linux, count cpus based on "cpu MHz" lines */ + if (strncmp (temp, "cpu MHz", 7) == 0) + ncpus++; +#endif /* ARCH(Intel) */ + } + fclose (fp); + } + + if (clock_rate != 0) + sprintf (msgbuf, + "Clock rate = %d MHz (from reading /proc/cpuinfo) %d CPUs\n", + clock_rate, ncpus); + + /* did we get a clock rate? */ + if (clock_rate == 0) + { + clock_rate = 1000; + sprintf (msgbuf, "Clock rate = %d MHz (set by default) %d CPUs\n", + clock_rate, ncpus); + } + return clock_rate; +} + +int +get_ncpus (void) +{ + if (clock_rate == 0) + get_clock_rate (); + return ncpus; +} + +/* gethrvtime -- generic solution, getting user time from + * clock_gettime(CLOCK_THREAD_CPUTIME_ID,..), and reformatting. + * need -lrt to compile.*/ +hrtime_t +gethrvtime () +{ + struct timespec tp; + hrtime_t rc = 0; + int r = clock_gettime (CLOCK_THREAD_CPUTIME_ID, &tp); + if (r == 0) + rc = ((hrtime_t) tp.tv_sec) * 1000000000 + (hrtime_t) tp.tv_nsec; + return rc; +} + +/* + * CLOCK_MONOTONIC + * Clock that cannot be set and represents monotonic time since some + * unspecified starting point. + */ +hrtime_t +gethrtime (void) +{ + struct timespec tp; + hrtime_t rc = 0; + + /* + * For er_kernel on Linux, we want to match how DTrace gets its timestamps. + * This is CLOCK_MONOTONIC_RAW. It might be changing to CLOCK_MONOTONIC. + * For now, we change to "RAW" and can change back if DTrace changes. + * + * The two can be different. Check the clock_gettime() man page. + * CLOCK_MONOTONIC_RAW is Linux-specific and introduced in 2.6.28. + * It is impervious to NTP or adjtime adjustments. + * + * We must match the timer used in perfan/libcollector/src/gethrtime.c. + * + * There is no issue on Solaris, where gethrtime() is provided by the kernel + * and used by DTrace. + */ + int r = clock_gettime (CLOCK_MONOTONIC_RAW, &tp); + if (r == 0) + rc = ((hrtime_t) tp.tv_sec) * 1000000000 + (hrtime_t) tp.tv_nsec; + return rc; +} diff --git a/gprofng/src/gp-archive.cc b/gprofng/src/gp-archive.cc new file mode 100644 index 0000000..8d3fc23 --- /dev/null +++ b/gprofng/src/gp-archive.cc @@ -0,0 +1,700 @@ +/* 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 <errno.h> +#include <unistd.h> +#include <getopt.h> + +#include "util.h" +#include "StringMap.h" +#include "LoadObject.h" +#include "DbeSession.h" +#include "DbeFile.h" +#include "SourceFile.h" +#include "Elf.h" +#include "gp-archive.h" +#include "ArchiveExp.h" +#include "Print.h" +#include "Module.h" + +er_archive::er_archive (int argc, char *argv[]) : DbeApplication (argc, argv) +{ + force = 0; + common_archive_dir = NULL; + quiet = 0; + descendant = 1; + use_relative_path = 0; + s_option = ARCH_EXE_ONLY; + mask = NULL; +} + +er_archive::~er_archive () +{ + if (mask) + { + for (long i = 0, sz = mask->size (); i < sz; i++) + { + regex_t *regex_desc = mask->get (i); + regfree (regex_desc); + delete regex_desc; + } + delete mask; + } + delete common_archive_dir; +} + +int +er_archive::mask_is_on (const char *str) +{ + if (mask == NULL) + return 1; + for (long i = 0, sz = mask->size (); i < sz; i++) + { + regex_t *regex_desc = mask->get (i); + if (regexec (regex_desc, str, 0, NULL, 0) == 0) + return 1; + } + return 0; +} + +void +er_archive::usage () +{ +/* + fprintf (stderr, GTXT ("Usage: %s [-nqFV] [-a on|ldobjects|src|usedldobjects|usedsrc|off] [-m regexp] experiment\n"), whoami); +*/ + +/* + Ruud - Isolate this line because it has an argument. Otherwise it would be at the + end of this long list. +*/ + printf ( GTXT ( + "Usage: gprofng archive [OPTION(S)] EXPERIMENT\n")); + + printf ( GTXT ( + "\n" + "Archive the associated application binaries and source files in a gprofng\n" + "experiment to make it self contained and portable.\n" + "\n" + "Options:\n" + "\n" + " --version print the version number and exit.\n" + " --help print usage information and exit.\n" + " --verbose {on|off} enable (on) or disable (off) verbose mode; the default is \"off\".\n" + "\n" + " -a {off|on|ldobjects|src|usedldobjects|usedsrc} specify archiving of binaries and other files;\n" + " in addition to disable this feature (off), or enable archiving off all\n" + " loadobjects and sources (on), the other options support a more\n" + " refined selection. All of these options enable archiving, but the\n" + " keyword controls what exactly is selected: all load objects (ldobjects),\n" + " all source files (src), the loadobjects asscoiated with a program counter\n" + " (usedldobjects), or the source files associated with a program counter\n" + " (usedsrc); the default is \"-a ldobjects\".\n" + "\n" + " -n archive the named experiment only, not any of its descendants.\n" + "\n" + " -q do not write any warnings to stderr; messages are archived and\n" + " can be retrieved later.\n" + "\n" + " -F force writing or rewriting of the archive; ignored with the -n\n" + " or -m options, or if this is a subexperiment.\n" + "\n" + " -d <path> specifies the location of a common archive; this is a directory that\n" + " contains archived files.\n" + "\n" + " -m <regex> archive only those source, object, and debug info files whose full\n" + " path name matches the given POSIX compliant regular expression.\n" + "\n" + "Limitations:\n" + "\n" + "Default archiving does not occur in case the application profiled terminates prematurely,\n" + "or if archiving is disabled when collecting the performance data. In such cases, this\n" + "tool can be used to afterwards archive the information, but it has to run on the same \n" + "system where the profiling data was recorded.\n" + "\n" + "Documentation:\n" + "\n" + "A getting started guide for gprofng is maintained as a Texinfo manual. If the info and\n" + "gprofng programs are properly installed at your site, the command \"info gprofng\"\n" + "should give you access to this document.\n" + "\n" + "See also:\n" + "\n" + "gprofng(1), gp-collect-app(1), gp-display-html(1), gp-display-src(1), gp-display-text(1)\n")); +// Ruud +/* + fprintf (stderr, GTXT ("GNU %s version %s\n"), get_basename (prog_name), VERSION); +*/ + exit (1); +} + +Vector <LoadObject*> * +er_archive::get_loadObjs () +{ + Vector <LoadObject*> *objs = new Vector<LoadObject*>(); + Vector <LoadObject*> *loadObjs = dbeSession->get_text_segments (); + if (s_option != ARCH_NOTHING) + { + for (long i = 0, sz = VecSize(loadObjs); i < sz; i++) + { + LoadObject *lo = loadObjs->get (i); + if ((lo->flags & SEG_FLAG_DYNAMIC) != 0) + continue; + DbeFile *df = lo->dbeFile; + if (df && ((df->filetype & DbeFile::F_FICTION) != 0)) + continue; + if (!lo->isUsed && ((s_option & (ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY)) != 0)) + continue; + objs->append (lo); + } + } + if (DEBUG_ARCHIVE) + { + Dprintf (DEBUG_ARCHIVE, NTXT ("get_text_segments(): %d\n"), + (int) (loadObjs ? loadObjs->size () : -1)); + for (long i = 0, sz = loadObjs ? loadObjs->size () : 0; i < sz; i++) + { + LoadObject *lo = loadObjs->get (i); + Dprintf (DEBUG_ARCHIVE, NTXT ("%s:%d [%2ld] %s\n"), + get_basename (__FILE__), (int) __LINE__, i, STR (lo->dump ())); + } + Dprintf (DEBUG_ARCHIVE, NTXT ("\nget_loadObjs(): %d\n"), + (int) (objs ? objs->size () : -1)); + for (long i = 0, sz = VecSize(objs); i < sz; i++) + { + LoadObject *lo = objs->get (i); + Dprintf (DEBUG_ARCHIVE, NTXT ("%s:%d [%2ld] %s\n"), + get_basename (__FILE__), (int) __LINE__, i, STR (lo->dump ())); + } + } + delete loadObjs; + return objs; +} + +/** + * Clean old archive + * Except the following cases: + * 1. Founder experiment is an MPI experiment + * 2. "-n" option is passed (do not archive descendants) + * 3. "-m" option is passed (partial archiving) + * 4. Experiment name is not the founder experiment (it is a sub-experiment) + * @param expname + * @param founder_exp + * @return 0 - success + */ +int +er_archive::clean_old_archive (char *expname, ArchiveExp *founder_exp) +{ + if (0 == descendant) + { // do not archive descendants + fprintf (stderr, GTXT ("Warning: Option -F is ignored because -n option is specified (do not archive descendants)\n")); + return 1; + } + if (NULL != mask) + { // partial archiving + fprintf (stderr, GTXT ("Warning: Option -F is ignored because -m option is specified\n")); + return 1; + } + // Check if the experiment is the founder + char *s1 = dbe_strdup (expname); + char *s2 = dbe_strdup (founder_exp->get_expt_name ()); + if (!s1 || !s2) + { + fprintf (stderr, GTXT ("Cannot allocate memory\n")); + exit (1); + } + // remove trailing slashes + for (int n = strlen (s1); n > 0; n--) + { + if ('/' != s1[n - 1]) + break; + s1[n - 1] = 0; + } + for (int n = strlen (s2); n > 0; n--) + { + if ('/' != s2[n - 1]) + break; + s2[n - 1] = 0; + } + if (strcmp (s1, s2) != 0) + { // not founder + fprintf (stderr, GTXT ("Warning: Option -F is ignored because specified experiment name %s does not match founder experiment name %s\n"), s1, s2); + free (s1); + free (s2); + return 1; + } + // Remove old "archives" + char *arch = founder_exp->get_arch_name (); + fprintf (stderr, GTXT ("INFO: removing existing archive: %s\n"), arch); + if (dbe_stat (arch, NULL) == 0) + { + char *cmd = dbe_sprintf ("/bin/rm -rf %s", arch); + system (cmd); + free (cmd); + if (dbe_stat (arch, NULL) != 0) + { // create "archives" + if (!founder_exp->create_dir (founder_exp->get_arch_name ())) + { + fprintf (stderr, GTXT ("Unable to create directory `%s'\n"), founder_exp->get_arch_name ()); + exit (1); + } + } + } + free (s1); + free (s2); + return 0; +} // clean_old_archive_if_necessary + +void +er_archive::start (int argc, char *argv[]) +{ + int last = argc - 1; + if (check_args (argc, argv) != last) + usage (); + check_env_var (); + if (s_option == ARCH_NOTHING) + return; + + ArchiveExp *founder_exp = new ArchiveExp (argv[last]); + if (founder_exp->get_status () == Experiment::FAILURE) + { + if (!quiet) + fprintf (stderr, GTXT ("er_archive: %s: %s\n"), argv[last], + pr_mesgs (founder_exp->fetch_errors (), NTXT (""), NTXT (""))); + exit (1); + } + if (!founder_exp->create_dir (founder_exp->get_arch_name ())) + { + fprintf (stderr, GTXT ("Unable to create directory `%s'\n"), founder_exp->get_arch_name ()); + exit (1); + } + if (!common_archive_dir) + common_archive_dir = dbe_strdup (getenv ("GPROFNG_ARCHIVE_COMMON_DIR")); + if (common_archive_dir) + { + if (!founder_exp->create_dir (common_archive_dir)) + if (dbe_stat (common_archive_dir, NULL) != 0) + { + fprintf (stderr, GTXT ("Unable to create directory for common archive `%s'\n"), common_archive_dir); + exit (1); + } + } + // Clean old archives if necessary + if (force) + clean_old_archive (argv[last], founder_exp); + Vector<ArchiveExp*> *exps = new Vector<ArchiveExp*>(); + exps->append (founder_exp); + if (descendant) + { + Vector<char*> *exp_names = founder_exp->get_descendants_names (); + if (exp_names) + { + for (long i = 0, sz = exp_names->size (); i < sz; i++) + { + char *exp_path = exp_names->get (i); + ArchiveExp *exp = new ArchiveExp (exp_path); + if (exp->get_status () == Experiment::FAILURE) + { + if (!quiet) + fprintf (stderr, GTXT ("er_archive: %s: %s\n"), exp_path, + pr_mesgs (exp->fetch_errors (), NTXT (""), NTXT (""))); + delete exp; + continue; + } + exps->append (exp); + } + exp_names->destroy (); + delete exp_names; + } + } + for (long i = 0, sz = exps->size (); i < sz; i++) + { + ArchiveExp *exp = exps->get (i); + exp->read_data (s_option); + } + + Vector <DbeFile*> *copy_files = new Vector<DbeFile*>(); + Vector <LoadObject*> *loadObjs = get_loadObjs (); + for (long i = 0, sz = VecSize(loadObjs); i < sz; i++) + { + LoadObject *lo = loadObjs->get (i); + if (strcmp (lo->get_pathname (), "LinuxKernel") == 0) + continue; + DbeFile *df = lo->dbeFile; + if ((df->filetype & DbeFile::F_FICTION) != 0) + continue; + if (df->get_location () == NULL) + { + copy_files->append (df); + continue; + } + if ((df->filetype & DbeFile::F_JAVACLASS) != 0) + { + if (df->container) + { // Found in .jar file + copy_files->append (df->container); + } + copy_files->append (df); + if ((s_option & ARCH_EXE_ONLY) != 0) + continue; + } + lo->sync_read_stabs (); + Elf *elf = lo->get_elf (); + if (elf && (lo->checksum != 0) && (lo->checksum != elf->elf_checksum ())) + { + if (!quiet) + fprintf (stderr, GTXT ("er_archive: '%s' has an unexpected checksum value; perhaps it was rebuilt. File ignored\n"), + df->get_location ()); + continue; + } + copy_files->append (df); + if (elf) + { + Elf *f = elf->find_ancillary_files (lo->get_pathname ()); + if (f) + copy_files->append (f->dbeFile); + for (long i1 = 0, sz1 = VecSize(elf->ancillary_files); i1 < sz1; i1++) + { + Elf *ancElf = elf->ancillary_files->get (i1); + copy_files->append (ancElf->dbeFile); + } + } + Vector<Module*> *modules = lo->seg_modules; + for (long i1 = 0, sz1 = VecSize(modules); i1 < sz1; i1++) + { + Module *mod = modules->get (i1); + if ((mod->flags & MOD_FLAG_UNKNOWN) != 0) + continue; + else if ((s_option & (ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY)) != 0 && + !mod->isUsed) + continue; + if ((s_option & ARCH_ALL) != 0) + mod->read_stabs (false); // Find all Sources + if (mod->dot_o_file && mod->dot_o_file->dbeFile) + copy_files->append (mod->dot_o_file->dbeFile); + } + } + delete loadObjs; + + int bmask = DbeFile::F_LOADOBJ | DbeFile::F_JAVACLASS | DbeFile::F_JAR_FILE | + DbeFile::F_DOT_O | DbeFile::F_DEBUG_FILE; + if ((s_option & (ARCH_USED_SRC_ONLY | ARCH_ALL)) != 0) + { + bmask |= DbeFile::F_JAVA_SOURCE | DbeFile::F_SOURCE; + Vector<SourceFile*> *sources = dbeSession->get_sources (); + for (long i = 0, sz = VecSize(sources); i < sz; i++) + { + SourceFile *src = sources->get (i); + if ((src->flags & SOURCE_FLAG_UNKNOWN) != 0) + continue; + else if ((s_option & ARCH_USED_SRC_ONLY) != 0) + { + if ((src->dbeFile->filetype & DbeFile::F_JAVA_SOURCE) != 0 && + !src->isUsed) + continue; + } + if (src->dbeFile) + copy_files->append (src->dbeFile); + } + } + + Vector <DbeFile*> *notfound_files = new Vector<DbeFile*>(); + for (long i = 0, sz = VecSize(copy_files); i < sz; i++) + { + DbeFile *df = copy_files->get (i); + char *fnm = df->get_location (); + char *nm = df->get_name (); + Dprintf (DEBUG_ARCHIVE, + "%s::%d copy_files[%ld] filetype=%4d inArchive=%d '%s' --> '%s'\n", + get_basename (__FILE__), (int) __LINE__, i, + df->filetype, df->inArchive ? 1 : 0, STR (nm), STR (fnm)); + Dprintf (DEBUG_ARCHIVE && df->container, + " copy_files[%ld]: Found '%s' in '%s'\n", + i, STR (nm), STR (df->container->get_name ())); + if (fnm == NULL) + { + if (!quiet) + notfound_files->append (df); + continue; + } + else if (df->inArchive) + { + Dprintf (DEBUG_ARCHIVE, + " NOT COPIED: copy_files[%ld]: inArchive=1 '%s'\n", + i, STR (nm)); + continue; + } + else if ((df->filetype & bmask) == 0) + { + Dprintf (DEBUG_ARCHIVE, + " NOT COPIED: copy_files[%ld]: container=%p filetype=%d bmask=%d '%s'\n", + i, df->container, df->filetype, bmask, STR (nm)); + continue; + } + else if (df->container && + (df->filetype & (DbeFile::F_JAVA_SOURCE | DbeFile::F_SOURCE)) == 0) + { + Dprintf (DEBUG_ARCHIVE, + " NOT COPIED: copy_files[%ld]: container=%p filetype=%d bmask=%d '%s'\n", + i, df->container, df->filetype, bmask, STR (nm)); + continue; + } + else if (!mask_is_on (df->get_name ())) + { + Dprintf (DEBUG_ARCHIVE, + " NOT COPIED: copy_files[%ld]: mask is off for '%s'\n", + i, STR (nm)); + continue; + } + char *anm = founder_exp->getNameInArchive (nm, false); + if (force) + unlink (anm); + int res = founder_exp->copy_file (fnm, anm, quiet, common_archive_dir, use_relative_path); + if (0 == res) // file successfully archived + df->inArchive = 1; + delete anm; + } + delete copy_files; + + if (notfound_files->size () > 0) + { + for (long i = 0, sz = notfound_files->size (); i < sz; i++) + { + DbeFile *df = notfound_files->get (i); + fprintf (stderr, GTXT ("er_archive: Cannot find file: `%s'\n"), df->get_name ()); + } + fprintf (stderr, GTXT ("\n If you know the correct location of the missing file(s)" + " you can help %s to find them by manually editing the .gprofng.rc file." + " See %s man pages for more details.\n"), + whoami, whoami); + } + delete notfound_files; +} + +int +er_archive::check_args (int argc, char *argv[]) +{ + int opt; + int rseen = 0; + int dseen = 0; + // Parsing the command line + opterr = 0; + optind = 1; + static struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + {"whoami", required_argument, 0, 'w'}, + {"outfile", required_argument, 0, 'O'}, + {NULL, 0, 0, 0} + }; + while (1) + { + int option_index = 0; + opt = getopt_long (argc, argv, NTXT (":VFa:d:qnr:m:"), + long_options, &option_index); + if (opt == EOF) + break; + switch (opt) + { + case 'F': + force = 1; + break; + case 'd': // Common archive directory (absolute path) + if (rseen) + { + fprintf (stderr, GTXT ("Error: invalid combination of options: -r and -d are in conflict.\n")); + return -1; + } + if (dseen) + fprintf (stderr, GTXT ("Warning: option -d was specified several times. Last value is used.\n")); + free (common_archive_dir); + common_archive_dir = strdup (optarg); + dseen = 1; + break; + case 'q': + quiet = 1; + break; + case 'n': + descendant = 0; + break; + case 'r': // Common archive directory (relative path) + if (dseen) + { + fprintf (stderr, GTXT ("Error: invalid combination of options: -d and -r are in conflict.\n")); + return -1; + } + if (rseen) + fprintf (stderr, GTXT ("Warning: option -r was specified several times. Last value is used.\n")); + free (common_archive_dir); + common_archive_dir = strdup (optarg); + use_relative_path = 1; + rseen = 1; + break; + case 'a': + if (strcmp (optarg, "off") == 0) + s_option = ARCH_NOTHING; + else if (strcmp (optarg, "on") == 0 || + strcmp (optarg, "ldobjects") == 0) + s_option = ARCH_EXE_ONLY; + else if (strcmp (optarg, "usedldobjects") == 0) + s_option = ARCH_USED_EXE_ONLY; + else if (strcmp (optarg, "usedsrc") == 0) + s_option = ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY; + else if (strcmp (optarg, "all") == 0 || strcmp (optarg, "src") == 0) + s_option = ARCH_ALL; + else + { + fprintf (stderr, GTXT ("Error: invalid option: `-%c %s'\n"), + optopt, optarg); + return -1; + } + break; + case 'm': + { + regex_t *regex_desc = new regex_t (); + if (regcomp (regex_desc, optarg, REG_EXTENDED | REG_NOSUB | REG_NEWLINE)) + { + delete regex_desc; + fprintf (stderr, GTXT ("Error: invalid option: `-%c %s'\n"), + optopt, optarg); + return -1; + } + if (mask == NULL) + mask = new Vector<regex_t *>(); + mask->append (regex_desc); + break; + } + case 'O': + { + int fd = open (optarg, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd == -1) + { + fprintf (stderr, GTXT ("er_archive: Can't open %s: %s\n"), + optarg, strerror (errno)); + break; + } + if (dup2 (fd, 2) == -1) + { + close (fd); + fprintf (stderr, GTXT ("er_archive: Can't divert stderr: %s\n"), + strerror (errno)); + break; + } + if (dup2 (fd, 1) == -1) + { + close (fd); + fprintf (stderr, GTXT ("er_archive: Can't divert stdout: %s\n"), + strerror (errno)); + break; + } + close (fd); + struct timeval tp; + gettimeofday (&tp, NULL); + fprintf (stderr, "### Start %s#", ctime (&tp.tv_sec)); + for (int i = 0; i < argc; i++) + fprintf (stderr, " %s", argv[i]); + fprintf (stderr, "\n"); + break; + } + case 'V': +// Ruud + Application::print_version_info (); +/* + printf (GTXT ("GNU %s version %s\n"), get_basename (prog_name), VERSION); +*/ + exit (0); + case 'w': + whoami = optarg; + break; + case 'h': + usage (); + exit (0); + case ':': // -s -m without operand + fprintf (stderr, GTXT ("Option -%c requires an operand\n"), optopt); + return -1; + case '?': + default: + fprintf (stderr, GTXT ("Unrecognized option: -%c\n"), optopt); + return -1; + } + } + return optind; +} + +void +er_archive::check_env_var () +{ + char *ename = NTXT ("GPROFNG_ARCHIVE"); + char *var = getenv (ename); + if (var == NULL) + return; + var = dbe_strdup (var); + Vector<char*> *opts = new Vector<char*>(); + opts->append (ename); + for (char *s = var;;) + { + while (*s && isblank (*s)) + s++; + if (*s == 0) + break; + opts->append (s); + while (*s && !isblank (*s)) + s++; + if (*s == 0) + break; + *s = 0; + s++; + } + if (opts->size () > 0) + { + char **arr = (char **) malloc (sizeof (char *) *opts->size ()); + for (long i = 0; i < opts->size (); i++) + arr[i] = opts->get (i); + if (-1 == check_args (opts->size (), arr)) + fprintf (stderr, GTXT ("Error: Wrong SP_ER_ARCHIVE: '%s'\n"), var); + free (arr); + } + delete opts; + free (var); +} + +static int +real_main (int argc, char *argv[]) +{ + er_archive *archive = new er_archive (argc, argv); + dbeSession->archive_mode = 1; + archive->start (argc, argv); + dbeSession->unlink_tmp_files (); + return 0; +} + +/** + * Call catch_out_of_memory(int (*real_main)(int, char*[]), int argc, char *argv[]) which will call real_main() + * @param argc + * @param argv + * @return + */ +int +main (int argc, char *argv[]) +{ + return catch_out_of_memory (real_main, argc, argv); +} diff --git a/gprofng/src/gp-archive.h b/gprofng/src/gp-archive.h new file mode 100644 index 0000000..1d937e0 --- /dev/null +++ b/gprofng/src/gp-archive.h @@ -0,0 +1,64 @@ +/* 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. */ + +#ifndef _GP_ARCHIVE_H_ +#define _GP_ARCHIVE_H_ + +#include <regex.h> +#include "DbeApplication.h" + +class ArchiveExp; +class LoadObject; +template <class ITEM> class Vector; + +enum +{ + ARCH_NOTHING = 0, + ARCH_EXE_ONLY = 1, + ARCH_USED_EXE_ONLY = 2, + ARCH_USED_SRC_ONLY = 4, + ARCH_ALL = 8 +}; + +class er_archive : public DbeApplication +{ +public: + er_archive (int argc, char *argv[]); + ~er_archive (); + void start (int argc, char *argv[]); + +private: + void usage (); + int check_args (int argc, char *argv[]); + int clean_old_archive (char *expname, ArchiveExp *founder_exp); + int mask_is_on (const char *str); + void check_env_var (); + Vector <LoadObject*> *get_loadObjs (); + + Vector<regex_t *> *mask; // -m <regexp> + int s_option; // -s NO|ALL|USED + char *common_archive_dir; // -d // absolute path to common archive + int force; // -F + int quiet; // -q + int descendant; // -n + int use_relative_path; // -r +}; + +#endif
\ No newline at end of file diff --git a/gprofng/src/gp-collect-app.cc b/gprofng/src/gp-collect-app.cc new file mode 100644 index 0000000..afaae70 --- /dev/null +++ b/gprofng/src/gp-collect-app.cc @@ -0,0 +1,1598 @@ +/* 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 <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <strings.h> +#include <string.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/wait.h> +#include <sys/utsname.h> +#include <fcntl.h> +#include <signal.h> +#include <time.h> +#include <errno.h> +#include <sys/ptrace.h> + +#include "gp-defs.h" +#include "cpu_frequency.h" +#include "util.h" +#include "collctrl.h" +#include "hwcdrv.h" +#include "gp-experiment.h" +#include "collect.h" +#include "StringBuilder.h" + +#define SP_COLLECTOR_FOUNDER "SP_COLLECTOR_FOUNDER" + +extern char **environ; + +static volatile int interrupt = 0; +static int saved_stdout = -1; +static int saved_stderr = -1; +static int no_short_usage = 0; +static int usage_fd = 2; +static collect *collect_obj = NULL; +extern "C" void sigint_handler (int sig, siginfo_t *info, void *context); +static char *outredirect = NULL; +static int precheck; +static int nprocesses; +static Process **processes; + +int +main (int argc, char *argv[]) +{ + // disable any alarm that might be pending + int r = alarm (0); + if (r != 0) + dbe_write (2, GTXT ("collect has alarm(%d) pending\n"), r); + collect_obj = new collect (argc, argv, environ); + collect_obj->start (argc, argv); + delete collect_obj; + return 0; +} + +extern "C" void +sigint_handler (int, siginfo_t *, void *) +{ + interrupt = 1; + if (collect_obj->cc != NULL) + collect_obj->cc->interrupt (); + return; +} + +extern "C" void +sigalrm_handler (int, siginfo_t *, void *) +{ + dbe_write (2, GTXT ("collect: unexpected alarm clock signal received\n")); + return; +} + +extern "C" void +sigterm_handler (int, siginfo_t *, void *) +{ + for (int i = 0; i < nprocesses; i++) + { + Process *proc = processes[i]; + if (proc != NULL) + kill (proc->pid, SIGTERM); + } +} + +collect::collect (int argc, char *argv[], char **envp) +: Application (argc, argv) +{ + verbose = 0; + disabled = 0; + cc = NULL; + collect_warnings = NULL; + collect_warnings_idx = 0; + int ii; + for (ii = 0; ii < MAX_LD_PRELOAD_TYPES; ii++) + sp_preload_list[ii] = NULL; + for (ii = 0; ii < MAX_LD_PRELOAD_TYPES; ii++) + sp_libpath_list[ii] = NULL; + java_path = NULL; + java_how = NULL; + jseen_global = 0; + nlabels = 0; + origargc = argc; + origargv = argv; + origenvp = envp; + mem_so_me = false; +} + +collect::~collect () +{ + delete cc; +} + +struct sigaction old_sigint_handler; +struct sigaction old_sigalrm_handler; + +void +collect::start (int argc, char *argv[]) +{ + char *ccret; + char *extype; + /* create a collector control structure, disabling aggressive warning */ + cc = new Coll_Ctrl (0, false, false); + if (prog_name) + { + char *s = strrchr (prog_name, '/'); + if (s && (s - prog_name) > 5) // Remove /bin/ + { + s = dbe_sprintf (NTXT ("%.*s"), (int) (s - prog_name - 4), prog_name); + cc->set_project_home (s); + free (s); + } + } + char * errenable = cc->enable_expt (); + if (errenable) + { + writeStr (2, errenable); + free (errenable); + } + + /* install a handler for SIGALRM */ + struct sigaction act; + memset (&act, 0, sizeof (struct sigaction)); + sigemptyset (&act.sa_mask); + act.sa_handler = (SignalHandler) sigalrm_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (SIGALRM, &act, &old_sigalrm_handler) == -1) + { + writeStr (2, GTXT ("Unable to install SIGALRM handler\n")); + exit (-1); + } + + /* install a handler for SIGINT */ + sigemptyset (&act.sa_mask); + act.sa_handler = (SignalHandler) sigint_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (SIGINT, &act, &old_sigint_handler) == -1) + { + writeStr (2, GTXT ("Unable to install SIGINT handler\n")); + exit (-1); + } + + /* install a handler for SIGTERM */ + sigemptyset (&act.sa_mask); + act.sa_sigaction = sigterm_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (SIGTERM, &act, NULL) == -1) + { + writeStr (2, GTXT ("Unable to install SIGTERM handler\n")); + exit (-1); + } + if (argc > 1 && strncmp (argv[1], NTXT ("--whoami="), 9) == 0) + { + whoami = argv[1] + 9; + argc--; + argv++; + } + + /* check for no arguments -- usage message */ + if (argc == 1) + { + verbose = 1; + usage_fd = 1; + validate_config (0); + usage (); + exit (0); + } + else if (argc == 2 && strcmp (argv[1], NTXT ("-h")) == 0) + { + /* only one argument, -h */ + verbose = 1; + validate_config (0); + /* now print the HWC usage message */ + show_hwc_usage (); + exit (0); + } + else if (argc == 2 && (strcmp (argv[1], NTXT ("-help")) == 0 || + strcmp (argv[1], NTXT ("--help")) == 0)) + { + /* only one argument, -help or --help */ + verbose = 1; + usage_fd = 1; + validate_config (0); + usage (); + exit (0); + } +// Ruud + else if ((argc == 2) && + (strcmp (argv[1], NTXT ("--version")) == 0)) + { + /* only one argument, --version */ + + /* print the version info */ + Application::print_version_info (); + exit (0); + } + + /* precheck the arguments -- scan for -O, -M flagS */ + precheck = 1; + targ_index = check_args (argc, argv); + if (targ_index < 0) + { + /* message has already been written */ + usage_fd = 2; + short_usage (); + exit (1); + } + /* crack the arguments */ + precheck = 0; + targ_index = check_args (argc, argv); + if (targ_index <= 0) + { + /* message has already been written */ + usage_fd = 2; + short_usage (); + exit (1); + } + if (targ_index != 0) + check_target (argc, argv); + if (disabled != 0 && cc->get_count () == 0) + { + // show collection parameters; count data + ccret = cc->show (0); + writeStr (1, ccret); + } + + // see if Java version should be checked + if (cc->get_java_default () == 0 && java_path != NULL) + validate_java (java_path, java_how, verbose); + + /* if count data is requested, exec bit to do the real work */ + /* even for a dryrun */ + if (cc->get_count () != 0) + get_count_data (); + + /* if a dry run, just exit */ + if (disabled != 0) + { + writeStr (1, cc->show_expt ()); + StringBuilder sb; + sb.append (GTXT ("Exec argv[] = ")); + for (int i = 0; i < nargs; i++) + sb.appendf (NTXT ("%s "), arglist[i]); + sb.append (NTXT ("\n")); + char *s = sb.toString (); + writeStr (1, s); + free (s); + exit (0); + } + + // If the mem_so_me flag is set, preload mem.so + // and launch the process + if (mem_so_me) + { + /* set env vars for mem.so */ + if (putenv_memso () != 0) + exit (1); /* message has already been written */ + /* ensure original outputs restored for target */ + reset_output (); + + /* now exec the target ... */ + if (cc->get_debug_mode () == 1) + { + traceme (arglist[0], arglist); + extype = NTXT ("traceme"); + } + else + { + execvp (arglist[0], arglist); + extype = NTXT ("exevcp"); + } + /* oops, exec of the target failed */ + char *em = strerror (errno); + set_output (); /* restore output for collector */ + if (em == NULL) + dbe_write (2, GTXT ("memso %s of %s failed: errno = %d\n"), extype, argv[targ_index], errno); + else + dbe_write (2, GTXT ("memso %s of %s failed: %s\n"), extype, argv[targ_index], em); + exit (1); + } + + /* normal path, setting up an experiment and launching the target */ + /* set up the experiment */ + ccret = cc->setup_experiment (); + if (ccret != NULL) + { + dbe_write (2, NTXT ("%s\n"), ccret); + free (ccret); + exit (1); + } + /* Beyond this point, the experiment is created */ + if (collect_warnings != NULL) + { + warn_open (); + for (int i = 0; i < collect_warnings_idx; i++) + warn_comment (SP_JCMD_CWARN, COL_WARN_APP_NOT_READY, collect_warnings[i], (int) strlen (collect_warnings[i])); + warn_close (); + } + /* check cpu frequency variation for intel*/ + unsigned char mode = COL_CPUFREQ_NONE; + int max_freq = get_cpu_frequency (&mode); + char freq_scaling[256]; + char turbo_mode[256]; + *freq_scaling = 0; + *turbo_mode = 0; + if (mode & COL_CPUFREQ_SCALING) + snprintf (freq_scaling, sizeof (freq_scaling), NTXT (" frequency_scaling=\"enabled\"")); + if (mode & COL_CPUFREQ_TURBO) + snprintf (turbo_mode, sizeof (turbo_mode), NTXT (" turbo_mode=\"enabled\"")); + if (mode != COL_CPUFREQ_NONE) + { + warn_open (); + if (warn_file != NULL) + { + warn_write ("<powerm>\n<frequency clk=\"%d\"%s%s/>\n</powerm>\n", + max_freq, freq_scaling, turbo_mode); + warn_close (); + } + } + + /* check for labels to write to notes file */ + if (nlabels != 0) + { + char *nbuf; + char nbuf2[MAXPATHLEN]; + // fetch the experiment name and CWD + char *exp = cc->get_experiment (); + char *ev = getcwd (nbuf2, sizeof (nbuf2)); + + // format the environment variable for the experiment directory name + if (ev != NULL && exp[0] != '/') + // cwd succeeded, and experiment is a relative path + nbuf = dbe_sprintf (NTXT ("%s/%s/%s"), nbuf2, exp, SP_NOTES_FILE); + else + // getcwd failed or experiment is a fullpath + nbuf = dbe_sprintf (NTXT ("%s/%s"), exp, SP_NOTES_FILE); + + FILE *f = fopen (nbuf, NTXT ("w")); + free (nbuf); + if (f != NULL) + { + for (int i = 0; i < nlabels; i++) + fprintf (f, NTXT ("%s\n"), label[i]); + fclose (f); + } + } + /* check for user interrupt */ + if (interrupt == 1) + { + cc->delete_expt (); + writeStr (2, GTXT ("User interrupt\n")); + exit (0); + } + + /* print data-collection parameters */ + if (verbose) + { + ccret = cc->show (0); + if (ccret != NULL) + writeStr (2, ccret); + } + ccret = cc->show_expt (); + if (ccret != NULL) + writeStr (1, ccret); /* write this to stdout */ + + pid_t pid = (pid_t) cc->get_attach_pid (); + if (pid == (pid_t) 0) + { + /* No attach */ + /* Set the environment for libcollector */ + if (putenv_libcollector () != 0) + { + /* message has already been written */ + cc->delete_expt (); + exit (1); + } + /* ensure original output fds restored for target */ + reset_output (); + + /* now exec the target ... */ + if (cc->get_debug_mode () == 1) + { + traceme (arglist[0], arglist); + extype = NTXT ("traceme"); + } + else + { + execvp (arglist[0], arglist); + extype = NTXT ("execvp"); + } + + /* we reach this point only if the target launch failed */ + char *em = strerror (errno); + + /* restore output for collector */ + set_output (); + + /* exec failed; delete experiment */ + cc->delete_expt (); + + /* print a message and exit */ + if (em == NULL) + dbe_write (2, GTXT ("%s of %s failed: errno = %d\n"), extype, argv[targ_index], errno); + else + dbe_write (2, GTXT ("%s of %s failed: %s\n"), extype, argv[targ_index], em); + exit (1); + } + else + abort (); +} + +/** + * Prepare a warning message and pass it to warn_write() + * @Parameters: + * kind Type of comment + * num ID + * s Comment sting + * len Length of the string + * @Return: none. + */ +void +collect::warn_comment (const char *kind, int num, char *s, int len) +{ + if (len != 0) + warn_write (NTXT ("<event kind=\"%s\" id=\"%d\">%.*s</event>\n"), + kind, num, len, s); + else if (s == NULL) + warn_write (NTXT ("<event kind=\"%s\" id=\"%d\"/>\n"), kind, num); + else + warn_write (NTXT ("<event kind=\"%s\" id=\"%d\">%s</event>\n"), kind, num, s); +} + +/** + * Open the warnings file in Append mode ("aw") + */ +void +collect::warn_open () +{ + // open the warnings file + warnfilename = dbe_sprintf (NTXT ("%s/%s"), cc->get_experiment (), SP_WARN_FILE); + int fd = open (warnfilename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + warn_file = fdopen (fd, NTXT ("aw")); +} + +/** + * Close the warnings file + */ +void +collect::warn_close () +{ + (void) fclose (warn_file); +} + +/** + * Format the warning message and write it to the warnings file + */ +void +collect::warn_write (const char *format, ...) +{ + char buf[4096]; + // format the input arguments into a string + va_list va; + va_start (va, format); + vsnprintf (buf, sizeof (buf), format, va); + va_end (va); + // write it to the warnings file (warnings.xml) + fwrite (buf, 1, strlen (buf), warn_file); + fflush (warn_file); +} + +/* process the args, setting expt. params, + * and finding offset for a.out name + */ +int +collect::check_args (int argc, char *argv[]) +{ + int hseen = 0; + int hoffseen = 0; + int lseen = 0; + int tseen = 0; + int pseen = 0; + int sseen = 0; + int yseen = 0; + int Fseen = 0; + int Aseen = 0; + int Sseen = 0; + int Hseen = 0; + int iseen = 0; + int Jseen = 0; + int ofseen = 0; + char *expName = NULL; + bool overwriteExp = false; + char *ccret; + char *ccwarn; + for (targ_index = 1; targ_index < argc; targ_index++) + { + if (argv[targ_index] == NULL) + break; + if (dbe_strcmp (argv[targ_index], "--") == 0) + { + targ_index++; + break; + } + if (argv[targ_index][0] != '-') + break; + int param; + switch (argv[targ_index][1]) + { + case 'y': + { + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + char *ptr; + int resume = 1; + if (checkflagterm (argv[targ_index]) == -1) return -1; + if (yseen != 0) + { + dupflagseen ('y'); + return -1; + } + yseen++; + targ_index++; + if (argv[targ_index] == NULL) + { + writeStr (2, GTXT ("-y requires a signal argument\n")); + return -1; + } + if ((ptr = strrchr (argv[targ_index], ',')) != NULL) + { + if ((*(ptr + 1) != 'r') || (*(ptr + 2) != 0)) + { + /* not the right trailer */ + dbe_write (2, GTXT ("Invalid delay signal %s\n"), argv[targ_index]); + return -1; + } + resume = 0; + *ptr = 0; + } + param = cc->find_sig (argv[targ_index]); + if (param < 0) + { + /* invalid signal */ + dbe_write (2, GTXT ("Invalid delay signal %s\n"), argv[targ_index]); + return -1; + } + ccret = cc->set_pauseresume_signal (param, resume); + if (ccret != NULL) + { + /* invalid signal; write message */ + writeStr (2, ccret); + return -1; + } + break; + } + case 'l': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) return -1; + if (lseen != 0) + { + dupflagseen ('l'); + return -1; + } + lseen++; + targ_index++; + if (argv[targ_index] == NULL) + { + writeStr (2, GTXT ("-l requires a signal argument\n")); + return -1; + } + param = cc->find_sig (argv[targ_index]); + if (param < 0) + { + /* invalid signal */ + dbe_write (2, GTXT ("Invalid sample signal %s\n"), argv[targ_index]); + return -1; + } + ccret = cc->set_sample_signal (param); + if (ccret != NULL) + { + /* invalid signal; write message */ + writeStr (2, ccret); + free (ccret); + return -1; + } + break; + +#ifdef GPROFNG_DOES_NOT_SUPPORT + case 'P': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) + return -1; + if (Pseen != 0) + { + dupflagseen ('P'); + return -1; + } + Pseen++; + targ_index++; + if (argv[targ_index] == NULL) + { + writeStr (2, GTXT ("-P requires a process pid argument\n")); + return -1; + } + ccret = cc->set_attach_pid (argv[targ_index]); + if (ccret != NULL) + { + /* error; write message */ + writeStr (2, ccret); + free (ccret); + return -1; + } + break; +#endif + case 't': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + + if (checkflagterm (argv[targ_index]) == -1) return -1; + if (tseen != 0) + { + dupflagseen ('t'); + return -1; + } + tseen++; + targ_index++; + if (argv[targ_index] == NULL) + { + writeStr (2, GTXT ("-t requires a run-duration argument\n")); + return -1; + } + ccret = cc->set_time_run (argv[targ_index]); + if (ccret != NULL) + { + /* error; write message */ + writeStr (2, ccret); + free (ccret); + return -1; + } + break; + case 'p': + { + char *warnmsg; + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) return -1; + if (pseen != 0) + { + dupflagseen ('p'); + return -1; + } + pseen++; + targ_index++; + if (argv[targ_index] == NULL) + { + writeStr (2, GTXT ("-p requires a clock-profiling argument\n")); + return -1; + } + ccret = cc->set_clkprof (argv[targ_index], &warnmsg); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + if (warnmsg != NULL) + { + writeStr (2, warnmsg); + free (warnmsg); + } + break; + } + case 's': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) return -1; + if (sseen != 0) + { + dupflagseen ('s'); + return -1; + } + sseen++; + targ_index++; + if (argv[targ_index] == NULL) + { + writeStr (2, GTXT ("-s requires a synchronization-tracing argument\n")); + return -1; + } + ccret = cc->set_synctrace (argv[targ_index]); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + break; + case 'h': + { + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) + return -1; + targ_index++; + if ((argv[targ_index] == NULL) || (strlen (argv[targ_index]) == 0)) + { + writeStr (2, GTXT ("-h requires a HW-counter-profiling argument\n")); + return -1; + } + // Check for some special cases + char * string = argv[targ_index]; + if (strcmp (argv[targ_index], NTXT ("off")) == 0) + { + if (hseen != 0) + { + no_short_usage = 1; + writeStr (2, GTXT ("-h off cannot be used with any other -h arguments\n")); + return -1; + } + hoffseen = 1; + hseen = 1; + cc->disable_hwc (); + break; + } + // Check to see if we can use HWC + unsigned hwc_maxregs = hwc_get_max_concurrent (false); + if (hwc_maxregs == 0) + { + char buf[1024]; + char *pch = hwcfuncs_errmsg_get (buf, sizeof (buf), 0); + if (*pch) + dbe_write (2, GTXT ("HW counter profiling is not supported on this system: %s%s"), + pch, pch[strlen (pch) - 1] == '\n' ? "" : "\n"); + else + dbe_write (2, GTXT ("HW counter profiling is not supported on this system\n")); + no_short_usage = 1; + return -1; + } + // Make sure there's no other -h after -h off + if (hoffseen != 0) + { + no_short_usage = 1; + writeStr (2, GTXT ("No -h arguments can be used after -h off\n")); + return -1; + } + // set up to process HW counters (to know about default counters) + cc->setup_hwc (); + hseen++; + char *warnmsg; + if (strcmp (argv[targ_index], NTXT ("on")) == 0) + ccret = cc->add_default_hwcstring ("on", &warnmsg, true); + else if (strcmp (argv[targ_index], NTXT ("hi")) == 0 || + strcmp (argv[targ_index], NTXT ("high")) == 0) + ccret = cc->add_default_hwcstring ("hi", &warnmsg, true); + else if (strcmp (argv[targ_index], NTXT ("lo")) == 0 || + strcmp (argv[targ_index], NTXT ("low")) == 0) + ccret = cc->add_default_hwcstring ("lo", &warnmsg, true); + else if (strcmp (argv[targ_index], NTXT ("auto")) == 0) + ccret = cc->add_default_hwcstring ("auto", &warnmsg, true); + else + ccret = cc->add_hwcstring (string, &warnmsg); + if (ccret != NULL) + { + /* set global flag to suppress the short_usage message for any subsequent HWC errors */ + no_short_usage = 1; + writeStr (2, ccret); + free (ccret); + return -1; + } + if (warnmsg != NULL) + { + writeStr (2, warnmsg); + free (warnmsg); + } + break; + } + case 'O': + overwriteExp = true; + __attribute__ ((fallthrough)); + case 'o': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) + return -1; + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s must be followed by a file name\n"), + argv[targ_index]); + return -1; + } + if (expName != NULL) + { + dbe_write (2, GTXT ("Only one -o or -O argument may be used\n")); + dupflagseen ('o'); + return -1; + } + expName = argv[targ_index + 1]; + targ_index++; + break; + case 'S': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) return -1; + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s must be followed by a sample interval name\n"), + argv[targ_index]); + return -1; + } + if (Sseen != 0) + { + dupflagseen ('S'); + return -1; + } + Sseen++; + ccret = cc->set_sample_period (argv[targ_index + 1]); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + targ_index++; + break; + case 'H': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) + return -1; + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s requires a heap-tracing argument\n"), + argv[targ_index]); + return -1; + } + if (Hseen != 0) + { + dupflagseen ('H'); + return -1; + } + Hseen++; + ccret = cc->set_heaptrace (argv[targ_index + 1]); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + if (cc->get_java_default () == 1) + cc->set_java_mode (NTXT ("off")); + targ_index++; + break; + case 'i': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) + return -1; + if (argv[targ_index + 1] == NULL) + { + fprintf (stderr, GTXT ("Argument %s requires an I/O-tracing argument\n"), + argv[targ_index]); + return -1; + } + if (iseen != 0) + { + dupflagseen ('i'); + return -1; + } + iseen++; + ccret = cc->set_iotrace (argv[targ_index + 1]); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + targ_index++; + break; + case 'j': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) return -1; + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s requires a java-profiling argument\n"), + argv[targ_index]); + return -1; + } + if (jseen_global != 0) + { + dupflagseen ('j'); + return -1; + } + jseen_global++; + ccret = cc->set_java_mode (argv[targ_index + 1]); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + targ_index++; + break; + case 'J': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) return -1; + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s requires a java argument\n"), + argv[targ_index]); + return -1; + } + if (Jseen != 0) + { + dupflagseen ('J'); + return -1; + } + Jseen++; + ccret = cc->set_java_args (argv[targ_index + 1]); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + targ_index++; + break; + case 'F': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) + return -1; + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s requires a descendant-following argument\n"), + argv[targ_index]); + return -1; + } + if (Fseen != 0) + { + dupflagseen ('F'); + return -1; + } + Fseen++; + ccret = cc->set_follow_mode (argv[targ_index + 1]); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + targ_index++; + break; + case 'a': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) + return -1; + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s requires a load-object archiving argument\n"), + argv[targ_index]); + return -1; + } + if (Aseen != 0) + { + dupflagseen ('a'); + return -1; + } + Aseen++; + ccret = cc->set_archive_mode (argv[targ_index + 1]); + if (ccret != NULL) + { + writeStr (2, ccret); + free (ccret); + return -1; + } + targ_index++; + break; + case 'C': + if (precheck == 1) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + if (checkflagterm (argv[targ_index]) == -1) + return -1; + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s must be followed by a comment\n"), + argv[targ_index]); + return -1; + } + if (nlabels == MAXLABELS) + { + dbe_write (2, GTXT ("No more than %d comments may be specified\n"), + MAXLABELS); + return -1; + } + label[nlabels] = argv[targ_index + 1]; + nlabels++; + targ_index++; + break; + case 'n': + case 'v': + case 'V': + if (precheck == 1) + break; + do_flag (&argv[targ_index][1]); + break; + case 'Z': + // special undocumented argument for debug builds only to allow analyzer to + // LD_PRELOAD mem.so for the target it spawns + mem_so_me = true; + break; + case '-': + if (strcmp (argv[targ_index], NTXT ("--verbose")) == 0) + do_flag ("v"); + else if (strcmp (argv[targ_index], "--outfile") == 0) + { + if (precheck == 0) + { + targ_index++; + if (argv[targ_index] == NULL) + return 0; + break; + } + // process this argument now + if (argv[targ_index + 1] == NULL) + { + dbe_write (2, GTXT ("Argument %s requires a file argument\n"), + argv[targ_index]); + return -1; + } + if (ofseen != 0) + { + dupflagseen (argv[targ_index]); + return -1; + } + ofseen++; + if (outredirect == NULL) + { + outredirect = argv[targ_index + 1]; + set_output (); + } // else already redirected; ignore with no message + targ_index++; + } + else + { + dbe_write (2, GTXT ("collect: unrecognized argument `%s'\n"), argv[targ_index]); + return -1; + } + break; + default: + dbe_write (2, GTXT ("collect: unrecognized argument `%s'\n"), argv[targ_index]); + return -1; + } + } + if (targ_index >= argc) + return -1; + if (argv[targ_index] == NULL) + { + if (precheck == 1) + return 0; + if (cc->get_attach_pid () != 0) /* no target is OK, if we're attaching */ + return 0; + writeStr (2, GTXT ("Name of target must be specified\n")); + return -1; + } + if (expName) + { + ccwarn = NULL; + ccret = cc->set_expt (expName, &ccwarn, overwriteExp); + if (ccwarn) + { + writeStr (2, ccwarn); + free (ccwarn); + } + if (ccret) + { + writeStr (2, ccret); + return -1; + } + } + if (cc->get_attach_pid () != 0) + { + writeStr (2, GTXT ("Name of target must not be specified when -P is used\n")); + return -1; + } + return targ_index; +} + +int +collect::checkflagterm (const char *c) +{ + if (c[2] != 0) + { + dbe_write (2, GTXT ("collect: unrecognized argument `%s'\n"), c); + return -1; + } + return 0; +} + +int +collect::do_flag (const char *flags) +{ + char *s; + for (int i = 0;; i++) + { + switch (flags[i]) + { + case 0: // end of string + return 0; + case 'n': + disabled = 1; + if (verbose != 1) + { +// Ruud + Application::print_version_info (); +/* + dbe_write (2, NTXT ("GNU %s version %s\n"), + get_basename (prog_name), VERSION); +*/ + verbose = 1; + } + break; + case 'x': + s = cc->set_debug_mode (1); + if (s) + { + writeStr (2, s); + free (s); + } + break; + case 'v': + if (verbose != 1) + { +// Ruud + Application::print_version_info (); +/* + dbe_write (2, NTXT ("GNU %s version %s\n"), + get_basename (prog_name), VERSION); +*/ + verbose = 1; + } + break; + case 'V': +// Ruud + Application::print_version_info (); +/* + dbe_write (2, NTXT ("GNU %s version %s\n"), + get_basename (prog_name), VERSION); +*/ + /* no further processing.... */ + exit (0); + } + } +} + +/* + * traceme - cause the caller to stop at the end of the next exec() + * so that a debugger can attach to the new program + * + * Takes same arguments as execvp() + */ +int +collect::traceme (const char *execvp_file, char *const execvp_argv[]) +{ + int ret = -1; + pid_t pid = fork (); + if (pid == 0) + { // child + // child will set up itself to be PTRACE'd, and then exec the target executable + /* reset the SP_COLLECTOR_FOUNDER value to the new pid */ + pid_t mypid = getpid (); + char *ev = dbe_sprintf (NTXT ("%s=%d"), SP_COLLECTOR_FOUNDER, mypid); + if (putenv (ev) != 0) + { + dbe_write (2, GTXT ("fork-child: Can't putenv of \"%s\": run aborted\n"), ev); + return 1; + } + ptrace (PTRACE_TRACEME, 0, NULL, NULL); // initiate trace + ret = execvp (execvp_file, execvp_argv); // execvp user command + return ret; // execvp failed + } + else if (pid > 0) + { // parent + int status; + if (waitpid (pid, &status, 0) != pid) + { // wait for execvp to cause signal + writeStr (2, GTXT ("parent waitpid() failed\n")); + return -2; + } + if (!WIFSTOPPED (status)) + writeStr (2, GTXT ("WIFSTOPPED(status) failed\n")); + + // originally, PTRACE_DETACH would send SIGTSTP, but now we do it here: + if (kill (pid, SIGTSTP) != 0) + writeStr (2, GTXT ("kill(pid, SIGTSTP) failed\n")); + if (ptrace (PTRACE_DETACH, pid, NULL, 0) != 0) + { // detach trace + writeStr (2, GTXT ("ptrace(PTRACE_DETACH) failed\n")); + return -4; + } + dbe_write (2, GTXT ("Waiting for attach from debugger: pid=%d\n"), (int) pid); + + // wait for an external debugger to attach + if (waitpid (pid, &status, 0) != pid) + { // keep parent alive until child quits + writeStr (2, GTXT ("parent final waitpid() failed\n")); + return -5; + } + } + else + return -1; // fork failed + exit (0); +} + +void +collect::dupflagseen (char c) +{ + dbe_write (2, GTXT ("Only one -%c argument may be used\n"), c); +} + +void +collect::dupflagseen (const char *s) +{ + dbe_write (2, GTXT ("Only one %s argument may be used\n"), s); +} + +int +collect::set_output () +{ + static int initial = 1; + if (outredirect) + { + int fd = open (outredirect, O_WRONLY | O_CREAT | O_APPEND, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd == -1) + { + dbe_write (2, GTXT ("Warning: Can't open collector output `%s': %s\n"), + outredirect, strerror (errno)); + } + else + { + if ((saved_stdout = dup (1)) == -1 || dup2 (fd, 1) == -1) + dbe_write (2, GTXT ("Warning: Can't divert collector %s: %s\n"), + NTXT ("stdout"), strerror (errno)); + if ((saved_stderr = dup (2)) == -1 || dup2 (fd, 2) == -1) + dbe_write (2, GTXT ("Warning: Can't divert collector %s: %s\n"), + NTXT ("stderr"), strerror (errno)); + close (fd); + if ((saved_stdout != -1) && (saved_stderr != -1)) + { + if (initial) + { + struct timeval tp; + gettimeofday (&tp, NULL); + writeStr (2, ctime (&tp.tv_sec)); + initial = 0; + } + return 1; // diversion in place + } + } + } + return 0; // no diversion +} + +void +collect::reset_output () +{ + if (saved_stdout != -1 && + (dup2 (saved_stdout, 1) == -1 || close (saved_stdout))) + dbe_write (2, GTXT ("Warning: Can't restore collector stdout: %s\n"), + strerror (errno)); + if (saved_stderr != -1 && + (dup2 (saved_stderr, 2) == -1 || close (saved_stderr))) + dbe_write (2, GTXT ("Warning: Can't restore collector stderr: %s\n"), + strerror (errno)); +} + +void +collect::usage () +{ + +/* + Ruud - Isolate this line because it has an argument. Otherwise it would be at the + end of this long list. +*/ + printf ( GTXT ( + "Usage: gprofng collect app [OPTION(S)] TARGET [TARGET_ARGUMENTS]\n")), + +/* +------------------------------------------------------------------------------- + For a reason I don't understand, the continuation line(s) need to start at + column 26 in order for help2man to do the righ thing. Ruud +------------------------------------------------------------------------------- +*/ + printf ( GTXT ( + "\n" + "Collect performance data on the target program. In addition to Program\n" + "Counter PC) sampling, hardware event counters and various tracing options\n" + "are supported.\n" + "\n" + "Options:\n" + "\n" + " --version print the version number and exit.\n" + " --help print usage information and exit.\n" + " --verbose {on|off} enable (on) or disable (off) verbose mode; the default is \"off\".\n" + "\n" + " -p {off|on|lo|hi|<value>} disable (off) or enable (on) clock-profiling using a default\n" + " sampling granularity, or enable clock-profiling implicitly by\n" + " setting the sampling granularity (lo, hi, or a specific value\n" + " in ms); by default clock profiling is enabled.\n" + "\n" + " -h {<ctr_def>...,<ctr_n_def>} enable hardware event counter profiling and select\n" + " the counter(s); to see the supported counters on this system use\n" + " the -h option without other arguments.\n" + "\n" + " -o <exp_name> specify the name for (and path to) the experiment directory; the\n" + " the default path is the current directory.\n" + "\n" + " -O <exp_name> the same as -o, but unlike the -o option, silently overwrite an\n" + " existing experiment directory with the same name.\n" + "\n" + " -C <label> add up to 10 comment labels to the experiment; comments appear in\n" + " the notes section of the header.\n" + "\n" + " -j {on|off|<path>} enable (on), or disable (off) Java profiling when the target\n" + " program is a JVM; optionally set the <path> to a non-default JVM;\n" + " the default is \"-j on\".\n" + "\n" + " -J <java-args> specify arguments to the JVM.\n" + "\n" + " -t <duration>[m|s] specify the duration over which to record data; the default unit\n" + " is seconds (s), but can be set to minutes (m).\n" + "\n" + " -n dry run; display several run-time settings, but do not run the\n" + " target, or collect performance data.\n" + "\n" + " -y <signal>[,r] specify delayed initialization and a pause/resume signal; by default\n" + " the target starts in paused mode; if the optional r keyword is\n" + " provided, start in resumed mode.\n" + "\n" + " -F {off|on|=<regex>} control to follow descendant processes; disable (off), enable (on),\n" + " or collect data on all descendant processes whose name matches the\n" + " specified regular expression; the default is \"-F on\".\n" + "\n" + " -a {off|on|ldobjects|src|usedldobjects|usedsrc} specify archiving of binaries and other files;\n" + " in addition to disable this feature (off), or enable archiving off all\n" + " loadobjects and sources (on), the other options support a more\n" + " refined selection. All of these options enable archiving, but the\n" + " keyword controls what exactly is selected: all load objects (ldobjects),\n" + " all source files (src), the loadobjects asscoiated with a program counter\n" + " (usedldobjects), or the source files associated with a program counter\n" + " (usedsrc); the default is \"-a ldobjects\".\n" + "\n" + " -S {off|on|<seconds>} disable (off) or enable (on) periodic sampling of process-wide resource\n" + " utilization; by default sampling occurs every second; use the <seconds>\n" + " option to change this; the default is \"-S on\".\n" + "\n" + " -l <signal> specify a signal that will trigger a sample of process-wide resource utilization.\n" + "\n" + " -s <option>[,<API>] enable synchronization wait tracing; <option> is used to define the specifics\n" + " of the tracing (on, off, <threshold>, or all); <API> is used to select the API:\n" + " \"n\" selects native/Pthreads, \"j\" selects Java, and \"nj\" selects both;\n" + " the default is \"-s off\".\n" + "\n" + " -H {off|on} disable (off), or enable (on) heap tracing; the default is \"-H off\".\n" + "\n" + " -i {off|on} disable (off), or enable (on) I/O tracing; the default is \"-i off\".\n" + "\n" + "Documentation:\n" + "\n" + "A getting started guide for gprofng is maintained as a Texinfo manual. If the info and\n" + "gprofng programs are properly installed at your site, the command \"info gprofng\"\n" + "should give you access to this document.\n" + "\n" + "See also:\n" + "\n" + "gprofng(1), gp-archive(1), gp-display-html(1), gp-display-src(1), gp-display-text(1)\n")); +/* + char *s = dbe_sprintf (GTXT ("Usage: %s <args> target <target-args>\n"), + whoami); + writeStr (usage_fd, s); + free (s); + writeStr (usage_fd, GTXT (" -p {lo|on|hi|off|<value>}\tspecify clock-profiling\n")); + writeStr (usage_fd, GTXT ("\t`lo' per-thread rate of ~10 samples/second\n")); + writeStr (usage_fd, GTXT ("\t`on' per-thread rate of ~100 samples/second (default)\n")); + writeStr (usage_fd, GTXT ("\t`hi' per-thread rate of ~1000 samples/second\n")); + writeStr (usage_fd, GTXT ("\t`off' disable clock profiling\n")); + writeStr (usage_fd, GTXT ("\t<value> specify profile timer period in millisec.\n")); + s = dbe_sprintf (GTXT ("\t\t\tRange on this system is from %.3f to %.3f millisec.\n\t\t\tResolution is %.3f millisec.\n"), + (double) cc->get_clk_min () / 1000., + (double) cc->get_clk_max () / 1000., + (double) cc->get_clk_res () / 1000.); + writeStr (usage_fd, s); + free (s); + writeStr (usage_fd, GTXT (" -h <ctr_def>...[,<ctr_n_def>]\tspecify HW counter profiling\n")); + s = dbe_sprintf (GTXT ("\tto see the supported HW counters on this machine, run \"%s -h\" with no other arguments\n"), + whoami); + writeStr (usage_fd, s); + free (s); + writeStr (usage_fd, GTXT (" -s <threshold>[,<scope>]\tspecify synchronization wait tracing\n")); + writeStr (usage_fd, GTXT ("\t<scope> is \"j\" for tracing Java-APIs, \"n\" for tracing native-APIs, or \"nj\" for tracing both\n")); + writeStr (usage_fd, GTXT (" -H {on|off}\tspecify heap tracing\n")); + writeStr (usage_fd, GTXT (" -i {on|off}\tspecify I/O tracing\n")); + writeStr (usage_fd, GTXT (" -N <lib>\tspecify library to exclude count from instrumentation (requires -c also)\n")); + writeStr (usage_fd, GTXT (" \tmultiple -N arguments can be provided\n")); + writeStr (usage_fd, GTXT (" -j {on|off|path}\tspecify Java profiling\n")); + writeStr (usage_fd, GTXT (" -J <java-args>\tspecify arguments to Java for Java profiling\n")); + writeStr (usage_fd, GTXT (" -t <duration>\tspecify time over which to record data\n")); + writeStr (usage_fd, GTXT (" -n\tdry run -- don't run target or collect performance data\n")); + writeStr (usage_fd, GTXT (" -y <signal>[,r]\tspecify delayed initialization and pause/resume signal\n")); + writeStr (usage_fd, GTXT ("\tWhen set, the target starts in paused mode;\n\t if the optional r is provided, it starts in resumed mode\n")); + writeStr (usage_fd, GTXT (" -F {on|off|=<regex>}\tspecify following descendant processes\n")); + writeStr (usage_fd, GTXT (" -a {on|ldobjects|src|usedldobjects|usedsrc|off}\tspecify archiving of binaries and other files;\n")); + writeStr (usage_fd, GTXT (" -S {on|off|<seconds>}\t Set the interval for periodic sampling of process-wide resource utilization\n")); + writeStr (usage_fd, GTXT (" -l <signal>\tspecify signal that will trigger a sample of process-wide resource utilization\n")); + writeStr (usage_fd, GTXT (" -o <expt>\tspecify experiment name\n")); + writeStr (usage_fd, GTXT (" --verbose\tprint expanded log of processing\n")); + writeStr (usage_fd, GTXT (" -C <label>\tspecify comment label (up to 10 may appear)\n")); + writeStr (usage_fd, GTXT (" -V|--version\tprint version number and exit\n")); +*/ + /* don't document this feature */ + // writeStr (usage_fd, GTXT(" -Z\tPreload mem.so, and launch target [no experiment]\n") ); +/* + writeStr (usage_fd, GTXT ("\n See the gp-collect(1) man page for more information\n")); +*/ + +#if 0 + /* print an extended usage message */ + /* find a Java for Java profiling, set Java on to check Java */ + find_java (); + cc->set_java_mode (NTXT ("on")); + + /* check for variable-clock rate */ + unsigned char mode = COL_CPUFREQ_NONE; + get_cpu_frequency (&mode); + if (mode != COL_CPUFREQ_NONE) + writeStr (usage_fd, GTXT ("NOTE: system has variable clock frequency, which may cause variable program run times.\n")); + + /* show the experiment that would be run */ + writeStr (usage_fd, GTXT ("\n Default experiment:\n")); + char *ccret = cc->setup_experiment (); + if (ccret != NULL) + { + writeStr (usage_fd, ccret); + free (ccret); + exit (1); + } + cc->delete_expt (); + ccret = cc->show (1); + if (ccret != NULL) + { + writeStr (usage_fd, ccret); + free (ccret); + } +#endif +} + +void +collect::short_usage () +{ + if (no_short_usage == 0) + dbe_write (usage_fd, GTXT ("Run \"%s --help\" for a usage message.\n"), whoami); +} + +void +collect::show_hwc_usage () +{ + usage_fd = 1; + short_usage (); + cc->setup_hwc (); + hwc_usage (false, whoami, NULL); +} + +void +collect::writeStr (int f, const char *buf) +{ + if (buf != NULL) + write (f, buf, strlen (buf)); +} diff --git a/gprofng/src/gp-display-src.cc b/gprofng/src/gp-display-src.cc new file mode 100644 index 0000000..ca7853d --- /dev/null +++ b/gprofng/src/gp-display-src.cc @@ -0,0 +1,752 @@ +/* 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 <unistd.h> +#include <fcntl.h> + +#include "util.h" +#include "DbeApplication.h" +#include "DbeSession.h" +#include "Function.h" +#include "LoadObject.h" +#include "Module.h" +#include "DbeView.h" +#include "Print.h" +#include "DbeFile.h" +#include "Command.h" + +class er_src : public DbeApplication +{ +public: + er_src (int argc, char *argv[]); + void start (int argc, char *argv[]); + +private: + + // override methods in base class + void usage (); + int check_args (int argc, char *argv[]); + void run_args (int argc, char *argv[]); + + enum Obj_Types + { + OT_EXE_ELF = 0, OT_JAVA_CLASS, OT_JAR_FILE, OT_UNKNOWN + }; + + void open (char *exe); + void dump_annotated (char *name, char* sel, char *src, DbeView *dbev, + bool is_dis, bool first); + void checkJavaClass (char *exe); + void print_header (bool first, const char* text); + void proc_cmd (CmdType cmd_type, bool first, char *arg1, + const char *arg2, const char *arg3 = NULL); + FILE *set_outfile (char *cmd, FILE *&set_file); + + bool is_java_class () { return obj_type == OT_JAVA_CLASS; } + + int dbevindex; + DbeView *dbev; + LoadObject *lo; + Obj_Types obj_type; + const char *out_fname; + FILE *out_file; + bool isDisasm; + bool isFuncs; + bool isDFuncs; + bool isSrc; + bool v_opt; + int multiple; + char *str_compcom; + bool hex_visible; + int src_visible; + int vis_src; + int vis_dis; + int threshold_src; + int threshold_dis; + int threshold; + int vis_bits; +}; + +static int +real_main (int argc, char *argv[]) +{ + er_src *src = new er_src (argc, argv); + src->start (argc, argv); + delete src; + return 0; +} + +int +main (int argc, char *argv[]) +{ + return catch_out_of_memory (real_main, argc, argv); +} + +er_src::er_src (int argc, char *argv[]) +: DbeApplication (argc, argv) +{ + obj_type = OT_UNKNOWN; + out_fname = "<stdout>"; + out_file = stdout; + isDisasm = false; + isFuncs = false; + isDFuncs = false; + isSrc = false; + v_opt = false; + multiple = 0; + lo = NULL; +} + +static int +FuncNameCmp (const void *a, const void *b) +{ + Function *item1 = *((Function **) a); + Function *item2 = *((Function **) b); + return strcmp (item1->get_mangled_name (), item2->get_mangled_name ()); +} + +static int +FuncAddrCmp (const void *a, const void *b) +{ + Function *item1 = *((Function **) a); + Function *item2 = *((Function **) b); + return (item1->img_offset == item2->img_offset) ? + FuncNameCmp (a, b) : item1->img_offset > item2->img_offset ? 1 : -1; +} + +void +er_src::usage () +{ + +/* + Ruud - Isolate this line because it has an argument. Otherwise it would be at the + end of a long usage list. +*/ + printf ( GTXT ( + "Usage: gprofng display src [OPTION(S)] TARGET-OBJECT\n")); + + printf ( GTXT ( + "\n" + "Display the source code listing, or source code interleaved with disassembly code,\n" + "as extracted from the target object (an executable, shared object, object file, or\n" + "a Java .class file).\n" + "\n" + "Options:\n" + "\n" + " --version print the version number and exit.\n" + " --help print usage information and exit.\n" + " --verbose {on|off} enable (on) or disable (off) verbose mode; the default is \"off\".\n" + "\n" + " -func list all the functions from the given object.\n" + "\n" + " -source item tag show the source code for item; the tag is used to\n" + " differentiate in case of multiple occurences with\n" + " the same name; the combination of \"all -1\" selects\n" + " all the functions in the object; the default is\n" + " \"-source all -1\".\n" + "\n" + " -disasm item tag show the source code, interleaved with the disassembled\n" + " instructions; the same definitions for item and tag apply.\n" + "\n" + " -outfile <filename> write results to file <filename>; a dash (-) writes to\n" + " stdout; this is also the default; note that this only\n" + " affects options included to the right of this option.\n" + "\n" + "Documentation:\n" + "\n" + "A getting started guide for gprofng is maintained as a Texinfo manual. If the info and\n" + "gprofng programs are properly installed at your site, the command \"info gprofng\"\n" + "should give you access to this document.\n" + "\n" + "See also:\n" + "\n" + "gprofng(1), gp-archive(1), gp-collect-app(1), gp-display-html(1), gp-display-text(1)\n")); +/* + printf (GTXT ("Usage: %s [OPTION] a.out/.so/.o/.class\n\n"), whoami); + printf (GTXT (" -func List all the functions from the given object\n" + " -source, -src item tag Show the annotated source for the listed item\n" + " -disasm item tag Include the disassembly in the listing\n" + " -V Print the current release version of er_src\n" + " -cc, -scc, -dcc com_spec Define the compiler commentary classes to show\n" + " -outfile filename Open filename for output\n")); +*/ + exit (0); +} + +void +er_src::start (int argc, char *argv[]) +{ + dbevindex = dbeSession->createView (0, -1); + dbev = dbeSession->getView (dbevindex); + + // get options + check_args (argc, argv); + run_args (argc, argv); + if (out_file != stdout) + fclose (out_file); +} + +FILE * +er_src::set_outfile (char *cmd, FILE *&set_file) +{ + FILE *new_file; + if (!strcasecmp (cmd, "-")) + { + new_file = stdout; + out_fname = "<stdout>"; + } + else + { + char *cmdpath; + char *fname = strstr (cmd, "~/"); + // Handle ~ in file names + char *home = getenv ("HOME"); + if (fname != NULL && home != NULL) + cmdpath = dbe_sprintf ("%s/%s", home, fname + 2); + else if ((fname = strstr (cmd, "~")) != NULL && home != NULL) + cmdpath = dbe_sprintf ("/home/%s", fname + 1); + else + cmdpath = strdup (cmd); + new_file = fopen (cmdpath, "w"); + if (new_file == NULL) + { + fprintf (stderr, GTXT ("Unable to open file: %s"), cmdpath); + free (cmdpath); + return NULL; + } + out_fname = cmdpath; + } + if (set_file && (set_file != stdout)) + fclose (set_file); + + set_file = new_file; + return set_file; +} + +void +er_src::proc_cmd (CmdType cmd_type, bool first, char *arg1, + const char *arg2, const char *arg3) +{ + Cmd_status status; + Module *module; + Function *fitem; + int mindex, findex; + switch (cmd_type) + { + case SOURCE: + dbev->set_view_mode (VMODE_USER); + print_anno_file (arg1, arg2, arg3, false, + stdout, stdin, out_file, dbev, false); + break; + case DISASM: + dbev->set_view_mode (VMODE_MACHINE); + print_header (first, GTXT ("Annotated disassembly\n")); + print_anno_file (arg1, arg2, arg3, true, + stdout, stdin, out_file, dbev, false); + break; + case OUTFILE: + if (arg1) + set_outfile (arg1, out_file); + break; + case FUNCS: + print_header (false, GTXT ("Function list\n")); + fprintf (out_file, GTXT ("Functions sorted in lexicographic order\n")); + fprintf (out_file, GTXT ("\nLoad Object: %s\n\n"), lo->get_name ()); + if (lo->wsize == W32) + fprintf (out_file, GTXT (" Address Size Name\n\n")); + else + fprintf (out_file, GTXT (" Address Size Name\n\n")); + + Vec_loop (Module*, lo->seg_modules, mindex, module) + { + module->functions->sort (FuncNameCmp); + const char *fmt = (lo->wsize == W32) ? + GTXT (" 0x%08llx %8lld %s\n") : + GTXT (" 0x%016llx %16lld %s\n"); + Vec_loop (Function*, module->functions, findex, fitem) + { + fprintf (out_file, fmt, + (ull_t) fitem->img_offset, + (ull_t) fitem->size, + fitem->get_name ()); + } + } + break; + case DUMPFUNC: + lo->functions->sort (FuncAddrCmp); + print_header (first, GTXT ("Dump functions\n")); + lo->dump_functions (out_file); + first = false; + break; + case SCOMPCOM: + status = dbev->proc_compcom (arg1, true, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s"), Command::get_err_string (status)); + break; + case DCOMPCOM: + status = dbev->proc_compcom (arg1, false, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s"), Command::get_err_string (status)); + break; + case COMPCOM: + status = dbev->proc_compcom (arg1, true, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s"), Command::get_err_string (status), arg1); + status = dbev->proc_compcom (arg1, false, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s"), Command::get_err_string (status), arg1); + break; + case HELP: + usage (); + break; + case VERSION_cmd: + if (out_file != stdout) +// Ruud + Application::print_version_info (); +/* + fprintf (out_file, "GNU %s version %s\n", get_basename (prog_name), VERSION); +*/ + break; + default: + fprintf (stderr, GTXT ("Invalid option")); + break; + } +} + +void +er_src::run_args (int argc, char *argv[]) +{ + CmdType cmd_type; + int arg_count, cparam; + char *arg; + char *arg1; + const char *arg2; + bool first = true; + bool space; + Module *module; + int mindex; + + for (int i = 1; i < argc; i++) + { + if (*argv[i] != '-') + { + if (!multiple) + { // er_src -V exe + space = false; + dbev->set_view_mode (VMODE_USER); + print_header (first, GTXT ("Annotated source\n")); + Vec_loop (Module*, lo->seg_modules, mindex, module) + { + if ((module->flags & MOD_FLAG_UNKNOWN) != 0 || + module->lang_code == Sp_lang_unknown) + continue; + if (space) + fprintf (out_file, "\n"); + print_anno_file (module->file_name, "1", NULL, false, + stdout, stdin, out_file, dbev, false); + space = true; + } + } + break; + } + if (strncmp (argv[i], NTXT ("--whoami="), 9) == 0) + { + whoami = argv[i] + 9; + continue; + } + switch (cmd_type = Command::get_command (argv[i] + 1, arg_count, cparam)) + { + case SOURCE: + case DISASM: + { + i += arg_count; + multiple++; + if (i >= argc || argv[i] == NULL || + (*(argv[i]) == '-' && atoi (argv[i]) != -1) || i + 1 == argc) + { + i--; + arg = argv[i]; + arg2 = "1"; + } + else + { + arg = argv[i - 1]; + if (*(argv[i]) == '-' && atoi (argv[i]) == -1 && + streq (arg, NTXT ("all"))) + { + space = false; + if (cmd_type == SOURCE) + print_header (first, GTXT ("Annotated source\n")); + else + print_header (first, GTXT ("Annotated disassembly\n")); + Vec_loop (Module*, lo->seg_modules, mindex, module) + { + if ((module->flags & MOD_FLAG_UNKNOWN) != 0 || + module->lang_code == Sp_lang_unknown) + continue; + if (space) + fprintf (out_file, "\n"); + proc_cmd (cmd_type, first, module->file_name, "1"); + space = true; + } + first = false; + break; + } + arg2 = argv[i]; + } + char *fcontext = NULL; + arg1 = parse_fname (arg, &fcontext); + if (arg1 == NULL) + { + fprintf (stderr, GTXT ("Error: Invalid function/file setting: %s\n"), arg1); + free (fcontext); + break; + } + proc_cmd (cmd_type, first, arg1, arg2, fcontext); + free (arg1); + free (fcontext); + first = false; + break; + } + case OUTFILE: + case FUNCS: + case DUMPFUNC: + case COMPCOM: + case SCOMPCOM: + case DCOMPCOM: + case VERSION_cmd: + case HELP: + proc_cmd (cmd_type, first, (arg_count > 0) ? argv[i + 1] : NULL, + (arg_count > 1) ? argv[i + 2] : NULL); + i += arg_count; + break; + default: + if (streq (argv[i] + 1, NTXT ("all")) || streq (argv[i] + 1, NTXT ("dall"))) + { + first = false; + multiple++; + if (streq (argv[i] + 1, NTXT ("all"))) + proc_cmd (FUNCS, first, NULL, NULL); + else + proc_cmd (DUMPFUNC, first, NULL, NULL); + space = false; + print_header (first, GTXT ("Annotated source\n")); + Vec_loop (Module*, lo->seg_modules, mindex, module) + { + if ((module->flags & MOD_FLAG_UNKNOWN) != 0 || + module->lang_code == Sp_lang_unknown) + continue; + if (space) + fprintf (out_file, "\n"); + proc_cmd (SOURCE, first, module->file_name, "1"); + space = true; + } + print_header (first, GTXT ("Annotated disassembly\n")); + Vec_loop (Module*, lo->seg_modules, mindex, module) + { + if ((module->flags & MOD_FLAG_UNKNOWN) != 0 || + module->lang_code == Sp_lang_unknown) + continue; + if (space) + fprintf (out_file, "\n"); + proc_cmd (DISASM, first, module->file_name, "1"); + space = true; + } + } + else + { + proc_cmd (cmd_type, first, (arg_count > 0) ? argv[i + 1] : NULL, + (arg_count > 1) ? argv[i + 2] : NULL); + i += arg_count; + break; + } + } + } +} + +int +er_src::check_args (int argc, char *argv[]) +{ + CmdType cmd_type = UNKNOWN_CMD; + int arg_count, cparam; + int i; + char *exe; + bool first = true; + if (argc == 1) + usage (); + + // If any comments from the .rc files, log them to stderr + Emsg * rcmsg = fetch_comments (); + while (rcmsg != NULL) + { + fprintf (stderr, "%s: %s\n", prog_name, rcmsg->get_msg ()); + rcmsg = rcmsg->next; + } + + // Parsing the command line + opterr = 0; + exe = NULL; + for (i = 1; i < argc; i++) + { + if (*argv[i] != '-') + { + exe = argv[i]; + if (i == 1) + { // er_src exe ? + if (!exe) + usage (); + if (argc == 3) // er_src exe file + usage (); + } + else if (v_opt && !multiple && !exe && !str_compcom) // just er_src -V + exit (0); + if (argc < i + 1 || argc > i + 3) + usage (); + i++; + if (argc > i) + usage (); + open (exe); + return i; + } + switch (cmd_type = Command::get_command (argv[i] + 1, arg_count, cparam)) + { + case WHOAMI: + whoami = argv[i] + 1 + cparam; + break; + case HELP: + i += arg_count; + multiple++; + usage (); + break; + case VERSION_cmd: + if (first) + { +// Ruud + Application::print_version_info (); +/* + printf ("GNU %s version %s\n", get_basename (prog_name), VERSION); +*/ + v_opt = true; + first = false; + } + break; + case SOURCE: + case DISASM: + i += arg_count; + multiple++; + isDisasm = true; + if (i >= argc || argv[i] == NULL || + (*(argv[i]) == '-' && atoi (argv[i]) != -1) || (i + 1 == argc)) + i--; + break; + case DUMPFUNC: + i += arg_count; + multiple++; + break; + case FUNCS: + i += arg_count; + multiple++; + break; + case OUTFILE: + case COMPCOM: + case SCOMPCOM: + case DCOMPCOM: + i += arg_count; + break; + default: + if (!(streq (argv[i] + 1, NTXT ("all")) || + streq (argv[i] + 1, NTXT ("dall")))) + { + fprintf (stderr, "Error: invalid option: `%s'\n", argv[i]); + exit (1); + } + } + } + if (!exe && !(argc == 2 && cmd_type == VERSION_cmd)) + usage (); + return i; +} + +void +er_src::checkJavaClass (char* exe) +{ + unsigned char cf_buf[4]; + unsigned int magic_number; + int fd = ::open (exe, O_RDONLY | O_LARGEFILE); + if (fd == -1) + return; + if (sizeof (cf_buf) == read_from_file (fd, cf_buf, sizeof (cf_buf))) + { + magic_number = cf_buf[0] << 24; + magic_number |= cf_buf[1] << 16; + magic_number |= cf_buf[2] << 8; + magic_number |= cf_buf[3]; + if (magic_number == 0xcafebabe) + obj_type = OT_JAVA_CLASS; + } + close (fd); +} + +void +er_src::print_header (bool first, const char* text) +{ + if (!first) + fprintf (out_file, "\n"); + if (multiple > 1) + { + fprintf (out_file, NTXT ("%s"), text); + fprintf (out_file, "---------------------------------------\n"); + } +} + +void +er_src::dump_annotated (char *name, char *sel, char *src, DbeView *dbevr, + bool is_dis, bool first) +{ + Module *module; + bool space; + int mindex; + print_header (first, (is_dis) ? ((is_java_class ()) ? + GTXT ("Annotated bytecode\n") : + GTXT ("Annotated disassembly\n")) : + GTXT ("Annotated source\n")); + if (!name) + { + space = false; + Vec_loop (Module*, lo->seg_modules, mindex, module) + { + if ((module->flags & MOD_FLAG_UNKNOWN) != 0 || + (!is_dis && module->lang_code == Sp_lang_unknown)) + continue; + if (space) + fprintf (out_file, "\n"); + print_anno_file (module->file_name, sel, src, is_dis, + stdout, stdin, out_file, dbevr, false); + space = true; + } + } + else + print_anno_file (name, sel, src, is_dis, stdout, stdin, out_file, dbevr, false); +} + +static bool +isFatal (bool isDisasm, LoadObject::Arch_status status) +{ + if (isDisasm) + { + switch (status) + { + // non-fatal errors for disassembly + case LoadObject::ARCHIVE_BAD_STABS: + case LoadObject::ARCHIVE_NO_STABS: + return false; + default: + return true; + } + } + return true; +} + +void +er_src::open (char *exe) +{ + LoadObject::Arch_status status; + char *errstr; + Module *module; + Vector<Histable*> *module_lst; + + // Construct the Segment structure + char *path = strdup (exe); + lo = dbeSession->createLoadObject (path); + if (NULL == lo->dbeFile->find_file (lo->dbeFile->get_name ())) + { + fprintf (stderr, GTXT ("%s: Error: unable to open file %s\n"), prog_name, lo->dbeFile->get_name ()); + exit (1); + } + checkJavaClass (exe); + + if (is_java_class ()) + { + lo->type = LoadObject::SEG_TEXT; + lo->set_platform (Java, Wnone); + lo->id = (uint64_t) - 1; // see AnalyzerSession::ask_which for details + module = dbeSession->createClassFile (dbe_strdup (exe)); + module->loadobject = lo; + lo->seg_modules->append (module); + module->dbeFile->set_location (exe); + if (module->readFile () != module->AE_OK) + { + Emsg *emsg = module->get_error (); + if (emsg) + { + fprintf (stderr, GTXT ("%s: Error: %s\n"), prog_name, emsg->get_msg ()); + return; + } + fprintf (stderr, GTXT ("%s: Error: Could not read class file `%s'\n"), prog_name, exe); + return; + } + status = lo->sync_read_stabs (); + if (status != LoadObject::ARCHIVE_SUCCESS) + { + if (status == LoadObject::ARCHIVE_ERR_OPEN) + { + fprintf (stderr, GTXT ("%s: Error: Could not read class file `%s'\n"), prog_name, exe); + return; + } + else + { + if (isDisasm) + if (status == LoadObject::ARCHIVE_NO_STABS) + { + fprintf (stderr, GTXT ("%s: Error: `%s' is interface; disassembly annotation not available\n"), prog_name, exe); + return; + } + } + } + } + else + { + status = lo->sync_read_stabs (); + if (status != LoadObject::ARCHIVE_SUCCESS) + { + errstr = lo->status_str (status); + if (errstr) + { + fprintf (stderr, "%s: %s\n", prog_name, errstr); + free (errstr); + } + if (isFatal (isDisasm, status)) + return; + } + obj_type = OT_EXE_ELF; + + // if .o file, then set file as the exe name + if (lo->is_relocatable ()) + { + // find the module, if we can + module_lst = new Vector<Histable*>; + module = dbeSession->map_NametoModule (path, module_lst, 0); + if (module == NULL) + // Create a module with the right name + module = dbeSession->createModule (lo, path); + } + } +} diff --git a/gprofng/src/gp-display-text.cc b/gprofng/src/gp-display-text.cc new file mode 100644 index 0000000..7183553 --- /dev/null +++ b/gprofng/src/gp-display-text.cc @@ -0,0 +1,2834 @@ +/* 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 <unistd.h> // isatty + +#include "gp-print.h" +#include "ipcio.h" +#include "Command.h" +#include "Dbe.h" +#include "DbeApplication.h" +#include "DbeSession.h" +#include "Experiment.h" +#include "Emsg.h" +#include "DbeView.h" +#include "DataObject.h" +#include "Function.h" +#include "Hist_data.h" +#include "PathTree.h" +#include "LoadObject.h" +#include "Function.h" +#include "FilterSet.h" +#include "Filter.h" +#include "MetricList.h" +#include "MemorySpace.h" +#include "Module.h" +#include "util.h" +#include "i18n.h" +#include "StringBuilder.h" +#include "debug.h" +#include "UserLabel.h" + +static char *exe_name; +static char **new_argv; + +void +reexec () +{ + if (dbeSession != NULL) + dbeSession->unlink_tmp_files (); + execv (exe_name, new_argv); +} + +/** + * Run application under enhance if the following requirements are satisfied: + * 1. Environment variable GPROFNG_ENHANCE is not set to "no" + * 2. Standard input is terminal + * 3. Standard output is terminal + * 4. /bin/enhance exists and can work on this system + */ +static void +reexec_enhance (int argc, char *argv[]) +{ + char *gp_enhance = getenv ("GPROFNG_ENHANCE"); + if (NULL != gp_enhance && 0 == strcasecmp (gp_enhance, "no")) + return; // Do not enhance + // Verify that input and output are tty + if (!isatty (fileno (stdin))) // stdin is not a terminal + return; // Do not enhance + if (!isatty (fileno (stdout))) // stdout is not a terminal + return; // Do not enhance + char *enhance_name = NTXT ("/bin/enhance"); + struct stat sbuf; + int res = stat (enhance_name, &sbuf); // Check if enhance exists + if (res == 0) + res = system (NTXT ("/bin/enhance /bin/true")); // Check if enhance can work + if (res != 0) + { + fflush (stdout); + printf (GTXT ("Warning: History and command editing is not supported on this system.\n")); + fflush (stdout); + return; + } + else + { + printf (GTXT ("Note: History and command editing is supported on this system.\n")); + fflush (stdout); + } + char **nargv = new char*[argc + 2]; + for (int i = 0; i < argc; i++) + nargv[i + 1] = argv[i]; + nargv[0] = enhance_name; + nargv[argc + 1] = NULL; + putenv (NTXT ("GPROFNG_ENHANCE=no")); // prevent recursion + execv (enhance_name, nargv); + // execv failed. Continue to run the program + delete[] nargv; +} + +int +main (int argc, char *argv[]) +{ + er_print *erprint; + int ind = 1; + if (argc > ind && *argv[ind] == '-') + { + int arg_count, cparam; + if (Command::get_command (argv[ind] + 1, arg_count, cparam) == WHOAMI) + ind = ind + 1 + arg_count; + } + if (argc > ind && argv[ind] != NULL && *argv[ind] != '-') + reexec_enhance (argc, argv); + + // Save argv for reexec()) + exe_name = argv[0]; + new_argv = argv; + + if (argc > ind && argv[ind] != NULL && strcmp (argv[ind], "-IPC") == 0) + { + putenv (NTXT ("LC_NUMERIC=C")); // Use non-localized numeric data in IPC packets + erprint = new er_print (argc, argv); + theDbeApplication->rdtMode = false; + ipc_mainLoop (argc, argv); + } + else + { + erprint = new er_print (argc, argv); + erprint->start (argc, argv); + } + + dbeSession->unlink_tmp_files (); + if (DUMP_CALL_STACK) + { + extern long total_calls_add_stack, total_stacks, total_nodes, call_stack_size[201]; + fprintf (stderr, NTXT ("total_calls_add_stack=%lld\ntotal_stacks=%lld\ntotal_nodes=%lld\n"), + (long long) total_calls_add_stack, (long long) total_stacks, (long long) total_nodes); + for (int i = 0; i < 201; i++) + if (call_stack_size[i] != 0) + fprintf (stderr, NTXT (" call_stack_size[%d] = %6lld\n"), i, + (long long) call_stack_size[i]); + } +#if defined(DEBUG) + delete erprint; +#endif + return 0; +} + +er_print::er_print (int argc, char *argv[]) +: DbeApplication (argc, argv) +{ + out_fname = GTXT ("<stdout>"); + inp_file = stdin; + out_file = stdout; + dis_file = stdout; + cov_string = NULL; + limit = 0; + cstack = new Vector<Histable*>(); + was_QQUIT = false; +} + +er_print::~er_print () +{ + free (cov_string); + delete cstack; + if (inp_file != stdin) + fclose (inp_file); +} + +void +er_print::start (int argc, char *argv[]) +{ + Vector<String> *res = theDbeApplication->initApplication (NULL, NULL, NULL); + res->destroy (); + delete res; + + // Create a view on the session + dbevindex = dbeSession->createView (0, -1); + dbev = dbeSession->getView (dbevindex); + limit = dbev->get_limit (); + (void) check_args (argc, argv); + int ngood = dbeSession->ngoodexps (); + if (ngood == 0) + { + fprintf (stderr, GTXT ("No valid experiments loaded; exiting\n")); + return; + } + dbeDetectLoadMachineModel (dbevindex); + run (argc, argv); +} + +bool +er_print::free_memory_before_exit () +{ + return was_QQUIT; +} + +void +er_print::usage () +{ + +/* + Ruud - Isolate this line because it has an argument. Otherwise it would be at the + end of the long option list. +*/ + printf ( GTXT ( + "Usage: gprofng display text [OPTION(S)] [COMMAND(S)] [-script <script_file>] EXPERIMENT(S)\n")); + + printf ( GTXT ( + "\n" + "Print a plain text version of the various displays supported by gprofng.\n" + "\n" + "Options:\n" + "\n" + " --version print the version number and exit.\n" + " --help print usage information and exit.\n" + " --verbose {on|off} enable (on) or disable (off) verbose mode; the default is \"off\".\n" + "\n" + " -script <script-file> execute the commands stored in the script file;\n" + " this feature may be combined with commands specified\n" + " at the command line.\n" + "\n" + "Commands:\n" + "\n" + "This tool supports a rich set of commands to control the display of the\n" + "data; instead of, or in addition to, including these commands in a script\n" + "file, it is also allowed to include such commands at the command line;\n" + "in this case, the commands need to be prepended with the \"-\" symbol; the\n" + "commands are processed and interpreted left from right, so the order matters;\n" + "The gprofng manual documents the commands that are supported.\n" + "\n" + "If this tool is invoked without options, commands, or a script file, it starts\n" + "in interpreter mode. The user can then issue the commands interactively; the\n" + "session is terminated with the \"exit\" command in the interpreter.\n" + "\n" + "Documentation:\n" + "\n" + "A getting started guide for gprofng is maintained as a Texinfo manual. If the info and\n" + "gprofng programs are properly installed at your site, the command \"info gprofng\"\n" + "should give you access to this document.\n" + "\n" + "See also:\n" + "\n" + "gprofng(1), gp-archive(1), gp-collect-app(1), gp-display-html(1), gp-display-src(1)\n")); +} + +int // returns count of experiments read +er_print::check_args (int argc, char *argv[]) +{ + CmdType cmd_type; + int arg_count; + int cparam; + int exp_no; + error_msg = NULL; + + Emsg *rcmsg = fetch_comments (); + while (rcmsg != NULL) + { + fprintf (stderr, NTXT ("%s: %s\n"), prog_name, rcmsg->get_msg ()); + rcmsg = rcmsg->next; + } + delete_comments (); + + // Set up the list of experiments to add after checking the args + Vector<Vector<char*>*> *exp_list = new Vector<Vector<char*>*>(); + + // Prescan the command line arguments, processing only a few + for (int i = 1; i < argc; i++) + { + if (*argv[i] != '-') + { + // we're at the end -- get the list of experiments + // Build the list of experiments, and set the searchpath + Vector<char*> *list = dbeSession->get_group_or_expt (argv[i]); + if (list->size () > 0) + { + for (int j = 0, list_sz = list->size (); j < list_sz; j++) + { + char *path = list->fetch (j); + if (strlen (path) == 0 || strcmp (path, NTXT ("\\")) == 0) + continue; + char *p = strrchr (path, '/'); + if (p) + { + // there's a directory in front of the name; add it to search path + *p = '\0'; + dbeSession->set_search_path (path, false); + } + } + list->destroy (); + list->append (dbe_strdup (argv[i])); + exp_list->append (list); + } + else + delete list; + continue; + } + + // Not at the end yet, treat the next argument as a command + switch (cmd_type = Command::get_command (argv[i] + 1, arg_count, cparam)) + { + case WHOAMI: + whoami = argv[i] + 1 + cparam; + break; + case HELP: + if (i + 1 + arg_count == argc) + { + usage(); + exit (0); + } + break; + case HHELP: + Command::print_help (whoami, true, false, stdout); + fprintf (stdout, "\n"); + indxo_list (false, stdout); + fprintf (stdout, "\n"); + mo_list (false, stdout); + if (!getenv ("_BUILDING_MANPAGE")) + fprintf (stdout, GTXT ("\nSee gprofng(1) for more details\n")); + exit (0); + case ADD_EXP: + case DROP_EXP: + case OPEN_EXP: + printf (GTXT ("Error: command %s can not appear on the command line\n"), argv[i]); + exit (2); + case VERSION_cmd: + Application::print_version_info (); + exit (0); + case AMBIGUOUS_CMD: + fprintf (stderr, GTXT ("Error: Ambiguous command: %s\n"), argv[i]); + exit (2); + case UNKNOWN_CMD: + fprintf (stderr, GTXT ("Error: Invalid command: %s\n"), argv[i]); + exit (2); + // it's a plausible argument; see if we process now or later + case SOURCE: + case DISASM: + case CSINGLE: + case CPREPEND: + case CAPPEND: + case FSINGLE: + case SAMPLE_DETAIL: + case STATISTICS: + case HEADER: + //skip the arguments to that command + i += arg_count; + if (i >= argc || end_command (argv[i])) + i--; + break; + case PRINTMODE: + case INDXOBJDEF: + case ADDPATH: + case SETPATH: + case PATHMAP: + case OBJECT_SHOW: + case OBJECT_HIDE: + case OBJECT_API: + case OBJECTS_DEFAULT: + case EN_DESC: + // these are processed in the initial pass over the arguments + proc_cmd (cmd_type, cparam, (arg_count > 0) ? argv[i + 1] : NULL, + (arg_count > 1) ? argv[i + 2] : NULL, + (arg_count > 2) ? argv[i + 3] : NULL, + (arg_count > 3) ? argv[i + 4] : NULL); + i += arg_count; + break; + default: + // any others, we skip for now + i += arg_count; + break; + } + } + + // Make sure some experiments were specified + exp_no = exp_list->size (); + if (exp_no == 0) + { // no experiment name + fprintf (stderr, GTXT ("%s: Missing experiment directory (use the --help option to get a usage overview)\n"), whoami); + exit (1); + } + + // add the experiments to the session + char *errstr = dbeOpenExperimentList (0, exp_list, false); + for (long i = 0; i < exp_list->size (); i++) + { + Vector<char*>* p = exp_list->get (i); + Destroy (p); + } + delete exp_list; + if (errstr != NULL) + { + fprintf (stderr, NTXT ("%s"), errstr); + free (errstr); + } + + return exp_no; +} + +int +er_print::is_valid_seg_name (char *lo_name, int prev) +{ + // prev is the loadobject segment index that was last returned + // search starts following that loadobject + int index; + LoadObject *lo; + char *p_lo_name = lo_name; + char *name = NULL; + + // strip angle brackets from all but <Unknown> and <Total> + if (strcmp (lo_name, "<Unknown>") && strcmp (lo_name, "<Total>")) + { + if (*lo_name == '<') + { + name = dbe_strdup (lo_name + 1); + p_lo_name = name; + char *p = strchr (name, '>'); + if (p) + *p = '\0'; + } + } + + // get the load object list from the session + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + Vec_loop (LoadObject*, lobjs, index, lo) + { + if (prev > 0) + { + if (lo->seg_idx == prev) // this is where we left off + prev = -1; + continue; + } + + // does this one match? + if (cmp_seg_name (lo->get_pathname (), p_lo_name)) + { + delete lobjs; + free (name); + size_t len = strlen (lo_name); + if ((len > 7 && streq (lo_name + len - 7, NTXT (".class>"))) || + (len > 6 && streq (lo_name + len - 6, NTXT (".class")))) + { + fprintf (stderr, GTXT ("Error: Java class `%s' is not selectable\n"), lo_name); + return -1; + } + return lo->seg_idx; + } + } + delete lobjs; + free (name); + return -1; +} + +int +er_print::cmp_seg_name (char *full_name, char *lo_name) +{ + char *cmp_name; + if (!strchr (lo_name, '/') && (cmp_name = strrchr (full_name, '/'))) + cmp_name++; // basename + else + cmp_name = full_name; // full path name + return !strcmp (lo_name, cmp_name); +} + +// processing object_select +// Note that this does not affect the strings in Settings, +// unlike object_show, object_hide, and object_api +int +er_print::process_object_select (char *names) +{ + int index; + LoadObject *lo; + int no_lobj = 0; + bool got_err = false; + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + if ((names == NULL) || !strcasecmp (names, Command::ALL_CMD)) + { // full coverage + Vec_loop (LoadObject*, lobjs, index, lo) + { + dbev->set_lo_expand (lo->seg_idx, LIBEX_SHOW); + } + } + else + { // parsing coverage + // first, hide functions from all loadobjects + // except the java ones + Vec_loop (LoadObject*, lobjs, index, lo) + { + char *lo_name = lo->get_name (); + if (lo_name != NULL) + { + size_t len = strlen (lo_name); + if ((len > 7 && streq (lo_name + len - 7, NTXT (".class>"))) || + (len > 6 && streq (lo_name + len - 6, NTXT (".class")))) + continue; + } + dbev->set_lo_expand (lo->seg_idx, LIBEX_HIDE); + } + + Vector <char *> *tokens = split_str (names, ','); + for (long j = 0, sz = VecSize (tokens); j < sz; j++) + { + // loop over the provided names + char *lo_name = tokens->get (j); + int seg_idx = -1; + seg_idx = is_valid_seg_name (lo_name, seg_idx); + while (seg_idx != -1) + { + dbev->set_lo_expand (seg_idx, LIBEX_SHOW); + no_lobj++; + seg_idx = is_valid_seg_name (lo_name, seg_idx); + } + if (no_lobj == 0) + { + got_err = true; + fprintf (stderr, GTXT ("Error: Unknown load object: `%s'\n"), lo_name); + } + free (lo_name); + } + delete tokens; + } + + if (!got_err) + { // good coverage string + free (cov_string); + cov_string = strdup (names); + } + else + { // bad, restore original coverage + no_lobj = -1; + process_object_select (cov_string); + } + delete lobjs; + return no_lobj; +} + +int +er_print::set_libexpand (char *cov, enum LibExpand expand) +{ + bool changed = dbev->set_libexpand (cov, expand); + if (changed == true) + dbev->update_lo_expands (); + return 0; +} + +int +er_print::set_libdefaults () +{ + dbev->set_libdefaults (); + return 0; +} + +bool +er_print::end_command (char *cmd) +{ + if (cmd == NULL || *cmd == '-') + return true; + size_t len = strlen (cmd); + if (cmd[len - 1] == '/') + len--; + if ((len > 3 && !strncmp (&cmd[len - 3], NTXT (".er"), 3)) || + (len > 4 && !strncmp (&cmd[len - 4], NTXT (".erg"), 4))) + return true; + return false; +} + +// Now actually start processing the arguments +void +er_print::run (int argc, char *argv[]) +{ + CmdType cmd_type; + int arg_count, cparam, i; + bool got = false; + char *arg1, *arg2; + for (i = 1; i < argc; i++) + { + if (*argv[i] != '-') // open experiment pointer files + continue; + switch (cmd_type = Command::get_command (argv[i] + 1, arg_count, cparam)) + { + case WHOAMI: + whoami = argv[i] + 1 + cparam; + break; + case SCRIPT: + got = true; + inp_file = fopen (argv[++i], "r"); + if (inp_file == NULL) + { + fprintf (stderr, GTXT ("Error: Script file cannot be opened: %s\n"), argv[i]); + exit (3); + } + proc_script (); + break; + case STDIN: + got = true; + inp_file = stdin; + proc_script (); + break; + case SOURCE: // with option arg_count == 2 + case DISASM: + got = true; + i += arg_count; + if ((i >= argc) || end_command (argv[i])) + { + i--; + arg1 = argv[i]; + arg2 = NTXT (""); + } + else + { + arg1 = argv[i - 1]; + arg2 = argv[i]; + } + proc_cmd (cmd_type, cparam, arg1, arg2, NULL, NULL, true); + break; + case CSINGLE: + case CPREPEND: + case CAPPEND: + case FSINGLE: + got = true; + i += arg_count; + if ((i >= argc) || end_command (argv[i])) + { + i--; + proc_cmd (cmd_type, cparam, argv[i], NTXT ("1")); + } + else + proc_cmd (cmd_type, cparam, argv[i - 1], argv[i]); + break; + case SAMPLE_DETAIL: // with option arg_count == 1 + case STATISTICS: + case HEADER: + got = true; + // now fall through to process the command + case COMPARE: + got = true; + i += arg_count; + if ((i >= argc) || end_command (argv[i])) + { + i--; + proc_cmd (cmd_type, cparam, NULL, NULL); + } + else + proc_cmd (cmd_type, cparam, argv[i], NULL); + break; + case PRINTMODE: + case INDXOBJDEF: + case ADDPATH: + case SETPATH: + case PATHMAP: + case OBJECT_SHOW: + case OBJECT_HIDE: + case OBJECT_API: + case OBJECTS_DEFAULT: + case EN_DESC: + got = true; + // these have been processed already + i += arg_count; + break; + case LIMIT: + got = true; + proc_cmd (cmd_type, cparam, (arg_count > 0) ? argv[i + 1] : NULL, + (arg_count > 1) ? argv[i + 2] : NULL); + i += arg_count; + break; + default: + got = true; + proc_cmd (cmd_type, cparam, (arg_count > 0) ? argv[i + 1] : NULL, + (arg_count > 1) ? argv[i + 2] : NULL); + i += arg_count; + break; + } + } + if (!got) // no command has been specified + proc_script (); +} + +#define MAXARGS 20 + +void +er_print::proc_script () +{ + CmdType cmd_type; + int arg_count, cparam; + char *cmd, *end_cmd; + char *script = NULL; + char *arglist[MAXARGS]; + char *line = NULL; + int lineno = 0; + while (!feof (inp_file)) + { + if (inp_file == stdin) + printf (NTXT ("(%s) "), get_basename (prog_name)); + free (script); + script = read_line (inp_file); + if (script == NULL) + continue; + free (line); + line = dbe_strdup (script); + lineno++; + for (int i = 0; i < MAXARGS; i++) + arglist[i] = NULL; + + // ensure it's terminated by a \n, and remove that character + strtok (script, NTXT ("\n")); + + // extract the command + cmd = strtok (script, NTXT (" \t")); + if (cmd == NULL) + continue; + if (*cmd == '#') + { + fprintf (stderr, NTXT ("%s"), line); + continue; + } + if (*cmd == '\n') + continue; + + char *remainder = strtok (NULL, NTXT ("\n")); + // now extract the arguments + int nargs = 0; + for (;;) + { + end_cmd = NULL; + if (nargs >= MAXARGS) + fprintf (stderr, GTXT ("Warning: more than %d arguments to %s command, line %d\n"), + MAXARGS, cmd, lineno); + char *nextarg = strtok (remainder, NTXT ("\n")); + if ((nextarg == NULL) || (*nextarg == '#')) + // either the end of the line, or a comment indicator + break; + if (nargs >= MAXARGS) + { + parse_qstring (nextarg, &end_cmd); + nargs++; + } + else + arglist[nargs++] = parse_qstring (nextarg, &end_cmd); + remainder = end_cmd; + if (remainder == NULL) + break; + // skip any blanks or tabs to get to next argument + while (*remainder == ' ' || *remainder == '\t') + remainder++; + } + + cmd_type = Command::get_command (cmd, arg_count, cparam); + + // check for extra arguments + if (cmd_type != UNKNOWN_CMD && cmd_type != INDXOBJDEF && nargs > arg_count) + fprintf (stderr, GTXT ("Warning: extra arguments to %s command, line %d\n"), + cmd, lineno); + switch (cmd_type) + { + case SOURCE: + case DISASM: + // ignore any third parameter + // if there was, we have written a warning + proc_cmd (cmd_type, cparam, arglist[0], arglist[1], NULL, NULL, + (inp_file != stdin)); + break; + case QUIT: + free (script); + free (line); + exit (0); + case QQUIT: + was_QQUIT = true; + free (script); + free (line); + return; + case STDIN: + break; + case COMMENT: + fprintf (dis_file, NTXT ("%s"), line); + break; + case AMBIGUOUS_CMD: + fprintf (stderr, GTXT ("Error: Ambiguous command: %s\n"), cmd); + break; + case UNKNOWN_CMD: + if (*cmd != '\n') + fprintf (stderr, GTXT ("Error: Invalid command: %s\n"), cmd); + break; + default: + proc_cmd (cmd_type, cparam, arglist[0], arglist[1]); + break; + } + } + // free up the input line + free (script); + free (line); +} + +void +er_print::proc_cmd (CmdType cmd_type, int cparam, + char *arg1, char *arg2, char *arg3, char *arg4, bool xdefault) +{ + er_print_common_display *cd; + FILE *ck_file, *save_file; + char *name; + int bgn_index, end_index, index; + Cmd_status status; + char *scratch, *scratch1; + switch (cmd_type) + { + case FUNCS: + print_func (Histable::FUNCTION, MODE_LIST, + dbev->get_metric_list (MET_NORMAL), dbev->get_metric_list (MET_NORMAL)); + break; + case FDETAIL: + print_func (Histable::FUNCTION, MODE_DETAIL, + dbev->get_metric_list (MET_NORMAL), dbev->get_metric_ref (MET_NORMAL)); + break; + case FSINGLE: + print_func (Histable::FUNCTION, MODE_DETAIL, + dbev->get_metric_list (MET_NORMAL), dbev->get_metric_ref (MET_NORMAL), + arg1, arg2); + break; + case HOTPCS: + print_func (Histable::INSTR, MODE_LIST, + dbev->get_metric_list (MET_NORMAL), dbev->get_metric_list (MET_NORMAL)); + break; + case PDETAIL: + print_func (Histable::INSTR, MODE_DETAIL, + dbev->get_metric_list (MET_NORMAL), dbev->get_metric_ref (MET_NORMAL)); + break; + case HOTLINES: + print_func (Histable::LINE, MODE_LIST, + dbev->get_metric_list (MET_NORMAL), dbev->get_metric_list (MET_NORMAL)); + break; + case LDETAIL: + print_func (Histable::LINE, MODE_DETAIL, + dbev->get_metric_list (MET_NORMAL), dbev->get_metric_ref (MET_NORMAL)); + break; + case OBJECTS: + print_objects (); + break; + case OVERVIEW_NEW: + print_overview (); + break; + case LOADOBJECT: + print_segments (); + break; + case GPROF: + print_func (Histable::FUNCTION, MODE_GPROF, + dbev->get_metric_list (MET_CALL), dbev->get_metric_list (MET_NORMAL)); + break; + case CALLTREE: + if (dbev->comparingExperiments ()) + { + fprintf (out_file, GTXT ("\nNot available when comparing experiments\n\n")); + break; + } + print_ctree (cmd_type); + break; + case CSINGLE: + case CPREPEND: + case CAPPEND: + case CRMFIRST: + case CRMLAST: + print_gprof (cmd_type, arg1, arg2); + break; + case EXP_LIST: + exp_list (); + break; + case DESCRIBE: + describe (); + break; + case SCOMPCOM: + status = dbev->proc_compcom (arg1, true, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + break; + case STHRESH: + status = dbev->proc_thresh (arg1, true, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + break; + case DCOMPCOM: + status = dbev->proc_compcom (arg1, false, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + break; + case COMPCOM: + status = dbev->proc_compcom (arg1, true, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + status = dbev->proc_compcom (arg1, false, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + break; + case DTHRESH: + status = dbev->proc_thresh (arg1, false, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + break; + case SOURCE: + case DISASM: + { + if (arg3 != NULL) + abort (); + if (arg1 == NULL) + { + fprintf (stderr, GTXT ("Error: Invalid function/file setting: \n")); + break; + } + char *fcontext = NULL; + char *arg = parse_fname (arg1, &fcontext); + if (arg == NULL) + { + fprintf (stderr, GTXT ("Error: Invalid function/file setting: %s\n"), arg1); + free (fcontext); + break; + } + if (arg2 && (strlen (arg2) == 0)) + arg2 = NULL; + print_anno_file (arg, arg2, fcontext, cmd_type == DISASM, + dis_file, inp_file, out_file, dbev, xdefault); + free (arg); + free (fcontext); + break; + } + case METRIC_LIST: + proc_cmd (METRICS, cparam, NULL, NULL); + dbev->get_metric_ref (MET_NORMAL)->print_metric_list (dis_file, + GTXT ("Available metrics:\n"), false); + break; + case METRICS: + if (arg1) + { + char *ret = dbev->setMetrics (arg1, false); + if (ret != NULL) + { + fprintf (stderr, GTXT ("Error: %s\n"), ret); + proc_cmd (METRIC_LIST, cparam, NULL, NULL); + break; + } + } + scratch = dbev->get_metric_list (MET_NORMAL)->get_metrics (); + fprintf (dis_file, GTXT ("Current metrics: %s\n"), scratch); + free (scratch); + proc_cmd (SORT, cparam, NULL, NULL); + break; + case GMETRIC_LIST: + scratch = dbev->get_metric_list (MET_CALL)->get_metrics (); + fprintf (dis_file, GTXT ("Current caller-callee metrics: %s\n"), scratch); + free (scratch); + fprintf (dis_file, GTXT ("Current caller-callee sort Metric: %s\n"), + dbev->getSort (MET_DATA)); + break; + case INDX_METRIC_LIST: + scratch = dbev->get_metric_list (MET_INDX)->get_metrics (); + fprintf (dis_file, GTXT ("Current index-object metrics: %s\n"), scratch); + free (scratch); + scratch = dbev->getSort (MET_INDX); + fprintf (dis_file, GTXT ("Current index-object sort Metric: %s\n"), scratch); + free (scratch); + break; + case SORT: + if (arg1) + { + char *ret = dbev->setSort (arg1, MET_NORMAL, false); + if (ret != NULL) + { + fprintf (stderr, GTXT ("Error: %s\n"), ret); + proc_cmd (METRICS, cparam, NULL, NULL); + break; + } + dbev->setSort (arg1, MET_SRCDIS, false); + dbev->setSort (arg1, MET_CALL, false); + dbev->setSort (arg1, MET_DATA, false); + dbev->setSort (arg1, MET_INDX, false); + dbev->setSort (arg1, MET_CALL_AGR, false); + dbev->setSort (arg1, MET_IO, false); + dbev->setSort (arg1, MET_HEAP, false); + } + scratch = dbev->getSort (MET_NORMAL); + scratch1 = dbev->getSortCmd (MET_NORMAL); + fprintf (dis_file, + GTXT ("Current Sort Metric: %s ( %s )\n"), scratch, scratch1); + free (scratch1); + free (scratch); + break; + case OBJECT_SHOW: + if (arg1) + set_libexpand (arg1, LIBEX_SHOW); + obj_list (); + break; + case OBJECT_HIDE: + if (arg1) + set_libexpand (arg1, LIBEX_HIDE); + obj_list (); + break; + case OBJECT_API: + if (arg1) + set_libexpand (arg1, LIBEX_API); + obj_list (); + break; + case OBJECTS_DEFAULT: + set_libdefaults (); + obj_list (); + break; + case OBJECT_LIST: + obj_list (); + break; + case OBJECT_SELECT: + if (arg1) + { + if (process_object_select (arg1) != -1) + proc_cmd (OBJECT_LIST, cparam, NULL, NULL); + else + fprintf (stderr, GTXT ("Error: Type \"object_list\" for a list of all load objects.\n")); + } + else + fprintf (stderr, GTXT ("Error: No load object has been specified.\n")); + break; + case LOADOBJECT_LIST: + seg_list (); + break; + case LOADOBJECT_SELECT: + if (arg1) + { + if (process_object_select (arg1) != -1) + proc_cmd (LOADOBJECT_LIST, cparam, NULL, NULL); + else + fprintf (stderr, GTXT ("Error: Type \"segment_list\" for a list of all segments.\n")); + } + else + fprintf (stderr, GTXT ("Error: No segment has been specified.\n")); + break; + case SAMPLE_LIST: + filter_list (SAMPLE_LIST); + break; + case SAMPLE_SELECT: + if (arg1 && !dbev->set_pattern (SAMPLE_FILTER_IDX, arg1)) + fprintf (stderr, GTXT ("Error: Invalid filter pattern specification %s\n"), arg1); + proc_cmd (SAMPLE_LIST, cparam, NULL, NULL); + break; + case THREAD_LIST: + filter_list (THREAD_LIST); + break; + case THREAD_SELECT: + if (arg1 && !dbev->set_pattern (THREAD_FILTER_IDX, arg1)) + fprintf (stderr, GTXT ("Error: Invalid filter pattern specification %s\n"), arg1); + proc_cmd (THREAD_LIST, cparam, NULL, NULL); + break; + case LWP_LIST: + filter_list (LWP_LIST); + break; + case LWP_SELECT: + if (arg1 && !dbev->set_pattern (LWP_FILTER_IDX, arg1)) + fprintf (stderr, GTXT ("Error: Invalid filter pattern specification %s\n"), arg1); + proc_cmd (LWP_LIST, cparam, NULL, NULL); + break; + case CPU_LIST: + filter_list (CPU_LIST); + break; + case CPU_SELECT: + if (arg1 && !dbev->set_pattern (CPU_FILTER_IDX, arg1)) + fprintf (stderr, GTXT ("Error: Invalid filter pattern specification %s\n"), arg1); + proc_cmd (CPU_LIST, cparam, NULL, NULL); + break; + case FILTERS: + if (arg1 != NULL) + { + if (strcmp (arg1, NTXT ("True")) == 0) + scratch = dbev->set_filter (NULL); + else + scratch = dbev->set_filter (arg1); + if (scratch != NULL) + fprintf (stderr, GTXT ("Error: %s\n"), scratch); + } + scratch = dbev->get_filter (); + fprintf (dis_file, GTXT ("current filter setting: \"%s\"\n"), + scratch == NULL ? GTXT ("<none>") : scratch); + break; + case OUTFILE: + if (arg1) + { + set_outfile (arg1, out_file, false); + if (inp_file != stdin) + dis_file = out_file; + } + break; + case APPENDFILE: + if (arg1) + { + set_outfile (arg1, out_file, true); + if (inp_file != stdin) + dis_file = out_file; + } + break; + case LIMIT: + if (arg1) + { + limit = (int) strtol (arg1, (char **) NULL, 10); + char *res = dbeSetPrintLimit (dbevindex, limit); + if (res != NULL) + fprintf (stderr, NTXT ("%s\n"), res); + } + + limit = dbeGetPrintLimit (dbevindex); + fprintf (stderr, GTXT ("Print limit set to %d\n"), limit); + break; + case NAMEFMT: + if (arg1) + { + status = dbev->set_name_format (arg1); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + } + else + fprintf (stderr, GTXT ("Error: No format has been specified.\n")); + break; + case VIEWMODE: + { + if (arg1) + { + status = dbev->set_view_mode (arg1, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + } + const char *vname = "unknown"; + int vm = dbev->get_view_mode (); + switch (vm) + { + case VMODE_USER: + vname = "user"; + break; + case VMODE_EXPERT: + vname = "expert"; + break; + case VMODE_MACHINE: + vname = "machine"; + break; + } + fprintf (stderr, GTXT ("Viewmode set to %s\n"), vname); + } + break; + + // EN_DESC does not make sense after experiments are read, but it does make sense on the command line, + // processed before the experiments are read. + case EN_DESC: + if (arg1) + { + status = dbev->set_en_desc (arg1, false); + if (status != CMD_OK) + fprintf (stderr, GTXT ("Error: %s: %s\n"), Command::get_err_string (status), arg1); + } + else + fprintf (stderr, GTXT ("Error: No descendant processing has been specified.\n")); + break; + case SETPATH: + case ADDPATH: + if (arg1) + dbeSession->set_search_path (arg1, (cmd_type == SETPATH)); + fprintf (dis_file, GTXT ("search path:\n")); + Vec_loop (char*, dbeSession->get_search_path (), index, name) + { + fprintf (dis_file, NTXT ("\t%s\n"), name); + } + break; + case PATHMAP: + { + Vector<pathmap_t*> *pathMaps = dbeSession->get_pathmaps (); + if (arg1 != NULL) + { + if (arg2 == NULL) + { + fprintf (stderr, GTXT ("Error: No replacement path prefix has been specified.\n")); + break; + } + // add this mapping to the session + char *err = Settings::add_pathmap (pathMaps, arg1, arg2); + if (err != NULL) + { + fprintf (stderr, NTXT ("%s"), err); + free (err); + } + } + fprintf (dis_file, GTXT ("Path mappings: from -> to\n")); + for (int i = 0, sz = pathMaps->size (); i < sz; i++) + { + pathmap_t *thismap = pathMaps->get (i); + fprintf (dis_file, NTXT ("\t`%s' -> `%s'\n"), thismap->old_prefix, thismap->new_prefix); + } + } + break; + case SAMPLE_DETAIL: + if (get_exp_id (arg1, bgn_index, end_index) != -1) + { + cd = new er_print_experiment (dbev, bgn_index, end_index, false, + false, false, true, true); + print_cmd (cd); + delete cd; + } + break; + case STATISTICS: + if (get_exp_id (arg1, bgn_index, end_index) != -1) + { + cd = new er_print_experiment (dbev, bgn_index, end_index, false, + false, true, true, false); + print_cmd (cd); + delete cd; + } + break; + case PRINTMODE: + { + if (arg1 == NULL) + { + fprintf (stderr, GTXT ("printmode is set to `%s'\n\n"), dbeGetPrintModeString (dbevindex)); + break; + } + char *s = dbeSetPrintMode (dbevindex, arg1); + if (s != NULL) + { + fprintf (stderr, NTXT ("%s\n"), s); + break; + } + fprintf (stderr, GTXT ("printmode is set to `%s'\n\n"), dbeGetPrintModeString (dbevindex)); + } + break; + case HEADER: + if (get_exp_id (arg1, bgn_index, end_index) != -1) + { + cd = new er_print_experiment (dbev, bgn_index, end_index, false, + true, false, false, false); + print_cmd (cd); + delete cd; + } + break; + case COMPARE: + if (arg1 == NULL) + { + fprintf (out_file, GTXT ("The argument to `compare' must be `on', `off', `delta', or `ratio'\n\n")); + break; + } + else + { + int cmp; + if (strcasecmp (arg1, NTXT ("OFF")) == 0 || strcmp (arg1, NTXT ("0")) == 0) + cmp = CMP_DISABLE; + else if (strcasecmp (arg1, NTXT ("ON")) == 0 || strcmp (arg1, NTXT ("1")) == 0) + cmp = CMP_ENABLE; + else if (strcasecmp (arg1, NTXT ("DELTA")) == 0) + cmp = CMP_DELTA; + else if (strcasecmp (arg1, NTXT ("RATIO")) == 0) + cmp = CMP_RATIO; + else + { + fprintf (out_file, GTXT ("The argument to `compare' must be `on', `off', `delta', or `ratio'\n\n")); + break; + } + int oldMode = dbev->get_compare_mode (); + dbev->set_compare_mode (cmp); + if (oldMode != cmp) + { + dbev->reset_data (false); + dbeSession->reset_data (); + } + } + break; + case LEAKS: + if (!dbeSession->is_leaklist_available ()) + { + fprintf (out_file, GTXT ("\nHeap trace information was not requested when recording experiments\n\n")); + break; + } + if (dbev->comparingExperiments ()) + { // XXXX show warning for compare + fprintf (out_file, GTXT ("\nNot available when comparing experiments\n\n")); + break; + } + cd = new er_print_leaklist (dbev, true, false, dbev->get_limit ()); + print_cmd (cd); + delete cd; + break; + case ALLOCS: + if (!dbeSession->is_leaklist_available ()) + { + fprintf (out_file, GTXT ("\nHeap trace information was not requested when recording experiments\n\n")); + break; + } + cd = new er_print_leaklist (dbev, false, true, dbev->get_limit ()); + print_cmd (cd); + delete cd; + break; + case HEAP: + if (!dbeSession->is_heapdata_available ()) + { + fprintf (out_file, GTXT ("Heap trace information was not requested when recording experiments\n\n")); + break; + } + cd = new er_print_heapactivity (dbev, Histable::HEAPCALLSTACK, false, dbev->get_limit ()); + print_cmd (cd); + delete cd; + break; + case HEAPSTAT: + if (!dbeSession->is_heapdata_available ()) + { + fprintf (out_file, GTXT ("Heap trace information was not requested when recording experiments\n\n")); + break; + } + cd = new er_print_heapactivity (dbev, Histable::HEAPCALLSTACK, true, dbev->get_limit ()); + print_cmd (cd); + delete cd; + break; + case IOACTIVITY: + if (!dbeSession->is_iodata_available ()) + { + fprintf (out_file, GTXT ("I/O trace information was not requested when recording experiments\n\n")); + break; + } + if (dbev->comparingExperiments ()) + { // XXXX show warning for compare + fprintf (out_file, GTXT ("\nNot available when comparing experiments\n\n")); + break; + } + cd = new er_print_ioactivity (dbev, Histable::IOACTFILE, false, dbev->get_limit ()); + print_cmd (cd); + delete cd; + break; + case IOVFD: + if (!dbeSession->is_iodata_available ()) + { + fprintf (out_file, GTXT ("I/O trace information was not requested when recording experiments\n\n")); + break; + } + cd = new er_print_ioactivity (dbev, Histable::IOACTVFD, false, dbev->get_limit ()); + print_cmd (cd); + delete cd; + break; + case IOCALLSTACK: + if (!dbeSession->is_iodata_available ()) + { + fprintf (out_file, GTXT ("I/O trace information was not requested when recording experiments\n\n")); + break; + } + cd = new er_print_ioactivity (dbev, Histable::IOCALLSTACK, false, dbev->get_limit ()); + print_cmd (cd); + delete cd; + break; + case IOSTAT: + if (!dbeSession->is_iodata_available ()) + { + fprintf (out_file, GTXT ("I/O trace information was not requested when recording experiments\n\n")); + break; + } + cd = new er_print_ioactivity (dbev, Histable::IOACTVFD, true, dbev->get_limit ()); + print_cmd (cd); + delete cd; + break; + case HELP: + Command::print_help(whoami, false, true, out_file); + break; + case VERSION_cmd: + Application::print_version_info (); + break; + case SCRIPT: + if (arg1) + { + ck_file = fopen (arg1, NTXT ("r")); + if (ck_file == NULL) + fprintf (stderr, GTXT ("Error: Script file cannot be opened: %s\n"), arg1); + else + { + save_file = inp_file; + inp_file = ck_file; + proc_script (); + inp_file = save_file; + } + } + else + fprintf (stderr, GTXT ("Error: No filename has been specified.\n")); + break; + case QUIT: + exit (0); + break; + + // commands relating to index Objects + case INDXOBJ: + if ((cparam == -1) && (arg1 == NULL)) + { + fprintf (stderr, GTXT ("Error: No index object name has been specified.\n")); + break; + } + // automatically load machine model if applicable + dbeDetectLoadMachineModel (dbevindex); + indxobj (arg1, cparam); + break; + case INDXOBJLIST: + // automatically load machine model if applicable + dbeDetectLoadMachineModel (dbevindex); + indxo_list (false, out_file); + break; + + // define a new IndexObject type + case INDXOBJDEF: + if (arg1 == NULL) + { + fprintf (stderr, GTXT ("Error: No index object name has been specified.\n")); + break; + } + if (arg2 == NULL) + { + fprintf (stderr, GTXT ("Error: No index-expr has been specified.\n")); + break; + } + indxo_define (arg1, arg2, arg3, arg4); + break; + + // the commands following this are unsupported/hidden + case IFREQ: + if (!dbeSession->is_ifreq_available ()) + { + fprintf (out_file, GTXT ("\nInstruction frequency data was not requested when recording experiments\n\n")); + break; + } + ifreq (); + break; + case DUMPNODES: + dump_nodes (); + break; + case DUMPSTACKS: + dump_stacks (); + break; + case DUMPUNK: + dump_unk_pcs (); + break; + case DUMPFUNC: + dump_funcs (arg1); + break; + case DUMPDOBJS: + dump_dataobjects (arg1); + break; + case DUMPMAP: + dump_map (); + break; + case DUMPENTITIES: + dump_entities (); + break; + case DUMP_PROFILE: + dbev->dump_profile (out_file); + break; + case DUMP_SYNC: + dbev->dump_sync (out_file); + break; + case DUMP_HWC: + dbev->dump_hwc (out_file); + break; + case DUMP_HEAP: + if (!dbeSession->is_leaklist_available ()) + { + fprintf (out_file, GTXT ("\nHeap trace information was not requested when recording experiments\n\n")); + break; + } + dbev->dump_heap (out_file); + break; + case DUMP_IOTRACE: + if (!dbeSession->is_iodata_available ()) + { + fprintf (out_file, GTXT ("\nI/O trace information was not requested when recording experiments\n\n")); + break; + } + dbev->dump_iotrace (out_file); + break; + case DMEM: + if (arg1 == NULL) + fprintf (stderr, GTXT ("Error: No sample has been specified.\n")); + else + { + Experiment *exp = dbeSession->get_exp (0); + if (exp != NULL) + exp->DBG_memuse (arg1); + } + break; + case DUMP_GC: + if (!dbeSession->has_java ()) + { + fprintf (out_file, GTXT ("\nJava garbage collection information was not requested when recording experiments\n\n")); + break; + } + dbev->dump_gc_events (out_file); + break; + case DKILL: + { + if (arg1 == NULL) + { + fprintf (stderr, GTXT ("Error: No process has been specified.\n")); + break; + } + if (arg2 == NULL) + { + fprintf (stderr, GTXT ("Error: No signal has been specified.\n")); + break; + } + pid_t p = (pid_t) atoi (arg1); + int signum = atoi (arg2); + char *ret = dbeSendSignal (p, signum); + if (ret != NULL) + fprintf (stderr, GTXT ("Error: %s"), ret); + } + break; + case PROCSTATS: + dump_stats (); + break; + case ADD_EXP: + case OPEN_EXP: + if (arg1 == NULL) + fprintf (stderr, GTXT ("Error: No experiment name has been specified.\n")); + else + { + Vector<Vector<char*>*> *groups = new Vector<Vector<char*>*>(1); + Vector<char*> *list = new Vector<char*>(1); + list->append (arg1); + groups->append (list); + char *res = dbeOpenExperimentList (dbevindex, groups, cmd_type == OPEN_EXP); + if (cmd_type == OPEN_EXP) + fprintf (stderr, GTXT ("Previously loaded experiment have been dropped.\n")); + if (res != NULL) + fprintf (stderr, NTXT ("%s"), res); + else + fprintf (stderr, GTXT ("Experiment %s has been loaded\n"), arg1); + free (res); + delete list; + delete groups; + } + break; + case DROP_EXP: + { + if (arg1 == NULL) + fprintf (stderr, GTXT ("Error: No experiment name has been specified.\n")); + else + { + int exp_index = dbeSession->find_experiment (arg1); + if (exp_index < 0) + fprintf (stderr, GTXT ("Error: experiment %s has not been opened.\n"), arg1); + else + { + Vector<int> *expid = new Vector<int> (1); + expid->append (exp_index); + char *res = dbeDropExperiment (dbevindex, expid); + if (res != NULL) + fprintf (stderr, NTXT ("%s"), res); + else + fprintf (stderr, GTXT ("Experiment %s has been dropped\n"), arg1); + delete expid; + free (res); + } + } + } + break; + case HHELP: + // automatically load machine model if applicable + dbeDetectLoadMachineModel (dbevindex); + Command::print_help (whoami, false, false, out_file); + fprintf (out_file, NTXT ("\n")); + indxo_list (false, out_file); + fprintf (out_file, NTXT ("\n")); + mo_list (false, out_file); + if (!getenv ("_BUILDING_MANPAGE")) + fprintf (out_file, GTXT ("\nSee gprofng(1) for more details\n")); + break; + case QQUIT: + was_QQUIT = true; + return; + default: + fprintf (stderr, GTXT ("Error: Invalid option\n")); + break; + } + + // check for any processing error messages + dump_proc_warnings (); + fflush (out_file); +} + +#define MAX_NUM_HEADER 4 + +void +er_print::disp_list (int num_header, int size, int align[], char *header[], + char **lists[]) +{ + size_t maxlen[MAX_NUM_HEADER]; + char fmt[MAX_NUM_HEADER][64]; + if (num_header > MAX_NUM_HEADER) + abort (); + for (int i = 0; i < num_header; i++) + { + maxlen[i] = strlen (header[i]); + for (int j = 0; j < size; j++) + { + size_t len = strlen (lists[i][j]); + if (maxlen[i] < len) + maxlen[i] = len; + } + + // get format string + if ((align[i] == -1) && (i == num_header - 1)) + snprintf (fmt[i], sizeof (fmt[i]), NTXT ("%%s ")); + else + snprintf (fmt[i], sizeof (fmt[i]), NTXT ("%%%ds "), (int) (align[i] * maxlen[i])); + + // write header + fprintf (out_file, fmt[i], header[i]); + } + putc ('\n', out_file); + + // write separator "===" + size_t np = 0; + for (int i = 0; (i < num_header) && (np < 132); i++) + { + size_t nc = maxlen[i]; + if (nc + np > 132) + nc = 132 - np; + for (size_t j = 0; j < nc; j++) + putc ('=', out_file); + putc (' ', out_file); + np += nc + 1; + } + putc ('\n', out_file); + + // write lists + for (int j = 0; j < size; j++) + { + for (int i = 0; i < num_header; i++) + fprintf (out_file, fmt[i], lists[i][j]); + putc ('\n', out_file); + } +} + +void +er_print::exp_list () +{ + int size, index; + int align[MAX_NUM_HEADER]; + char *header[MAX_NUM_HEADER]; + char **lists[MAX_NUM_HEADER]; + + align[0] = 1; // right-justify + align[1] = 1; // right-justify + align[2] = 1; // right-justify + align[3] = -1; // left-justify + header[0] = GTXT ("ID"); + header[1] = GTXT ("Sel"); + header[2] = GTXT ("PID"); + header[3] = GTXT ("Experiment"); + + size = dbeSession->nexps (); + lists[0] = new char*[size]; + lists[1] = new char*[size]; + lists[2] = new char*[size]; + lists[3] = new char*[size]; + for (index = 0; index < size; index++) + { + lists[0][index] = dbe_sprintf (NTXT ("%d"), index + 1); + lists[1][index] = strdup (dbev->get_exp_enable (index) ? GTXT ("yes") : GTXT ("no")); + lists[2][index] = dbe_sprintf (NTXT ("%d"), dbeSession->get_exp (index)->getPID ()); + lists[3][index] = strdup (dbeSession->get_exp (index)->get_expt_name ()); + } + disp_list (4, size, align, header, lists); + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < size; j++) + free (lists[i][j]); + delete[] lists[i]; + } +} + +void +er_print::describe () +{ + Vector<void*> *res = dbeGetFilterKeywords (dbev->vindex); + if (res == NULL) + return; + Vector <char*> *kwCategoryI18N = (Vector<char*>*) res->fetch (1); + Vector <char*> *kwKeyword = (Vector<char*>*) res->fetch (3); + Vector <char*> *kwFormula = (Vector<char*>*) res->fetch (4); + Vector <char*> *kwDescrip = (Vector<char*>*) res->fetch (5); + Vector <void*> *kwEnumDescs = (Vector<void*>*) res->fetch (6); + String sectionFormat = NTXT ("\n------ %s ------\n"); + String categoryFormat = NTXT ("\n%s\n"); + String keywordFormat = NTXT (" %-20s %s\n"); + String empty = NTXT (""); + String previousCategory = empty; + + for (int i = 0; i < kwKeyword->size (); i++) + { + if (kwKeyword->fetch (i) == NULL) + { + fprintf (dis_file, sectionFormat, kwCategoryI18N->fetch (i)); + continue; + } + String cat = kwCategoryI18N->fetch (i); + if (dbe_strcmp (previousCategory, cat) != 0) + fprintf (dis_file, categoryFormat, cat); + previousCategory = cat; + Vector <String> *enumDescs = (Vector <String> *) kwEnumDescs->fetch (i); + String keyword = kwKeyword->fetch (i); + if (kwDescrip->fetch (i) != NULL) + { + fprintf (dis_file, keywordFormat, keyword, kwDescrip->fetch (i)); + keyword = empty; + } + if (kwFormula->fetch (i) != NULL) + { + fprintf (dis_file, keywordFormat, keyword, kwFormula->fetch (i)); + keyword = empty; + continue; + } + int numEnums = enumDescs != NULL ? enumDescs->size () : 0; + for (int jj = 0; jj < numEnums; jj++) + { + fprintf (dis_file, keywordFormat, keyword, enumDescs->fetch (jj)); + keyword = empty; + } + } + destroy (res); +} + +void +er_print::obj_list () +{ + LoadObject *lo; + int index; + int align[MAX_NUM_HEADER]; + char *header[MAX_NUM_HEADER]; + char **lists[MAX_NUM_HEADER]; + Vector<LoadObject*> *text_segments = dbeSession->get_text_segments (); + if (text_segments->size () == 0) + { + fprintf (dis_file, GTXT ("There are no load objects in this experiment\n")); + return; + } + align[0] = -1; // left-justify + align[1] = -1; // left-justify + align[2] = -1; // left-justify + align[3] = -1; // left-justify + header[0] = GTXT ("Sel"); + header[1] = GTXT ("Load Object"); + header[2] = GTXT ("Index"); + header[3] = GTXT ("Path"); + + int size = text_segments->size (); + lists[0] = new char*[size]; + lists[1] = new char*[size]; + lists[2] = new char*[size]; + lists[3] = new char*[size]; + + char *lo_name; + int new_index = 0; + Vec_loop (LoadObject*, text_segments, index, lo) + { + lo_name = lo->get_name (); + if (lo_name != NULL) + { + size_t len = strlen (lo_name); + if (len > 7 && streq (lo_name + len - 7, NTXT (".class>"))) + continue; + } + LibExpand expand = dbev->get_lo_expand (lo->seg_idx); + switch (expand) + { + case LIBEX_SHOW: + lists[0][new_index] = dbe_strdup (GTXT ("show")); + break; + case LIBEX_HIDE: + lists[0][new_index] = dbe_strdup (GTXT ("hide")); + break; + case LIBEX_API: + lists[0][new_index] = dbe_strdup (GTXT ("API-only")); + break; + } + lists[1][new_index] = dbe_strdup (lo_name); + lists[2][new_index] = dbe_sprintf (NTXT ("%d"), lo->seg_idx); + lists[3][new_index] = dbe_strdup (lo->get_pathname ()); + new_index++; + } + disp_list (4, new_index, align, header, lists); + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < new_index; j++) + free (lists[i][j]); + delete[] lists[i]; + } + delete text_segments; +} + +void +er_print::seg_list () +{ + LoadObject *lo; + int index; + int align[MAX_NUM_HEADER]; + char *header[MAX_NUM_HEADER]; + char **lists[MAX_NUM_HEADER]; + + // XXX seg_list only prints text segments; should extend to all + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + if (lobjs->size () == 0) + { + fprintf (dis_file, GTXT ("There are no segments in this experiment\n")); + return; + } + align[0] = -1; // left-justify + align[1] = 1; // right-justify + align[2] = -1; // left-justify + header[0] = GTXT ("Sel"); + header[1] = GTXT ("Size"); + header[2] = GTXT ("Segment"); + + int size = lobjs->size (); + lists[0] = new char*[size]; + lists[1] = new char*[size]; + lists[2] = new char*[size]; + + char *lo_name; + int new_index = 0; + Vec_loop (LoadObject*, lobjs, index, lo) + { + lo_name = lo->get_name (); + if (lo_name != NULL) + { + size_t len = strlen (lo_name); + if (len > 7 && streq (lo_name + len - 7, NTXT (".class>"))) + continue; + } + bool expand = dbev->get_lo_expand (lo->seg_idx); + lists[0][new_index] = strdup (expand ? GTXT ("yes") : GTXT ("no")); + lists[1][new_index] = dbe_sprintf (NTXT ("%lld"), (ll_t) lo->get_size ()); + lists[2][new_index] = strdup (lo->get_pathname ()); + new_index++; + } + + disp_list (3, new_index, align, header, lists); + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < new_index; j++) + free (lists[i][j]); + delete[] lists[i]; + } + delete lobjs; +} + +void +er_print::filter_list (CmdType cmd_type) +{ + FilterNumeric *select; + int index; + int align[MAX_NUM_HEADER]; + char *header[MAX_NUM_HEADER]; + char **lists[MAX_NUM_HEADER]; + char *pattern; + + // first ensure that the data has been read + MetricList *mlist = dbev->get_metric_list (MET_INDX); + Hist_data *data = dbev->get_hist_data (mlist, Histable::INDEXOBJ, 0, Hist_data::ALL); + delete data; + + align[0] = 1; // right-justify + align[1] = -1; // left-justify + align[2] = 1; // right-justify + align[3] = 1; // right-justify + header[0] = GTXT ("Exp"); + header[1] = GTXT ("Sel"); + header[2] = GTXT ("Total"); + header[3] = GTXT ("Status"); + + int size = dbeSession->nexps (); + lists[0] = new char*[size]; + lists[1] = new char*[size]; + lists[2] = new char*[size]; + lists[3] = new char*[size]; + int new_index = 0; + for (index = 0; index < size; index++) + { + switch (cmd_type) + { + case SAMPLE_LIST: + select = dbev->get_FilterNumeric (index, SAMPLE_FILTER_IDX); + break; + case THREAD_LIST: + select = dbev->get_FilterNumeric (index, THREAD_FILTER_IDX); + break; + case LWP_LIST: + select = dbev->get_FilterNumeric (index, LWP_FILTER_IDX); + break; + case CPU_LIST: + select = dbev->get_FilterNumeric (index, CPU_FILTER_IDX); + break; + default: + abort (); // internal error + } + if (select == NULL) + continue; + lists[0][new_index] = dbe_sprintf (NTXT ("%d"), index + 1); + pattern = dbev->get_exp_enable (index) ? select->get_pattern () : NULL; + lists[1][new_index] = strdup (pattern && *pattern ? pattern : GTXT ("none")); + lists[2][new_index] = dbe_sprintf (NTXT ("%lld"), (ll_t) select->nelem ()); + lists[3][new_index] = select->get_status (); + new_index++; + } + disp_list (3, size, align, header, lists); + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < new_index; j++) + free (lists[i][j]); + delete[] lists[i]; + } +} + +int +er_print::check_exp_id (int exp_id, char *sel) +{ + if (exp_id < 0 || exp_id >= dbeSession->nexps ()) + { + fprintf (stderr, GTXT ("Error: Invalid number entered: %s\nType \"exp_list\" for a list of all experiments.\n"), + sel); + return -1; + } + return exp_id; +} + +int +er_print::get_exp_id (char *sel, int &bgn_index, int &end_index) +{ + int id, exp_id; + if (sel == NULL || strcmp (sel, NTXT ("all")) == 0) + { + // loop over all experiments + bgn_index = 0; + end_index = dbeSession->nexps () - 1; + } + else + { + id = (int) strtol (sel, (char **) NULL, 10) - 1; + exp_id = check_exp_id (id, sel); + if (exp_id == -1) + return -1; + bgn_index = end_index = exp_id; + } + return 0; +} + +void +er_print::print_objects () +{ + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + char *msg = pr_load_objects (lobjs, NTXT ("")); + delete lobjs; + fprintf (out_file, NTXT ("%s\n"), msg); + free (msg); +} + +void +er_print::print_overview () +{ + //fprintf(out_file, NTXT("%s\n"), GTXT("Not implemented yet."));//YXXX + Vector<char*> *status = dbeGetOverviewText (dbevindex); + StringBuilder sb; + sb.append (GTXT ("Experiment(s):\n\n")); + for (int i = 0; i < status->size (); i++) + sb.appendf (NTXT ("%s\n"), status->fetch (i)); + sb.append (GTXT ("Metrics:\n")); + sb.toFile (out_file); + + Vector<void*> *data = dbeGetRefMetricTree (dbevindex, false); + Vector<char *> *metric_cmds = new Vector<char *>(); + Vector<char *> *non_metric_cmds = new Vector<char *>(); + print_overview_nodes (data, 0, metric_cmds, non_metric_cmds); + Vector<void*> *values = dbeGetRefMetricTreeValues (0, metric_cmds, non_metric_cmds); + print_overview_tree (data, 0, values, metric_cmds, non_metric_cmds); + + StringBuilder sb2; + sb2.append (GTXT ("\nNotes: '*' indicates hot metrics, '[X]' indicates currently enabled metrics.\n")); + sb2.append (GTXT (" The metrics command can be used to change selections. The metric_list command lists all available metrics.\n")); + sb2.toFile (out_file); +} + +void +er_print::print_overview_nodes (Vector<void*> * data, int level, Vector<char *> *metric_cmds, Vector<char *> *non_metric_cmds) +{ + Vector<void*> *fields = (Vector<void*> *) data->fetch (0); + Vector<void*> *children = (Vector<void*> *) data->fetch (1); + char *name = ((Vector<char*> *)fields->fetch (0))->fetch (0); + int vstyles_capable = ((Vector<int>*) fields->fetch (5))->fetch (0); //bitmask e.g.VAL_TIMEVAL + bool has_value = ((Vector<bool>*) fields->fetch (10))->fetch (0); + bool selectable = (vstyles_capable != 0) ? true : false; + if (selectable) + metric_cmds->append (name); + else if (has_value) + non_metric_cmds->append (name); + + level++; + for (int i = 0; i < children->size (); i++) + print_overview_nodes ((Vector<void*> *)(children->fetch (i)), level, metric_cmds, non_metric_cmds); +} + +void +er_print::print_overview_tree (Vector<void*> * data, int level, Vector<void*> * values, Vector<char *> *metric_cmds, Vector<char *> *non_metric_cmds) +{ + Vector<void*> * fields = (Vector<void*> *) data->fetch (0); + Vector<void*> * children = (Vector<void*> *) data->fetch (1); + char *name = ((Vector<char*> *)fields->fetch (0))->fetch (0); + char *username = ((Vector<char*> *)fields->fetch (1))->fetch (0); + int flavors = ((Vector<int>*) fields->fetch (3))->fetch (0); //bitmask e.g. EXCLUSIVE + int vstyles_capable = ((Vector<int>*) fields->fetch (5))->fetch (0); //bitmask e.g.VAL_TIMEVAL + // bool aggregation = ((Vector<bool>*) fields->fetch(9))->fetch(0); + // bool has_value = ((Vector<bool>*) fields->fetch(10))->fetch(0); + char *unit = ((Vector<char*> *) fields->fetch (11))->fetch (0); + + StringBuilder sb; + for (int i = 0; i < level * 2; i++) + sb.append (NTXT (" ")); // NOI18N + + bool selectable = (vstyles_capable != 0) ? true : false; + if (selectable) + { + bool isSelected = dbev->get_metric_list (MET_NORMAL)->find_metric_by_name (name) == NULL ? false : true; + if (isSelected) + sb.append (NTXT ("[X]")); + else + sb.append (NTXT ("[ ]")); + } + if ((unit != NULL && dbe_strcmp (unit, UNIT_SECONDS) == 0) + || (unit == NULL && vstyles_capable & VAL_TIMEVAL)) + unit = GTXT ("Seconds"); + + bool isHiddenInOverview = ((flavors & BaseMetric::STATIC) != 0); + if (name != NULL && dbe_strcmp (name, L1_STATIC) == 0) + isHiddenInOverview = true; + if (!dbeSession->has_java () && name != NULL && dbe_strcmp (name, L1_GCDURATION) == 0) + isHiddenInOverview = true; + if (isHiddenInOverview) + return; + + sb.append (username == NULL ? NTXT ("") : username); // NOI18N + int show = 0; + if (name == NULL) + show = 0; + else if (strstr (name, NTXT ("PROFDATA_TYPE_")) == NULL) + show = 1; + + if (show) + { + sb.append (username == NULL ? NTXT ("") : NTXT (" - ")); // NOI18N + sb.append (name == NULL ? NTXT ("") : name); // NOI18N + } + + // "Bugs 16624403 and 19539622" (leave this string intact for searches) + // add an extra condition for now + // once we have proper fixes, eliminate test on Bug16624402_extra_condition + int Bug16624402_extra_condition = 1; + if (username) + { + if (strcmp (username, NTXT ("Block Covered %")) == 0) Bug16624402_extra_condition = 0; + if (strcmp (username, NTXT ("Instr Covered %")) == 0) Bug16624402_extra_condition = 0; + } + if (Bug16624402_extra_condition > 0 && values->size () > 0) + { + Vector<void*> * valueColumns = (Vector<void*> *)values->fetch (0); + Vector<void*> * highlightColumns = (Vector<void*> *)values->fetch (1); + int jj = 0; + int found = 0; + for (jj = 0; jj < valueColumns->size (); jj++) + { + const char *value_name = ""; + if (jj < metric_cmds->size ()) + value_name = metric_cmds->fetch (jj); + else + value_name = non_metric_cmds->fetch (jj - metric_cmds->size ()); + if (dbe_strcmp (value_name, name) != 0) + continue; + else + { + found = 1; + break; + } + } + if (found) + { + Vector<void*> * valueVec = (Vector<void*> *)valueColumns->fetch (jj); + Vector<bool> * highlights = (Vector<bool> *)highlightColumns->fetch (jj); + for (int kk = 0; kk < valueVec->size (); kk++) + { + char * value_str; + int show_value = 0; + switch (valueVec->type ()) + { + case VEC_INTEGER: + value_str = dbe_sprintf (NTXT ("%ld"), (long) (((Vector<int> *)valueVec)->fetch (kk))); + show_value = 1; + break; + case VEC_DOUBLE: + value_str = dbe_sprintf (NTXT ("%.3f"), (double) (((Vector<double> *)valueVec)->fetch (kk))); + show_value = 1; + break; + case VEC_LLONG: + value_str = dbe_sprintf (NTXT ("%lld"), (long long) (((Vector<long> *)valueVec)->fetch (kk))); + show_value = 1; + break; + case VEC_STRING: + value_str = NTXT (""); + break; + default: + value_str = NTXT (""); + } + if (show_value) + { + if (kk == 0) + { + sb.append (unit == NULL ? NTXT ("") : NTXT (" (")); + sb.append (unit == NULL ? NTXT ("") : unit); + sb.append (unit == NULL ? NTXT ("") : NTXT (")")); + sb.append (NTXT (":")); + } + bool highlight = highlights->fetch (kk); + const char * hilite = highlight ? NTXT ("*") : NTXT (""); + sb.append (NTXT (" [")); + sb.append (hilite); + sb.append (value_str); + sb.append (NTXT ("]")); + } + } + } + } + sb.append (NTXT ("\n")); + sb.toFile (out_file); + level++; + for (int i = 0; i < children->size (); i++) + print_overview_tree ((Vector<void*> *)(children->fetch (i)), level, values, metric_cmds, non_metric_cmds); +} + +void +er_print::print_segments () +{ + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + char *msg = pr_load_objects (lobjs, NTXT ("")); + delete lobjs; + fprintf (dis_file, NTXT ("Not implemented yet!\n")); + free (msg); +} + +void +er_print::print_dobj (Print_mode mode, MetricList *mlist1, + char *dobj_name, char *sel) +{ + Hist_data *hist_data = NULL; + char *errstr; + er_print_common_display *cd; + int list_limit = limit; + Histable *sobj = NULL; + Dprintf (DEBUG_DATAOBJ, NTXT ("er_print::print_dobj(mode=%d,dobj=%s,sel=%s)\n"), + mode, (dobj_name == NULL) ? NTXT ("0") : dobj_name, (sel == NULL) ? NTXT ("0") : sel); + char *name = dbev->getSort (MET_DATA); + switch (mode) + { + case MODE_LIST: + hist_data = dbev->get_hist_data (mlist1, Histable::DOBJECT, 0, Hist_data::ALL); + break; + case MODE_DETAIL: + // if specified, find the dataobject from the name + if (dobj_name && strcmp (dobj_name, NTXT ("<All>"))) + { + if (!dbeSession->find_obj (dis_file, inp_file, sobj, dobj_name, + sel, Histable::DOBJECT, (inp_file != stdin))) + return; + if (sobj == NULL) + { // dataobject/segment not found + hist_data = dbev->get_hist_data (mlist1, Histable::DOBJECT, 0, Hist_data::DETAIL); + if (!dbeSession->find_obj (dis_file, inp_file, sobj, dobj_name, + sel, Histable::DOBJECT, (inp_file != stdin))) + return; + if (sobj == NULL) + { // dataobject/segment not found + fprintf (stderr, GTXT ("Error: No dataobject with given name `%s' found.\n"), + dobj_name); + return; + } + } + + list_limit = 1; + } + if (!hist_data) + hist_data = dbev->get_hist_data (mlist1, Histable::DOBJECT, 0, Hist_data::DETAIL); + break; + case MODE_ANNOTATED: + hist_data = dbev->get_hist_data (mlist1, Histable::DOBJECT, 0, Hist_data::LAYOUT); + break; + default: // MODE_GPROF is not relevant for DataObjects + abort (); + } + + if (hist_data->get_status () != Hist_data::SUCCESS) + { + // XXXX is this error message adequate? + errstr = DbeView::status_str (DbeView::DBEVIEW_NO_DATA); + if (errstr) + { + fprintf (stderr, GTXT ("Error: %s\n"), errstr); + free (errstr); + } + delete hist_data; + return; + } + cd = (er_print_common_display *) new er_print_histogram (dbev, hist_data, + hist_data->get_metric_list (), mode, list_limit, name, sobj, false, false); + free (name); + print_cmd (cd); + + delete hist_data; + delete cd; +} + +void +er_print::print_func (Histable::Type type, Print_mode mode, MetricList *mlist1, + MetricList *mlist2, char *func_name, char *sel) +{ + Hist_data *hist_data; + Hist_data::HistItem *hitem; + int index; + char *errstr; + int list_limit = limit; + Histable *sobj = NULL; + MetricList *mlist; + StringBuilder sb; + char *sname = dbev->getSort (MET_NORMAL); + sb.append (sname); + free (sname); + + switch (mode) + { + case MODE_DETAIL: + { + // The first metric list, mlist1, is only used to pick out the sort + // mlist2 is the one used to generate the data + char *prevsort = NULL; + // if specified, find the function from the function name + if (func_name && strcmp (func_name, NTXT ("<All>"))) + { + if ((!dbeSession->find_obj (dis_file, inp_file, sobj, func_name, + sel, Histable::FUNCTION, (inp_file != stdin)) || (sobj == NULL)) && + !dbeSession->find_obj (dis_file, inp_file, sobj, func_name, + sel, Histable::LOADOBJECT, (inp_file != stdin))) + return; + if (sobj == NULL) + { // function/segment object not found + fprintf (stderr, GTXT ("Error: No function with given name `%s' found.\n"), + func_name); + return; + } + list_limit = 1; + } + else + { + // find the sort metric from the reference list + prevsort = mlist2->get_sort_cmd (); + + // find the current sort metric from the current list + char *cursort = mlist1->get_sort_cmd (); + + // find the corresponding metric in the reference list + (void) mlist2->set_sort (cursort, false); + free (cursort); + // if it fails, nothing is needed + } + hist_data = dbev->get_hist_data (mlist2, type, 0, Hist_data::ALL); + + // restore + if (sobj == NULL) + { + if (prevsort == NULL) + abort (); + (void) mlist2->set_sort (prevsort, false); + } + mlist = mlist2; + free (prevsort); + break; + } + case MODE_GPROF: + // if specified, find the function from the function name + if (func_name && strcmp (func_name, NTXT ("<All>"))) + { + if (!dbeSession->find_obj (dis_file, inp_file, sobj, func_name, + sel, Histable::FUNCTION, (inp_file != stdin))) + return; + if (sobj == NULL) + { // function/segment object not found + fprintf (stderr, GTXT ("Error: No function with given name `%s' found.\n"), + func_name); + return; + } + list_limit = 1; + sb.setLength (0); + } + sb.append (GTXT ("\nCallers and callees sorted by metric: ")); + sname = dbev->getSort (MET_CALL); + sb.append (sname); + free (sname); + + // Use mlist2 to generate the sort order. + // mlist1 is used to generate the data. + hist_data = dbev->get_hist_data (mlist2, type, 0, Hist_data::ALL); + mlist = mlist1; + break; + default: + hist_data = dbev->get_hist_data (mlist1, type, 0, Hist_data::ALL); + mlist = mlist1; + } + + if (hist_data->get_status () != Hist_data::SUCCESS) + { + errstr = DbeView::status_str (DbeView::DBEVIEW_NO_DATA); + if (errstr) + { + fprintf (stderr, GTXT ("Error: %s\n"), errstr); + free (errstr); + } + delete hist_data; + return; + } + + if (type == Histable::FUNCTION) + { + for (index = 0; index < hist_data->size (); index++) + { + hitem = hist_data->fetch (index); + if (hitem->obj->get_type () == Histable::FUNCTION) + // fetch the name, since that will force a format conversion + ((Function *) hitem->obj)->get_name (); + } + } + + char *name = sb.toString (); + er_print_histogram *cd = new er_print_histogram (dbev, hist_data, + mlist, mode, list_limit, name, sobj, false, false); + print_cmd (cd); + delete hist_data; + free (name); + delete cd; +} + +void +er_print::print_gprof (CmdType cmd_type, char *func_name, char *sel) +{ + Histable *sobj = NULL; + if (func_name != NULL) + { + if ((!dbeSession->find_obj (dis_file, inp_file, sobj, func_name, + sel, Histable::FUNCTION, (inp_file != stdin)) + || sobj == NULL) + && !dbeSession->find_obj (dis_file, inp_file, sobj, func_name, + sel, Histable::LOADOBJECT, (inp_file != stdin))) + return; + if (sobj == NULL) + { // function/segment object not found + fprintf (stderr, GTXT ("Error: No function with given name `%s' found.\n"), + func_name); + return; + } + } + if (cmd_type == CPREPEND) + { + if (sobj == NULL) + { + fprintf (stderr, GTXT ("Error: No function name has been specified.\n")); + return; + } + cstack->insert (0, sobj); + } + else if (cmd_type == CAPPEND) + { + if (sobj == NULL) + { + fprintf (stderr, GTXT ("Error: No function name has been specified.\n")); + return; + } + cstack->append (sobj); + } + else if (cmd_type == CSINGLE) + { + if (sobj != NULL) + { + cstack->reset (); + cstack->append (sobj); + } + else if (cstack->size () == 0) + { + fprintf (stderr, GTXT ("Error: No function name has been specified.\n")); + return; + } + } + else if (cmd_type == CRMFIRST) + { + if (cstack->size () <= 1) + { + fprintf (stderr, GTXT ("Warning: there is only one function in the stack segment; cannot remove it.\n")); + return; + } + cstack->remove (0); + } + else if (cmd_type == CRMLAST) + { + if (cstack->size () <= 1) + { + fprintf (stderr, GTXT ("Warning: there is only one function in the stack segment; cannot remove it.\n")); + return; + } + cstack->remove (cstack->size () - 1); + } + + er_print_gprof *cd = new er_print_gprof (dbev, cstack); + print_cmd (cd); + delete cd; +} + +/* + * Method print_ctree() prints Functions Call Tree. + */ +void +er_print::print_ctree (CmdType cmd_type) +{ + if (cmd_type != CALLTREE) + { + fprintf (stderr, GTXT ("Error: Invalid command type: %d\n"), cmd_type); + return; + } + + Histable *sobj = dbeSession->get_Total_Function (); + Vector<Histable*> *ctree_cstack = new Vector<Histable*>(); + ctree_cstack->reset (); + er_print_ctree *cd = new er_print_ctree (dbev, ctree_cstack, sobj, limit); + print_cmd (cd); + delete ctree_cstack; + delete cd; +} + +void +er_print::memobj (char *name, int cparam) +{ + int type; + if (name != NULL) + { + // find the memory object index for the name + MemObjType_t *mot = MemorySpace::findMemSpaceByName (name); + if (mot == NULL) + { + // unknown type, report the error + fprintf (stderr, GTXT ("Error: Unknown Memory Object type: %s\n"), name); + return; + } + type = mot->type; + } + else + { + MemObjType_t *mot = MemorySpace::findMemSpaceByIndex (cparam); + if (mot == NULL) + { + // unknown type, report the error + fprintf (stderr, GTXT ("Error: Unknown Memory Object type: %s\n"), name); + return; + } + type = cparam; + } + dbePrintData (0, DSP_MEMOBJ, type, NULL, NULL, out_file); +} + +void +er_print::mo_define (char *moname, char *mo_index_exp, char *machmodel, char *short_desc, char *long_desc) +{ + char *ret = MemorySpace::mobj_define (moname, mo_index_exp, machmodel, short_desc, long_desc); + if (ret != NULL) + fprintf (stderr, GTXT ("mobj_define for %s failed: %s\n"), moname, ret); +} + +void +er_print::mo_list (bool showtab, FILE *outf) +{ + Vector<bool> *mtab = NULL; + Vector<void*>*res = MemorySpace::getMemObjects (); + if (showtab) + mtab = dbev->get_MemTabState (); + if (res == NULL) + // Since we checked already, this is an internal error + abort (); + + // unpack the return + // Vector<char*> *index = (Vector<int> *)res->fetch(0); // not used + Vector<char*> *mo_names = (Vector<char*> *)res->fetch (1); + // Vector<char*> *mnemonic = (Vector<char> *)res->fetch(2); // not used + Vector<char*> *mo_expr = (Vector<char*> *)res->fetch (3); + Vector<char*> *mo_mach_m = (Vector<char*> *)res->fetch (4); + // Vector<char*> *tmpOrder = (Vector<int> *)res->fetch(5); // not used + + int size = mo_names->size (); + if (size == 0) + { + if (!getenv ("_BUILDING_MANPAGE")) + fprintf (outf, GTXT (" No Memory Object Types Defined\n")); + } + else + { + if (!getenv ("_BUILDING_MANPAGE")) + fprintf (outf, GTXT (" Memory Object Types Available:\n")); + else + fprintf (outf, GTXT ("*Memory Object Types*\n")); + for (int i = 0; i < size; i++) + { + if (mtab) + fprintf (outf, NTXT (" %c %s\n"), mtab->fetch (i) ? 'T' : 'F', + mo_names->fetch (i)); + else + { + if (mo_mach_m->fetch (i) != NULL) + fprintf (outf, NTXT (" %s\t\t\"%s\"\t\t(machinemodel: %s)\n"), + mo_names->fetch (i), mo_expr->fetch (i), mo_mach_m->fetch (i)); + else + fprintf (outf, NTXT (" %s\t\t\"%s\"\n"), + mo_names->fetch (i), mo_expr->fetch (i)); + } + } + } + delete mo_names; + delete mo_expr; + delete mo_mach_m; + delete res; +} + +void +er_print::indxobj (char *name, int cparam) +{ + int type; + if (name != NULL) + { + // find the index object index for the name + type = dbeSession->findIndexSpaceByName (name); + if (type < 0) + { + // unknown type, report the error + fprintf (stderr, GTXT ("Error: Unknown Index Object type: %s\n"), name); + return; + } + } + else + { + char *indxname = dbeSession->getIndexSpaceName (cparam); + if (indxname == NULL) + { + // unknown type, report the error + fprintf (stderr, GTXT ("Error: Unknown Index Object type: %d\n"), cparam); + return; + } + type = cparam; + } + dbePrintData (0, DSP_INDXOBJ, type, NULL, NULL, out_file); +} + +void +er_print::indxo_define (char *ioname, char *io_index_exp, char *sdesc, char *ldesc) +{ + char *ret = dbeDefineIndxObj (ioname, io_index_exp, sdesc, ldesc); + if (ret != NULL) + fprintf (stderr, GTXT ("indxobj_define for %s failed: %s\n"), ioname, ret); +} + +void +er_print::indxo_list (bool showtab, FILE *outf) +{ + Vector<bool> *indxtab = NULL; + char *name; + char *i18n_name; + if (!getenv ("_BUILDING_MANPAGE")) + fprintf (outf, GTXT (" Index Object Types Available:\n")); + else + fprintf (outf, GTXT ("*Index Object Types*\n")); + Vector<void*>*res = dbeGetIndxObjDescriptions (0); + if (showtab) + indxtab = dbev->get_IndxTabState (); + if (res == NULL) // If none is defined + return; + Vector<char*> *indxo_names = (Vector<char*> *)res->fetch (1); + Vector<char*> *indxo_i18nnames = (Vector<char*> *)res->fetch (3); + Vector<char*> *indxo_exprlist = (Vector<char*> *)res->fetch (5); + int size = indxo_names->size (); + for (int i = 0; i < size; i++) + { + name = indxo_names->fetch (i); + i18n_name = indxo_i18nnames->fetch (i); + if (indxtab) + { + if ((i18n_name != NULL) && (strcmp (i18n_name, name) != 0)) + fprintf (outf, NTXT (" %c %s (%s)\n"), indxtab->fetch (i) ? 'T' : 'F', + i18n_name, name); + else + fprintf (outf, NTXT (" %c %s\n"), indxtab->fetch (i) ? 'T' : 'F', name); + } + else + { + if (i18n_name != NULL && strcmp (i18n_name, indxo_names->fetch (i)) != 0) + fprintf (outf, NTXT (" %s (%s)"), i18n_name, name); + else + fprintf (outf, NTXT (" %s"), name); + } + char *exprs = indxo_exprlist->fetch (i); + if (exprs != NULL) + fprintf (outf, NTXT (" \t%s\n"), exprs); + else + fprintf (outf, NTXT ("\n")); + } + delete indxo_names; + if (showtab) + delete res; +} + +void +er_print::ifreq () +{ + dbev->ifreq (out_file); +} + +void +er_print::dump_nodes () +{ + dbev->dump_nodes (out_file); +} + +void +er_print::dump_stacks () +{ + dbeSession->dump_stacks (out_file); +} + +void +er_print::dump_unk_pcs () +{ + // Dump the nodes associated with the <Unknown> function + dbev->get_path_tree ()->dumpNodes (out_file, dbeSession->get_Unknown_Function ()); + + // Dump the nodes associated with the <no Java callstack recorded> function + Vector<Function *> *matches = dbeSession->match_func_names ("<no Java callstack recorded>", dbev->get_name_format ()); + if (matches == NULL || matches->size () == 0) + fprintf (out_file, GTXT ("No %s functions found\n"), "<no Java callstack recorded>"); + else + { + Function *fitem; + int index; + Vec_loop (Function*, matches, index, fitem) + { + dbev->get_path_tree ()->dumpNodes (out_file, fitem); + } + delete matches; + } +} + +void +er_print::dump_funcs (char *arg1) +{ + if (arg1 == NULL || strlen (arg1) == 0) + dbeSession->dump_segments (out_file); + else + { + Vector<Function *> *matches = dbeSession->match_func_names (arg1, dbev->get_name_format ()); + if (matches == NULL) + { + fprintf (stderr, GTXT ("Invalid argument `%s' -- not a regular expression\n"), arg1); + return; + } + fprintf (out_file, GTXT ("%d Function's match `%s'\n"), (int) matches->size (), arg1); + Function *fitem; + int index; + Vec_loop (Function*, matches, index, fitem) + { + fprintf (out_file, NTXT (" %5lld -- %s (%s) [%s]\n"), + (ll_t) fitem->id, fitem->get_name (), + (fitem->module ? fitem->module->file_name : NTXT ("<unknown>")), + ((fitem->module && fitem->module->loadobject) ? + get_basename (fitem->module->loadobject->get_name ()) : NTXT ("<unknown>"))); + } + delete matches; + } +} + +void +er_print::dump_dataobjects (char *arg1) +{ + // Force computation of data objects, to update master table; discard it + MetricList *mlist1 = dbev->get_metric_list (MET_DATA); + Hist_data *data = dbev->get_hist_data (mlist1, Histable::DOBJECT, 0, Hist_data::ALL); + delete data; + + if (arg1 == NULL || strlen (arg1) == 0) + dbeSession->dump_dataobjects (out_file); + else + { + Vector<DataObject *> *matches = dbeSession->match_dobj_names (arg1); + if (matches == NULL) + { + fprintf (stderr, GTXT ("Invalid argument `%s' -- not a regular expression\n"), arg1); + return; + } + fprintf (out_file, GTXT ("%d DataObject's match `%s'\n"), (int) matches->size (), arg1); + DataObject *ditem; + int index; + Vec_loop (DataObject*, matches, index, ditem) + { + fprintf (out_file, NTXT (" %5lld -- %s\n"), (ll_t) ditem->id, ditem->get_name ()); + } + delete matches; + } +} + +void +er_print::dump_map () +{ + dbeSession->dump_map (out_file); +} + +void +er_print::dump_entities () +{ + int ent_prop_ids[] = {PROP_THRID, PROP_LWPID, PROP_CPUID, PROP_EXPID, -1}; + + // loop over experiments + for (int exp_id = 0; exp_id < dbeSession->nexps (); exp_id++) + { + Experiment *exp = dbeSession->get_exp (exp_id); + fprintf (out_file, GTXT ("Experiment %d (%s)\n"), + exp_id, exp->get_expt_name ()); + + for (int kk = 0; ent_prop_ids[kk] != -1; kk++) + { + int ent_prop_id = ent_prop_ids[kk]; + Vector<void*> *elist = dbeGetEntities (0, exp_id, ent_prop_id); + if (!elist) + continue; + Vector<int> *entity_vals = (Vector<int> *) elist->fetch (0); + Vector<char*> *jthr_names = (Vector<char*> *)elist->fetch (1); + Vector<char*> *jthr_g_names = (Vector<char*> *)elist->fetch (2); + Vector<char*> *jthr_p_names = (Vector<char*> *)elist->fetch (3); + Vector<char*> *entity_name = (Vector<char*> *)elist->fetch (4); + int nent = entity_vals->size (); + char *entName = entity_name->fetch (0); + if (!entName) + entName = NTXT ("<unknown>"); + fprintf (out_file, GTXT (" %s\n"), entName); + for (int i = 0; i < nent; i++) + fprintf (out_file, GTXT (" %s=%d: %s, %s, %s\n"), + entName, entity_vals->fetch (i), + jthr_names->fetch (i) != NULL ? jthr_names->fetch (i) : NTXT ("N/A"), + jthr_g_names->fetch (i) != NULL ? jthr_g_names->fetch (i) : NTXT ("N/A"), + jthr_p_names->fetch (i) != NULL ? jthr_names->fetch (i) : NTXT ("N/A")); + destroy (elist); + } + } +} + +void +er_print::dump_stats () +{ + Emsg *m = dbev->get_path_tree ()->fetch_stats (); + while (m != NULL) + { + fprintf (out_file, NTXT ("%s\n"), m->get_msg ()); + m = m->next; + } + dbev->get_path_tree ()->delete_stats (); +} + +void +er_print::dump_proc_warnings () +{ + PathTree *p = dbev->get_path_tree (); + if (p == NULL) + return; + Emsg *m = p->fetch_warnings (); + while (m != NULL) + { + fprintf (out_file, NTXT ("%s\n"), m->get_msg ()); + m = m->next; + } + dbev->get_path_tree ()->delete_warnings (); +} + +void +er_print::print_cmd (er_print_common_display *cd) +{ + cd->set_out_file (out_file); + cd->data_dump (); +} + +FILE * +er_print::set_outfile (char *cmd, FILE *&set_file, bool append) +{ + FILE *new_file; + char *home; + if (!strcasecmp (cmd, NTXT ("-"))) + { + new_file = stdout; + out_fname = NTXT ("<stdout>"); + } + else if (!strcasecmp (cmd, NTXT ("--"))) + { + new_file = stderr; + out_fname = NTXT ("<stderr>"); + } + else + { + char *fname; + char *path = NULL; + // Handle ~ in file names + home = getenv (NTXT ("HOME")); + if ((fname = strstr (cmd, NTXT ("~/"))) != NULL && home != NULL) + path = dbe_sprintf (NTXT ("%s/%s"), home, fname + 2); + else if ((fname = strstr (cmd, NTXT ("~"))) != NULL && home != NULL) + path = dbe_sprintf (NTXT ("/home/%s"), fname + 1); + else + path = strdup (cmd); + new_file = fopen (path, append ? NTXT ("a") : NTXT ("w")); + if (new_file == NULL) + { + fprintf (stderr, GTXT ("Error: Unable to open file: %s\n"), cmd); + free (path); + return NULL; + } + out_fname = path; + } + if (set_file && set_file != stdout) + fclose (set_file); + set_file = new_file; + return set_file; +} diff --git a/gprofng/src/gp-print.h b/gprofng/src/gp-print.h new file mode 100644 index 0000000..80c922f --- /dev/null +++ b/gprofng/src/gp-print.h @@ -0,0 +1,118 @@ +/* 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. */ + +#ifndef _GP_PRINT_H +#define _ER_PRINT_H + +#include "Command.h" +#include "DbeApplication.h" +#include "Histable.h" +#include "Print.h" + +void ipc_mainLoop (int argc, char *argv[]); + +class DbeView; +template <class ITEM> class Vector; + +// er_print object +class er_print : public DbeApplication +{ +public: + + er_print (int argc, char *argv[]); + virtual ~er_print (); + void start (int argc, char *argv[]); + bool free_memory_before_exit (); + +private: + + char *error_msg; + DbeView *dbev; + char *out_fname; + FILE *inp_file; + FILE *dis_file; + FILE *out_file; + int dbevindex; + char *cov_string; + int limit; + Vector<Histable*> *cstack; + bool was_QQUIT; + + // override methods in base class + int check_args (int argc, char *argv[]); + void usage (); + + int is_valid_seg_name (char *seg_name, int prev); + int cmp_seg_name (char *full_name, char *seg_name); + int process_object_select (char *cov); + int set_libexpand (char *cov, enum LibExpand expand); + int set_libdefaults (); + + bool end_command (char *cmd); + void run (int argc, char *argv[]); + void proc_script (); + void proc_cmd (CmdType cmd_type, int cparam, char *arg1, char *arg2, + char *arg3 = NULL, char *arg4 = NULL, bool xdefault = true); + void disp_list (int no_header, int size, int align[], + char *header[], char **lists[]); + void exp_list (); + void describe (); + void obj_list (); + void seg_list (); + void print_objects (); + void print_overview (); + void print_overview_nodes (Vector<void*> *data, int level, + Vector<char *> *metric_cmds, Vector<char *> *non_metric_cmds); + void print_overview_tree (Vector<void*> *data, int level, Vector<void*> *values, + Vector<char *> *metric_cmds, Vector<char *> *non_metric_cmds); + void print_segments (); + void filter_list (CmdType cmd_type); + int check_exp_id (int exp_id, char *sel); + int get_exp_id (char *sel, int &bgn_index, int &end_index); + void print_func (Histable::Type type, Print_mode mode, + MetricList *mlist1, MetricList *mlist2, + char *func_name = NULL, char *sel = NULL); + void print_gprof (CmdType cmd_type, char *func_name, char *sel); + void print_ctree (CmdType cmd_type); + void print_dobj (Print_mode type, MetricList *mlist1, + char *dobj_name = NULL, char *sel = NULL); + void memobj (char *, int); + void mo_list (bool showtab, FILE *outf); + void mo_define (char *, char *, char *, char *, char *); + void indxobj (char *, int); + void indxo_list (bool showtab, FILE *outf); + void indxo_define (char *, char *, char *, char *); + void ifreq (); + void dump_nodes (); + void dump_stacks (); + void dump_unk_pcs (); + void dump_funcs (char *); + void dump_dataobjects (char *); + void dump_map (); + void dump_entities (); + void dump_stats (); + void dump_proc_warnings (); + void send_signal (); + void print_cmd (er_print_common_display *); + FILE *set_outfile (char *cmd, FILE *&set_file, bool append); + void gen_mapfile (char *seg_name, char *cmd); +}; + +#endif /* _ER_PRINT_H */ diff --git a/gprofng/src/gprofng.cc b/gprofng/src/gprofng.cc new file mode 100644 index 0000000..1bf5679 --- /dev/null +++ b/gprofng/src/gprofng.cc @@ -0,0 +1,301 @@ +/* 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <getopt.h> + +#include "Application.h" +#include "i18n.h" +#include "util.h" + +static int verbose = 0; + +class Gprofng : Application +{ +public: + Gprofng (int _argc, char *_argv[]); + ~Gprofng (); + void start(); + void usage(); + +private: + void exec_cmd(char *tool_name, int argc, char **argv); + int argc; + char **argv; +}; + +int +main (int argc, char *argv[]) +{ + Gprofng *gprofng = new Gprofng (argc, argv); + gprofng->start(); + delete gprofng; + return 0; +} + +Gprofng::Gprofng (int _argc, char *_argv[]) : Application(_argc, _argv, NULL) +{ + argc = _argc; + argv = _argv; +} + +Gprofng::~Gprofng () { } + +void +Gprofng::usage () +{ + /* + * Isolate the first line because it has an argument. + * Otherwise it would be at the end of this long list. + */ + printf ( GTXT ( + "Usage: %s [OPTION(S)] COMMAND [KEYWORD] [ARGUMENTS]\n"), whoami); + + printf ( GTXT ( + "\n" + "This is the driver for the GPROFNG tools suite to gather and analyze performance data.\n" + "\n" + "Options:\n" + "\n" + " --version print the version number and exit.\n" + " --help print usage information and exit.\n" + " --check verify if the hardware and software environment is supported.\n" + " --verbose {on|off} enable (on) or disable (off) verbose mode; the default is \"off\".\n" + "\n" + "Commands:\n" + "\n" + "The driver supports various commands. These are listed below.\n" + "\n" + "It is also possible to invoke the lower level commands directly, but since these \n" + "are subject to change, in particular the options, we recommend to use the driver.\n" + "\n" + "The man pages for the commands below can be viewed using the command name with\n" + "\"gprofng\" replaced by \"gp\" and the spaces replaced by a dash (\"-\"). For\n" + "example the man page name for \"gprofng collect app\" is \"gp-collect-app\".\n" + "\n" + "The following combination of commands and keywords are supported:\n" + "\n" + "Collect performance data\n" + "\n" + " gprofng collect app collect application performance data.\n" + "\n" + "Display the performance results\n" + "\n" + " gprofng display text display the performance data in ASCII format.\n" + " gprofng display html generate an HTML file from one or more experiments.\n" +/* + " gprofng display gui invoke the GUI to graphically analyze the results.\n" +*/ + " gprofng display src display source or disassembly with compiler annotations.\n" + "\n" + "Miscellaneous commands\n" + "\n" + " gprofng archive include binaries and source code in an experiment directory.\n" + "\n" + "Environment:\n" + "\n" + "The following environment variables are supported:\n" + "\n" + " GPROFNG_MAX_CALL_STACK_DEPTH set the depth of the call stack (default is 256).\n" + "\n" + " GPROFNG_USE_JAVA_OPTIONS may be set when profiling a C/C++ application\n" + " that uses dlopen() to execute Java code.\n" + "\n" + " GPROFNG_SSH_REMOTE_DISPLAY use this variable to define the ssh command\n" + " executed by the remote display tool.\n" + "\n" + " GPROFNG_SKIP_VALIDATION set this variable to disable checking hardware,\n" + " system, and Java versions.\n" + "\n" + " GPROFNG_ALLOW_CORE_DUMP set this variable to allow a core file to be\n" + " generated; otherwise an error report is created on /tmp.\n" + "\n" + " GPROFNG_ARCHIVE use this variable to define the settings for automatic\n" + " archiving upon experiment recording completion.\n" + "\n" + " GPROFNG_ARCHIVE_COMMON_DIR set this variable to the location of the common archive.\n" + "\n" + " GPROFNG_JAVA_MAX_CALL_STACK_DEPTH set the depth of the Java call stack; the default\n" + " is 256; set to 0 to disable capturing of call stacks.\n" + "\n" + " GPROFNG_JAVA_NATIVE_MAX_CALL_STACK_DEPTH set the depth of the Java native call stack;\n" + " the default is 256; set to 0 to disable capturing\n" + " of call stacks (JNI and assembly call stacks\n" + " are not captured).\n" + "\n" + "Documentation:\n" + "\n" + "A getting started guide for gprofng is maintained as a Texinfo manual. If the info and\n" + "gprofng programs are properly installed at your site, the command \"info gprofng\"\n" + "should give you access to this document.\n" + "\n" + "See also:\n" + "\n" + "gp-archive(1), gp-collect-app(1), gp-display-html(1), gp-display-src(1), gp-display-text(1)\n")); + +/* + printf ( GTXT ( + "Usage: %s [--verbose] [--version] [--help] <tool-name> [<keyword>] <args>\n" + "\n%s\n" + " archive Archive binaries and sources\n" + " collect [app] Collect performance data\n" + " display [text] Print an ASCII report\n" + " display gui Graphical tool for analyzing an experiment\n" + " display html Generate an HTML file from an experiment\n" + " display src Print source or dissasembly\n"), + whoami, getenv ("_BUILDING_MANPAGE") + ? "*Available subcommands*" + : "Available Subcommands"); +*/ +} + +void +Gprofng::exec_cmd (char *tool_name, int argc, char **argv) +{ + static const struct + { + const char *tool_name; + const char *keyword; + const char *app_name; + } app_names [] = { + { "archive", NULL, "gp-archive"}, + { "collect", "app", "gp-collect-app"}, + { "collect", "kernel", "gp-collect-kernel"}, + { "display", "text", "gp-display-text"}, + { "display", "gui", "gp-display-gui"}, + { "display", "html", "gp-display-html"}, + { "display", "src", "gp-display-src"}, + { NULL, NULL} + }; + + const char *keyword = argc > 1 ? argv[1] : ""; + int first = -1; + int find_tool_name = -1; + for (int i = 0; app_names[i].tool_name; i++) + if (!strcmp (tool_name, app_names[i].tool_name)) + { + if (app_names[i].keyword == NULL) + { + first = i; + break; + } + if (!strcmp (keyword, app_names[i].keyword)) + { + first = i; + argc--; + argv++; + break; + } + if (find_tool_name == -1) + find_tool_name = i; + } + + if (first == -1) + { + if (find_tool_name == -1) + fprintf (stderr, GTXT ("%s: error: keyword '%s' is not supported\n"), + get_basename (get_name ()), tool_name); + else if (*keyword == 0) + fprintf (stderr, GTXT ("%s %s: error: no qualifier\n"), + get_basename (get_name ()), tool_name); + else + fprintf (stderr, GTXT ("%s %s: error: qualifier '%s' is not supported\n"), + get_basename (get_name ()), tool_name, keyword); + exit (1); + } + + const char *aname = app_names[first].app_name;; + + char **arr = (char **) malloc ((argc + 3) * sizeof (char *)); + int n = 0; + char *pname = get_name (); + arr[n++] = dbe_sprintf ("%.*s%s", (int) (get_basename (pname) - pname), + pname, aname); + if (app_names[first].keyword) + arr[n++] = dbe_sprintf ("--whoami=%s %s %s", whoami, tool_name, + app_names[first].keyword); + else + arr[n++] = dbe_sprintf ("--whoami=%s %s", whoami, tool_name); + for (int i = 1; i < argc; i++) + arr[n++] = argv[i]; + arr[n] = NULL; + if (verbose) + { + printf ("gprofng::exec\n"); + for (int i = 0; arr[i]; i++) + printf ("%5d: %s\n", i, arr[i]); + printf("\n"); + } + execv (arr[0], arr); + + // If execv returns, it must have failed. + fprintf (stderr, GTXT ("%s failed: %s\n"), arr[0], STR (strerror (errno))); + exit(1); +} + +void +Gprofng::start () +{ + if (argc == 1) + { + usage (); + exit (0); + } + for (int i = 1; i < argc; i++) + { + char *s = argv[i]; + if (*s != '-') + { + exec_cmd(s, argc - i, argv + i); + return; + } + else if (!strcmp (s, "--help")) + { + usage (); + exit (0); + } + else if (!strcmp (s, "--version") || !strcmp (s, "-v")) + { + Application::print_version_info (); + exit (0); + } + else if (!strcmp (s, "--verbose")) + verbose = 1; + else if (!strcmp (s, "--check")) + { + fprintf (stderr, GTXT ("%s: error: --check is not implemented yet\n"), + get_basename (get_name ())); + exit (1); + } + else + { + fprintf (stderr, GTXT ("%s: error: unknown option %s\n"), + get_basename (get_name ()), s); + exit(1); + } + } + fprintf (stderr, GTXT ("%s: error: expected argument after options\n"), + get_basename (get_name ())); +} diff --git a/gprofng/src/gprofng.h2m b/gprofng/src/gprofng.h2m new file mode 100644 index 0000000..8786768 --- /dev/null +++ b/gprofng/src/gprofng.h2m @@ -0,0 +1,4 @@ +[SEE ALSO] + +.B +\fBgprofng-archive\fR(1), \fBgprofng-collect\fR(1), \fBgprofng-display-text\fR(1), \fBgprofng-display-src\fR(1), \fBgprofng-display-html\fR(1), \fBgprofng-display-gui\fR(1) diff --git a/gprofng/src/gprofng.rc b/gprofng/src/gprofng.rc new file mode 100644 index 0000000..3870220 --- /dev/null +++ b/gprofng/src/gprofng.rc @@ -0,0 +1,132 @@ +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file 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 of the License, 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. +# +# Specify which classes of compiler commentary will be shown +# with annotated source. +scc all + +# Specify which classes of compiler commentary will be shown +# with annotated disassembly +dcc all:src + +# Set the default function-list metrics +# for heap data, show inclusive leaks and bytes leaked; not allocations +dmetrics i.heapleakbytes:e!heapleakbytes +dmetrics i.heapleakcnt:e!heapleakcnt +dmetrics i.heapallocbytes:e!heapallocbytes +dmetrics i.heapalloccnt:e!heapalloccnt: + +# Clock profiling data +# Note: use same display order of LMS_* in: er.rc, TimelineVariable.java, +# Ovw_data.h, BaseMetricTreeNode.cc and Experiment.cc metric registration +dmetrics i!total:e!.total +# Show total cpu time +dmetrics ei.totalcpu +dmetrics i!.user:e!.user +dmetrics i!system:e!.system +dmetrics i!trap:e!.trap +dmetrics i!lock:e!.lock +dmetrics i!datapfault:e!.datapfault +dmetrics i!textpfault:e!.textpfault +dmetrics i!kernelpfault:e!.kernelpfault +dmetrics i!stop:e!.stop +dmetrics i!wait:e!.wait +dmetrics i!sleep:e!.sleep + +# for kernel clock profiling data, show inclusive and exclusive KCPU +dmetrics ei.kcpu +###dmetrics ie.kcpu + +# for count data, show exclusive metrics only +dmetrics i!bit:e.bit + +# for er_generic data, show exclusive metrics only +dmetrics i!icount:e.icount + +# Hide implementation hack. Functionmark column only serves +# to force zero-count functions to be displayed. +dmetrics e!bit_FM + +# for kernel profiles, show inclusive and exclusive kucycles and kcycles +# (kucycles and kcycles are for 12.3 and older experiments, Obsolete TBR) +dmetrics ei.kucycles:ei.kcycles +###dmetrics ie.kucycles:ie.kcycles + +# for derived HWC metrics, show exclusive only +dmetrics i!IPC:e!.IPC +dmetrics i!CPI:e!.CPI +dmetrics i!K_IPC:e!.K_IPC +dmetrics i!K_CPI:e!.K_CPI + +# for HWC, show exclusive only +dmetrics i!hwc:e.hwc + +# for synctrace, show inclusive only +dmetrics i.sync:e!sync +dmetrics i.syncn:e!syncn + +# Set the default function-list metrics for OMP profiling +dmetrics i.ompwork:e!ompwork +dmetrics i.ompwait:e!ompwait +dmetrics i!.masterthread:e!.masterthread + +#set the default function-list metrics for deadlock detection +dmetrics i!deadlocks:e.deadlocks + +# io data +dmetrics i.ioreadtime:e!ioreadtime +dmetrics i.iowritetime:e!iowritetime +dmetrics i.ioothertime:e!ioothertime +dmetrics i.ioerrortime:e!ioerrortime +dmetrics i!.ioreadcnt:e!ioreadcnt +dmetrics i!.ioreadbytes:e!ioreadbytes +dmetrics i!.iowritecnt:e!iowritecnt +dmetrics i!.iowritebytes:e!iowritebytes +dmetrics i!.ioothercnt:e!ioothercnt +dmetrics i!.ioerrorcnt:e!ioerrorcnt + +# for any other unnamed metrics, don't show them +dmetrics ie!.any + +# don't show size or address; show name +dmetrics !size:!address:name + +# Select the default function-list sorting metric +dsort ei.any:name +###dsort ie.any:name + +# Set function name style +name long + +# Set View mode to user +viewmode user + +# Set compare mode +compare off + +# Set enabling descendants to on +en_desc on + +# Set path where the gprofng libraries are installed +preload_libdirs ../lib:../lib32:../lib64 + +# Add search path for annotated source and disasm +addpath $expts:. + +# Add controls for specific load objects +# object_hide <Unknown> + +# version "@(#)er.rc 1.62 11/10/31" diff --git a/gprofng/src/i18n.cc b/gprofng/src/i18n.cc new file mode 100644 index 0000000..32c9960 --- /dev/null +++ b/gprofng/src/i18n.cc @@ -0,0 +1,30 @@ +/* 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 "i18n.h" + +extern "C" +void +init_locale (char *Path) //set up for internationalization +{ + bindtextdomain (PACKAGE_NAME, LOCALEDIR); + textdomain (PACKAGE_NAME); +} diff --git a/gprofng/src/i18n.h b/gprofng/src/i18n.h new file mode 100644 index 0000000..d02ec0e --- /dev/null +++ b/gprofng/src/i18n.h @@ -0,0 +1,40 @@ +/* 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. */ + +#ifndef _I18N_H +#define _I18N_H + +#include <libintl.h> + +#define GTXT(x) gettext(x) /* x - string literal to be i18n-ed */ +#define PTXT(x) gettext(x) /* x - expression to be i18n-ed */ +#define STXT(x) ((char *) (x)) /* x - static string literal to be i18n-ed */ +#define NTXT(x) ((char *) (x)) /* x - string literal not to be i18n-ed */ + +#ifdef __cplusplus +extern "C" +{ +#endif + void init_locale (char *Path); +#ifdef __cplusplus +} +#endif + +#endif /* _I18N_H */ diff --git a/gprofng/src/info.h b/gprofng/src/info.h new file mode 100644 index 0000000..5b05833 --- /dev/null +++ b/gprofng/src/info.h @@ -0,0 +1,73 @@ +/* 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. */ + +#ifndef _INFO_H +#define _INFO_H + +/* Header file for .info section format */ +#include <inttypes.h> + +/* The format of the .info section from a single object file is: + * Fixed-length info_header + * Variable length string padded to a multiple of 4 bytes, giving + * the name of the source file from which this contribution comes. + * Zero or more entries. + * + * In an executable, there will be multiple occurrences of the above. + * The size of the info section will be a multiple of 4 bytes. + */ + +struct info_header +{ + char endian; /* 0 for big, 1 for little */ + char magic[3]; /* The string "SUN" */ + uint32_t cnt; /* number of entries for this section */ + uint16_t len; /* The length of the header, including the string */ + uint16_t version; /* The version number of this block */ + uint16_t phase; /* The compiler phase that produced this info */ + uint16_t spare; +}; + +#define PHASE_UNKNOWN 0 +#define PHASE_F77 1 +#define PHASE_CC 2 +#define PHASE_CPLUS 3 +#define PHASE_F95 4 +#define PHASE_IROPT 5 +#define PHASE_MAX 255 +#define F95_COPYINOUT ((PHASE_F95 << 24) | 1) + +/* An entry consists of a fixed-size struct entry, possibly followed by + * a variable length data structure whose format is determined by the + * type of an entry. The size of an entry is a multiple of 4 bytes. + */ + +struct entry_header +{ + uint32_t type; /* The type of this entry. High 8 bits is the phase. + * Low 24 bits is the type. */ + uint16_t len; /* length of this entry */ + uint16_t col; /* Column number in source line */ + uint32_t msgnum; /* Message number. High 8 bits is the phase. + * Low 24 bits is the type. */ + uint32_t line; /* Line number in source file */ +}; + +#endif diff --git a/gprofng/src/ipc.cc b/gprofng/src/ipc.cc new file mode 100644 index 0000000..932423c --- /dev/null +++ b/gprofng/src/ipc.cc @@ -0,0 +1,2829 @@ +/* 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 <stdio.h> +#include <signal.h> +#include <stdarg.h> +#include <fcntl.h> // creat +#include <unistd.h> // sleep +#include <pthread.h> // pthread_exit +#include <sys/wait.h> // wait +#include <locale.h> + +#include "DbeApplication.h" +#include "Histable.h" +#include "ipcio.h" +#include "Dbe.h" +#include "DbeSession.h" +#include "DbeThread.h" +#include "DbeView.h" + +int ipc_flags = 0; +IPCTraceLevel ipc_trace_level = TRACE_LVL_0; +int ipc_single_threaded_mode = 0; +const char *IPC_PROTOCOL_UNKNOWN = "IPC_PROTOCOL_UNKNOWN"; +const char *IPC_PROTOCOL_CURR = IPC_PROTOCOL_STR; +char const *ipc_protocol = NULL; + +DbeThreadPool *ipcThreadPool; + +extern int currentRequestID; +extern int currentChannelID; +extern BufferPool *responseBufferPool; +extern bool cancelNeeded (int); +extern void reexec (); + +/* Simple implementation of support for cancel of open experiment. Since we have only one cancellable + operation supported at this moment, we are using just a global variable. + As we support more and more cancellable ops we need a more sophisticated data struture such + as a mt-safe array to keep track of all cancellable requests/channels and update the supporting + routines - setCancellableChannel, cancelNeeded (in ipcio.cc) setCancelRequestedCh */ +int cancellableChannelID = 0xFFFFFFFF; +int cancelRequestedChannelID; + +static const char *table_name (int); + +#define VSIZE(v) ((long long) ((v) ? (v)->size() : 0)) + +inline const char* +bool2str (bool v) +{ + return v ? "true" : "false"; +} + +inline char* +str2str (String v) +{ + return (char*) (v ? v : "NULL"); +} + +inline char* +str2s (String v) +{ + return (char*) (v ? v : ""); +} + +inline DbeView * +getView (int index) +{ + return dbeSession->getView (index); +} + +extern "C" +{ + typedef void (*SignalHandler)(int); +} + +/* + * Fatal error handlers + */ +extern "C" void fatalErrorHadler (int sig, siginfo_t *info, void *context); +extern "C" void sigSEGV_handler (int sig, siginfo_t *info, void *context); +extern "C" void sigABRT_handler (int sig, siginfo_t *info, void *context); +static char fatalErrorBuffer1[1024 * 8]; +static char fatalErrorBuffer2[1024 * 8]; +static int fatalErrorCode = 1; +static int fatalErrorCounter = 0; +static void *fatalErrorContext = 0; +static siginfo_t *fatalErrorInfo = 0; +static char *fatalErrorDynamicMemory = NULL; + +extern "C" void +fatalErrorHadler (int sig, siginfo_t *info, void *context) +{ + if (fatalErrorCounter > 0) + { // Need synchronization here + //sleep(10); // Wait 10 seconds to make sure previous processing is done + return; // exit(fatalErrorCode); // Already in processing + } + fatalErrorCounter = 1; + fatalErrorCode = sig; + fatalErrorContext = context; + fatalErrorInfo = info; + // Free reserved memory + if (fatalErrorDynamicMemory != NULL) + { + free (fatalErrorDynamicMemory); + fatalErrorDynamicMemory = NULL; + } + // Get process ID + pid_t pid = getpid (); + // Create dump file + snprintf (fatalErrorBuffer1, sizeof (fatalErrorBuffer1), "/tmp/analyzer.%lld", + (long long) pid); + mkdir (fatalErrorBuffer1, 0700); + snprintf (fatalErrorBuffer1, sizeof (fatalErrorBuffer1), + "/tmp/analyzer.%lld/crash.sig%d.%lld", (long long) pid, sig, + (long long) pid); + // Dump stack trace in background using pstack + snprintf (fatalErrorBuffer2, sizeof (fatalErrorBuffer2), + "/usr/bin/pstack %lld > %s.pstack", (long long) pid, fatalErrorBuffer1); + system (fatalErrorBuffer2); + int fd = creat (fatalErrorBuffer1, 0600); + if (fd >= 0) + { + // Write error message + snprintf (fatalErrorBuffer2, sizeof (fatalErrorBuffer2), + "A fatal error has been detected by er_print: Signal %lld\n", + (long long) sig); + write (fd, fatalErrorBuffer2, strlen (fatalErrorBuffer2)); +// snprintf (fatalErrorBuffer2, sizeof (fatalErrorBuffer2), +// "If you would like to submit a bug report, please use your support contract.\n")); +// write(fd, fatalErrorBuffer2, strlen(fatalErrorBuffer2)); + snprintf (fatalErrorBuffer2, sizeof (fatalErrorBuffer2), + "Protocol Version: %d\n", IPC_VERSION_NUMBER); + write (fd, fatalErrorBuffer2, strlen (fatalErrorBuffer2)); + close (fd); + // Send postmortem error message to the GUI + // snprintf(fatalErrorBuffer1, sizeof (fatalErrorBuffer1), + // "%s: %s: /tmp/analyzer.%lld", + // "Unexpected signal in er_print", + // "Crash dump", + // (long long) pid); + // res = write(2, fatalErrorBuffer2, strlen(fatalErrorBuffer1)); + } + wait (0); // wait for pstack + //sleep(10); // Wait 10 seconds to make sure processing of fatal error is done + // Exit with correct status + exit (fatalErrorCode); +} + +// SIGABRT Handler +extern "C" void +sigABRT_handler (int sig, siginfo_t *info, void *context) +{ + fatalErrorHadler (sig, info, context); + pthread_exit (&fatalErrorCode); +} + +// SIGSEGV Handler +extern "C" void +sigSEGV_handler (int sig, siginfo_t *info, void *context) +{ + //if (fatalErrorCounter > 0) sleep(1); // Wait 1 second + fatalErrorHadler (sig, info, context); + pthread_exit (&fatalErrorCode); +} + +// SIGTERM Handler +extern "C" void sigterm_handler (int sig, siginfo_t *info, void *context); +struct sigaction old_sigterm_handler; + +volatile int term_flag; +int error_flag; + +extern "C" void +sigterm_handler (int, siginfo_t *, void *) +{ + if (fatalErrorCounter > 0) + { + //sleep(10); // Wait 10 seconds to make sure processing of fatal error is done + //return; // Fatal error processing will exit it + pthread_exit (&fatalErrorCode); + } + term_flag = 1; +} + +#define ipc_log ipc_default_log +#define ipc_request_trace ipc_request_log +#define ipc_response_trace ipc_response_log +static const char *ipc_log_name = NULL; +static const char *ipc_request_log_name = NULL; +static const char *ipc_response_log_name = NULL; +FILE *requestLogFileP = stderr; +FILE *responseLogFileP = stderr; +hrtime_t begin_time; +long long delta_time = 0; +int ipc_delay_microsec = 0; + +void +ipc_default_log (const char *fmt, ...) +{ + if (!ipc_log_name || !ipc_flags) + return; + if (ipc_trace_level >= TRACE_LVL_3) + { + hrtime_t cur_time = gethrtime (); + unsigned long long time_stamp = (cur_time - begin_time) / 1000000 + delta_time; + fprintf (stderr, "%7llu: ", time_stamp); + } + va_list vp; + va_start (vp, fmt); + vfprintf (stderr, fmt, vp); + va_end (vp); + fflush (stderr); +} + +extern "C" void sigint_handler (int sig, siginfo_t *info, void *context); +struct sigaction old_sigint_handler; + +extern "C" void +sigint_handler (int, siginfo_t *, void *) +{ + ipc_log ("SIGINT signal happens\n"); +} + +void +ipc_request_log (IPCTraceLevel trace_level, const char *fmt, ...) +{ + if (!ipc_request_log_name || !ipc_flags || trace_level > ipc_trace_level) + return; + fprintf (responseLogFileP, "thr: %llu ", (unsigned long long) pthread_self ()); + if (ipc_trace_level >= TRACE_LVL_3) + { + hrtime_t cur_time = gethrtime (); + unsigned long long time_stamp = (cur_time - begin_time) / 1000000 + delta_time; + fprintf (requestLogFileP, "%7llu: ", time_stamp); + } + va_list vp; + va_start (vp, fmt); + vfprintf (requestLogFileP, fmt, vp); + va_end (vp); + fflush (requestLogFileP); +} + +void +ipc_response_log (IPCTraceLevel trace_level, const char *fmt, ...) +{ + if (!ipc_response_log_name || !ipc_flags || trace_level > ipc_trace_level) + return; + fprintf (responseLogFileP, "thr: %llu ", (unsigned long long) pthread_self ()); + if (ipc_trace_level >= TRACE_LVL_3) + { + hrtime_t cur_time = gethrtime (); + unsigned long long time_stamp = (cur_time - begin_time) / 1000000 + delta_time; + fprintf (responseLogFileP, "%7llu: ", time_stamp); + } + va_list vp; + va_start (vp, fmt); + vfprintf (responseLogFileP, fmt, vp); + va_end (vp); + fflush (responseLogFileP); +} + +#ifdef IPC_LOG +void +ipc_dump (char *s, Vector<bool> *v) +{ + if (v == NULL) + { + ipc_log (" Vector<bool> %s is NULL\n", str2s (s)); + return; + } + ipc_log (" Vector<bool> %s size=%lld\n", str2s (s), VSIZE (v)); + for (int i = 0; i < v->size (); i++) + ipc_log (" [%d]: %s\n", i, bool2str (v->fetch (i))); +} + +void +ipc_dump (char *s, Vector<String> *v) +{ + if (v == NULL) + { + ipc_log (" Vector<bool> %s is NULL\n", str2s (s)); + return; + } + ipc_log (" Vector<String> %s size=%lld\n", str2s (s), VSIZE (v)); + for (int i = 0; i < v->size (); i++) + { + String str = v->fetch (i); + ipc_log (" [%d]: '%s'\n", i, str2str (str)); + } +} + +void +ipc_dump (char *s, Vector<Obj> *v) +{ + if (v == NULL) + { + ipc_log (" Vector<Obj> %s is NULL\n", str2s (s)); + return; + } + ipc_log (" Vector<void *> %s size=%lld\n", str2s (s), VSIZE (v)); + for (int i = 0; i < v->size (); i++) + ipc_log (" [%d]: 0x%08llx\n", i, (long long) (v->fetch (i))); +} + +#else +#define ipc_dump(s, v) +#endif + +static MetricList * +readMetricListV2 (int dbevindex, IPCrequest* req) +{ + MetricType mtype = (MetricType) readInt (req); + Vector<int> *type = (Vector<int>*)readArray (req); + Vector<int> *subtype = (Vector<int>*)readArray (req); + Vector<bool> *sort = (Vector<bool>*)readArray (req); + Vector<int> *vis = (Vector<int>*)readArray (req); + Vector<char*> *cmd = (Vector<char*>*)readArray (req); + Vector<char*> *expr_spec = (Vector<char*>*)readArray (req); + Vector<char*> *legends = (Vector<char*>*)readArray (req); + MetricList *mlist = dbeGetMetricListV2 (dbevindex, mtype, type, subtype, sort, + vis, cmd, expr_spec, legends); + return mlist; +} + +static void +setCancellableChannel (int chID) +{ + cancellableChannelID = chID; +} + +/* Add more entries here for other cancellable operations */ +static void +checkCancellableOp (char *inp, IPCrequest* req) +{ + if (!strcmp (inp, "setFilterStr")) + setCancellableChannel (currentChannelID); + else if (!strcmp (inp, "openExperimentList")) + setCancellableChannel (currentChannelID); + else if (!strcmp (inp, "getFiles") || !strcmp (inp, "getFileAttributes")) + { + setCancellableChannel (currentChannelID); + req->setCancelImmediate (); + } +} + +/* This is what used to be the core of ipc_mainLoop before asynch ipc. + Read the details of the request from the request buffer: name, args etc + do the work by calling the appropriate dbe routine(s) and write the + response to a response buffer and queue it up in the response queue */ + +int +ipc_doWork (void *arg) +{ + IPCrequest *req = (IPCrequest *) arg; + currentRequestID = req->getRequestID (); + currentChannelID = req->getChannelID (); + req->setStatus (IN_PROGRESS); + String inp = readString (req); + if (inp == NULL) + { + ipc_log ("NULL ipc command received, exiting\n"); + return 0; + } + ipc_log ("ipc: %s Req %x Ch %x\n", inp, currentRequestID, currentChannelID); + checkCancellableOp (inp, req); + if (!strcmp (inp, "initApplication")) + { + bool nbm = readBoolean (req); + String arg1 = readString (req); + String arg2 = readString (req); + Vector<String> *arg3 = (Vector<String>*)readArray (req); + ipc_log (" nbm: %s, arg1: '%s', arg2: '%s'\n", bool2str (nbm), str2str (arg1), str2str (arg2)); + ipc_dump ("arg3", arg3); + // set the session to be interactive + dbeSession->set_interactive (true); + if (nbm) + theApplication->set_name ("analyzer-NBM"); + else + theApplication->set_name ("analyzer"); + + // XXX Why does it reset the install directory???? Or a licensing directory??? + // Vector<String> *res = theDbeApplication->initApplication (arg1, arg2, &setProgress); + Vector<String> *res = theDbeApplication->initApplication (NULL, NULL, &setProgress); + writeArray (res, req); + free (arg1); + free (arg2); + destroy (arg3); + destroy (res); + } + else if (!strcmp (inp, "syncTime")) + { + long long anl_time = readLong (req); + hrtime_t cur_time = gethrtime (); + long long time_stamp = (cur_time - begin_time) / 1000000; + delta_time = anl_time - time_stamp; + ipc_log (" syncTime %llu %llu \n", anl_time, delta_time); + writeString (NULL, req); + } + else if (!strcmp (inp, "getInitMessages")) + { + Vector<String> *res = dbeGetInitMessages (); + ipc_log (" returned = %lld msgs\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "reExec")) + { + ipc_log (" started reexec()\n"); + reexec (); + } + else if (!strcmp (inp, "dbeCreateDirectories")) + { + String arg1 = readString (req); // path + ipc_log (" arg = %s\n", arg1); + String res = dbeCreateDirectories (arg1); + writeString (res, req); + free (arg1); + free (res); + } + else if (!strcmp (inp, "dbeDeleteFile")) + { + String arg1 = readString (req); // path + ipc_log (" arg = %s\n", arg1); + String res = dbeDeleteFile (arg1); + writeString (res, req); + free (arg1); + free (res); + } + else if (!strcmp (inp, "dbeReadFile")) + { + String arg1 = readString (req); + ipc_log (" arg = %s\n", arg1); // path + Vector<String> *res = dbeReadFile (arg1); + writeArray (res, req); + free (arg1); + destroy (res); + } + else if (!strcmp (inp, "dbeWriteFile")) + { + String arg1 = readString (req); // path + String arg2 = readString (req); // contents + ipc_log (" arg1 = %s arg2 = %s\n", arg1, arg2); + int res = dbeWriteFile (arg1, arg2); + writeInt (res, req); + free (arg1); + } + else if (!strcmp (inp, "getExpPreview")) + { + // XXX add another argument == DbeView index + String arg1 = readString (req); + ipc_log (" arg = %s\n", arg1); + Vector<String> *res = dbeGetExpPreview (0, arg1); + writeArray (res, req); + free (arg1); + destroy (res); + } + else if (!strcmp (inp, "getFileAttributes")) + { + String arg1 = readString (req); // filename + String arg2 = readString (req); // format + ipc_log (" arg1 = %s arg2 = %s\n", arg1, arg2); + String res = dbeGetFileAttributes (arg1, arg2); + writeString (res, req); + free (arg1); + free (arg2); + free (res); + } + else if (!strcmp (inp, "getFiles")) + { + String arg1 = readString (req); // dirname + String arg2 = readString (req); // format + ipc_log (" arg1 = %s arg2 = %s\n", arg1, arg2); + String res = dbeGetFiles (arg1, arg2); + writeString (res, req); + free (arg1); + free (arg2); + free (res); + } + else if (!strcmp (inp, "getOSFamily")) + writeString ("Linux", req); + else if (!strcmp (inp, "getRunningProcesses")) + { + String arg1 = readString (req); // format + ipc_log (" arg = %s\n", arg1); + String res = dbeGetRunningProcesses (arg1); + writeString (res, req); + free (arg1); + free (res); + } + else if (!strcmp (inp, "getCurrentDirectory")) + { + char buf [2048]; + String res = getcwd (buf, (size_t) sizeof (buf)); // Get current directory + writeString (res, req); + } + else if (!strcmp (inp, "getHomeDirectory")) + { + String res = getenv ("HOME"); // Get HOME directory + writeString (res, req); + } + else if (!strcmp (inp, "setCurrentDirectory")) + { + String arg1 = readString (req); // dirname + ipc_log (" arg = %s\n", arg1); + int res = chdir (arg1); // Change directory + writeInt (res, req); + free (arg1); + } + else if (!strcmp (inp, "getLocale")) + { + String res = setlocale (LC_ALL, ""); // Get locale + writeString (res, req); + } + else if (!strcmp (inp, "setLocale")) + { + String arg1 = readString (req); // locale + ipc_log (" arg = %s\n", arg1); + String res = setlocale (LC_ALL, arg1); // Set locale + writeString (res, req); + free (arg1); + } + else if (!strcmp (inp, "readRCFile")) + { + int arg1 = readInt (req); + String arg2 = readString (req); // file name + ipc_log (" arg1=%d, arg2=%s\n", arg1, arg2); + String res = dbeReadRCFile (arg1, arg2); // Read RC File + writeString (res, req); + free (res); + } + else if (!strcmp (inp, "dbeGetExpParams")) + { + // XXX add another argument == DbeView index + String arg1 = readString (req); + ipc_log (" arg = %s\n", arg1); + String res = dbeGetExpParams (0, arg1); + writeString (res, req); + free (arg1); + free (res); + } + else if (!strcmp (inp, "getExperimentsGroups")) + { + Vector<Vector<char*>*> *groups = dbeGetExperimensGroups (); + writeArray (groups, req); + destroy (groups); + } + else if (!strcmp (inp, "setExperimentsGroups")) + { + Vector<Vector<char*>*> *groups = (Vector<Vector<char*>*> *)readArray (req); + ipc_log (" groups.size = %lld\n", VSIZE (groups)); + char *msg_str = dbeSetExperimentsGroups (groups); + writeString (msg_str, req); + free (msg_str); + destroy (groups); + } + else if (!strcmp (inp, "dropExperiment")) + { + int arg1 = readInt (req); + Vector<int> *arg2 = (Vector<int>*)readArray (req); + ipc_log (" arg = %d, exps = %lld\n", arg1, VSIZE (arg2)); + char *res = dbeDropExperiment (arg1, arg2); + writeString (res, req); + free (res); + delete arg2; + } + else if (!strcmp (inp, "getUserExpId")) + { + Vector<int> *arg = (Vector<int>*)readArray (req); + ipc_log (" expIds = %lld\n", VSIZE (arg)); + Vector<int> *res = dbeGetUserExpId (arg); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getFounderExpId")) + { + Vector<int> *arg = (Vector<int>*)readArray (req); + ipc_log (" expIds = %lld\n", VSIZE (arg)); + Vector<int> *res = dbeGetFounderExpId (arg); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getExpGroupId")) + { + Vector<int> *arg = (Vector<int>*)readArray (req); + ipc_log (" expIds = %lld\n", VSIZE (arg)); + Vector<int> *res = dbeGetExpGroupId (arg); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getExpsProperty")) + { + String arg = readString (req); + Vector<String> *res = dbeGetExpsProperty (arg); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getExpName")) + { + // XXX add argument == DbeView index + Vector<String> *res = dbeGetExpName (0); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getExpState")) + { + // XXX add argument == DbeView index + Vector<int> *res = dbeGetExpState (0); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getExpEnable")) + { + int arg1 = readInt (req); + ipc_log (" arg1 = %d\n", arg1); + Vector<bool> *res = dbeGetExpEnable (arg1); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "setExpEnable")) + { + int arg1 = readInt (req); + Vector<bool> *arg2 = (Vector<bool>*)readArray (req); + ipc_log (" arg1=%d\n", arg1); + ipc_dump ("arg2", arg2); + bool b = dbeSetExpEnable (arg1, arg2); + writeBoolean (b, req); + ipc_log (" dbeSetExpEnable returns %s\n", bool2str (b)); + delete arg2; + } + else if (!strcmp (inp, "getExpInfo")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<String> *res = dbeGetExpInfo (arg1); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getViewModeEnable")) + { + bool res = dbeGetViewModeEnable (); + writeBoolean (res, req); + } + else if (!strcmp (inp, "getJavaEnable")) + { + bool res = dbeGetJavaEnable (); + writeBoolean (res, req); + } + else if (!strcmp (inp, "updateNotes")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + String arg4 = readString (req); + bool arg5 = readBoolean (req); + ipc_log (" args = %d, %d\n", arg1, arg2); + int i = dbeUpdateNotes (arg1, arg2, arg3, arg4, arg5); + writeInt (i, req); + } + else if (!strcmp (inp, "getLoadObjectList")) + { + int arg1 = readInt (req); + ipc_log (" arg = %d\n", arg1); + Vector<void *> *res = dbeGetLoadObjectList (arg1); + if (res == NULL) + ipc_log (" returning = NULL for LoadObjectList\n"); + else + { + Vector<char*> *s = (Vector<char*> *) res->fetch (0); + ipc_log (" returning = %lld vectors for %lld LoadObjects\n", + VSIZE (res), VSIZE (s)); + } + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getLoadObjectName")) + { + int arg1 = readInt (req); + ipc_log (" arg = %d\n", arg1); + Vector<String> *res = dbeGetLoadObjectName (arg1); + ipc_log (" returning = %lld strings\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getTabListInfo")) + { + int arg1 = readInt (req); + ipc_log (" arg = %d\n", arg1); + Vector<void*> *res = dbeGetTabListInfo (arg1); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getSearchPath")) + { + // XXX add argument == DbeView index + ipc_log (" no args\n"); + Vector<String> *res = dbeGetSearchPath (0); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "setSearchPath")) + { + // XXX add another argument == DbeView index + Vector<String> *res = (Vector<String>*)readArray (req); + ipc_log (" %lld strings\n", VSIZE (res)); + dbeSetSearchPath (0, res); + writeString (NULL, req); + destroy (res); + } + else if (!strcmp (inp, "getPathmaps")) + { + Vector<void*> *res = dbeGetPathmaps (0); + ipc_log (" returns = %lld objects, number of pathmaps = %lld\n", + VSIZE (res), VSIZE ((Vector<int>*)res->fetch (0))); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "setPathmaps")) + { + Vector<String> *from = (Vector<String>*)readArray (req); + Vector<String> *to = (Vector<String>*)readArray (req); + char *res = dbeSetPathmaps (from, to); + writeString (res, req); + free (res); + if (from) + { + from->destroy (); + delete from; + } + if (to) + { + to->destroy (); + delete to; + } + } + else if (!strcmp (inp, "addPathmap")) + { + // XXX add another argument == DbeView index + String arg1 = readString (req); + String arg2 = readString (req); + ipc_log (" args = '%s', '%s'\n", arg1 ? arg1 : "NULL", arg2 ? arg2 : "NULL"); + char *res = dbeAddPathmap (0, arg1, arg2); + ipc_log (" returns = '%s'\n", (res != NULL ? res : "NULL")); + writeString (res, req); + free (arg1); + free (arg2); + } + else if (!strcmp (inp, "getMsg")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + ipc_log (" args = %d, %d\n", arg1, arg2); + String res = dbeGetMsg (arg1, arg2); + ipc_log (" returns = '%s'\n", (res != NULL ? res : "<NULL>")); + writeString (res, req); + free (res); + } + else if (!strcmp (inp, "initView")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + ipc_log (" new view = %d; clone of view %d\n", arg1, arg2); + dbeInitView (arg1, arg2); + writeString (NULL, req); + } + else if (!strcmp (inp, "disposeWindow")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + dbeDeleteView (arg1); + writeString (NULL, req); + } +#if 0 + else if (!strcmp (inp, "createMapfile")) + { + int arg1 = readInt (); + String arg2 = readString (); + int arg3 = readInt (); + ipc_log (" args = %d, %s, %d\n", arg1, arg2, arg3); + String res = dbeCreateMapfile (arg1, arg2, arg3); + writeString (res); + free (arg2); + free (res); + } +#endif + else if (!strcmp (inp, "setCompareModeV2")) + { + int dbevindex = readInt (req); + int cmp_mode = readInt (req); + getView (dbevindex)->set_compare_mode (cmp_mode); + writeResponseGeneric (RESPONSE_STATUS_SUCCESS, currentRequestID, currentChannelID); + } + else if (!strcmp (inp, "getCompareModeV2")) + { + int dbevindex = readInt (req); + int res = CMP_DISABLE; + if (dbeSession->expGroups && dbeSession->expGroups->size () > 1) + res = getView (dbevindex)->get_compare_mode (); + ipc_log (" %s: %d returns %d\n", inp, dbevindex, res); + writeInt (res, req); + } + else if (!strcmp (inp, "getRefMetricsV2")) + { + Vector<void*> *res = dbeGetRefMetricsV2 (); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "setCurMetricsV2")) + { + int dbevindex = readInt (req); + int cmp_mode = readInt (req); + MetricList *mlist = readMetricListV2 (dbevindex, req); + getView (dbevindex)->reset_metric_list (mlist, cmp_mode); + writeResponseGeneric (RESPONSE_STATUS_SUCCESS, currentRequestID, currentChannelID); + } + else if (!strcmp (inp, "getCurMetricsV2")) + { + int arg1 = readInt (req); + MetricType arg2 = (MetricType) readInt (req); + ipc_log (" args = %d, %d\n", arg1, arg2); + Vector<void*> *res = dbeGetCurMetricsV2 (arg1, arg2); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getRefMetricTree")) + { + int dbevindex = readInt (req); + bool include_unregistered = readBoolean (req); + ipc_log (" args = %d, %d\n", dbevindex, include_unregistered); + Vector<void*> *res = dbeGetRefMetricTree (dbevindex, include_unregistered); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getRefMetricTreeValues")) + { + int dbevindex = readInt (req); + Vector<String> *metcmds = (Vector<String>*)readArray (req); + Vector<String> *nonmetcmds = (Vector<String>*)readArray (req); + ipc_log (" args = %d, metcmds->size()=%lld, nonmetcmds->size()=%lld\n", + dbevindex, VSIZE (metcmds), VSIZE (nonmetcmds)); + ipc_dump ("metcmds", metcmds); + ipc_dump ("nonmetcmds", nonmetcmds); + Vector<void*> *res = dbeGetRefMetricTreeValues (dbevindex, metcmds, nonmetcmds); +#ifdef IPC_LOG + if (res != NULL) + ipc_log (" returns = %lld objects, length = %lld\n", + VSIZE (res), VSIZE (((Vector<int>*)res->fetch (0)))); + else + ipc_log (" returns NULL\n"); +#endif + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getOverviewText")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<char*> *res = dbeGetOverviewText (arg1); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "setSort")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + MetricType arg3 = (MetricType) readInt (req); + bool arg4 = readBoolean (req); + ipc_log (" args = %d, %d, %d, %c\n", arg1, arg2, arg3, (arg4 ? 'T' : 'F')); + dbeSetSort (arg1, arg2, arg3, arg4); + writeString (NULL, req); + } + + else if (!strcmp (inp, "getAnoValue")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<int> *res = dbeGetAnoValue (arg1); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "setAnoValue")) + { + int arg1 = readInt (req); + ipc_log (" args = %d, array\n", arg1); + Vector<int> *arg2 = (Vector<int>*)readArray (req); + dbeSetAnoValue (arg1, arg2); + writeString (NULL, req); + delete arg2; + } + else if (!strcmp (inp, "getNameFormat")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + int b = dbeGetNameFormat (arg1); + writeInt (b, req); + } + else if (!strcmp (inp, "getSoName")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + bool b = dbeGetSoName (arg1); + writeBoolean (b, req); + } + else if (!strcmp (inp, "setNameFormat")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + bool arg3 = readBoolean (req); + ipc_log (" args = %d, %d, %d\n", arg1, arg2, arg3); + dbeSetNameFormat (arg1, arg2, arg3); + writeString (NULL, req); + } + else if (!strcmp (inp, "getViewMode")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + int i = dbeGetViewMode (arg1); + ipc_log (" returns = %d\n", i); + writeInt (i, req); + } + else if (!strcmp (inp, "setViewMode")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + ipc_log (" args = %d, %d\n", arg1, arg2); + dbeSetViewMode (arg1, arg2); + writeString (NULL, req); + } + else if (!strcmp (inp, "getTLValue")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<void*> *res = dbeGetTLValue (arg1); + ipc_log (" returns = %lld void*'s\n", VSIZE (res)); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "setTLValue")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + String tldata_cmd = readString (req); + int entity_prop_id = readInt (req); + int align = readInt (req); + int depth = readInt (req); + dbeSetTLValue (arg1, tldata_cmd, entity_prop_id, align, depth); + writeString (NULL, req); + free (tldata_cmd); + } + else if (!strcmp (inp, "getExpFounderDescendants")) + { + Vector<void*> *res = dbeGetExpFounderDescendants (); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getExpSelection")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<void*> *res = dbeGetExpSelection (arg1); + writeArray (res, req); + destroy (res); + } + + else if (!strcmp (inp, "setFilterStr")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + ipc_log (" args = %d, %s\n", arg1, arg2); + String res = dbeSetFilterStr (arg1, arg2); + ipc_log (" returns = '%s'\n", res ? res : "NULL"); + writeString (res, req); + free (arg2); + free (res); + } + + else if (!strcmp (inp, "getFilterStr")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + String res = dbeGetFilterStr (arg1); + ipc_log (" returns = '%s'\n", res ? res : "NULL"); + writeString (res, req); + free (res); + } + + else if (!strcmp (inp, "validateFilterExpression")) + { + String arg1 = readString (req); + int res = dbeValidateFilterExpression (arg1); + ipc_log (" validateFilterExpression('%s') returned %d\n", str2str (arg1), res); + free (arg1); + writeInt (res, req); + } + + else if (!strcmp (inp, "getFilterKeywords")) + { + int dbevindex = readInt (req); + Vector<void*>*res = dbeGetFilterKeywords (dbevindex); + writeArray (res, req); + destroy (res); + } + + else if (!strcmp (inp, "getFilters")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + ipc_log (" args: view = %d, experiment = %d\n", arg1, arg2); + Vector<void*>*res = dbeGetFilters (arg1, arg2); + ipc_log (" -- returned %lld Filters\n", VSIZE (res)); + writeArray (res, req); + delete res; + } + + else if (!strcmp (inp, "updateFilters")) + { + int arg1 = readInt (req); + Vector<bool> *arg2 = (Vector<bool>*)readArray (req); + Vector<String> *arg3 = (Vector<String>*)readArray (req); + ipc_log ("arg1=%d arg2->size()=%lld arg3->size()=%lld\n", + arg1, VSIZE (arg2), VSIZE (arg3)); + ipc_dump ("arg2", arg2); + ipc_dump ("arg3", arg3); + bool b = dbeUpdateFilters (arg1, arg2, arg3); + writeBoolean (b, req); + ipc_log (" returns %s\n", (b == true ? "true" : "false")); + delete arg2; + delete arg3; + } + else if (!strcmp (inp, "getLoadObjectState")) + { + int arg1 = readInt (req); + ipc_log (" args = %d \n", arg1); + Vector<int> *res = dbeGetLoadObjectState (arg1); + ipc_log (" returning = %lld int's\n", VSIZE (res)); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "setLoadObjectState")) + { + int arg1 = readInt (req); + Vector<int> *arg2 = (Vector<int>*)readArray (req); + ipc_log (" args = %d, %lld objects\n", arg1, VSIZE (arg2)); + dbeSetLoadObjectState (arg1, arg2); + writeString (NULL, req); + delete arg2; + } + else if (!strcmp (inp, "setLoadObjectDefaults")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + dbeSetLoadObjectDefaults (arg1); + writeString (NULL, req); + } + else if (!strcmp (inp, "getMemTabSelectionState")) + { + int arg1 = readInt (req); + ipc_log (" arg = %d\n", arg1); + Vector<bool> *res = dbeGetMemTabSelectionState (arg1); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "setMemTabSelectionState")) + { + int arg1 = readInt (req); + Vector<bool> *arg2 = (Vector<bool> *)readArray (req); + ipc_log (" args = %d\n arg2 = %lld objects\n", arg1, VSIZE (arg2)); + dbeSetMemTabSelectionState (arg1, arg2); + writeString (NULL, req); + destroy (arg2); + } + else if (!strcmp (inp, "getIndxTabSelectionState")) + { + int arg1 = readInt (req); + ipc_log (" arg = %d\n", arg1); + Vector<bool> *res = dbeGetIndxTabSelectionState (arg1); + ipc_log (" -- returned %lld-vector [bool]\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "setIndxTabSelectionState")) + { + int arg1 = readInt (req); + Vector<bool> *arg2 = (Vector<bool> *)readArray (req); + ipc_log (" args = %d\n arg2 = %lld objects\n", arg1, VSIZE (arg2)); + dbeSetIndxTabSelectionState (arg1, arg2); + writeString (NULL, req); + destroy (arg2); + } + else if (!strcmp (inp, "getTabSelectionState")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<bool> *res = dbeGetTabSelectionState (arg1); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "setTabSelectionState")) + { + int arg1 = readInt (req); + Vector<bool> *arg2 = (Vector<bool>*)readArray (req); + ipc_log (" args = %d\n arg2 = %lld objects\n", arg1, VSIZE (arg2)); + dbeSetTabSelectionState (arg1, arg2); + writeString (NULL, req); + delete arg2; + } + else if (!strcmp (inp, "getMemObjects")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<void*> *res = dbeGetMemObjects (arg1); + +#ifdef IPC_LOG + if (res == NULL) + ipc_log (" -- returned NULL\n"); + else + { + Vector<int> *mo_types = (Vector<int> *)res->fetch (0); + ipc_log (" -- returned %lld-vector [ %lld-vectors]\n", + VSIZE (res), VSIZE (mo_types)); + } +#endif + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "loadMachineModel")) + { + String arg1 = readString (req); +#ifdef IPC_LOG + ipc_log (" arg = `%s'\n", arg1); +#endif + String sts = dbeLoadMachineModel (arg1); +#ifdef IPC_LOG + ipc_log (" returns '%s'\n", sts ? sts : "NULL"); +#endif + writeString (sts, req); + free (arg1); + } + else if (!strcmp (inp, "getMachineModel")) + { + String sts = dbeGetMachineModel (); +#ifdef IPC_LOG + ipc_log (" returns '%s'\n", sts ? sts : "NULL"); +#endif + writeString (sts, req); + } + else if (!strcmp (inp, "getCPUVerMachineModel")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<char*> *res = dbeGetCPUVerMachineModel (arg1); + writeArray (res, req); + ipc_log (" returns %lld char*'s\n", VSIZE (res)); + destroy (res); + } + else if (!strcmp (inp, "listMachineModels")) + { + Vector<String> *res = dbeListMachineModels (); +#ifdef IPC_LOG + if (res != NULL) + ipc_log (" returns = %lld strings\n", VSIZE (res)); + else + ipc_log (" returns NULL\n"); +#endif + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "defineMemObj")) + { + String arg1 = readString (req); + String arg2 = readString (req); + String arg3 = readString (req); + String arg4 = readString (req); +#ifdef IPC_LOG + ipc_log (" args = %s, %s, %s, %s\n", arg1, arg2, arg3 == NULL ? "NULL" : arg3, arg4 == NULL ? "NULL" : arg4); +#endif + String sts = dbeDefineMemObj (arg1, arg2, NULL, arg3, arg4); +#ifdef IPC_LOG + ipc_log (" returns '%s'\n", sts ? sts : "NULL"); +#endif + writeString (sts, req); + free (arg1); + free (arg2); + free (arg3); + free (arg4); + } + else if (!strcmp (inp, "deleteMemObj")) + { + String arg1 = readString (req); +#ifdef IPC_LOG + ipc_log (" args = %s\n", arg1); +#endif + String sts = dbeDeleteMemObj (arg1); +#ifdef IPC_LOG + ipc_log (" returns '%s'\n", sts ? sts : "NULL"); +#endif + writeString (sts, req); + free (arg1); + } + else if (!strcmp (inp, "getIndxObjDescriptions")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<void*> *res = dbeGetIndxObjDescriptions (arg1); +#ifdef IPC_LOG + if (res == NULL) + ipc_log (" -- returned NULL\n"); + else + { + Vector<int> *indxo_types = (Vector<int> *)res->fetch (0); + ipc_log (" -- returned %lld-vector [ %lld-vectors]\n", + VSIZE (res), VSIZE (indxo_types)); + } +#endif + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getCustomIndxObjects")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<void*> *res = dbeGetCustomIndxObjects (arg1); +#ifdef IPC_LOG + if (res == NULL) + ipc_log (" -- returned NULL\n"); + else + { + Vector<char *> *indxo_names = (Vector<char *> *)res->fetch (0); + ipc_log (" -- returned %lld-vector [ %lld-vectors]\n", + VSIZE (res), VSIZE (indxo_names)); + } +#endif + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "defineIndxObj")) + { + String arg1 = readString (req); + String arg2 = readString (req); + String arg3 = readString (req); + String arg4 = readString (req); + ipc_log (" args = %s, %s, %s, %s\n", arg1, arg2, arg3 == NULL ? "NULL" : arg3, arg4 == NULL ? "NULL" : arg4); + String sts = dbeDefineIndxObj (arg1, arg2, arg3, arg4); + ipc_log (" returns '%s'\n", sts ? sts : "NULL"); + writeString (sts, req); + free (arg1); + free (arg2); + free (arg3); + free (arg4); + } + else if (!strcmp (inp, "setSelObj")) + { + int arg1 = readInt (req); + Obj arg2 = readObject (req); + int arg3 = readInt (req); + int arg4 = readInt (req); + ipc_log (" args = %d, %ld, %s, %d\n", arg1, (long) arg2, table_name (arg3), arg4); + dbeSetSelObj (arg1, arg2, arg3, arg4); + writeString (NULL, req); + } + else if (!strcmp (inp, "setSelObjV2")) + { + int arg1 = readInt (req); + uint64_t arg2 = readLong (req); + ipc_log (" args = %d, %ld\n", arg1, arg2); + dbeSetSelObjV2 (arg1, arg2); + writeString (NULL, req); + } + else if (!strcmp (inp, "getSelObj")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + ipc_log (" args = %d, %s, %d\n", arg1, table_name (arg2), arg3); + Obj i = dbeGetSelObj (arg1, arg2, arg3); + ipc_log (" returns = %ld (0x%08lx)\n", (long) i, (long) i); + writeObject (i, req); + } + else if (!strcmp (inp, "getSelObjV2")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + ipc_log (" arg1 = %d agr2 = %s\n", arg1, arg2 ? arg2 : "NULL"); + Obj res = dbeGetSelObjV2 (arg1, arg2); + ipc_log (" returns = %lld\n", (long long) res); + writeObject (res, req); + free (arg2); + } + else if (!strcmp (inp, "getSelObjIO")) + { + int arg1 = readInt (req); + uint64_t arg2 = readLong (req); + int arg3 = readInt (req); + ipc_log (" arg1 = %d, arg2 = %lld, arg3 = %d\n", arg1, (long long) arg2, arg3); + Vector<uint64_t> *res = dbeGetSelObjIO (arg1, arg2, arg3); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getSelObjsIO")) + { + int arg1 = readInt (req); + Vector<uint64_t> *arg2 = (Vector<uint64_t>*)readArray (req); + int arg3 = readInt (req); + ipc_log (" arg1 = %d, arg2 size = %lld, arg3 = %d\n", + arg1, VSIZE (arg2), arg3); + Vector<uint64_t> *res = dbeGetSelObjsIO (arg1, arg2, arg3); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getSelObjHeapTimestamp")) + { + int arg1 = readInt (req); + uint64_t arg2 = readLong (req); + ipc_log (" arg1 = %d, arg2 = %llu\n", arg1, (unsigned long long) arg2); + uint64_t st = dbeGetSelObjHeapTimestamp (arg1, arg2); + ipc_log (" returns = %llu\n", (unsigned long long) st); + writeLong (st, req); + } + else if (!strcmp (inp, "getSelObjHeapUserExpId")) + { + int arg1 = readInt (req); + uint64_t arg2 = readLong (req); + ipc_log (" arg1 = %d, arg2 = %llu\n", arg1, (unsigned long long) arg2); + int userExpId = dbeGetSelObjHeapUserExpId (arg1, arg2); + ipc_log (" returns = %d\n", userExpId); + writeInt (userExpId, req); + } + else if (!strcmp (inp, "getSelIndex")) + { + int arg1 = readInt (req); + Obj arg2 = readObject (req); + int arg3 = readInt (req); + int arg4 = readInt (req); + ipc_log (" args = %d, 0x%08lx, %s, %d\n", arg1, (long) arg2, table_name (arg3), arg4); + int i = dbeGetSelIndex (arg1, arg2, arg3, arg4); + ipc_log (" returns = %d\n", i); + writeInt (i, req); + } + else if (!strcmp (inp, "printData")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + String arg4 = readString (req); + String arg5 = readString (req); + ipc_log (" args = %d, %s, %d, `%s', `%s'\n", + arg1, table_name (arg2), arg3, + (arg4 == NULL ? "NULL" : arg4), + (arg5 == NULL ? "NULL" : arg5)); + String res = dbePrintData (arg1, arg2, arg3, arg4, arg5, NULL); + writeString (res, req); + free (arg4); + free (arg5); + free (res); + } + else if (!strcmp (inp, "getPrintLimit")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + int i = dbeGetPrintLimit (arg1); + ipc_log (" returns = %d\n", i); + writeInt (i, req); + } + else if (!strcmp (inp, "setPrintLimit")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + ipc_log (" args = %d, %d\n", arg1, arg2); + String res = dbeSetPrintLimit (arg1, arg2); + writeString (res, req); + free (res); + } + else if (!strcmp (inp, "getPrintMode")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + int i = dbeGetPrintMode (arg1); + ipc_log (" returns = %d\n", i); + writeInt (i, req); + } + else if (!strcmp (inp, "setPrintMode")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + ipc_log (" args = %d, %s\n", arg1, arg2); + String res = dbeSetPrintMode (arg1, arg2); + writeString (res, req); + free (arg2); + free (res); + } + else if (!strcmp (inp, "getPrintDelim")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + char i = dbeGetPrintDelim (arg1); + ipc_log (" returns = %c\n", i); + writeInt ((int) i, req); + } + else if (!strcmp (inp, "getHotMarks")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + ipc_log (" args = %d, %s (%d) \n", arg1, table_name (arg2), arg2); + Vector<void*> *res = dbeGetHotMarks (arg1, arg2); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getHotMarksInc")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + ipc_log (" args = %d, %s (%d) \n", arg1, table_name (arg2), arg2); + Vector<void*> *res = dbeGetHotMarksInc (arg1, arg2); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getSummaryHotMarks")) + { + int arg1 = readInt (req); + Vector<Obj> *arg2 = (Vector<Obj>*)readArray (req); + int arg3 = readInt (req); + ipc_log (" args = %d, 0x%llx, %s (%d)\n", arg1, (long long) arg2, table_name (arg3), arg3); + Vector<void*> *res = dbeGetSummaryHotMarks (arg1, arg2, arg3); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getFuncId")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + int arg4 = readInt (req); + ipc_log (" args = %d, %s, %d, %d\n", arg1, table_name (arg2), arg3, arg4); + Vector<uint64_t> *res = dbeGetFuncId (arg1, arg2, arg3, arg4); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getFuncCalleeInfo")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + Vector<int> *arg3 = (Vector<int>*)readArray (req); + int arg4 = readInt (req); + ipc_log (" args = %d, %s, %lld, %d\n", arg1, table_name (arg2), VSIZE (arg3), arg4); + Vector<void*> *res = dbeGetFuncCalleeInfo (arg1, arg2, arg3, arg4); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getFuncCallerInfo")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + Vector<int> *arg3 = (Vector<int>*)readArray (req); + int arg4 = readInt (req); + ipc_log (" args = %d, %s, %lld, %d\n", arg1, table_name (arg2), VSIZE (arg3), arg4); + Vector<void*> *res = dbeGetFuncCallerInfo (arg1, arg2, arg3, arg4); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "setFuncData")) + { + int arg1 = readInt (req); + Obj arg2 = readObject (req); + int arg3 = readInt (req); + int arg4 = readInt (req); + ipc_log (" args = %d, %ld, %s, %d\n", arg1, (long) arg2, table_name (arg3), arg4); + int i = dbeSetFuncData (arg1, arg2, arg3, arg4); + ipc_log (" returns = %d\n", i); + writeInt (i, req); + } + else if (!strcmp (inp, "setFuncDataV2")) + { + int dbevindex = readInt (req); + Obj sel_obj = readObject (req); + int type = readInt (req); + int subtype = readInt (req); + Vector<long long> *longs = new Vector<long long>(2); + Vector<char *> *strings = new Vector<char *>(2); + + longs->append (dbeSetFuncData (dbevindex, sel_obj, type, subtype)); + strings->append (dbeGetMsg (dbevindex, ERROR_MSG)); + String sf_name = NULL; + long long sf_id = 0; + switch (type) + { + case DSP_SOURCE: + case DSP_DISASM: + { + Histable *obj = (Histable *) sel_obj; + if (obj) + { + Histable *sf = obj->convertto (Histable::SOURCEFILE); + if (sf) + { + sf_id = sf->id; + sf_name = dbe_strdup (sf->get_name ()); + } + } + break; + } + } + longs->append (sf_id); + strings->append (sf_name); + ipc_log (" setFuncData(%d, %ld, %s, %d) returns (%lld, %lld)\n (%s, %s)\n", + dbevindex, (long) sel_obj, table_name (type), subtype, longs->get (0), longs->get (1), + STR (strings->get (0)), STR (strings->get (1))); + + Vector<void *> *res = new Vector<void *>(2); + res->append (longs); + res->append (strings); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getFuncList")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + ipc_log (" args = %d, %s, %d\n", arg1, table_name (arg2), arg3); + Vector<void*> *res = dbeGetFuncList (arg1, arg2, arg3); +#ifdef IPC_LOG + if (res != NULL) + ipc_log (" returns = %lld objects, length = %lld\n", + VSIZE (res), VSIZE ((Vector<int>*)res->fetch (0))); + else + ipc_log (" returns NULL\n"); +#endif + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getFuncListV2")) + { + int dbevindex = readInt (req); + int mtype = readInt (req); + Obj sel_obj = readObject (req); + int type = readInt (req); + int subtype = readInt (req); + Vector<void*> *res = dbeGetFuncListV2 (dbevindex, mtype, sel_obj, type, subtype); + ipc_log (" args = %d 0x%x %ld, %s, %d returns = %d objects, length = %d\n", + dbevindex, mtype, (long) sel_obj, table_name (type), subtype, + (int) (res ? res->size () : 0), + (int) (res ? ((Vector<int>*)res->fetch (0))->size () : 0)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getFuncListMini")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + ipc_log (" args = %d, %s, %d\n", arg1, table_name (arg2), arg3); + Vector<void*> *res = dbeGetFuncListMini (arg1, arg2, arg3); +#ifdef IPC_LOG + if (res != NULL) + ipc_log (" returns = %lld objects, length = %lld\n", + VSIZE (res), VSIZE ((Vector<int>*)res->fetch (0))); + else + ipc_log (" returns NULL\n"); +#endif + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "dbeGetTotals")) + { + int dbevindex = readInt (req); + int dsptype = readInt (req); + int subtype = readInt (req); + Vector<void *> *res = dbeGetTotals (dbevindex, dsptype, subtype); + ipc_log (" dbeGetTotals(%d, %d, %d) returns %lld objects\n", + dbevindex, dsptype, subtype, VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getComparableObjsV2")) + { + int arg1 = readInt (req); + Obj arg2 = readObject (req); + int arg3 = readInt (req); + Vector<Obj> *res = dbeGetComparableObjsV2 (arg1, arg2, arg3); + ipc_log (" args = %d 0x%lx %d\n", arg1, (long) arg2, arg3); + ipc_dump ("getComparableObjsV2:res", res); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "dbeConvertSelObj")) + { + Obj obj = readObject (req); + int type = readInt (req); + Obj res = dbeConvertSelObj (obj, type); + ipc_log (" args = %lld %d res=%lld \n", (long long) obj, type, + (long long) res); + writeObject (res, req); + } + else if (!strcmp (inp, "getTableDataV2")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + String arg3 = readString (req); + String arg4 = readString (req); + String arg5 = readString (req); + Vector<uint64_t> *arg6 = (Vector<uint64_t>*)readArray (req); + ipc_log (" args = %d, %s, %s, %s, %s, %lld\n", arg1, STR (arg2), + STR (arg3), STR (arg4), STR (arg5), VSIZE (arg6)); + Vector<void*> *res = dbeGetTableDataV2 (arg1, arg2, arg3, arg4, arg5, arg6); +#ifdef IPC_LOG + if (res != NULL) + ipc_log (" returns = %lld objects, length = %lld\n", + VSIZE (res), VSIZE ((Vector<int>*)res->fetch (0))); + else + ipc_log (" returns NULL\n"); +#endif + writeArray (res, req); + //destroy( arg6 ); + destroy (res); + } + else if (!strcmp (inp, "getCallTreeNumLevels")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + int res = dbeGetCallTreeNumLevels (arg1); +#ifdef IPC_LOG + ipc_log (" returns = %d\n", res); +#endif + writeInt (res, req); + } + else if (!strcmp (inp, "getCallTreeLevel")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + int arg3 = readInt (req); + ipc_log (" args = %d, %s, %d\n", arg1, arg2, arg3); + Vector<void*> *res = dbeGetCallTreeLevel (arg1, arg2, arg3); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getCallTreeChildren")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + Vector<int> *arg3 = (Vector<int> *) readArray (req); /*NodeIdx array*/ + ipc_log (" args = %d, %s, vec_size=%lld\n", arg1, arg2, (long long) (arg3 ? arg3->size () : 0)); + Vector<void*> *res = dbeGetCallTreeChildren (arg1, arg2, arg3); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getCallTreeLevels")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + ipc_log (" args = %d, %s\n", arg1, arg2); + Vector<void*> *res = dbeGetCallTreeLevels (arg1, arg2); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getCallTreeLevelFuncs")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + ipc_log (" args = %d, %d, %d\n", arg1, arg2, arg3); + Vector<void*> *res = dbeGetCallTreeLevelFuncs (arg1, arg2, arg3); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getCallTreeFuncs")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<void*> *res = dbeGetCallTreeFuncs (arg1); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getGroupIds")) + { + int arg1 = readInt (req); + Vector<int> *res = dbeGetGroupIds (arg1); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getNames")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + Obj arg3 = readObject (req); +#ifdef IPC_LOG + ipc_log (" args = %d, %s 0x%lx\n", arg1, table_name (arg2), (long) arg3); +#endif + Vector<String> *res = dbeGetNames (arg1, arg2, arg3); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getTotalMax")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + ipc_log (" args = %d, %s, %d\n", arg1, table_name (arg2), arg3); + Vector<void*> *res = dbeGetTotalMax (arg1, arg2, arg3); +#ifdef IPC_LOG + if (res != NULL) + ipc_log (" returns = %lld vectors, length %lld\n", + VSIZE (res), VSIZE ((Vector<void*>*)res->fetch (0))); + else + ipc_log (" returns NULL\n"); +#endif + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "composeFilterClause")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + Vector<int> *arg4 = (Vector<int>*)readArray (req); + ipc_log (" args = %d, %s, %d, %lld selections\n", + arg1, table_name (arg2), arg3, VSIZE (arg4)); + String s = dbeComposeFilterClause (arg1, arg2, arg3, arg4); + ipc_log (" returns %s\n", (s == NULL ? "<NULL>" : s)); + writeString (s, req); + } + else if (!strcmp (inp, "getStatisOverviewList")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<Object> *res = dbeGetStatisOverviewList (arg1); + ipc_log (" dbeStatisGetOverviewList returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getStatisList")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<Object> *res = dbeGetStatisList (arg1); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getSummary")) + { + int arg1 = readInt (req); + Vector<Obj> *arg2 = (Vector<Obj>*)readArray (req); + int arg3 = readInt (req); + int arg4 = readInt (req); + ipc_log (" args = %d, 0x%llx, %s (%d), %d\n", arg1, (long long) arg2, table_name (arg3), arg3, arg4); + Vector<Object> *res = dbeGetSummary (arg1, arg2, arg3, arg4); + ipc_log (" dbeGetSummary returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getSummaryV2")) + { + int dbevindex = readInt (req); + Vector<Obj> *sel_objs = (Vector<Obj>*)readArray (req); + int type = readInt (req); + int subtype = readInt (req); + Vector<void*> *res = dbeGetSummaryV2 (dbevindex, sel_objs, type, subtype); + ipc_log (" args = %d, [%lld], %s (%d), %d res=[%lld] 0x%llx \n", + dbevindex, VSIZE (sel_objs), table_name (type), type, subtype, + VSIZE (res), (unsigned long long) res); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getExpName1")) + { + // XXX add an argument = DbeView index + String arg1 = readString (req); + ipc_log (" arg = `%s'\n", arg1 ? arg1 : "NULL"); + String res = dbeGetExpName (0, arg1); + writeString (res, req); + ipc_log (" returns `%s'\n", res ? res : "NULL"); + free (arg1); + free (res); + } + else if (!strcmp (inp, "getHwcHelp")) + { + // XXX add an argument = DbeView index + bool forKernel = readBoolean (req); + Vector<String> *res = dbeGetHwcHelp (0, forKernel); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getHwcSets")) + { + // XXX add an argument = DbeView index + bool forKernel = readBoolean (req); + Vector<Vector<char*>*> *res = dbeGetHwcSets (0, forKernel); + writeArray (res, req); + ipc_log (" returns %lld char*'s\n", VSIZE (res)); + destroy (res); + } + else if (!strcmp (inp, "getHwcsAll")) + { + // XXX add an argument = DbeView index + bool forKernel = readBoolean (req); + Vector<void*> *res = dbeGetHwcsAll (0, forKernel); + writeArray (res, req); + ipc_log (" returns %lld char*'s\n", VSIZE (res)); + destroy (res); + } + else if (!strcmp (inp, "getHwcAttrList")) + { + // XXX add an argument = DbeView index + bool forKernel = readBoolean (req); + Vector<char*> *res = dbeGetHwcAttrList (0, forKernel); + ipc_log (" returns %lld char*'s\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getHwcMaxConcurrent")) + { + // XXX add an argument = DbeView index + bool forKernel = readBoolean (req); + int res = dbeGetHwcMaxConcurrent (0, forKernel); + writeInt (res, req); + } + else if (!strcmp (inp, "getIfreqData")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<char*> *res = dbeGetIfreqData (arg1); + ipc_log (" returns %lld char*'s\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getNewLeakListInfo")) + { + int arg1 = readInt (req); + bool arg2 = readBoolean (req); + ipc_log (" args = %d, %d\n", arg1, arg2); + Vector<void*> *res = dbeGetLeakListInfo (arg1, arg2); + ipc_log (" returns %lld void*'s\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getObject")) + { + int arg1 = readInt (req); + Obj arg2 = readObject (req); + Obj arg3 = readObject (req); + Obj i = dbeGetObject (arg1, arg2, arg3); + writeObject (i, req); + } + else if (!strcmp (inp, "getExpVerboseName")) + { + Vector<int> *arg = (Vector<int>*)readArray (req); + ipc_log (" expIds = %lld\n", VSIZE (arg)); + Vector<String> *res = dbeGetExpVerboseName (arg); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getName")) + { + // XXX add an argument = DbeView index + int arg1 = readInt (req); + String res = dbeGetName (0, arg1); + writeString (res, req); + free (res); + } + else if (!strcmp (inp, "getStartTime")) + { + // XXX add an argument = DbeView index + int arg1 = readInt (req); + long long l = dbeGetStartTime (0, arg1); + ipc_log (" returns = %llu\n", l); + writeLong (l, req); + } + else if (!strcmp (inp, "getRelativeStartTime")) + { + // XXX add an argument = DbeView index + int arg1 = readInt (req); + long long l = dbeGetRelativeStartTime (0, arg1); + ipc_log (" returns = %llu\n", l); + writeLong (l, req); + } + else if (!strcmp (inp, "getEndTime")) + { + // XXX add an argument = DbeView index + int arg1 = readInt (req); + long long l = dbeGetEndTime (0, arg1); + ipc_log (" returns = %llu\n", l); + writeLong (l, req); + } + else if (!strcmp (inp, "getClock")) + { + // XXX add an argument = DbeView index + int arg1 = readInt (req); + int i = dbeGetClock (0, arg1); + writeInt (i, req); + } + /* + else if ( !strcmp( inp, "getFounderExpId" ) ) { + // XXX add an argument = DbeView index + int arg1 = readInt(req); + int i = dbeGetFounderExpId(0, arg1 ); + writeInt( i, req ); + } + */ + else if (!strcmp (inp, "getEntityProps")) + { + int arg1 = readInt (req); + ipc_log (" args = %d\n", arg1); + Vector<void*> *res = dbeGetEntityProps (arg1); + writeArray (res, req); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + destroy (res); + } + else if (!strcmp (inp, "getEntities")) + { + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + ipc_log (" args = %d, %d, %d\n", arg1, arg2, arg3); + Vector<void*> *res = dbeGetEntities (arg1, arg2, arg3); + writeArray (res, req); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + destroy (res); + } + else if (!strcmp (inp, "getEntitiesV2")) + { + int arg1 = readInt (req); + Vector<int> *arg2 = (Vector<int>*)readArray (req); + int arg3 = readInt (req); + ipc_log (" args = %d, %lld, %d\n", arg1, VSIZE (arg2), arg3); + Vector<void*> *res = dbeGetEntitiesV2 (arg1, arg2, arg3); + writeArray (res, req); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + destroy (res); + } + else if (!strcmp (inp, "getTLDetails")) + {//TBR + int arg1 = readInt (req); + int arg2 = readInt (req); + int arg3 = readInt (req); + int arg4 = readInt (req); + long long arg5 = readLong (req); + ipc_log (" dbevindex= %d, exp_id = %d, data_id = %d, " + "entity_prop_id = %d, event_id = %lld\n", + arg1, arg2, arg3, arg4, arg5); + Vector<void*> *res = dbeGetTLDetails (arg1, arg2, arg3, arg4, arg5); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getStackNames")) + { + int arg1 = readInt (req); + Obj arg2 = readObject (req); + ipc_log (" args = %d, %ld\n", arg1, (long) arg2); + Vector<String> *res = dbeGetStackNames (arg1, arg2); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getStackFunctions")) + { + // XXX add an argument = DbeView index + Obj arg1 = readObject (req); + ipc_log (" args = %ld\n", (long) arg1); + Vector<Obj> *res = dbeGetStackFunctions (0, arg1); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getStacksFunctions")) + { + // XXX add an argument = DbeView index + Vector<Obj> *arg1 = (Vector<Obj>*)readArray (req); + ipc_log (" argc = %ld\n", (long) arg1->size ()); + Vector<void*> *res = dbeGetStacksFunctions (0, arg1); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getStackPCs")) + { + // XXX add an argument = DbeView index + Obj arg1 = readObject (req); + ipc_log (" args = %ld\n", (long) arg1); + Vector<Obj> *res = dbeGetStackPCs (0, arg1); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + delete res; + } + else if (!strcmp (inp, "getIOStatistics")) + { + int dbevindex = readInt (req); + Vector<Vector<char*>*> *res = dbeGetIOStatistics (dbevindex); + writeArray (res, req); + ipc_log (" returns %lld char*'s\n", VSIZE (res)); + destroy (res); + } + else if (!strcmp (inp, "getHeapStatistics")) + { + int dbevindex = readInt (req); + Vector<Vector<char*>*> *res = dbeGetHeapStatistics (dbevindex); + writeArray (res, req); + ipc_log (" returns %lld char*'s\n", VSIZE (res)); + destroy (res); + } + else if (!strcmp (inp, "getSamples")) + { + int dbev_id = readInt (req); + int exp_id = readInt (req); + int64_t lo = readLong (req); + int64_t hi = readLong (req); + ipc_log (" dbevindex= %d, exp_id = %d, lo_idx:%lld, hi_idx:%lld\n", + dbev_id, exp_id, (long long) lo, (long long) hi); + Vector<void*> *res = dbeGetSamples (dbev_id, exp_id, lo, hi); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getGCEvents")) + { + int dbev_id = readInt (req); + int exp_id = readInt (req); + int64_t lo = readLong (req); + int64_t hi = readLong (req); + ipc_log (" dbevindex= %d, exp_id = %d, lo_idx:%lld, hi_idx:%lld\n", + dbev_id, exp_id, (long long) lo, (long long) hi); + Vector<void*> *res = dbeGetGCEvents (dbev_id, exp_id, lo, hi); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getFuncNames")) + { + int arg1 = readInt (req); + Vector<Obj> *arg2 = (Vector<Obj>*)readArray (req); + ipc_log (" arg1 = %d, arg2 absent, size = %lld\n", arg1, VSIZE (arg2)); + Vector<String> *res = dbeGetFuncNames (arg1, arg2); + writeArray (res, req); + delete arg2; + destroy (res); + } + else if (!strcmp (inp, "getFuncIds")) + { + int arg1 = readInt (req); + Vector<Obj> *arg2 = (Vector<Obj>*)readArray (req); + ipc_log (" arg1 = %d, arg2 absent, size = %lld\n", arg1, VSIZE (arg2)); + Vector<uint64_t> *res = dbeGetFuncIds (arg1, arg2); + writeArray (res, req); + delete arg2; + destroy (res); + } + else if (!strcmp (inp, "getObjNamesV2")) + { + int arg1 = readInt (req); + Vector<uint64_t> *arg2 = (Vector<uint64_t>*)readArray (req); + ipc_log (" arg1 = %d, arg2 absent, size = %lld\n", arg1, VSIZE (arg2)); + Vector<String> *res = dbeGetObjNamesV2 (arg1, arg2); + writeArray (res, req); + delete arg2; + destroy (res); + } + else if (!strcmp (inp, "getFuncName")) + { + int arg1 = readInt (req); + Obj arg2 = readObject (req); + ipc_log (" arg1 = %d, arg2 = %lld\n", arg1, (long long) arg2); + String res = dbeGetFuncName (arg1, arg2); + ipc_log (" returning = %s\n", res ? res : "NULL"); + writeString (res, req); + free (res); + } + else if (!strcmp (inp, "getObjNameV2")) + { + int arg1 = readInt (req); + uint64_t arg2 = readLong (req); + ipc_log (" arg1 = %d, arg2 = %llu\n", arg1, (unsigned long long) arg2); + String res = dbeGetObjNameV2 (arg1, arg2); + ipc_log (" returning = %s\n", res ? res : "NULL"); + writeString (res, req); + free (res); + } + else if (!strcmp (inp, "getDataspaceTypeDesc")) + { + // XXX add an argument = DbeView index + Obj arg1 = readObject (req); + ipc_log (" arg1 absent, index = %ld\n", (long) arg1); + String res = dbeGetDataspaceTypeDesc (0, arg1); + ipc_log (" returning = %s\n", res ? res : "NULL"); + writeString (res, req); + free (res); + } + /* + * New Interface with Timeline + */ +#if 0 //YXXX TBR + else if (!strcmp (inp, "dbeInit")) + dbeInit (); + else if (!strcmp (inp, "getDefaultExperimentName")) + { + String res = dbeGetDefaultExperimentName (); + ipc_log (" returning = %s\n", res); + writeString (res); + free (res); + } + else if (!strcmp (inp, "getExperimentState")) + { + String res = dbeGetExperimentState (); + ipc_log (" returning = %s\n", res); + writeString (res); + free (res); + } + else if (!strcmp (inp, "getExpStartTime")) + { + long long l = dbeGetExpStartTime (); + ipc_log (" returns = %llu\n", l); + writeLong (l); + } + else if (!strcmp (inp, "getExpEndTime")) + { + long long l = dbeGetExpEndTime (); + ipc_log (" returns = %llu\n", l); + writeLong (l); + } +#endif + else if (!strcmp (inp, "getDataDescriptorsV2")) + {//TBR? TBD + int exp_id = readInt (req); + ipc_log (" exp_id = %d\n", exp_id); + Vector<void*> *res = dbeGetDataDescriptorsV2 (exp_id); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getDataPropertiesV2")) + {//TBR? TBD + int exp_id = readInt (req); + int arg2 = readInt (req); + ipc_log (" exp_id = %d, data_idx = %d\n", exp_id, arg2); + Vector<void*> *res = dbeGetDataPropertiesV2 (exp_id, arg2); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getExperimentTimeInfo")) + { + Vector<int> *exp_ids = (Vector<int>*)readArray (req); + ipc_log (" cnt = %lld\n", VSIZE (exp_ids)); + Vector<void*> *res = dbeGetExperimentTimeInfo (exp_ids); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getExperimentDataDescriptors")) + { + Vector<int> *exp_ids = (Vector<int>*)readArray (req); + ipc_log (" cnt = %lld\n", VSIZE (exp_ids)); + Vector<void*> *res = dbeGetExperimentDataDescriptors (exp_ids); + ipc_log (" returns = %lld objects\n", VSIZE (res)); + writeArray (res, req); + destroy (res); + } +#if 0 //YXXX TBR? + else if (!strcmp (inp, "getExprValues")) + {//TBR? TBD + int arg1 = readInt (); + String arg2 = readString (); + ipc_log (" data_idx = %d expr = %s\n", arg1, arg2 ? arg2 : "NULL"); + Vector<long long> *res = dbeGetExprValues (arg1, arg2); + ipc_log (" returns = %d objects\n", res ? res->size () : 0); + writeArray (res); + delete res; + free (arg2); + } +#endif + else if (!strcmp (inp, "hasTLData")) + { + int dbevindex = readInt (req); + Vector<int> *exp_ids = (Vector<int>*)readArray (req); + Vector<int> *data_ids = (Vector<int>*)readArray (req); + Vector<int> *eprop_ids = (Vector<int>*)readArray (req); + Vector<int> *eprop_vals = (Vector<int>*)readArray (req); + Vector<int> *auxs = (Vector<int>*)readArray (req); + ipc_log (" dbev_id = %d, cnt = %lld\n", dbevindex, VSIZE (exp_ids)); + Vector<bool> *res = dbeHasTLData (dbevindex, + exp_ids, data_ids, eprop_ids, eprop_vals, auxs); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getTLData")) + { + int dbevindex = readInt (req); + int exp_id = readInt (req); + int tldata_type = readInt (req); + int entity_prop_id = readInt (req); + int entity_prop_val = readInt (req); + int aux = readInt (req); + long long arg5 = readLong (req); + long long arg6 = readLong (req); + int arg7 = readInt (req); + bool getReps = readBoolean (req); + Vector<String> *secondaryProps = (Vector<String>*)readArray (req); + + ipc_log (" args = %d:%d; tldata_type=%d entity_prop_id=%d ent=%d aux=%d" + "\n tstart=%lld delta=%lld ndeltas=%d getReps=%d nProps=%lld\n", + dbevindex, exp_id, + tldata_type, entity_prop_id, entity_prop_val, aux, + arg5, arg6, arg7, (int) getReps, VSIZE (secondaryProps)); + Vector<void*> *res = dbeGetTLData (dbevindex, exp_id, + tldata_type, entity_prop_id, entity_prop_val, aux, + arg5, arg6, arg7, getReps, secondaryProps); +#ifdef IPC_LOG + if (res) + { + Vector<Obj> *reps = (Vector<Obj>*)res->fetch (0); + Vector<Obj> *props = (Vector<Obj>*)res->fetch (1); + if (reps) + { + Vector <long long> *fids = (Vector <long long> *)reps->fetch (2); + int sz = fids ? fids->size () : 0; + ipc_log (" returning TL reps (dDscrs); nreps=%d:", sz); + int i; + for (i = 0; i < sz && i < 7; i++) + ipc_log (" %lld", fids->fetch (i)); + if (i < sz) + ipc_log (" ... %lld", fids->fetch (sz - 1)); + ipc_log ("\n"); + } + if (props) + { + int nprops = props->size (); + ipc_log (" returning values for %d properties:\n", nprops); + assert (secondaryProps->size () == nprops); + } + } + else + ipc_log (" returning NULL\n"); +#endif + writeArray (res, req); + destroy (res); + destroy (secondaryProps); + } + else if (!strcmp (inp, "getTLEventCenterTime")) + { + int dbevindex = readInt (req); + int exp_id = readInt (req); + int tldata_type = readInt (req); + int entity_prop_id = readInt (req); + int entity_prop_val = readInt (req); + int aux = readInt (req); + long long event_id = readLong (req); + long long move_count = readLong (req); + ipc_log (" args = %d:%d; tldata_type = %d entity_prop_id = %d " + "ent = %d aux = %d idx = %lld move=%lld\n", + dbevindex, exp_id, + tldata_type, entity_prop_id, entity_prop_val, aux, event_id, move_count); + Vector<long long> * res = dbeGetTLEventCenterTime (dbevindex, exp_id, + tldata_type, entity_prop_id, entity_prop_val, aux, event_id, move_count); + ipc_log (" returning idx = %lld, time = %lld\n", + res ? res->fetch (0) : -1, res ? res->fetch (1) : -1); + writeArray (res, req); + } + else if (!strcmp (inp, "getTLEventIdxNearTime")) + { + int dbevindex = readInt (req); + int exp_id = readInt (req); + int tldata_type = readInt (req); + int entity_prop_id = readInt (req); + int entity_prop_val = readInt (req); + int aux = readInt (req); + int searchDirection = readInt (req); + long long value = readLong (req); + ipc_log (" args = %d:%d; tldata_type = %d entity_prop_id = %d " + "ent = %d aux = %d direction = %d value = %lld(0x%llx)\n", + dbevindex, exp_id, + tldata_type, entity_prop_id, entity_prop_val, aux, + searchDirection, value, value); + long long res = dbeGetTLEventIdxNearTime (dbevindex, exp_id, + tldata_type, entity_prop_id, entity_prop_val, aux, + searchDirection, value); + ipc_log (" returning = %lld\n", res); + writeLong (res, req); + } + else if (!strcmp (inp, "getAggregatedValue")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + String arg3 = readString (req); + String arg4 = readString (req); + long long arg5 = readLong (req); + long long arg6 = readLong (req); + int arg7 = readInt (req); + String arg8 = readString (req); + String arg9 = readString (req); + ipc_log (" data_idx = %d lfilter = \"%s\" fexpr = \"%s\" " + "time = \"%s\" tstart = %lld delta = %lld " + "num = %d key = \"%s\" aggr = \"%s\"\n", + arg1, arg2 ? arg2 : "NULL", arg3 ? arg3 : "NULL", + arg4 ? arg4 : "NULL", arg5, arg6, + arg7, arg8 ? arg8 : "NULL", arg9 ? arg9 : "NULL"); + Vector<long long> *res = dbeGetAggregatedValue (arg1, arg2, arg3, + arg4, arg5, arg6, arg7, arg8, arg9); +#ifdef IPC_LOG + if (res) + { + int sz = res->size (); + ipc_log (" returning = %d values:", sz); + if (sz > 10) + sz = 10; + for (int i = 0; i < sz; i++) + ipc_log (" %lld", res->fetch (i)); + ipc_log ("\n"); + } + else + ipc_log (" returning NULL\n"); +#endif + writeArray (res, req); + delete res; + free (arg2); + free (arg3); + free (arg4); + free (arg8); + free (arg9); + } +#if 0//YXXX TBR + else if (!strcmp (inp, "getExprValue")) + { + int exp_id = readInt (); + int arg1 = readInt (); + int arg2 = readInt (); + String arg3 = readString (); + ipc_log (" exp_id %d, data_id = %d, event_id = %d, expr = %s\n", + exp_id, arg1, arg2, arg3 ? arg3 : "NULL"); + String res = dbeGetExprValue (exp_id, arg1, arg2, arg3); + ipc_log (" returning = %s\n", res ? res : ""); + writeString (res); + free (res); + free (arg3); + } + else if (!strcmp (inp, "getListValues")) + { + Obj arg1 = readObject (); + ipc_log (" stack = %lu\n", (long) arg1); + Vector<Obj> *res = dbeGetListValues (arg1); + ipc_log (" returns = %d objects\n", res ? res->size () : 0); + writeArray (res); + destroy (res); + } + else if (!strcmp (inp, "getListNames")) + { + Obj arg1 = readObject (); + ipc_log (" stack = %lu\n", (long) arg1); + Vector<String> *res = dbeGetListNames (arg1); + ipc_log (" returns = %d objects\n", res ? res->size () : 0); + writeArray (res); + destroy (res); + } +#endif + else if (!strcmp (inp, "getLineInfo")) + { + Obj arg1 = readObject (req); + ipc_log (" pc = %lu\n", (long) arg1); + Vector<String> *res = dbeGetLineInfo (arg1); + ipc_log (" returning File name: '%s'\n", res ? res->fetch (0) : ""); + ipc_log (" returning Lineno: '%s'\n", res ? res->fetch (1) : ""); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "setAlias")) + { + String arg1 = readString (req); + String arg2 = readString (req); + String arg3 = readString (req); + ipc_log (" name=\"%s\" uname=\"%s\" expr=\"%s\"\n", + arg1 ? arg1 : "", arg2 ? arg2 : "", arg3 ? arg3 : ""); + int res = dbeSetAlias (arg1, arg2, arg3); + ipc_log (" returning = %d\n", res); + writeInt (res, req); + } + else if (!strcmp (inp, "getAlias")) + { + String arg1 = readString (req); + ipc_log (" name=\"%s\"\n", arg1 ? arg1 : ""); + Vector<char*> *res = dbeGetAlias (arg1); + ipc_log (" returning uname: '%s'\n", res && res->fetch (0) ? res->fetch (0) : ""); + ipc_log (" returning expr: '%s'\n", res && res->fetch (1) ? res->fetch (0) : ""); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getXYPlotData")) + { + int arg1 = readInt (req); + String arg2 = readString (req); + String arg3 = readString (req); + String arg4 = readString (req); + String arg5 = readString (req); + String arg6 = readString (req); + String arg7 = readString (req); + String arg8 = readString (req); + String arg9 = readString (req); + ipc_log (" data_idx = %d lfilter = \"%s\" arg = \"%s\" " + "func1 = \"%s\" aggr1 = \"%s\" " + "func2 = \"%s\" aggr2 = \"%s\" " + "func3 = \"%s\" aggr3 = \"%s\" \n", + arg1, arg2 ? arg2 : "NULL", arg3 ? arg3 : "NULL", + arg4 ? arg4 : "NULL", arg5 ? arg5 : "NULL", arg6 ? arg6 : "NULL", + arg7 ? arg7 : "NULL", arg8 ? arg8 : "NULL", arg9 ? arg9 : "NULL"); + Vector<Vector<long long>*> *res = dbeGetXYPlotData (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); + +#ifdef IPC_LOG + if (res) + { + long nvals = res->size (); + for (long i = 0; i < nvals; ++i) + { + Vector<long long> *vals = res->fetch (i); + long long sz = VSIZE (vals); + ipc_log (" returning = %lld values:", sz); + if (sz > 10) + sz = 10; + for (long j = 0; j < sz; j++) + ipc_log (" %lld", vals->fetch (j)); + ipc_log ("\n"); + } + } + else + ipc_log (" returning NULL\n"); +#endif + writeArray (res, req); + destroy (res); + } + else if (strcmp (inp, "dbe_archive") == 0) + { + Vector<long long> *ids = (Vector<long long> *) readArray (req); + Vector<const char*> *locations = (Vector<const char*> *) readArray (req); + dbe_archive (ids, locations); + delete ids; + destroy (locations); + writeResponseGeneric (RESPONSE_STATUS_SUCCESS, currentRequestID, currentChannelID); + } + else if (strcmp (inp, "dbeSetLocations") == 0) + { + Vector<const char*> *fnames = (Vector<const char*> *) readArray (req); + Vector<const char*> *locations = (Vector<const char*> *) readArray (req); + dbeSetLocations (fnames, locations); + destroy (fnames); + destroy (locations); + writeResponseGeneric (RESPONSE_STATUS_SUCCESS, currentRequestID, currentChannelID); + } + else if (strcmp (inp, "dbeResolvedWith_setpath") == 0) + { + char *path = readString (req); + Vector<void *> *res = dbeResolvedWith_setpath (path); + free (path); + writeArray (res, req); + destroy (res); + } + else if (strcmp (inp, "dbeResolvedWith_pathmap") == 0) + { + char *old_prefix = readString (req); + char *new_prefix = readString (req); + Vector<void *> *res = dbeResolvedWith_pathmap (old_prefix, new_prefix); + free (old_prefix); + free (new_prefix); + writeArray (res, req); + destroy (res); + } + else if (!strcmp (inp, "getCollectorControlValue")) + { + /* int dbevindex =*/ readInt (req); + char *control = readString (req); + ipc_log (" args = %s\n", control); + char *ret = dbeGetCollectorControlValue (control); + ipc_log (" returning %s\n", STR (ret)); + writeString (ret, req); + } + else if (!strcmp (inp, "setCollectorControlValue")) + { + /* int dbevindex =*/ readInt (req); + char *control = readString (req); + char *value = readString (req); +#ifdef IPC_LOG + ipc_log (" args = %s %s\n", control, value); +#endif + char *ret = dbeSetCollectorControlValue (control, value); +#ifdef IPC_LOG + if (ret) + ipc_log (" returning %s\n", ret); + else + ipc_log (" returning NULL\n"); +#endif + writeString (ret, req); + } + else if (!strcmp (inp, "unsetCollectorControlValue")) + { + /* int dbevindex =*/ readInt (req); + char *control = readString (req); + ipc_log (" args = %s\n", control); + char *ret = dbeUnsetCollectorControlValue (control); + ipc_log (" returning %s\n", STR (ret)); + writeString (ret, req); + } + else if (!strcmp (inp, "getSignalValue")) + { + String arg1 = readString (req); + ipc_log (" arg1=\"%s\"\n", arg1 ? arg1 : ""); + int res = dbeGetSignalValue (arg1); + ipc_log (" returning = %d\n", res); + writeInt (res, req); + } + else if (!strcmp (inp, "sendSignal")) + { + long long p = readLong (req); + int signum = readInt (req); + ipc_log (" args = %llu, %d\n", (long long) p, signum); + char * ret = dbeSendSignal ((pid_t) p, signum); +#ifdef IPC_LOG + if (ret) + ipc_log (" returning %s\n", ret); + else + ipc_log (" returning NULL\n"); +#endif + writeString (ret, req); + } + else if (!strcmp (inp, "checkConnection")) + { + String arg1 = readString (req); + ipc_log (" arg = `%s'\n", arg1 ? arg1 : "NULL"); + String res = dbeCheckConnection (arg1); + writeString (res, req); + ipc_log (" returns `%s'\n", res ? res : "NULL"); + free (arg1); + free (res); + } + else if (!strcmp (inp, "QUIT")) + { +#ifdef IPC_LOG + ipc_log (" %s\n", inp); +#endif + exit (0); + } + else + { + ipc_log ("Unrecognized input cmd \"%s\"; Aborting.\n", inp); + return 1; + } + ipc_log (" processing IPC command %s complete\n", inp); + free (inp); + fflush (stdout); + if (req->getStatus () != CANCELLED_IMMEDIATE) + // wake up the main working thread, let it take care of delete + req->setStatus (COMPLETED); + delete req; + return 0; +} + +void +check_env_args (int argc, char *argv[]) +{ + int indx = 2; // Skip "-IPC" + const char *MINUS_E = "-E"; + const char *SP_ER_PRINT_TRACE_LEVEL = "SP_ER_PRINT_TRACE_LEVEL"; + const char *SP_IPC_PROTOCOL = "SP_IPC_PROTOCOL"; + const char SEPARATOR = '='; + char *cmd_env_var = NULL; + while (argc - indx >= 2) + { + char *option = argv[indx++]; + if (!streq (option, MINUS_E)) + continue; + cmd_env_var = argv[indx++]; + char *separator = strchr (cmd_env_var, SEPARATOR); + if (!separator) + // Unrecognized option. Fatal error? + continue; + char *cmd_env_var_val = separator + 1; + if (!strncmp (cmd_env_var, SP_ER_PRINT_TRACE_LEVEL, + strlen (SP_ER_PRINT_TRACE_LEVEL))) + { + if (streq (cmd_env_var_val, "1")) + ipc_trace_level = TRACE_LVL_1; + else if (streq (cmd_env_var_val, "2")) + ipc_trace_level = TRACE_LVL_2; + else if (streq (cmd_env_var_val, "3")) + ipc_trace_level = TRACE_LVL_3; + else if (streq (cmd_env_var_val, "4")) + ipc_trace_level = TRACE_LVL_4; + continue; + } + if (!strncmp (cmd_env_var, SP_IPC_PROTOCOL, strlen (SP_IPC_PROTOCOL))) + { + if (streq (cmd_env_var_val, IPC_PROTOCOL_CURR)) + // Only one protocol is currently supported + ipc_protocol = IPC_PROTOCOL_CURR; + else + ipc_protocol = IPC_PROTOCOL_UNKNOWN; + continue; + } + // Unrecognized option. Fatal error? + } +} + +void +print_ipc_protocol_confirmation () +{ + if (NULL != ipc_protocol) + { + fprintf (stdout, "ER_IPC: %s\n", ipc_protocol); + fflush (stdout); + } +} + +void +ipc_mainLoop (int argc, char *argv[]) +{ + if (getenv ("GPROFNG_DBE_DELAY")) + sleep (20); +#ifdef IPC_LOG + ipc_flags = 1; +#endif + // check_env_args(argc, argv); + + char *er_print_trace_level = getenv ("SP_ER_PRINT_TRACE_LEVEL"); + if (er_print_trace_level != NULL) + { + if (streq (er_print_trace_level, "1")) + ipc_trace_level = TRACE_LVL_1; + else if (streq (er_print_trace_level, "2")) + ipc_trace_level = TRACE_LVL_2; + else if (streq (er_print_trace_level, "3")) + ipc_trace_level = TRACE_LVL_3; + else if (streq (er_print_trace_level, "4")) + ipc_trace_level = TRACE_LVL_4; + } + check_env_args (argc, argv); + print_ipc_protocol_confirmation (); + + if (ipc_flags || getenv ("SP_ER_PRINT_IPC_FLAG") || ipc_trace_level > TRACE_LVL_0) + { + ipc_flags = 1; + if (ipc_trace_level == TRACE_LVL_0) + ipc_trace_level = TRACE_LVL_1; + // reopen stderr as file "ipc_log" + ipc_log_name = getenv ("SP_ER_PRINT_IPC_LOG"); + if (ipc_log_name == NULL) + ipc_log_name = "ipc_log"; + freopen (ipc_log_name, "w", stderr); + if (ipc_trace_level >= TRACE_LVL_2) + { + ipc_request_log_name = "ipc_request_log"; + ipc_response_log_name = "ipc_response_log"; + requestLogFileP = fopen (ipc_request_log_name, "w"); + responseLogFileP = fopen (ipc_response_log_name, "w"); + } + else + { + ipc_request_log_name = "ipc_log"; + ipc_response_log_name = "ipc_log"; + } + begin_time = gethrtime (); + } + else + // Reopen stderr as /dev/null + freopen ("/dev/null", "w", stderr); + + struct sigaction act; + memset (&act, 0, sizeof (struct sigaction)); + term_flag = 0; + /* install a handler for TERM */ + ipc_request_trace (TRACE_LVL_1, "Installing SIGTERM handler to abort on error\n"); + sigemptyset (&act.sa_mask); + act.sa_handler = (SignalHandler) sigterm_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (SIGTERM, &act, &old_sigterm_handler) == -1) + { + ipc_request_trace (TRACE_LVL_1, "Unable to install SIGTERM handler\n"); + abort (); + } + /* install a handler for INT */ + ipc_request_trace (TRACE_LVL_1, "Installing SIGINT handler to send message to analyzer\n"); + sigemptyset (&act.sa_mask); + act.sa_handler = (SignalHandler) sigint_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (SIGINT, &act, &old_sigint_handler) == -1) + { + ipc_request_trace (TRACE_LVL_1, "Unable to install SIGINT handler\n"); + abort (); + } + ipc_log ("Installed SIGINT handler to handle Ctrl-C properly\n"); + int er_print_catch_crash = 1; // Default: catch fatal signals + char *s = getenv ("GPROFNG_ALLOW_CORE_DUMP"); + if (s && (strcasecmp (s, "no") == 0 || strcmp (s, "0") == 0)) + er_print_catch_crash = 0; + if (er_print_catch_crash) + { + /* reserve memory for fatal error processing */ + fatalErrorDynamicMemory = (char *) malloc (4 * 1024 * 1024); // reserve 4 MB + /* install a handler for SIGABRT */ + ipc_request_trace (TRACE_LVL_1, "Installing SIGABRT handler to send message to analyzer\n"); + sigemptyset (&act.sa_mask); + act.sa_handler = (SignalHandler) sigABRT_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (SIGABRT, &act, NULL) == -1) + { + ipc_request_trace (TRACE_LVL_1, "Unable to install SIGABRT handler\n"); + // abort(); + } + else + ipc_log ("Installed SIGABRT handler to handle crash properly\n"); + /* install a handler for SIGSEGV */ + ipc_request_trace (TRACE_LVL_1, "Installing SIGABRT handler to send message to analyzer\n"); + sigemptyset (&act.sa_mask); + act.sa_handler = (SignalHandler) sigSEGV_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction (SIGSEGV, &act, NULL) == -1) + { + ipc_request_trace (TRACE_LVL_1, "Unable to install SIGSEGV handler\n"); + // abort(); + } + else + ipc_log ("Installed SIGSEGV handler to handle crash properly\n"); + } + ipc_request_trace (TRACE_LVL_1, "Entering ipc_mainLoop; run dir `%s'\n", + theApplication->get_run_dir ()); + cancelRequestedChannelID = 0xFFFFFFFF; + ipcThreadPool = new DbeThreadPool (0); // DbeThreadPool (-1); + responseBufferPool = new BufferPool (); + ipc_log (ipc_single_threaded_mode ? + "RUNNING er_print -IPC IN SINGLE THREADED MODE\n" : + "RUNNING er_print -IPC IN MULTITHREAD MODE\n"); + + /* Send "Ready" signal to the GUI */ + setProgress (100, "Restart engine"); + + /* start listening for requests */ + error_flag = 0; + for (;;) + { + readRequestHeader (); + if (term_flag == 1 || error_flag == 1) + { + ipc_request_trace (TRACE_LVL_1, "SIGTERM received, exiting\n"); + return; + } + } +} + +static const char * +table_name (int flavor) +{ + static char def_name[64]; + + switch ((FuncListDisp_type) flavor) + { + case DSP_FUNCTION: + return ("FUNCTION"); + case DSP_LINE: + return ("LINE"); + case DSP_PC: + return ("PC"); + case DSP_SOURCE: + return ("SOURCE"); + case DSP_DISASM: + return ("DISASM"); + case DSP_SELF: + return ("SELF"); + case DSP_CALLER: + return ("CALLER"); + case DSP_CALLEE: + return ("CALLEE"); + case DSP_CALLTREE: + return ("CALLTREE"); + case DSP_TIMELINE: + return ("TIMELINE"); + case DSP_STATIS: + return ("STATIS"); + case DSP_EXP: + return ("EXP"); + case DSP_LEAKLIST: + return ("LEAKLIST"); + case DSP_HEAPCALLSTACK: + return ("HEAP"); + case DSP_MEMOBJ: + return ("MEMOBJ"); + case DSP_DATAOBJ: + return ("DATAOBJ"); + case DSP_DLAYOUT: + return ("DLAYOUT"); + case DSP_SRC_FILE: + return ("SRC_FILE"); + case DSP_IFREQ: + return ("IFREQ"); + case DSP_RACES: + return ("RACES"); + case DSP_INDXOBJ: + return ("INDXOBJ"); + case DSP_DUALSOURCE: + return ("DUALSOURCE"); + case DSP_SOURCE_DISASM: + return ("SOURCE_DISASM"); + case DSP_DEADLOCKS: + return ("DEADLOCKS"); + case DSP_SOURCE_V2: + return ("SOURCE_V2"); + case DSP_DISASM_V2: + return ("DISASM_V2"); + case DSP_IOACTIVITY: + return ("IOACTIVITY"); + case DSP_OVERVIEW: + return ("OVERVIEW"); + case DSP_SAMPLE: + return ("SAMPLE -- UNEXPECTED"); + default: + snprintf (def_name, sizeof (def_name), "table number %d", flavor); + return (def_name); + } +} diff --git a/gprofng/src/ipcio.cc b/gprofng/src/ipcio.cc new file mode 100644 index 0000000..57f2617 --- /dev/null +++ b/gprofng/src/ipcio.cc @@ -0,0 +1,1025 @@ +/* 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 <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <iostream> +#include <iomanip> +#include <sstream> +#include <queue> +#include "vec.h" +#include "util.h" +#include "ipcio.h" +#include "DbeThread.h" +#include "Experiment.h" + +#define ipc_trace if (ipc_flags) ipc_default_log +#define ipc_request_trace if (ipc_flags) ipc_request_log +#define ipc_response_trace if (ipc_flags) ipc_response_log + +using namespace std; + +// IPC implementation +static const int L_PROGRESS = 0; +static const int L_INTEGER = 1; +static const int L_BOOLEAN = 2; +static const int L_LONG = 3; +static const int L_STRING = 4; +static const int L_DOUBLE = 5; +static const int L_ARRAY = 6; +static const int L_OBJECT = 7; +static const int L_CHAR = 8; + +int currentRequestID; +int currentChannelID; +static long maxSize; + +extern int cancellableChannelID; +extern int error_flag; +extern int ipc_delay_microsec; +extern FILE *responseLogFileP; + +IPCresponse *IPCresponseGlobal; + +BufferPool *responseBufferPool; + +IPCrequest::IPCrequest (int sz, int reqID, int chID) +{ + size = sz; + requestID = reqID; + channelID = chID; + status = INITIALIZED; + idx = 0; + buf = (char *) malloc (size); + cancelImmediate = false; +} + +IPCrequest::~IPCrequest () +{ + free (buf); +} + +void +IPCrequest::read (void) +{ + for (int i = 0; i < size; i++) + { + int c = getc (stdin); + ipc_request_trace (TRACE_LVL_4, " IPCrequest:getc(stdin): %02x\n", c); + buf[i] = c; + } +} + +IPCrequestStatus +IPCrequest::getStatus (void) +{ + return status; +} + +void +IPCrequest::setStatus (IPCrequestStatus newStatus) +{ + status = newStatus; +} + +static int +readByte (IPCrequest* req) +{ + int c; + int val = 0; + for (int i = 0; i < 2; i++) + { + if (req == NULL) + { + c = getc (stdin); + ipc_request_trace (TRACE_LVL_4, " readByte:getc(stdin): %02x\n", c); + } + else + c = req->rgetc (); + switch (c) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + val = val * 16 + c - '0'; + break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + val = val * 16 + c - 'a' + 10; + break; + case EOF: + val = EOF; + break; + default: + fprintf (stderr, "readByte: Unknown byte: %d\n", c); + break; + } + } + return val; +} + +static int +readIVal (IPCrequest *req) +{ + int val = readByte (req); + for (int i = 0; i < 3; i++) + val = val * 256 + readByte (req); + ipc_trace (" readIVal: %d\n", val); + return val; +} + +static String +readSVal (IPCrequest *req) +{ + int len = readIVal (req); + if (len == -1) + { + ipc_trace (" readSVal: <NULL>\n"); + return NULL; + } + char *str = (char *) malloc (len + 1); + char *s = str; + *s = (char) 0; + while (len--) + *s++ = req->rgetc (); + *s = (char) 0; + ipc_trace (" readSVal: '%s'\n", str); + return str; +} + +static long long +readLVal (IPCrequest *req) +{ + long long val = readByte (req); + for (int i = 0; i < 7; i++) + val = val * 256 + readByte (req); + ipc_trace (" readLVal: %lld\n", val); + return val; +} + +static bool +readBVal (IPCrequest *req) +{ + int val = readByte (req); + ipc_trace (" readBVal: %s\n", val == 0 ? "true" : "false"); + return val != 0; +} + +static char +readCVal (IPCrequest *req) +{ + int val = readByte (req); + ipc_trace (" readCVal: %d\n", val); + return (char) val; +} + +static double +readDVal (IPCrequest *req) +{ + String s = readSVal (req); + double d = atof (s); + free (s); + return d; +} + +static Object +readAVal (IPCrequest *req) +{ + bool twoD = false; + int type = readByte (req); + if (type == L_ARRAY) + { + twoD = true; + type = readByte (req); + } + ipc_trace ("readAVal: twoD=%s type=%d\n", twoD ? "true" : "false", type); + + int len = readIVal (req); + if (len == -1) + return NULL; + switch (type) + { + case L_INTEGER: + if (twoD) + { + Vector<Vector<int>*> *array = new Vector<Vector<int>*>(len); + for (int i = 0; i < len; i++) + array->store (i, (Vector<int>*)readAVal (req)); + return array; + } + else + { + Vector<int> *array = new Vector<int>(len); + for (int i = 0; i < len; i++) + array->store (i, readIVal (req)); + return array; + } + //break; + case L_LONG: + if (twoD) + { + Vector<Vector<long long>*> *array = new Vector<Vector<long long>*>(len); + for (int i = 0; i < len; i++) + array->store (i, (Vector<long long>*)readAVal (req)); + return array; + } + else + { + Vector<long long> *array = new Vector<long long>(len); + for (int i = 0; i < len; i++) + array->store (i, readLVal (req)); + return array; + } + //break; + case L_DOUBLE: + if (twoD) + { + Vector<Vector<double>*> *array = new Vector<Vector<double>*>(len); + for (int i = 0; i < len; i++) + array->store (i, (Vector<double>*)readAVal (req)); + return array; + } + else + { + Vector<double> *array = new Vector<double>(len); + for (int i = 0; i < len; i++) + array->store (i, readDVal (req)); + return array; + } + //break; + case L_BOOLEAN: + if (twoD) + { + Vector < Vector<bool>*> *array = new Vector < Vector<bool>*>(len); + for (int i = 0; i < len; i++) + array->store (i, (Vector<bool>*)readAVal (req)); + return array; + } + else + { + Vector<bool> *array = new Vector<bool>(len); + for (int i = 0; i < len; i++) + array->store (i, readBVal (req)); + return array; + } + //break; + case L_CHAR: + if (twoD) + { + Vector<Vector<char>*> *array = new Vector<Vector<char>*>(len); + for (int i = 0; i < len; i++) + array->store (i, (Vector<char>*)readAVal (req)); + return array; + } + else + { + Vector<char> *array = new Vector<char>(len); + for (int i = 0; i < len; i++) + array->store (i, readCVal (req)); + return array; + } + //break; + case L_STRING: + if (twoD) + { + Vector<Vector<String>*> *array = new Vector<Vector<String>*>(len); + for (int i = 0; i < len; i++) + array->store (i, (Vector<String>*)readAVal (req)); + return array; + } + else + { + Vector<String> *array = new Vector<String>(len); + for (int i = 0; i < len; i++) + array->store (i, readSVal (req)); + return array; + } + //break; + case L_OBJECT: + if (twoD) + { + Vector<Vector<Object>*> *array = new Vector<Vector<Object>*>(len); + for (int i = 0; i < len; i++) + array->store (i, (Vector<Object>*)readAVal (req)); + return array; + } + else + { + Vector<Object> *array = new Vector<Object>(len); + for (int i = 0; i < len; i++) + array->store (i, readAVal (req)); + return array; + } + //break; + default: + fprintf (stderr, "readAVal: Unknown code: %d\n", type); + break; + } + return NULL; +} + +static int iVal; +static bool bVal; +static long long lVal; +static String sVal; +static double dVal; +static Object aVal; + +static void +readResult (int type, IPCrequest *req) +{ + int tVal = readByte (req); + switch (tVal) + { + case L_INTEGER: + iVal = readIVal (req); + break; + case L_LONG: + lVal = readLVal (req); + break; + case L_BOOLEAN: + bVal = readBVal (req); + break; + case L_DOUBLE: + dVal = readDVal (req); + break; + case L_STRING: + sVal = readSVal (req); + break; + case L_ARRAY: + aVal = readAVal (req); + break; + case EOF: + fprintf (stderr, "EOF read in readResult\n"); + sVal = NULL; + return; + default: + fprintf (stderr, "Unknown code: %d\n", tVal); + abort (); + } + if (type != tVal) + { + fprintf (stderr, "Internal error: readResult: parameter mismatch: type=%d should be %d\n", tVal, type); + abort (); + } +} + +int +readInt (IPCrequest *req) +{ + readResult (L_INTEGER, req); + return iVal; +} + +String +readString (IPCrequest *req) +{ + readResult (L_STRING, req); + return sVal; +} + +long long +readLong (IPCrequest *req) +{ + readResult (L_LONG, req); + return lVal; +} + +double +readDouble (IPCrequest *req) +{ + readResult (L_DOUBLE, req); + return dVal; +} + +bool +readBoolean (IPCrequest *req) +{ + readResult (L_BOOLEAN, req); + return bVal; +} + +DbeObj +readObject (IPCrequest *req) +{ + readResult (L_LONG, req); + return (DbeObj) lVal; +} + +Object +readArray (IPCrequest *req) +{ + readResult (L_ARRAY, req); + return aVal; +} + +// Write +IPCresponse::IPCresponse (int sz) +{ + requestID = -1; + channelID = -1; + responseType = -1; + responseStatus = RESPONSE_STATUS_SUCCESS; + sb = new StringBuilder (sz); + next = NULL; +} + +IPCresponse::~IPCresponse () +{ + delete sb; +} + +void +IPCresponse::reset () +{ + requestID = -1; + channelID = -1; + responseType = -1; + responseStatus = RESPONSE_STATUS_SUCCESS; + sb->setLength (0); +} + +void +IPCresponse::sendByte (int b) +{ + ipc_trace ("sendByte: %02x %d\n", b, b); + sb->appendf ("%02x", b); +} + +void +IPCresponse::sendIVal (int i) +{ + ipc_trace ("sendIVal: %08x %d\n", i, i); + sb->appendf ("%08x", i); +} + +void +IPCresponse::sendLVal (long long l) +{ + ipc_trace ("sendLVal: %016llx %lld\n", l, l); + sb->appendf ("%016llx", l); +} + +void +IPCresponse::sendSVal (const char *s) +{ + if (s == NULL) + { + sendIVal (-1); + return; + } + sendIVal ((int) strlen (s)); + ipc_trace ("sendSVal: %s\n", s); + sb->appendf ("%s", s); +} + +void +IPCresponse::sendBVal (bool b) +{ + sendByte (b ? 1 : 0); +} + +void +IPCresponse::sendCVal (char c) +{ + sendByte (c); +} + +void +IPCresponse::sendDVal (double d) +{ + char str[32]; + snprintf (str, sizeof (str), "%.12f", d); + sendSVal (str); +} + +void +IPCresponse::sendAVal (void *ptr) +{ + if (ptr == NULL) + { + sendByte (L_INTEGER); + sendIVal (-1); + return; + } + + VecType type = ((Vector<void*>*)ptr)->type (); + switch (type) + { + case VEC_INTEGER: + { + sendByte (L_INTEGER); + Vector<int> *array = (Vector<int>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendIVal (array->fetch (i)); + break; + } + case VEC_BOOL: + { + sendByte (L_BOOLEAN); + Vector<bool> *array = (Vector<bool>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendBVal (array->fetch (i)); + break; + } + case VEC_CHAR: + { + sendByte (L_CHAR); + Vector<char> *array = (Vector<char>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendCVal (array->fetch (i)); + break; + } + case VEC_LLONG: + { + sendByte (L_LONG); + Vector<long long> *array = (Vector<long long>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendLVal (array->fetch (i)); + break; + } + case VEC_DOUBLE: + { + sendByte (L_DOUBLE); + Vector<double> *array = (Vector<double>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendDVal (array->fetch (i)); + break; + } + case VEC_STRING: + { + sendByte (L_STRING); + Vector<String> *array = (Vector<String>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendSVal (array->fetch (i)); + break; + } + case VEC_STRINGARR: + { + sendByte (L_ARRAY); + sendByte (L_STRING); + Vector<void*> *array = (Vector<void*>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendAVal (array->fetch (i)); + break; + } + case VEC_INTARR: + { + sendByte (L_ARRAY); + sendByte (L_INTEGER); + Vector<void*> *array = (Vector<void*>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendAVal (array->fetch (i)); + break; + } + case VEC_LLONGARR: + { + sendByte (L_ARRAY); + sendByte (L_LONG); + Vector<void*> *array = (Vector<void*>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendAVal (array->fetch (i)); + break; + } + case VEC_VOIDARR: + { + sendByte (L_OBJECT); + Vector<void*> *array = (Vector<void*>*)ptr; + sendIVal (array->size ()); + for (int i = 0; i < array->size (); i++) + sendAVal (array->fetch (i)); + break; + } + default: + fprintf (stderr, "sendAVal: Unknown type: %d\n", type); + abort (); + } +} + +static void +writeResponseHeader (int requestID, int responseType, int responseStatus, int nBytes) +{ + if (responseType == RESPONSE_TYPE_HANDSHAKE) + nBytes = IPC_VERSION_NUMBER; + int use_write = 2; + ipc_response_trace (TRACE_LVL_1, "ResponseHeaderBegin----- %x ---- %x ----- %x -----%x -------\n", requestID, responseType, responseStatus, nBytes); + if (use_write) + { + char buf[23]; + if (use_write == 1) + { + int i = 0; + snprintf (buf + i, 3, "%2x", HEADER_MARKER); + i += 2; + snprintf (buf + i, 9, "%8x", requestID); + i += 8; + snprintf (buf + i, 3, "%2x", responseType); + i += 2; + snprintf (buf + i, 3, "%2x", responseStatus); + i += 2; + snprintf (buf + i, 9, "%8x", nBytes); + } + else + snprintf (buf, 23, "%02x%08x%02x%02x%08x", HEADER_MARKER, requestID, + responseType, responseStatus, nBytes); + buf[22] = 0; + write (1, buf, 22); + } + else + { + cout << setfill ('0') << setw (2) << hex << HEADER_MARKER; + cout << setfill ('0') << setw (8) << hex << requestID; + cout << setfill ('0') << setw (2) << hex << responseType; + cout << setfill ('0') << setw (2) << hex << responseStatus; + cout << setfill ('0') << setw (8) << hex << nBytes; + cout.flush (); + } + ipc_response_trace (TRACE_LVL_1, "----------------------------ResponseHeaderEnd\n"); + if (nBytes > maxSize) + { + maxSize = nBytes; + ipc_trace ("New maxsize %ld\n", maxSize); + } +} + +bool +cancelNeeded (int chID) +{ + if (chID == cancellableChannelID && chID == cancelRequestedChannelID) + return true; + else + return false; +} + +static void +writeResponseWithHeader (int requestID, int channelID, int responseType, + int responseStatus, IPCresponse* os) +{ + if (cancelNeeded (channelID)) + { + responseStatus = RESPONSE_STATUS_CANCELLED; + ipc_trace ("CANCELLING %d %d\n", requestID, channelID); + // This is for gracefully cancelling regular ops like openExperiment - getFiles should never reach here + } + os->setRequestID (requestID); + os->setChannelID (channelID); + os->setResponseType (responseType); + os->setResponseStatus (responseStatus); + os->print (); + os->reset (); + responseBufferPool->recycle (os); +} + +void +writeAckFast (int requestID) +{ + writeResponseHeader (requestID, RESPONSE_TYPE_ACK, RESPONSE_STATUS_SUCCESS, 0); +} + +void +writeAck (int requestID, int channelID) +{ +#if DEBUG + char *s = getenv (NTXT ("SP_NO_IPC_ACK")); +#else /* ^DEBUG */ + char *s = NULL; +#endif /* ^DEBUG */ + if (s) + { + int i = requestID; + int j = channelID; + ipc_request_trace (TRACE_LVL_4, "ACK skipped: requestID=%d channelID=%d\n", i, j); + } + else + { + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_SMALL); + writeResponseWithHeader (requestID, channelID, RESPONSE_TYPE_ACK, + RESPONSE_STATUS_SUCCESS, OUTS); + } +} + +void +writeHandshake (int requestID, int channelID) +{ + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_SMALL); + writeResponseWithHeader (requestID, channelID, RESPONSE_TYPE_HANDSHAKE, RESPONSE_STATUS_SUCCESS, OUTS); + // writeResponseHeader(requestID, RESPONSE_TYPE_HANDSHAKE, RESPONSE_STATUS_SUCCESS, IPC_VERSION_NUMBER); +} + +void +writeResponseGeneric (int responseStatus, int requestID, int channelID) +{ + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_SMALL); + writeResponseWithHeader (requestID, channelID, RESPONSE_TYPE_COMPLETE, responseStatus, OUTS); +} + +BufferPool::BufferPool () +{ + pthread_mutex_init (&p_mutex, NULL); + smallBuf = NULL; + largeBuf = NULL; +} + +BufferPool::~BufferPool () +{ + for (IPCresponse *p = smallBuf; p;) + { + IPCresponse *tmp = p; + p = tmp->next; + delete tmp; + } + for (IPCresponse *p = largeBuf; p;) + { + IPCresponse *tmp = p; + p = tmp->next; + delete tmp; + } +} + +IPCresponse* +BufferPool::getNewResponse (int size) +{ + pthread_mutex_lock (&p_mutex); + if (ipc_single_threaded_mode && size < BUFFER_SIZE_LARGE) + size = BUFFER_SIZE_LARGE; + IPCresponse *newResponse = NULL; + if (size >= BUFFER_SIZE_LARGE) + { + if (largeBuf) + { + newResponse = largeBuf; + largeBuf = largeBuf->next; + } + } + else if (smallBuf) + { + newResponse = smallBuf; + smallBuf = smallBuf->next; + } + if (newResponse) + newResponse->reset (); + else + { + newResponse = new IPCresponse (size); + ipc_trace ("GETNEWBUFFER %d\n", size); + } + pthread_mutex_unlock (&p_mutex); + return newResponse; +} + +void +BufferPool::recycle (IPCresponse *respB) +{ + pthread_mutex_lock (&p_mutex); + if (respB->getCurBufSize () >= BUFFER_SIZE_LARGE) + { + respB->next = largeBuf; + largeBuf = respB; + } + else + { + respB->next = smallBuf; + smallBuf = respB; + } + pthread_mutex_unlock (&p_mutex); +} + +void +writeArray (void *ptr, IPCrequest* req) +{ + if (req->getStatus () == CANCELLED_IMMEDIATE) + return; + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_LARGE); + OUTS->sendByte (L_ARRAY); + OUTS->sendAVal (ptr); + writeResponseWithHeader (req->getRequestID (), req->getChannelID (), + RESPONSE_TYPE_COMPLETE, RESPONSE_STATUS_SUCCESS, OUTS); +} + +void +writeString (const char *s, IPCrequest* req) +{ + if (req->getStatus () == CANCELLED_IMMEDIATE) + return; + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_LARGE); + OUTS->sendByte (L_STRING); + OUTS->sendSVal (s); + writeResponseWithHeader (req->getRequestID (), req->getChannelID (), + RESPONSE_TYPE_COMPLETE, RESPONSE_STATUS_SUCCESS, OUTS); +} + +void +writeObject (DbeObj obj, IPCrequest* req) +{ + writeLong ((long long) obj, req); +} + +void +writeBoolean (bool b, IPCrequest* req) +{ + if (req->getStatus () == CANCELLED_IMMEDIATE) + return; + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_MEDIUM); + OUTS->sendByte (L_BOOLEAN); + OUTS->sendBVal (b); + writeResponseWithHeader (req->getRequestID (), req->getChannelID (), + RESPONSE_TYPE_COMPLETE, RESPONSE_STATUS_SUCCESS, OUTS); +} + +void +writeInt (int i, IPCrequest* req) +{ + if (req->getStatus () == CANCELLED_IMMEDIATE) + return; + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_MEDIUM); + OUTS->sendByte (L_INTEGER); + OUTS->sendIVal (i); + writeResponseWithHeader (req->getRequestID (), req->getChannelID (), RESPONSE_TYPE_COMPLETE, RESPONSE_STATUS_SUCCESS, OUTS); +} + +void +writeChar (char c, IPCrequest* req) +{ + if (req->getStatus () == CANCELLED_IMMEDIATE) + return; + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_MEDIUM); + OUTS->sendByte (L_CHAR); + OUTS->sendCVal (c); + writeResponseWithHeader (req->getRequestID (), req->getChannelID (), RESPONSE_TYPE_COMPLETE, RESPONSE_STATUS_SUCCESS, OUTS); +} + +void +writeLong (long long l, IPCrequest* req) +{ + if (req->getStatus () == CANCELLED_IMMEDIATE) + return; + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_MEDIUM); + OUTS->sendByte (L_LONG); + OUTS->sendLVal (l); + writeResponseWithHeader (req->getRequestID (), req->getChannelID (), RESPONSE_TYPE_COMPLETE, RESPONSE_STATUS_SUCCESS, OUTS); +} + +void +writeDouble (double d, IPCrequest* req) +{ + if (req->getStatus () == CANCELLED_IMMEDIATE) return; + IPCresponse *OUTS = responseBufferPool->getNewResponse (BUFFER_SIZE_MEDIUM); + OUTS->sendByte (L_DOUBLE); + OUTS->sendDVal (d); + writeResponseWithHeader (req->getRequestID (), req->getChannelID (), RESPONSE_TYPE_COMPLETE, RESPONSE_STATUS_SUCCESS, OUTS); +} + +int +setProgress (int percentage, const char *proc_str) +{ + if (cancelNeeded (currentChannelID)) + { + // ExperimentLoadCancelException *e1 = new ExperimentLoadCancelException(); + // throw (e1); + return 1; + } + if (NULL == proc_str) + return 1; + int size = strlen (proc_str) + 100; // 100 bytes for additional data + int bs = BUFFER_SIZE_MEDIUM; + if (size > BUFFER_SIZE_MEDIUM) + { + if (size > BUFFER_SIZE_LARGE) return 1; // This should never happen + bs = BUFFER_SIZE_LARGE; + } + IPCresponse *OUTS = responseBufferPool->getNewResponse (bs); + OUTS->sendByte (L_PROGRESS); + OUTS->sendIVal (percentage); + OUTS->sendSVal (proc_str); + writeResponseWithHeader (currentRequestID, currentChannelID, RESPONSE_TYPE_PROGRESS, RESPONSE_STATUS_SUCCESS, OUTS); + return 0; +} + +void +IPCresponse::print (void) +{ + if (ipc_delay_microsec) + usleep (ipc_delay_microsec); + int stringSize = sb->length (); + writeResponseHeader (requestID, responseType, responseStatus, stringSize); + if (stringSize > 0) + { + char *s = sb->toString (); + hrtime_t start_time = gethrtime (); + int use_write = 1; + if (use_write) + write (1, s, stringSize); // write(1, sb->toString(), stringSize); + else + { + cout << s; + cout.flush (); + } + hrtime_t end_time = gethrtime (); + unsigned long long time_stamp = end_time - start_time; + ipc_response_log (TRACE_LVL_3, "ReqID %x flush time %llu nanosec \n", requestID, time_stamp); + free (s); + } +} + +void +setCancelRequestedCh (int chID) +{ + cancelRequestedChannelID = chID; +} + +void +readRequestHeader () +{ + int marker = readByte (NULL); + if (marker != HEADER_MARKER) + { + fprintf (stderr, "Internal error: received request (%d) without header marker\n", marker); + error_flag = 1; + return; + } + else + ipc_request_trace (TRACE_LVL_1, "RequestHeaderBegin------------------------\n"); + int requestID = readIVal (NULL); + int requestType = readByte (NULL); + int channelID = readIVal (NULL); + int nBytes = readIVal (NULL); + if (requestType == REQUEST_TYPE_HANDSHAKE) + { + // write the ack directly to the wire, not through the response queue + // writeAckFast(requestID); + writeAck (requestID, channelID); + maxSize = 0; + writeHandshake (requestID, channelID); + ipc_request_trace (TRACE_LVL_1, "RQ: HANDSHAKE --- %x ----- %x ---- %x --- %x -RequestHeaderEnd\n", requestID, requestType, channelID, nBytes); + } + else if (requestType == REQUEST_TYPE_CANCEL) + { + writeAck (requestID, channelID); + ipc_request_trace (TRACE_LVL_1, "RQ: CANCEL --- RQ: %x ----- %x --- CH: %x --- %x -RequestHeaderEnd\n", requestID, requestType, channelID, nBytes); + if (channelID == cancellableChannelID) + { + // we have worked on at least one request belonging to this channel + writeResponseGeneric (RESPONSE_STATUS_SUCCESS, requestID, channelID); + setCancelRequestedCh (channelID); + ipc_trace ("CANCELLABLE %x %x\n", channelID, currentChannelID); + if (channelID == currentChannelID) + // request for this channel is currently in progress + ipc_request_trace (TRACE_LVL_1, "IN PROGRESS REQUEST NEEDS CANCELLATION"); + // ssp_post_cond(waitingToFinish); + } + else + { + // FIXME: + // it is possible that a request for this channel is on the requestQ + // or has been submitted to the work group queue but is waiting for a thread to pick it up + writeResponseGeneric (RESPONSE_STATUS_FAILURE, requestID, channelID); + setCancelRequestedCh (channelID); + ipc_request_trace (TRACE_LVL_1, "RETURNING FAILURE TO CANCEL REQUEST channel %d\n", channelID); + } + } + else + { + writeAck (requestID, channelID); + ipc_request_trace (TRACE_LVL_1, "RQ: --- %x ----- %x ---- %x --- %x -RequestHeaderEnd\n", requestID, requestType, channelID, nBytes); + IPCrequest *nreq = new IPCrequest (nBytes, requestID, channelID); + nreq->read (); + ipc_request_trace (TRACE_LVL_1, "RQ: --- %x Read from stream \n", requestID); + if (cancelNeeded (channelID)) + { + ipc_request_trace (TRACE_LVL_1, "CANCELLABLE REQ RECVD %x %x\n", channelID, requestID); + writeResponseGeneric (RESPONSE_STATUS_CANCELLED, requestID, channelID); + delete nreq; + return; + } + DbeQueue *q = new DbeQueue (ipc_doWork, nreq); + ipcThreadPool->put_queue (q); + } +} diff --git a/gprofng/src/ipcio.h b/gprofng/src/ipcio.h new file mode 100644 index 0000000..94a635e --- /dev/null +++ b/gprofng/src/ipcio.h @@ -0,0 +1,176 @@ +/* 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. */ + +/* Defines the external interface between er_ipc and the routines */ + +#ifndef _IPCIO_H +#define _IPCIO_H +#include <pthread.h> +#include "gp-defs.h" +#include "StringBuilder.h" + +class DbeThreadPool; +typedef long long DbeObj; +typedef void *Object; +typedef char *String; + +#define BUFFER_SIZE_SMALL 512 +#define BUFFER_SIZE_MEDIUM 512 +#define BUFFER_SIZE_LARGE 1024*1024 + +#define REQUEST_HAS_NO_BODY 0xFFFFFFFF +#define RESPONSE_STATUS_DEFAULT 0 +#define RESPONSE_STATUS_SUCCESS 1 +#define RESPONSE_STATUS_FAILURE 2 +#define RESPONSE_STATUS_CANCELLED 3 + +#define RESPONSE_TYPE_ACK 0 +#define RESPONSE_TYPE_PROGRESS 1 +#define RESPONSE_TYPE_COMPLETE 2 +#define RESPONSE_TYPE_HANDSHAKE 3 +#define HEADER_MARKER 0xff + +#define REQUEST_TYPE_DEFAULT 0 +#define REQUEST_TYPE_CANCEL 1 +#define REQUEST_TYPE_HANDSHAKE 2 + +#define IPC_PROTOCOL_STR "IPC_PROTOCOL_38" +#define IPC_VERSION_NUMBER 38 + +enum IPCrequestStatus +{ + INITIALIZED = 0, + IN_PROGRESS, + COMPLETED, + CANCELLED_DEFAULT, + CANCELLED_IMMEDIATE +}; + +enum IPCTraceLevel +{ + TRACE_LVL_0 = 0, + TRACE_LVL_1, + TRACE_LVL_2, + TRACE_LVL_3, + TRACE_LVL_4 +}; + +class IPCrequest +{ + char *buf; + int size; + int idx; + int requestID; + int channelID; + IPCrequestStatus status; + bool cancelImmediate; +public: + IPCrequest (int, int, int); + ~IPCrequest (); + IPCrequestStatus getStatus (); + void setStatus (IPCrequestStatus); + void read (); + + int getRequestID () { return requestID; } + int getChannelID () { return channelID; } + bool isCancelImmediate () { return cancelImmediate; } + void setCancelImmediate () { cancelImmediate = true; } + char rgetc () { return buf[idx++]; } +}; + +class IPCresponse +{ +public: + IPCresponse (int sz); + ~IPCresponse (); + + int getRequestID () { return requestID; } + int getChannelID () { return channelID; } + void setRequestID (int r) { requestID = r; } + void setChannelID (int c) { channelID = c; } + void setResponseType (int r) { responseType = r; } + void setResponseStatus (int s) { responseStatus = s; } + int getCurBufSize () { return sb->capacity (); } + void sendByte (int); + void sendIVal (int); + void sendLVal (long long); + void sendDVal (double); + void sendSVal (const char *); + void sendBVal (bool); + void sendCVal (char); + void sendAVal (void*); + void print (void); + void reset (); + IPCresponse *next; + +private: + int requestID; + int channelID; + int responseType; + int responseStatus; + StringBuilder *sb; +}; + +class BufferPool +{ +public: + BufferPool (); + ~BufferPool (); + IPCresponse* getNewResponse (int); + void recycle (IPCresponse *); +private: + pthread_mutex_t p_mutex; + IPCresponse *smallBuf; + IPCresponse *largeBuf; +}; + +// Read from the wire +int readInt (IPCrequest*); +bool readBoolean (IPCrequest*); +long long readLong (IPCrequest*); +DbeObj readObject (IPCrequest*); +Object readArray (IPCrequest*); +String readString (IPCrequest*); +void readRequestHeader (); + +// write to the wire +void writeString (const char *, IPCrequest*); +void writeBoolean (bool, IPCrequest*); +void writeInt (int, IPCrequest*); +void writeChar (char, IPCrequest*); +void writeLong (long long, IPCrequest*); +void writeDouble (double, IPCrequest*); +void writeArray (void *, IPCrequest*); +void writeObject (DbeObj, IPCrequest*); +void writeResponseGeneric (int, int, int); +int setProgress (int, const char *); // Update the progress bar +int ipc_doWork (void *); // The argument is an IPCrequest + +extern int ipc_flags; +extern int ipc_single_threaded_mode; +extern DbeThreadPool *responseThreadPool; +extern DbeThreadPool *ipcThreadPool; +extern int cancelRequestedChannelID; + +void ipc_default_log (const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +void ipc_response_log (IPCTraceLevel, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); +void ipc_request_log (IPCTraceLevel, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + +#endif diff --git a/gprofng/src/machinemodels/generic.ermm b/gprofng/src/machinemodels/generic.ermm new file mode 100644 index 0000000..19fcc8e --- /dev/null +++ b/gprofng/src/machinemodels/generic.ermm @@ -0,0 +1,32 @@ +# generic machinemodel file +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file 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 of the License, 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +mobj_define Memory_page_size "(EA_PAGESIZE ? EA_PAGESIZE : -1)" +mobj_define Memory_page "(((VADDR>255) && EA_PAGESIZE) ? VADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Memory_64B_cacheline "((VADDR>255)?(VADDR>>6<<6):-1)" +mobj_define Memory_address "((VADDR>255)?(VADDR):-1)" + +mobj_define Memory_in_home_lgrp (EA_LGRP==LWP_LGRP_HOME) +mobj_define Memory_lgrp (EA_LGRP) + +mobj_define Physical_page "((PADDR && EA_PAGESIZE) ? PADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Physical_64B_cacheline "(PADDR?(PADDR>>6<<6):-1)" +mobj_define Physical_address "(PADDR?(PADDR):-1)" + +#mobj_define Vpage_4K "(((ea_pagesize==1<<12 || !ea_pagesize) && VADDR>255)?(VADDR>>12<<12):-1)" +#mobj_define Ppage_4K "((ea_pagesize==1<<12 && PADDR)?(PADDR>>12<<12):-1)" diff --git a/gprofng/src/machinemodels/m5.ermm b/gprofng/src/machinemodels/m5.ermm new file mode 100644 index 0000000..83f125d --- /dev/null +++ b/gprofng/src/machinemodels/m5.ermm @@ -0,0 +1,65 @@ +# Machinemodel file for M5 systems +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file 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 of the License, 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +indxobj_define M5_Chip ((CPUID>>3)/6) +indxobj_define M5_Core (CPUID>>3) + +mobj_define Memory_page_size "(EA_PAGESIZE ? EA_PAGESIZE : -1)" +mobj_define Memory_page "(((VADDR>255) && EA_PAGESIZE) ? VADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Memory_64B_cacheline "((VADDR>255)?(VADDR>>6<<6):-1)" +mobj_define Memory_32B_cacheline "((VADDR>255)?(VADDR>>5<<5):-1)" +mobj_define Memory_address "((VADDR>255)?(VADDR):-1)" + +mobj_define Memory_in_home_lgrp (EA_LGRP==LWP_LGRP_HOME) +mobj_define Memory_lgrp (EA_LGRP) + +mobj_define Physical_page "((PADDR && EA_PAGESIZE) ? PADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Physical_64B_cacheline "(PADDR?(PADDR>>6<<6):-1)" +mobj_define Physical_32B_cacheline "(PADDR?(PADDR>>5<<5):-1)" +mobj_define Physical_address "(PADDR?(PADDR):-1)" + + +#mobj_define Vpage_8K "((ea_pagesize==1<<13 && VADDR>255)?(VADDR>>13<<13):-1)" +#mobj_define Vpage_64K "((ea_pagesize==1<<16 && VADDR>255)?(VADDR>>16<<16):-1)" +#mobj_define Vpage_4M "((ea_pagesize==1<<22 && VADDR>255)?(VADDR>>22<<22):-1)" +#mobj_define Vpage_256M "((ea_pagesize==1<<28 && VADDR>255)?(VADDR>>28<<28):-1)" +#mobj_define Vpage_2G "((ea_pagesize==1<<31 && VADDR>255)?(VADDR>>31<<31):-1)" + +#mobj_define Ppage_8K "((ea_pagesize==1<<13 && PADDR)?(PADDR>>13<<13):-1)" +#mobj_define Ppage_64K "((ea_pagesize==1<<16 && PADDR)?(PADDR>>16<<16):-1)" +#mobj_define Ppage_4M "((ea_pagesize==1<<22 && PADDR)?(PADDR>>22<<22):-1)" +#mobj_define Ppage_256M "((ea_pagesize==1<<28 && PADDR)?(PADDR>>28<<28):-1)" +#mobj_define Ppage_2G "((ea_pagesize==1<<31 && PADDR)?(PADDR>>31<<31):-1)" + +# comment out *CacheTag definitions since we don't have use cases to justify their complexity +# comment out other *Cache* definitions since we don't have use cases to justify their complexity +# further, meminfo() tends not to give us physical addresses + +#mobj_define M5_L1ICacheSet "((PHYSPC>>5)&0x7F)" +#mobj_define M5_L1ICacheTag "((PHYSPC>>12)&0x7FFFFFFFF)" +#mobj_define M5_L1DCacheSet "(PADDR?((PADDR>>5)&0x7F):-1)" +#mobj_define M5_L1DCacheTag "(PADDR?((PADDR>>12)&0x7FFFFFFFF):-1)" + +#mobj_define M5_L2ICacheSet "((((PHYSPC&0xFFFFFFF80FFF)|(((PHYSPC>>19)^(PHYSPC>>16)^(PHYSPC>>10)^(PHYSPC>>4)^(PHYSPC>>1)^PHYSPC)&0x7F000))>>5)&0x1FF)" +#mobj_define M5_L2ICacheTag "((((PHYSPC&0xFFFFFFF80FFF)|(((PHYSPC>>19)^(PHYSPC>>16)^(PHYSPC>>10)^(PHYSPC>>4)^(PHYSPC>>1)^PHYSPC)&0x7F000))>>14)&0x1FFFFFFFF)" +#mobj_define M5_L2DCacheSet "(PADDR?((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>5)&0x1FF):-1)" +#mobj_define M5_L2DCacheTag "(PADDR?((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>14)&0x1FFFFFFFF):-1)" + +#mobj_define M5_L3DCacheSet "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>6)&0xFFFF):-1)" +#mobj_define M5_L3DCacheTag "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>22)&0x3FFFFFF):-1)" +#mobj_define M5_L3DBank "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>6)&0x3):-1)" diff --git a/gprofng/src/machinemodels/m6.ermm b/gprofng/src/machinemodels/m6.ermm new file mode 100644 index 0000000..1071e9e --- /dev/null +++ b/gprofng/src/machinemodels/m6.ermm @@ -0,0 +1,65 @@ +# Machinemodel file for M6 systems +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file 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 of the License, 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +indxobj_define M6_Chip ((CPUID>>3)/12) +indxobj_define M6_Core (CPUID>>3) + +mobj_define Memory_page_size "(EA_PAGESIZE ? EA_PAGESIZE : -1)" +mobj_define Memory_page "(((VADDR>255) && EA_PAGESIZE) ? VADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Memory_64B_cacheline "((VADDR>255)?(VADDR>>6<<6):-1)" +mobj_define Memory_32B_cacheline "((VADDR>255)?(VADDR>>5<<5):-1)" +mobj_define Memory_address "((VADDR>255)?(VADDR):-1)" + +mobj_define Memory_in_home_lgrp (EA_LGRP==LWP_LGRP_HOME) +mobj_define Memory_lgrp (EA_LGRP) + +mobj_define Physical_page "((PADDR && EA_PAGESIZE) ? PADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Physical_64B_cacheline "(PADDR?(PADDR>>6<<6):-1)" +mobj_define Physical_32B_cacheline "(PADDR?(PADDR>>5<<5):-1)" +mobj_define Physical_address "(PADDR?(PADDR):-1)" + + +#mobj_define Vpage_8K "((ea_pagesize==1<<13 && VADDR>255)?(VADDR>>13<<13):-1)" +#mobj_define Vpage_64K "((ea_pagesize==1<<16 && VADDR>255)?(VADDR>>16<<16):-1)" +#mobj_define Vpage_4M "((ea_pagesize==1<<22 && VADDR>255)?(VADDR>>22<<22):-1)" +#mobj_define Vpage_256M "((ea_pagesize==1<<28 && VADDR>255)?(VADDR>>28<<28):-1)" +#mobj_define Vpage_2G "((ea_pagesize==1<<31 && VADDR>255)?(VADDR>>31<<31):-1)" + +#mobj_define Ppage_8K "((ea_pagesize==1<<13 && PADDR)?(PADDR>>13<<13):-1)" +#mobj_define Ppage_64K "((ea_pagesize==1<<16 && PADDR)?(PADDR>>16<<16):-1)" +#mobj_define Ppage_4M "((ea_pagesize==1<<22 && PADDR)?(PADDR>>22<<22):-1)" +#mobj_define Ppage_256M "((ea_pagesize==1<<28 && PADDR)?(PADDR>>28<<28):-1)" +#mobj_define Ppage_2G "((ea_pagesize==1<<31 && PADDR)?(PADDR>>31<<31):-1)" + +# comment out *CacheTag definitions since we don't have use cases to justify their complexity +# comment out other *Cache* definitions since we don't have use cases to justify their complexity +# further, meminfo() tends not to give us physical addresses + +#mobj_define M6_L1ICacheSet "((PHYSPC>>5)&0x7F)" +#mobj_define M6_L1ICacheTag "((PHYSPC>>12)&0x7FFFFFFFF)" +#mobj_define M6_L1DCacheSet "(PADDR?((PADDR>>5)&0x7F):-1)" +#mobj_define M6_L1DCacheTag "(PADDR?((PADDR>>12)&0x7FFFFFFFF):-1)" + +#mobj_define M6_L2ICacheSet "((((PHYSPC&0xFFFFFFF80FFF)|(((PHYSPC>>19)^(PHYSPC>>16)^(PHYSPC>>10)^(PHYSPC>>4)^(PHYSPC>>1)^PHYSPC)&0x7F000))>>5)&0x1FF)" +#mobj_define M6_L2ICacheTag "((((PHYSPC&0xFFFFFFF80FFF)|(((PHYSPC>>19)^(PHYSPC>>16)^(PHYSPC>>10)^(PHYSPC>>4)^(PHYSPC>>1)^PHYSPC)&0x7F000))>>14)&0x1FFFFFFFF)" +#mobj_define M6_L2DCacheSet "(PADDR?((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>5)&0x1FF):-1)" +#mobj_define M6_L2DCacheTag "(PADDR?((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>14)&0x1FFFFFFFF):-1)" + +#mobj_define M6_L3DCacheSet "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>6)&0xFFFF):-1)" +#mobj_define M6_L3DCacheTag "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>22)&0x3FFFFFF):-1)" +#mobj_define M6_L3DBank "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>6)&0x3):-1)" diff --git a/gprofng/src/machinemodels/m7.ermm b/gprofng/src/machinemodels/m7.ermm new file mode 100644 index 0000000..29e3bef --- /dev/null +++ b/gprofng/src/machinemodels/m7.ermm @@ -0,0 +1,64 @@ +# Machinemodel file for M7/T7 systems +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file 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 of the License, 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +indxobj_define M7_Chip (CPUID>>8) +indxobj_define M7_Core (CPUID>>3) + +mobj_define Memory_page_size "(EA_PAGESIZE ? EA_PAGESIZE : -1)" +mobj_define Memory_page "(((VADDR>255) && EA_PAGESIZE) ? VADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Memory_64B_cacheline "((VADDR>255)?(VADDR>>6<<6):-1)" +mobj_define Memory_32B_cacheline "((VADDR>255)?(VADDR>>5<<5):-1)" +mobj_define Memory_address "((VADDR>255)?(VADDR):-1)" + +mobj_define Memory_in_home_lgrp (EA_LGRP==LWP_LGRP_HOME) +mobj_define Memory_lgrp (EA_LGRP) + +mobj_define Physical_page "((PADDR && EA_PAGESIZE) ? PADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Physical_64B_cacheline "(PADDR?(PADDR>>6<<6):-1)" +mobj_define Physical_32B_cacheline "(PADDR?(PADDR>>5<<5):-1)" +mobj_define Physical_address "(PADDR?(PADDR):-1)" + + +#mobj_define Vpage_8K "((ea_pagesize==1<<13 && VADDR>255)?(VADDR>>13<<13):-1)" +#mobj_define Vpage_64K "((ea_pagesize==1<<16 && VADDR>255)?(VADDR>>16<<16):-1)" +#mobj_define Vpage_4M "((ea_pagesize==1<<22 && VADDR>255)?(VADDR>>22<<22):-1)" +#mobj_define Vpage_256M "((ea_pagesize==1<<28 && VADDR>255)?(VADDR>>28<<28):-1)" +#mobj_define Vpage_2G "((ea_pagesize==1<<31 && VADDR>255)?(VADDR>>31<<31):-1)" +#mobj_define Vpage_16G "((ea_pagesize==1<<34 && VADDR>255)?(VADDR>>34<<34):-1)" + +#mobj_define Ppage_8K "((ea_pagesize==1<<13 && PADDR)?(PADDR>>13<<13):-1)" +#mobj_define Ppage_64K "((ea_pagesize==1<<16 && PADDR)?(PADDR>>16<<16):-1)" +#mobj_define Ppage_4M "((ea_pagesize==1<<22 && PADDR)?(PADDR>>22<<22):-1)" +#mobj_define Ppage_256M "((ea_pagesize==1<<28 && PADDR)?(PADDR>>28<<28):-1)" +#mobj_define Ppage_2G "((ea_pagesize==1<<31 && PADDR)?(PADDR>>31<<31):-1)" +#mobj_define Ppage_16G "((ea_pagesize==1<<34 && PADDR)?(PADDR>>34<<34):-1)" + +# we dropped the *CacheTag definitions since: +# - they're rarely used +# - it's unclear if they are correct for S4 +# comment out other *Cache* definitions since we don't have use cases to justify their complexity +# further, meminfo() tends not to give us physical addresses + +#mobj_define M7_L1ICacheSet "((PHYSPC>>6)&0x3F)" +#mobj_define M7_L1DCacheSet "(PADDR?((PADDR>>5)&0x7F):-1)" + +#mobj_define M7_L2ICacheSet "((((PHYSPC&0xFFFFFFFFFFF00FFF)|(((PHYSPC>>24)^(PHYSPC>>16)^(PHYSPC>>8)^PHYSPC)&0xFF000))>>6)&0x1FF)" +#mobj_define M7_L2DCacheSet "(PADDR?((((PADDR&0x2000000000000)?PADDR:((PADDR&0xFFFFFFFFFFF00FFF)|(((PADDR>>24)^(PADDR>>16)^(PADDR>>8)^PADDR)&0xFF000)))>>6)&0x01FF):-1)" + +#mobj_define M7_L3DCacheSet "(PADDR?((((PADDR&0x2000000000000)?PADDR:((PADDR&0xFFFFFFFFFFF00FFF)|(((PADDR>>24)^(PADDR>>16)^(PADDR>>8)^PADDR)&0xFF000)))>>6)&0x3FFF):-1)" +#mobj_define M7_L3DBank "(PADDR?((((PADDR&0x2000000000000)?PADDR:((PADDR&0xFFFFFFFFFFF00FFF)|(((PADDR>>24)^(PADDR>>16)^(PADDR>>8)^PADDR)&0xFF000)))>>6)&0x0001):-1)" diff --git a/gprofng/src/machinemodels/t4.ermm b/gprofng/src/machinemodels/t4.ermm new file mode 100644 index 0000000..e27f3a4 --- /dev/null +++ b/gprofng/src/machinemodels/t4.ermm @@ -0,0 +1,67 @@ +# Machinemodel file for T4 systems +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file 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 of the License, 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +indxobj_define T4_Chip (CPUID>>6) +indxobj_define T4_Core (CPUID>>3) + +mobj_define Memory_page_size "(EA_PAGESIZE ? EA_PAGESIZE : -1)" +mobj_define Memory_page "(((VADDR>255) && EA_PAGESIZE) ? VADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Memory_64B_cacheline "((VADDR>255)?(VADDR>>6<<6):-1)" +mobj_define Memory_32B_cacheline "((VADDR>255)?(VADDR>>5<<5):-1)" +mobj_define Memory_address "((VADDR>255)?(VADDR):-1)" + +mobj_define Memory_in_home_lgrp (EA_LGRP==LWP_LGRP_HOME) +mobj_define Memory_lgrp (EA_LGRP) + +mobj_define Physical_page "((PADDR && EA_PAGESIZE) ? PADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Physical_64B_cacheline "(PADDR?(PADDR>>6<<6):-1)" +mobj_define Physical_32B_cacheline "(PADDR?(PADDR>>5<<5):-1)" +mobj_define Physical_address "(PADDR?(PADDR):-1)" + + +#mobj_define Vpage_8K "((ea_pagesize==1<<13 && VADDR>255)?(VADDR>>13<<13):-1)" +#mobj_define Vpage_64K "((ea_pagesize==1<<16 && VADDR>255)?(VADDR>>16<<16):-1)" +#mobj_define Vpage_512K "((ea_pagesize==1<<19 && VADDR>255)?(VADDR>>19<<19):-1)" +#mobj_define Vpage_4M "((ea_pagesize==1<<22 && VADDR>255)?(VADDR>>22<<22):-1)" +#mobj_define Vpage_256M "((ea_pagesize==1<<28 && VADDR>255)?(VADDR>>28<<28):-1)" +#mobj_define Vpage_2G "((ea_pagesize==1<<31 && VADDR>255)?(VADDR>>31<<31):-1)" + +#mobj_define Ppage_8K "((ea_pagesize==1<<13 && PADDR)?(PADDR>>13<<13):-1)" +#mobj_define Ppage_64K "((ea_pagesize==1<<16 && PADDR)?(PADDR>>16<<16):-1)" +#mobj_define Ppage_512K "((ea_pagesize==1<<19 && PADDR)?(PADDR>>19<<19):-1)" +#mobj_define Ppage_4M "((ea_pagesize==1<<22 && PADDR)?(PADDR>>22<<22):-1)" +#mobj_define Ppage_256M "((ea_pagesize==1<<28 && PADDR)?(PADDR>>28<<28):-1)" +#mobj_define Ppage_2G "((ea_pagesize==1<<31 && PADDR)?(PADDR>>31<<31):-1)" + +# comment out *CacheTag definitions since we don't have use cases to justify their complexity +# comment out other *Cache* definitions since we don't have use cases to justify their complexity +# further, meminfo() tends not to give us physical addresses + +#mobj_define T4_L1ICacheSet "((PHYSPC>>5)&0x7F)" +#mobj_define T4_L1ICacheTag "((PHYSPC>>12)&0x7FFFFFFFF)" +#mobj_define T4_L1DCacheSet "(PADDR?((PADDR>>5)&0x7F):-1)" +#mobj_define T4_L1DCacheTag "(PADDR?((PADDR>>12)&0x7FFFFFFFF):-1)" +#mobj_define T4_L2ICacheSet "((((PHYSPC&0xFFFFFFF80FFF)|(((PHYSPC>>19)^(PHYSPC>>16)^(PHYSPC>>10)^(PHYSPC>>4)^(PHYSPC>>1)^PHYSPC)&0x7F000))>>5)&0x1FF)" +#mobj_define T4_L2ICacheTag "((((PHYSPC&0xFFFFFFF80FFF)|(((PHYSPC>>19)^(PHYSPC>>16)^(PHYSPC>>10)^(PHYSPC>>4)^(PHYSPC>>1)^PHYSPC)&0x7F000))>>14)&0x1FFFFFFFF)" +#mobj_define T4_L2DCacheSet "(PADDR?((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>5)&0x1FF):-1)" +#mobj_define T4_L2DCacheTag "(PADDR?((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>14)&0x1FFFFFFFF):-1)" +#mobj_define T4_L3DCacheSet "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>6)&0xFFF):-1)" +#mobj_define T4_L3DCacheTag "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>18)&0x1FFFFFFF):-1)" +#mobj_define T4_L3DBank "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>6)&0x7):-1)" +#mobj_define T4_2_Socket "(PADDR?((PADDR>>33)&0x1):-1)" +#mobj_define T4_4_Socket "(PADDR?((PADDR>>33)&0x3):-1)" diff --git a/gprofng/src/machinemodels/t5.ermm b/gprofng/src/machinemodels/t5.ermm new file mode 100644 index 0000000..6d666a9 --- /dev/null +++ b/gprofng/src/machinemodels/t5.ermm @@ -0,0 +1,65 @@ +# Machinemodel file for T5 systems +# +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# This file 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 of the License, 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; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +indxobj_define T5_Chip (CPUID>>7) +indxobj_define T5_Core (CPUID>>3) + +mobj_define Memory_page_size "(EA_PAGESIZE ? EA_PAGESIZE : -1)" +mobj_define Memory_page "(((VADDR>255) && EA_PAGESIZE) ? VADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Memory_64B_cacheline "((VADDR>255)?(VADDR>>6<<6):-1)" +mobj_define Memory_32B_cacheline "((VADDR>255)?(VADDR>>5<<5):-1)" +mobj_define Memory_address "((VADDR>255)?(VADDR):-1)" + +mobj_define Memory_in_home_lgrp (EA_LGRP==LWP_LGRP_HOME) +mobj_define Memory_lgrp (EA_LGRP) + +mobj_define Physical_page "((PADDR && EA_PAGESIZE) ? PADDR & (~(EA_PAGESIZE-1)) : -1)" +mobj_define Physical_64B_cacheline "(PADDR?(PADDR>>6<<6):-1)" +mobj_define Physical_32B_cacheline "(PADDR?(PADDR>>5<<5):-1)" +mobj_define Physical_address "(PADDR?(PADDR):-1)" + + +#mobj_define Vpage_8K "((ea_pagesize==1<<13 && VADDR>255)?(VADDR>>13<<13):-1)" +#mobj_define Vpage_64K "((ea_pagesize==1<<16 && VADDR>255)?(VADDR>>16<<16):-1)" +#mobj_define Vpage_4M "((ea_pagesize==1<<22 && VADDR>255)?(VADDR>>22<<22):-1)" +#mobj_define Vpage_256M "((ea_pagesize==1<<28 && VADDR>255)?(VADDR>>28<<28):-1)" +#mobj_define Vpage_2G "((ea_pagesize==1<<31 && VADDR>255)?(VADDR>>31<<31):-1)" + +#mobj_define Ppage_8K "((ea_pagesize==1<<13 && PADDR)?(PADDR>>13<<13):-1)" +#mobj_define Ppage_64K "((ea_pagesize==1<<16 && PADDR)?(PADDR>>16<<16):-1)" +#mobj_define Ppage_4M "((ea_pagesize==1<<22 && PADDR)?(PADDR>>22<<22):-1)" +#mobj_define Ppage_256M "((ea_pagesize==1<<28 && PADDR)?(PADDR>>28<<28):-1)" +#mobj_define Ppage_2G "((ea_pagesize==1<<31 && PADDR)?(PADDR>>31<<31):-1)" + +# comment out *CacheTag definitions since we don't have use cases to justify their complexity +# comment out other *Cache* definitions since we don't have use cases to justify their complexity +# further, meminfo() tends not to give us physical addresses + +#mobj_define T5_L1ICacheSet "((PHYSPC>>5)&0x7F)" +#mobj_define T5_L1ICacheTag "((PHYSPC>>12)&0x7FFFFFFFF)" +#mobj_define T5_L1DCacheSet "(PADDR?((PADDR>>5)&0x7F):-1)" +#mobj_define T5_L1DCacheTag "(PADDR?((PADDR>>12)&0x7FFFFFFFF):-1)" + +#mobj_define T5_L2ICacheSet "((((PHYSPC&0xFFFFFFF80FFF)|(((PHYSPC>>19)^(PHYSPC>>16)^(PHYSPC>>10)^(PHYSPC>>4)^(PHYSPC>>1)^PHYSPC)&0x7F000))>>5)&0x1FF)" +#mobj_define T5_L2ICacheTag "((((PHYSPC&0xFFFFFFF80FFF)|(((PHYSPC>>19)^(PHYSPC>>16)^(PHYSPC>>10)^(PHYSPC>>4)^(PHYSPC>>1)^PHYSPC)&0x7F000))>>14)&0x1FFFFFFFF)" +#mobj_define T5_L2DCacheSet "(PADDR?((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>5)&0x1FF):-1)" +#mobj_define T5_L2DCacheTag "(PADDR?((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>14)&0x1FFFFFFFF):-1)" + +#mobj_define T5_L3DCacheSet "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>6)&0x1FFF):-1)" +#mobj_define T5_L3DCacheTag "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>19)&0x1FFFFFFF):-1)" +#mobj_define T5_L3DBank "(PADDR?((((PADDR&0x800000000000)?((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))):((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))&0x7FFFFFFFFF3F)|(((((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000)))>>6)^((PADDR&0x800000000000)?PADDR:((PADDR&0xFFFFFFF80FFF)|(((PADDR>>19)^(PADDR>>16)^(PADDR>>10)^(PADDR>>4)^(PADDR>>1)^PADDR)&0x7F000))))&0xC0)))>>6)&0x7):-1)" diff --git a/gprofng/src/parse.cc b/gprofng/src/parse.cc new file mode 100644 index 0000000..ab22270 --- /dev/null +++ b/gprofng/src/parse.cc @@ -0,0 +1,927 @@ +/* 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 <sys/param.h> +#include <sys/mman.h> + +#include "util.h" +#include "DbeFile.h" +#include "DbeSession.h" +#include "Experiment.h" +#include "Emsg.h" +#include "Function.h" +#include "LoadObject.h" +#include "Module.h" +#include "PRBTree.h" +#include "Sample.h" +#include "Elf.h" + +void +Experiment::mrec_insert (MapRecord *mrec) +{ + int sz = mrecs->size (); + MapRecord *tmp = sz > 0 ? mrecs->fetch (sz - 1) : NULL; + + // The following should work in most cases + if (tmp == NULL || tmp->ts <= mrec->ts) + { + mrecs->append (mrec); + return; + } + + // If it didn't... + int lo = 0; + int hi = sz - 1; + while (lo <= hi) + { + int md = (lo + hi) / 2; + tmp = mrecs->fetch (md); + if (tmp->ts < mrec->ts) + lo = md + 1; + else + hi = md - 1; + } + mrecs->insert (lo, mrec); +} + +int +Experiment::process_arglist_cmd (char *, char *arglist) +{ + uarglist = arglist; + + // find argv[0], and extract its basename + if (strcmp (uarglist, NTXT ("(fork)")) == 0) + return 0; // leaving target name NULL + char *p = uarglist; + char *pp = uarglist; + char *pl; + for (;;) + { + if (*p == '/') + pp = p + 1; + if (*p == ' ' || *p == 0) + { + pl = p; + break; + } + p++; + } + size_t len = pl - pp; + if (len > 0) + utargname = dbe_sprintf (NTXT ("%.*s"), (int) len, pp); + return 0; +} + +int +Experiment::process_desc_start_cmd (char *, hrtime_t ts, char *flavor, + char *nexp, int follow, char *txt) +{ + char *str; + Emsg *m; + + if (follow == 1) + str = dbe_sprintf (GTXT ("Starting %s %ld.%09ld, exp %s.er, \"%s\""), + flavor, (long) (ts / NANOSEC), (long) (ts % NANOSEC), + nexp, txt); + else + str = dbe_sprintf (GTXT ("Starting %s %ld.%09ld, no experiment, \"%s\""), + flavor, (long) (ts / NANOSEC), (long) (ts % NANOSEC), + txt); + m = new Emsg (CMSG_COMMENT, str); + free (str); + runlogq->append (m); + + free (flavor); + free (nexp); + free (txt); + return 0; +} + +int +Experiment::process_desc_started_cmd (char *, hrtime_t ts, char *flavor, + char *nexp, int follow, char *txt) +{ + char *str; + Emsg *m; + + if (follow == 1) + str = dbe_sprintf (GTXT ("Started %s %ld.%09ld, exp %s.er, \"%s\""), + flavor, (long) (ts / NANOSEC), (long) (ts % NANOSEC), + nexp, txt); + else + str = dbe_sprintf (GTXT ("Started %s %ld.%09ld, no experiment, \"%s\""), + flavor, (long) (ts / NANOSEC), (long) (ts % NANOSEC), + txt); + m = new Emsg (CMSG_COMMENT, str); + free (str); + runlogq->append (m); + free (flavor); + free (nexp); + free (txt); + return 0; +} + +LoadObject * +Experiment::get_dynfunc_lo (const char *loName) +{ + LoadObject *lo = loadObjMap->get (loName); + if (lo == NULL) + { + lo = createLoadObject (loName, expIdx);// DYNFUNC_SEGMENT is always unique + lo->dbeFile->filetype |= DbeFile::F_FICTION; + lo->flags |= SEG_FLAG_DYNAMIC; + lo->type = LoadObject::SEG_TEXT; + lo->set_platform (platform, wsize); + append (lo); + } + return lo; +} + +Function * +Experiment::create_dynfunc (Module *mod, char *fname, int64_t vaddr, + int64_t fsize) +{ + Function *f = dbeSession->createFunction (); + f->set_name (fname); + f->flags |= FUNC_FLAG_DYNAMIC; + f->size = fsize; + f->img_offset = vaddr; + f->module = mod; + mod->functions->append (f); + mod->loadobject->functions->append (f); + return f; +} + +static int +func_cmp (const void *a, const void *b) +{ + Function *fp1 = *((Function **) a); + Function *fp2 = *((Function **) b); + uint64_t i1 = fp1->img_offset; + uint64_t i2 = fp2->img_offset; + return i1 < i2 ? -1 : i1 == i2 ? 0 : 1; +} + +int +Experiment::process_fn_load_cmd (Module *mod, char *fname, Vaddr vaddr, + int fsize, hrtime_t ts) +{ + Dprintf (DEBUG_MAPS, + "process_fn_load_cmd:%s (%s) vaddr=0x%llx msize=%lld ts=%lld\n", + STR (mod ? mod->get_name () : NULL), STR (fname), + (unsigned long long) vaddr, (long long) fsize, (long long) ts); + if (mod != NULL) + { + mod->functions->sort (func_cmp); + uint64_t lastVaddr = vaddr; + for (int i = 0, sz = mod->functions->size (); i < sz; i++) + { + Function *f = mod->functions->fetch (i); + if (lastVaddr < f->img_offset) + { + char *fnm = dbe_sprintf (GTXT ("<static>@0x%llx (%s)"), + (unsigned long long) lastVaddr, fname); + create_dynfunc (mod, fnm, lastVaddr, f->img_offset - lastVaddr); + free (fnm); + } + lastVaddr = f->img_offset + f->size; + } + if (lastVaddr < vaddr + fsize) + { + char *fnm = dbe_sprintf (GTXT ("<static>@0x%llx (%s)"), + (unsigned long long) lastVaddr, fname); + create_dynfunc (mod, fnm, lastVaddr, vaddr + fsize - lastVaddr); + free (fnm); + } + mod->functions->sort (func_cmp); + for (int i = 0, sz = mod->functions->size (); i < sz; i++) + { + Function *f = mod->functions->fetch (i); + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::LOAD; + mrec->obj = f; + mrec->base = f->img_offset; + mrec->size = f->size; + mrec->ts = ts; + mrec->foff = 0; + mrec_insert (mrec); + } + return 0; + } + + LoadObject *ds = get_dynfunc_lo (DYNFUNC_SEGMENT); + Function *dfunc = create_dynfunc (ds->noname, fname, vaddr, fsize); + + // check for special functions, USER, IDLE, TRUNC to disable offsets in disassembly + // XXX -- check based on name now + // Optimization: use pre-initialized localized strings + static const char * localized_USER_MODE = NULL; + static const char * localized_IDLE = NULL; + static const char * localized_TRUNCATED_STACK = NULL; + if (localized_USER_MODE == NULL) + { + localized_USER_MODE = GTXT ("<USER_MODE>"); + localized_IDLE = GTXT ("<IDLE>"); + localized_TRUNCATED_STACK = GTXT ("<TRUNCATED_STACK>"); + } + if (strcmp (fname, localized_USER_MODE) == 0 + || strcmp (fname, localized_IDLE) == 0 + || strcmp (fname, localized_TRUNCATED_STACK) == 0) + dfunc->flags |= FUNC_FLAG_NO_OFFSET; + + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::LOAD; + mrec->obj = dfunc; + mrec->base = vaddr; + mrec->size = fsize; + mrec->ts = ts; + mrec->foff = 0; + mrec_insert (mrec); + return 0; +} + +int +Experiment::process_fn_unload_cmd (char *, Vaddr vaddr, hrtime_t ts) +{ + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::UNLOAD; + mrec->base = vaddr; + mrec->ts = ts; + mrec_insert (mrec); + return 0; +} + +void +Experiment::register_metric (Metric::Type type) +{ + BaseMetric *mtr = dbeSession->register_metric (type); + metrics->append (mtr); +} + +void +Experiment::register_metric (Hwcentry *ctr, const char* aux, const char* uname) +{ + BaseMetric *mtr = dbeSession->register_metric (ctr, aux, uname); + metrics->append (mtr); + if (mtr->get_dependent_bm ()) + metrics->append (mtr->get_dependent_bm ()); +} + +int +Experiment::process_hwcounter_cmd (char *, int cpuver, char *counter, + char * int_name, int interval, int tag, + int i_tpc, char *modstr) +{ + char *str; + Emsg *m; + Hwcentry *ctr; + ABST_type tpc = (ABST_type) i_tpc; + + // Use previously ignored tag to associate counter packets. + if (tag < 0 || tag >= MAX_HWCOUNT) + { + // invalid tag specified, warn user + str = dbe_sprintf (GTXT ("*** Error: HW counter tag %d out of range [%d - %d]; ignored"), + tag, 0, MAX_HWCOUNT - 1); + m = new Emsg (CMSG_ERROR, str); + free (str); + errorq->append (m); + free (counter); + return 0; + } + if (coll_params.hw_aux_name[tag]) + { + // duplicate tag used, warn user + str = dbe_sprintf (GTXT ("*** Error: Duplicate HW counter tag %d specified; ignored"), + tag); + m = new Emsg (CMSG_ERROR, str); + free (str); + errorq->append (m); + free (counter); + return 0; + } + hw_cpuver = cpuver; + + // map it to a machinemodel string + if (hw_cpuver != CPUVER_UNDEFINED) + { + free (machinemodel); + if (hw_cpuver == 1104) + machinemodel = dbe_strdup (NTXT ("t4")); + else if (hw_cpuver == 1110) + machinemodel = dbe_strdup (NTXT ("t5")); + else if (hw_cpuver == 1204) + machinemodel = dbe_strdup (NTXT ("m4")); + else if (hw_cpuver == 1210) + machinemodel = dbe_strdup (NTXT ("m5")); + else if (hw_cpuver == 1220) + machinemodel = dbe_strdup (NTXT ("m6")); + else if (hw_cpuver == 1230) + machinemodel = dbe_strdup (NTXT ("m7")); + else + machinemodel = dbe_strdup (NTXT ("generic")); + } + + // Find the entry in the machine table, and dup it + ctr = new Hwcentry; + dbeSession->append (ctr); + hwc_post_lookup (ctr, counter, int_name, cpuver); + ctr->sort_order = tag; + ctr->memop = tpc; + + // Check if HWC name is to be modified + if (modstr != NULL) + { + char *s = ctr->name; + ctr->name = dbe_sprintf (NTXT ("%s%s"), modstr, s); + s = ctr->int_name; + ctr->int_name = dbe_sprintf (NTXT ("%s%s"), modstr, s); + s = ctr->metric; + if (s) + ctr->metric = dbe_sprintf (NTXT ("%s%s"), modstr, s); + } + + char * cname = dbe_strdup (ctr->name); + char * uname = dbe_strdup (hwc_i18n_metric (ctr)); + coll_params.hw_aux_name[tag] = cname; + coll_params.hw_username[tag] = uname; + coll_params.hw_interval[tag] = interval; + coll_params.hw_tpc[tag] = tpc; + coll_params.hw_cpu_ver[tag] = cpuver; + + // set hw_mode and xhw_mode? + coll_params.hw_mode = 1; + if (ABST_MEMSPACE_ENABLED (tpc)) + { + // yes, dataspace data available + coll_params.xhw_mode = 1; + + // set dataspace available + dataspaceavail = true; + } + register_metric (ctr, cname, uname); + free (counter); + return 0; +} + +// TBR:? + +int +Experiment::process_hwsimctr_cmd (char *, int cpuver, char *nm, char *int_name, + char *metric, int reg, + int interval, int timecvt, int i_tpc, int tag) +{ + char *str; + Emsg *m; + Hwcentry *ctr; + ABST_type tpc = (ABST_type) i_tpc; + + // Use previously ignored tag to associate counter packets. + if (tag < 0 || tag >= MAX_HWCOUNT) + { + // invalid tag specified, warn user + str = dbe_sprintf (GTXT ("*** Error: HW counter tag %d out of range [%d - %d]; ignored"), + tag, 0, MAX_HWCOUNT - 1); + m = new Emsg (CMSG_ERROR, str); + free (str); + errorq->append (m); + + free (nm); + free (int_name); + free (metric); + return 0; + } + if (coll_params.hw_aux_name[tag]) + { + // duplicate tag used, warn user + str = dbe_sprintf (GTXT ("*** Error: Duplicate HW counter tag %d specified; ignored"), + tag); + m = new Emsg (CMSG_ERROR, str); + free (str); + errorq->append (m); + free (nm); + free (int_name); + free (metric); + return 0; + } + hw_cpuver = cpuver; + ctr = new Hwcentry; + { + static Hwcentry empty; + *ctr = empty; + } + ctr->name = nm; + ctr->int_name = int_name; + ctr->metric = metric; + ctr->reg_num = reg; + ctr->val = interval; + ctr->timecvt = timecvt; + ctr->memop = tpc; + ctr->sort_order = tag; + + char *cname = dbe_strdup (ctr->name); + char *uname = dbe_strdup (hwc_i18n_metric (ctr)); + + coll_params.hw_aux_name[tag] = cname; + coll_params.hw_username[tag] = uname; + coll_params.hw_interval[tag] = interval; + coll_params.hw_tpc[tag] = tpc; + coll_params.hw_cpu_ver[tag] = cpuver; + + // set hw_mode and xhw_mode? + coll_params.hw_mode = 1; + if (ABST_MEMSPACE_ENABLED (tpc)) + { + coll_params.xhw_mode = 1; + // set dataspace available + if (getenv ("ANALYZER_DATASPACE_COUNT") != 0) + dataspaceavail = true; + } + + register_metric (ctr, cname, uname); + return 0; +} + +int +Experiment::process_jcm_load_cmd (char *, Vaddr mid, Vaddr vaddr, + int msize, hrtime_t ts) +{ + if (jmaps == NULL) + return 1; + + JMethod *jfunc = (JMethod*) jmaps->locate_exact_match (mid, ts); + if (jfunc == NULL || jfunc->get_type () != Histable::FUNCTION) + return 1; + + LoadObject *ds = get_dynfunc_lo (JAVA_COMPILED_METHODS); + Module *jmodule = jfunc->module; + Module *dmodule = ds->noname; + if (jmodule) + { + dmodule = dbeSession->createModule (ds, jmodule->get_name ()); + dmodule->lang_code = Sp_lang_java; + dmodule->set_file_name (dbe_strdup (jmodule->file_name)); + } + + JMethod *dfunc = dbeSession->createJMethod (); + dfunc->flags |= FUNC_FLAG_DYNAMIC; + dfunc->size = msize; + dfunc->module = dmodule; + dfunc->usrfunc = jfunc; + dfunc->set_addr (vaddr); + dfunc->set_mid (mid); + dfunc->set_signature (jfunc->get_signature ()); + dfunc->set_name (jfunc->get_mangled_name ()); + ds->functions->append (dfunc); + dmodule->functions->append (dfunc); + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::LOAD; + mrec->obj = dfunc; + mrec->base = vaddr; + mrec->size = msize; + mrec->ts = ts; + mrec->foff = 0; + mrec_insert (mrec); + return 0; +} + +int +Experiment::process_jcm_unload_cmd (char *, Vaddr /*mid*/, hrtime_t /*ts*/) +{ + if (jmaps == NULL) + return 1; + + // We are ignoring this record because of the flaw in + // JVMPI desing that doesn't distinguish between two or more + // compiled instances of a method when an unload event is + // generated: + // JVMPI_COMPILED_METHOD_LOAD( mid, addr1, ... ) + // JVMPI_COMPILED_METHOD_LOAD( mid, addr2, ... ) + // JVMPI_COMPILED_METHOD_UNLOAD( mid ) -- which one? + // We rely on the ability of the PRBTree algorithms to + // perform mapping appropriately based on timestamps. + return 0; +} + +int +Experiment::process_jthr_end_cmd (char *, uint64_t tid64, Vaddr jthr, + Vaddr jenv, hrtime_t ts) +{ + int lt = 0; + int rt = jthreads_idx->size () - 1; + uint32_t ttid = mapTagValue (PROP_THRID, tid64); + while (lt <= rt) + { + int md = (lt + rt) / 2; + JThread *jthread = jthreads_idx->fetch (md); + if (jthread->tid < ttid) + lt = md + 1; + else if (jthread->tid > ttid) + rt = md - 1; + else + { + for (; jthread; jthread = jthread->next) + { + if (jthread->jenv == jenv) + { + jthread->end = ts; + return 0; + } + } + return 0; + } + } + JThread *jthread = new JThread; + jthread->tid = mapTagValue (PROP_THRID, tid64); + jthread->jthr = jthr; + jthread->jenv = jenv; + jthread->jthr_id = jthreads->size (); + jthread->start = ZERO_TIME; + jthread->end = ts; + jthread->next = NULL; + jthreads->append (jthread); + if (lt == jthreads_idx->size ()) + jthreads_idx->append (jthread); + else + jthreads_idx->insert (lt, jthread); + return 0; +} + +int +Experiment::process_jthr_start_cmd (char *, char *thread_name, char *group_name, + char *parent_name, uint64_t tid64, + Vaddr jthr, Vaddr jenv, hrtime_t ts) +{ + JThread *jthread = new JThread; + jthread->name = thread_name; + jthread->group_name = group_name; + jthread->parent_name = parent_name; + jthread->tid = mapTagValue (PROP_THRID, tid64); + jthread->jthr = jthr; + jthread->jenv = jenv; + jthread->jthr_id = jthreads->size (); + jthread->start = ts; + jthread->end = MAX_TIME; + jthread->next = NULL; + + jthreads->append (jthread); + + int lt = 0; + int rt = jthreads_idx->size () - 1; + while (lt <= rt) + { + int md = (lt + rt) / 2; + JThread *jtmp = jthreads_idx->fetch (md); + if (jtmp->tid < jthread->tid) + lt = md + 1; + else if (jtmp->tid > jthread->tid) + rt = md - 1; + else + { + jthread->next = jtmp; + jthreads_idx->store (md, jthread); + return 0; + } + } + if (lt == jthreads_idx->size ()) + jthreads_idx->append (jthread); + else + jthreads_idx->insert (lt, jthread); + return 0; +} + +int +Experiment::process_gc_end_cmd ( + hrtime_t ts) +{ + if (gcevents->size () == 0) + { + GCEvent *gcevent = new GCEvent; + gcevent->start = ZERO_TIME; + gcevent->end = ts; + gcevent->id = gcevents->size () + 1; + gcevents->append (gcevent); + return 0; + } + GCEvent *gcevent = gcevents->fetch (gcevents->size () - 1); + if (gcevent->end == MAX_TIME) + gcevent->end = ts; + else + // Weird: gc_end followed by another gc_end + gcevent->end = ts; // extend the previous event + return 0; +} + +int +Experiment::process_gc_start_cmd ( + hrtime_t ts) +{ + if (gcevents->size () != 0) + { + GCEvent *gcevent = gcevents->fetch (gcevents->size () - 1); + // Weird: gc_start followed by another gc_start + if (gcevent->end == MAX_TIME) + return 0; // ignore nested gc_starts + } + GCEvent *gcevent = new GCEvent; + gcevent->start = ts; + gcevent->end = MAX_TIME; + gcevent->id = gcevents->size () + 1; + gcevents->append (gcevent); + return 0; +} + +int +Experiment::process_sample_cmd (char */*cmd*/, hrtime_t /*log_xml_time*/, + int sample_number, char *label) +{ + // sample 0 is not a sample but the starting point + if (sample_number == 0) + { + first_sample_label = label; + return 0; + } + Sample *prev_sample = samples->size () > 0 ? + samples->fetch (samples->size () - 1) : NULL; + char *start_lable = prev_sample ? + prev_sample->end_label : first_sample_label; + Sample *sample = new Sample (sample_number); + sample->start_label = dbe_strdup (start_lable); + sample->end_label = label; + samples->append (sample); + return 0; +} + +int +Experiment::process_sample_sig_cmd (char *, int sig) +{ + char *str; + Emsg *m; + str = dbe_sprintf (GTXT ("Sample signal %d"), sig); + m = new Emsg (CMSG_COMMENT, str); + free (str); + runlogq->append (m); + return 0; +} + +int +Experiment::process_seg_map_cmd (char */*cmd*/, hrtime_t ts, Vaddr vaddr, + int mapsize, int /*pagesize*/, int64_t offset, + int64_t modeflags, int64_t chk, char *nm) +{ + if (nm == NULL || + strncmp (nm + 1, SP_MAP_UNRESOLVABLE, strlen (SP_MAP_UNRESOLVABLE)) == 0) + return 0; + + LoadObject *lo = loadObjMap->get (nm); + if (lo == NULL) + { + if (chk == 0) + { + char *archName = checkFileInArchive (nm, false); + if (archName) + { + Elf *elf = new Elf (archName); + if (elf->status == Elf::ELF_ERR_NONE) + { + chk = elf->elf_checksum (); + } + free (archName); + delete elf; + } + } + lo = dbeSession->find_lobj_by_name (nm, chk); + if (lo == NULL) + { + // Skip non-text segments + if (modeflags != (PROT_READ | PROT_EXEC)) + return 0; + // A new segment + lo = createLoadObject (nm, chk); + if (strstr (nm, NTXT ("libjvm.so"))) + { + lo->flags |= SEG_FLAG_JVM; + // Make sure <JVM-System> is created + (void) dbeSession->get_jvm_Function (); + } + else if (strstr (nm, NTXT ("libmtsk.so"))) + { + lo->flags |= SEG_FLAG_OMP; + // Make sure all pseudo functions are created + for (int i = 0; i < OMP_LAST_STATE; i++) + (void) dbeSession->get_OMP_Function (i); + } + else if (dbe_strcmp (utargname, get_basename (nm)) == 0) + { + lo->flags |= SEG_FLAG_EXE; + (void) dbeSession->comp_lobjs->get ((char *) COMP_EXE_NAME, lo); + } + lo->checksum = chk; + // This is the default segment type + lo->type = LoadObject::SEG_TEXT; + lo->flags = lo->flags | SEG_FLAG_REORDER; + lo->set_platform (platform, wsize); + } + if (lo->dbeFile->get_location (false) == NULL) + { + char *archName = checkFileInArchive (nm, false); + if (archName) + { + lo->dbeFile->set_location (archName); + lo->dbeFile->inArchive = true; + lo->dbeFile->check_access (archName); // init 'sbuf' + lo->dbeFile->sbuf.st_mtime = 0; // Don't check timestamps + free (archName); + } + else + { + archName = checkFileInArchive (nm, true); + if (archName) + { + lo->set_archname (archName); + lo->need_swap_endian = need_swap_endian; + } + } + if (!dbeSession->archive_mode) + lo->sync_read_stabs (); + } + append (lo); + } + if (lo->size == 0) + lo->size = mapsize; + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::LOAD; + mrec->obj = lo; + mrec->base = vaddr; + mrec->size = mapsize; + mrec->ts = ts; + mrec->foff = offset; + mrec_insert (mrec); + return 0; +} + +int +Experiment::process_seg_unmap_cmd (char */*cmd*/, hrtime_t ts, Vaddr vaddr) +{ + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::UNLOAD; + mrec->base = vaddr; + mrec->ts = ts; + mrec_insert (mrec); + return 0; +} + +int +Experiment::process_Linux_kernel_cmd (hrtime_t ts) +{ + LoadObject *lo = createLoadObject ("LinuxKernel"); + lo->flags |= SEG_FLAG_EXE; + lo->type = LoadObject::SEG_TEXT; + lo->set_platform (platform, wsize); + append (lo); + long long unsigned lo_min = (long long unsigned) (-1); + long long unsigned lo_max = 0; + Module *mod = dbeSession->createModule (lo, "LinuxKernel"); + /* + * XXX need to review mod initialization + * A specific issue is mod->file_name. Options include: + * *) NULL + * This leads to seg faults in, e.g., Timeline view. + * *) "/lib/modules/$(uname -r)/kernel/kernel/ctf/ctf.ko" + * This leads to garbage in the Source view. + * *) "/boot/vmlinuz-$(uname -r)" + * This cannot be parsed for DWARF and is sometimes not found, + * but the Analyzer seems to handle such problems. + * *) "LinuxKernel" + * This is not a proper file name, + * but again Analyzer handles the case of not finding the file or not reading DWARF from it. + */ + mod->set_file_name (dbe_strdup ("LinuxKernel")); + char last_mod_name[256]; + last_mod_name[0] = '\0'; + size_t line_n = 0; + char *line = NULL; + char kallmodsyms_copy[MAXPATHLEN]; + snprintf (kallmodsyms_copy, sizeof (kallmodsyms_copy), "%s/kallmodsyms", + expt_name); + FILE *fd = fopen (kallmodsyms_copy, "r"); + if (fd == NULL) + { + char *s = dbe_sprintf (GTXT ("*** Error: Cannot find kernel module symbols file %s; ignored"), + kallmodsyms_copy); + Emsg *m = new Emsg (CMSG_ERROR, s); + free (s); + errorq->append (m); + lo_min = 0; + } + else + { + while (getline (&line, &line_n, fd) > 0) + { + long long unsigned sym_addr; + long long unsigned sym_size; + char sym_type; + int sym_text; + char sym_name[256]; + char mod_name[256] = "vmlinux]"; /* note trailing ] */ + sscanf (line, "%llx %llx %c %s [%s", &sym_addr, &sym_size, &sym_type, + sym_name, mod_name); + if (line[0] == '\n' || line[0] == 0) + continue; + sym_text = (sym_type == 't' || sym_type == 'T'); + mod_name[strlen (mod_name) - 1] = '\0'; /* chop trailing ] */ + if (strcmp (mod_name, "ctf") == 0) + strcpy (mod_name, "shared_ctf"); + + char *mod_name_ptr; + int skip; +#define strstarts(var, x) (strncmp(var, x, strlen (x)) == 0) + if (strcmp (sym_name, "__per_cpu_start") == 0 + || strcmp (sym_name, "__per_cpu_end") == 0 + || strstarts (sym_name, "__crc_") + || strstarts (sym_name, "__ksymtab_") + || strstarts (sym_name, "__kcrctab_") + || strstarts (sym_name, "__kstrtab_") + || strstarts (sym_name, "__param_") + || strstarts (sym_name, "__syscall_meta__") + || strstarts (sym_name, "__p_syscall_meta__") + || strstarts (sym_name, "__event_") + || strstarts (sym_name, "event_") + || strstarts (sym_name, "ftrace_event_") + || strstarts (sym_name, "types__") + || strstarts (sym_name, "args__") + || strstarts (sym_name, "__tracepoint_") + || strstarts (sym_name, "__tpstrtab_") + || strstarts (sym_name, "__tpstrtab__") + || strstarts (sym_name, "__initcall_") + || strstarts (sym_name, "__setup_") + || strstarts (sym_name, "__pci_fixup_") + || strstarts (sym_name, "__dta_") + || strstarts (sym_name, "__dtrace_probe_") + || (strstr (sym_name, ".") != NULL + && strstr (sym_name, ".clone.") == NULL)) + { + mod_name_ptr = last_mod_name; + skip = 1; + } + else + { + mod_name_ptr = mod_name; + skip = 0; + } +#undef strstarts + + if (sym_text && skip == 0) + { + char fname[128]; + snprintf (fname, sizeof (fname), "%s`%s", mod_name_ptr, sym_name); + Function *func = dbeSession->createFunction (); + func->set_name (fname); + // func->flags |= FUNC_FLAG_???; // XXX + func->size = sym_size; + func->img_offset = sym_addr; + func->module = mod; + lo->functions->append (func); + mod->functions->append (func); + if (lo_min > sym_addr) + lo_min = sym_addr; + if (lo_max < sym_addr + sym_size) + lo_max = sym_addr + sym_size; + } + sprintf (last_mod_name, mod_name_ptr); + } + fclose (fd); + } + free (line); + lo->size = lo_max; + lo->functions->sort (func_cmp); + mod->functions->sort (func_cmp); + + MapRecord *mrec = new MapRecord; + mrec->kind = MapRecord::LOAD; + mrec->obj = lo; + mrec->base = lo_min; + mrec->size = lo_max - lo_min; + mrec->ts = ts; + mrec->foff = lo_min; + mrec_insert (mrec); + return 0; +} diff --git a/gprofng/src/stab.h b/gprofng/src/stab.h new file mode 100644 index 0000000..4610105 --- /dev/null +++ b/gprofng/src/stab.h @@ -0,0 +1,205 @@ +/* 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. */ + +/* + * This file gives definitions supplementing <a.out.h> + * for debugging symbol table entries. + * These entries must have one of the N_STAB bits on, + * and are subject to relocation according to the masks in <a.out.h> + * on 4.x (stabs not relocated on SVR4). + */ + +#ifndef _STAB_H +#define _STAB_H + +/* this file also contains fragments of a.out.h relevant to + * support of stab processing within ELF files + * (when a.out.h is not available) + */ +struct stab +{ + unsigned n_strx; /* index into file string table */ + unsigned char n_type; /* type flag (N_TEXT,..) */ + char n_other; /* used by N_SLINE stab */ + short n_desc; /* see stabs documentation */ + unsigned n_value; /* value of symbol (or sdb offset) */ +}; + +/* patchtypes for N_PATCH stab (n_desc field) */ +#define P_BITFIELD 0x1 +#define P_SPILL 0x2 +#define P_SCOPY 0x3 + +/* markers for N_CODETAG stab (n_other field) */ +#define CODETAG_BITFIELD 0x1 /* load/store of a bit field */ +#define CODETAG_SPILL 0x2 /* spill of registers */ +#define CODETAG_SCOPY 0x3 /* structure copy load/store */ +#define CODETAG_FSTART 0x4 /* points to first inst of new frame (0==leaf)*/ +#define CODETAG_END_CTORS 0x5 /* end of calls to super-class constructors */ +/* UNUSED 0x6 DW_ATCF_SUN_branch_target in dwarf, not used in stabs */ +#define CODETAG_STACK_PROBE 0x7 /* marks insns which probe the stack memory */ + +/* + * Simple values for n_type. + */ +#define N_UNDF 0x0 /* undefined */ +#define N_ABS 0x2 /* absolute */ +#define N_TEXT 0x4 /* text */ +#define N_DATA 0x6 /* data */ +#define N_BSS 0x8 /* bss */ +#define N_COMM 0x12 /* common (internal to ld) */ +#define N_FN 0x1f /* file name symbol */ +#define N_EXT 01 /* external bit, or'ed in */ +#define N_TYPE 0x1e /* mask for all the type bits */ + +/* + * maximum length of stab string before using continuation stab. + * (this is just a suggested limit), assembler has no limit. + */ +#define MAX_STAB_STR_LEN 250 + +/* + * for symbolic debuggers: + */ +#define N_GSYM 0x20 /* global symbol: name,,0,type,0 */ +#define N_FNAME 0x22 /* procedure name (f77 kludge): name,,0 */ +#define N_FUN 0x24 /* procedure: name,,0,linenumber,0 */ +#define N_OUTL 0x25 /* outlined func: name,,0,linenumber,0 */ +#define N_STSYM 0x26 /* static symbol: name,,0,type,0 or section relative */ +#define N_TSTSYM 0x27 /* thread static symbol: Ttdata.data */ +#define N_LCSYM 0x28 /* .lcomm symbol: name,,0,type,0 or section relative */ +#define N_TLCSYM 0x29 /* thread local symbol: Ttbss.bss */ +#define N_MAIN 0x2a /* name of main routine : name,,0,0,0 */ +#define N_ROSYM 0x2c /* ro_data: name,,0,type,0 or section relative */ +#define N_FLSYM 0x2e /* fragmented data: name,,0,type,0 */ +#define N_TFLSYM 0x2f /* thread fragmented data: name,,0,type,0 */ +#define N_PC 0x30 /* global pascal symbol: name,,0,subtype,line */ +#define N_CMDLINE 0x34 /* command line info */ +#define N_OBJ 0x38 /* object file path or name */ +#define N_OPT 0x3c /* compiler options */ +#define N_RSYM 0x40 /* register sym: name,,0,type,register */ +#define N_SLINE 0x44 /* src line: 0,,0,linenumber,function relative */ +#define N_XLINE 0x45 /* h.o. src line: 0,,0,linenumber>>16,0 */ +#define N_ILDPAD 0x4c /* now used as ild pad stab value=strtab delta + * was designed for "function start.end" */ +#define N_SSYM 0x60 /* structure elt: name,,0,type,struct_offset */ +#define N_ENDM 0x62 /* last stab emitted for object module */ +#define N_SO 0x64 /* source file name: name,,0,0,0 */ +#define N_MOD 0x66 /* f90 module: name,,0,0,0 */ +#define N_EMOD 0x68 /* end of f90 module: name,,0,0,0 */ +#define N_READ_MOD 0x6a /* use of f90 module: name;locallist,,0,0,0 */ +#define N_ALIAS 0x6c /* alias name: name,,0,0,0 */ +#define N_LSYM 0x80 /* local sym: name,,0,type,offset */ +#define N_BINCL 0x82 /* header file: name,,0,0,0 */ +#define N_SOL 0x84 /* #included file name: name,,0,0,0 */ +#define N_PSYM 0xa0 /* parameter: name,,0,type,offset */ +#define N_EINCL 0xa2 /* end of include file */ +#define N_ENTRY 0xa4 /* alternate entry: name,linenumber,0 */ +#define N_SINCL 0xa6 /* shared include file */ +#define N_LBRAC 0xc0 /* left bracket: 0,,0,nesting level,function relative */ +#define N_EXCL 0xc2 /* excluded include file */ +#define N_USING 0xc4 /* C++ using command */ +#define N_ISYM 0xc6 /* position independent type symbol, internal */ +#define N_ESYM 0xc8 /* position independent type symbol, external */ +#define N_PATCH 0xd0 /* Instruction to be ignored by run-time checking. */ +#define N_CONSTRUCT 0xd2 /* C++ constructor call. */ +#define N_DESTRUCT 0xd4 /* C++ destructor call. */ +#define N_CODETAG 0xd8 /* Generic code tag */ +#define N_FUN_CHILD 0xd9 /* Identifies a child function */ +#define N_RBRAC 0xe0 /* right bracket: 0,,0,nesting level,function relative */ +#define N_BCOMM 0xe2 /* begin common: name,, */ +#define N_TCOMM 0xe3 /* begin task common: name,, */ +#define N_ECOMM 0xe4 /* end task_common/common: name,, */ +#define N_XCOMM 0xe6 /* excluded common block */ +#define N_ECOML 0xe8 /* end common (local name): ,,address */ +#define N_WITH 0xea /* pascal with statement: type,,0,0,offset */ +#define N_LENG 0xfe /* second stab entry with length information */ + +/* + * for analyzer (cache profile feedback support) + */ +#define N_CPROF 0xf0 /* annotation for cache profile feedback */ + +/* + * n_descr values used in N_CPROF stabs. The n_descr field of + * an N_CPROF stab identifies the type of table whose location + * is defined by the N_CPROF stab. + */ +typedef enum n_cprof_instr_type_t +{ + N_CPROF_INSTR_TYPE_LOAD = 0, /* profiled load ops */ + N_CPROF_INSTR_TYPE_STORE, /* profiled store ops */ + N_CPROF_INSTR_TYPE_PREFETCH, /* profiled prefetch ops */ + N_CPROF_INSTR_TYPE_BRTARGET, /* branch target locations */ + N_CPROF_INSTR_TYPE_NTYPES /* number of types */ +} n_cprof_instr_type_t; + +/* + * for code browser only + */ +#define N_BROWS 0x48 /* path to associated .cb file */ + +/* + * for functions -- n_other bits for N_FUN stab + */ +#define N_FUN_PURE (1 << 0) +#define N_FUN_ELEMENTAL (1 << 1) +#define N_FUN_RECURSIVE (1 << 2) +#define N_FUN_AMD64_PARMDUMP (1 << 3) + +/* + * for variables -- n_other bits for N_LSYM, N_GSYM, N_LCSYM, N_STSYM, ... + */ +#define N_SYM_OMP_TLS (1 << 3) + +/* + * Optional language designations for N_SO (n_desc field) + */ +#define N_SO_AS 1 /* Assembler */ +#define N_SO_C 2 /* C */ +#define N_SO_ANSI_C 3 /* ANSI C */ +#define N_SO_CC 4 /* C++ */ +#define N_SO_FORTRAN 5 /* Fortran 77 */ +#define N_SO_FORTRAN77 5 /* Fortran 77 */ +#define N_SO_PASCAL 6 /* Pascal */ +#define N_SO_FORTRAN90 7 /* Fortran 90 */ +#define N_SO_JAVA 8 /* Java */ +#define N_SO_C99 9 /* C99 */ + +/* + * Floating point type values (encoded in "R" type specification string) + */ +#define NF_NONE 0 /* Undefined type */ +#define NF_SINGLE 1 /* Float IEEE 32 bit floating point */ +#define NF_DOUBLE 2 /* Double IEEE 64 bit floating point */ +#define NF_COMPLEX 3 /* Complex (2 32bit floats) */ +#define NF_COMPLEX16 4 /* Complex (2 64bit doubles) */ +#define NF_COMPLEX32 5 /* Complex (2 128bit long doubles) */ +#define NF_LDOUBLE 6 /* Long double 128 bit floating point */ +#define NF_INTERARITH 7 /* Interval (2 32bit floats) */ +#define NF_DINTERARITH 8 /* Interval (2 64bit doubles) */ +#define NF_QINTERARITH 9 /* Interval (2 128bit long doubles) */ +#define NF_IMAGINARY 10 /* Imaginary (1 32bit floats) */ +#define NF_DIMAGINARY 11 /* Imaginary (1 64bit doubles) */ +#define NF_QIMAGINARY 12 /* Imaginary (1 128bit long doubles) */ + +#endif + + diff --git a/gprofng/src/util.cc b/gprofng/src/util.cc new file mode 100644 index 0000000..b93deaf --- /dev/null +++ b/gprofng/src/util.cc @@ -0,0 +1,1582 @@ +/* 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 <sys/param.h> +#include <stdarg.h> +#include <unistd.h> +#include <dirent.h> // readdir() +#include <sys/param.h> // MAXPATHLEN +#include <pthread.h> // mutex +#include <libgen.h> // dirname +#include <sys/types.h> // open +#include <sys/stat.h> // open +#include <errno.h> // errno +#include <fcntl.h> // open + +#include "util.h" +#include "dbe_structs.h" +#include "StringBuilder.h" +#include "StringMap.h" // For directory names +#include "Application.h" // Only for get_prog_name +#include "vec.h" + +void +tsadd (timestruc_t *result, timestruc_t *time) +{ + // This routine will add "time" to "result". + result->tv_sec += time->tv_sec; + result->tv_nsec += time->tv_nsec; + if (result->tv_nsec >= NANOSEC) + { + result->tv_nsec -= NANOSEC; + result->tv_sec++; + } +} + +void +tssub (timestruc_t *result, timestruc_t *time1, timestruc_t *time2) +{ + // This routine will store "time1" - "time2" in "result". + + if (time1->tv_nsec >= time2->tv_nsec) + { + result->tv_nsec = time1->tv_nsec - time2->tv_nsec; + if (time1->tv_sec >= time2->tv_sec) + result->tv_sec = time1->tv_sec - time2->tv_sec; + else + { + result->tv_sec = -1; + result->tv_nsec = 0; + } + } + else + { + result->tv_nsec = time1->tv_nsec + NANOSEC - time2->tv_nsec; + if (time1->tv_sec - 1 >= time2->tv_sec) + result->tv_sec = time1->tv_sec - 1 - time2->tv_sec; + else + { + result->tv_sec = -1; + result->tv_nsec = 0; + } + } +} + +int +tscmp (timestruc_t *time1, timestruc_t *time2) +{ + // This routine will return 1 if "time1" is greater than "time2" + // and 0 if "time1" is equal to "time2" and -1 otherwise. + if (time1->tv_sec == time2->tv_sec) + return time1->tv_nsec > time2->tv_nsec ? 1 : + time1->tv_nsec == time2->tv_nsec ? 0 : -1; + else + return time1->tv_sec > time2->tv_sec ? 1 : -1; +} + +void +int_max (int *maximum, int count) +{ + if (count > *maximum) + *maximum = count; +} + +double +TValue::to_double () +{ + switch (tag) + { + case VT_DOUBLE: + return (double) d; + case VT_INT: + return (double) i; + case VT_ULLONG: + return (double) ull; + case VT_LLONG: + case VT_ADDRESS: + return (double) ll; + case VT_FLOAT: + return (double) f; + case VT_SHORT: + return (double) s; + default: + return 0.0; + } +} + +int +TValue::to_int () +{ + switch (tag) + { + case VT_DOUBLE: + return (int) d; + case VT_INT: + return (int) i; + case VT_ULLONG: + return (int) ull; + case VT_LLONG: + case VT_ADDRESS: + return (int) ll; + case VT_FLOAT: + return (int) f; + case VT_SHORT: + return (int) s; + default: + return 0; + } +} + +size_t +TValue::get_len () +{ + char buf[256]; + return strlen (to_str (buf, sizeof (buf))); +} + +char * +TValue::to_str (char *str, size_t strsz) +{ + switch (tag) + { + case VT_DOUBLE: + if (d == 0.) + { + if (sign) + snprintf (str, strsz, NTXT ("+0. ")); + else + snprintf (str, strsz, NTXT ("0. ")); + } + else if (sign) + snprintf (str, strsz, NTXT ("%+.3lf"), d); + else + snprintf (str, strsz, NTXT ("%.3lf"), d); + break; + case VT_INT: + snprintf (str, strsz, NTXT ("%u"), i); + break; + case VT_LLONG: + if (sign) + snprintf (str, strsz, NTXT ("%+lld"), ll); + else + snprintf (str, strsz, NTXT ("%lld"), ll); + break; + case VT_ULLONG: + snprintf (str, strsz, NTXT ("%llu"), ll); + break; + case VT_ADDRESS: + snprintf (str, strsz, NTXT ("%u:0x%08x"), ADDRESS_SEG (ll), ADDRESS_OFF (ll)); + break; + case VT_FLOAT: + snprintf (str, strsz, NTXT ("%.3f"), f); + break; + case VT_SHORT: + snprintf (str, strsz, NTXT ("%hu"), s); + break; + case VT_LABEL: + return l; // 'str' is not used !!! + default: + *str = '\0'; + break; + } + + return str; +} + +void +TValue::make_delta (TValue *v1, TValue *v2) +{ + assert (v1->tag == v2->tag); + tag = v1->tag; + sign = true; + switch (v1->tag) + { + case VT_INT: + i = v1->i - v2->i; + break; + case VT_LLONG: + ll = v1->ll - v2->ll; + break; + case VT_ULLONG: + case VT_ADDRESS: + tag = VT_LLONG; + ll = (long long) (v1->ull - v2->ull); + break; + case VT_FLOAT: + f = v1->f - v2->f; + break; + case VT_DOUBLE: + d = v1->d - v2->d; + break; + default: + assert (0); + break; + } +} + +void +TValue::make_ratio (TValue *v1, TValue *v2) +{ + assert (v1->tag == v2->tag); + double x1 = v1->to_double (); + double x2 = v2->to_double (); + sign = false; + if (x1 == 0.) + { + // if the numerator is 0, the ratio is 1. or 0. only + d = (x2 == 0.) ? 1. : 0.; + tag = VT_DOUBLE; + } + else + { + // EUGENE replace 99.999 with a variable that is known by both DBE and GUI + if (x1 > 99.999 * x2) + { + l = dbe_strdup (">99.999"); + tag = VT_LABEL; + } + else if (x1 < -99.999 * x2) + { + l = dbe_strdup ("<-99.999"); + tag = VT_LABEL; + } + else + { + d = x1 / x2; + tag = VT_DOUBLE; + } + } +} + +int +TValue::compare (TValue *v) +{ + if (tag != v->tag) + { // Only for comparison (Ratio) + if (tag == VT_LABEL) + { + if (v->tag == VT_LABEL) + return strcoll (l, v->l); + return 1; + } + if (v->tag == VT_LABEL) + return -1; + return ll < v->ll ? -1 : (ll == v->ll ? 0 : 1); + } + switch (tag) + { + case VT_SHORT: + return s < v->s ? -1 : (s == v->s ? 0 : 1); + case VT_INT: + return i < v->i ? -1 : (i == v->i ? 0 : 1); + case VT_FLOAT: + return f < v->f ? -1 : (f == v->f ? 0 : 1); + case VT_DOUBLE: + return d < v->d ? -1 : (d == v->d ? 0 : 1); + case VT_LABEL: + return strcoll (l, v->l); + case VT_LLONG: + case VT_ULLONG: + case VT_ADDRESS: + case VT_HRTIME: + default: + return (ll < v->ll) ? -1 : ((ll == v->ll) ? 0 : 1); + } +} + +char * +strstr_r (char *s1, const char *s2) +{ + char *str = NULL; + for (char *s = s1; s;) + { + s = strstr (s, s2); + if (s) + { + str = s; + s++; + } + } + return str; +} + +// reversal order of strpbrk + +char * +strrpbrk (const char *string, const char *brkset) +{ + const char *p; + const char *s; + for (s = string + strlen (string) - 1; s >= string; s--) + { + for (p = brkset; *p != '\0' && *p != *s; ++p) + ; + if (*p != '\0') + return ((char *) s); + } + return NULL; +} + +char * +read_line (FILE *fptr) +{ + // get an input line, no size limit + int line_sz = 128; // starting size + char *line = (char *) malloc (line_sz); + + // read as much of the line as will fit in memory + line[0] = 0; + int len = 0; + for (;;) + { + while (fgets (line + len, line_sz - len, fptr) != NULL) + { + len = (int) strlen (line); + if (len == 0 || line[len - 1] == '\n') + break; + // increase the buffer + char *lineNew = (char *) malloc (2 * line_sz); + strncpy (lineNew, line, line_sz); + lineNew[line_sz] = '\0'; + free (line); + line = lineNew; + line_sz *= 2; + if (line == NULL) + { + fprintf (stderr, GTXT (" Line too long -- out of memory; exiting\n")); + exit (1); + } + } + if (len == 0) + { + free (line); + return NULL; + } + // see if there's a continuation line + if ((len >= 2) && (line[len - 1] == '\n') && (line[len - 2] == '\\')) + { + // remove the trailing \ and the \n, and keep going + line[len - 2] = 0; + len -= 2; + } + else + break; + } + return line; // expecting the caller to free it +} + +Vector<char *> * +split_str (char *str, char delimiter) +{ + Vector<char *> *v = new Vector<char *>; + for (char *s = str; s;) + { + if (*s == '"') + { + char *next_s = NULL; + char *tok = parse_qstring (s, &next_s); + if (tok && *tok != '\0') + v->append (tok); + if (*next_s) + s = next_s + 1; + else + s = NULL; + } + else + { + char *next_s = strchr (s, delimiter); + if (next_s) + { + if (next_s != s) + v->append (dbe_strndup (s, next_s - s)); + s = next_s + 1; + } + else + { + if (*s != '\0') + v->append (dbe_strdup (s)); + s = NULL; + } + } + } + return v; +} + +// get quoted string +char * +parse_qstring (char *in_str, char **endptr) +{ + int i; + char c, c2; + char term; + char csnum[2 * MAXPATHLEN]; + + // Skip any leading blanks or tabs + while (*in_str == '\t' || *in_str == ' ') + in_str++; + + int gtxt = 0; + if (*in_str == 'G' && *(in_str + 1) == 'T' && *(in_str + 2) == 'X' + && *(in_str + 3) == 'T' && *(in_str + 4) == '(') + { + gtxt = 1; + in_str += 5; + } + // non-quoted string + if (*in_str == '"') + term = '"'; + else if (*in_str == '\'') + term = '\''; + else + return strtok_r (in_str, NTXT (" "), endptr); + + StringBuilder sb; + while ((c = *(++in_str)) != '\0') + { + if (c == term) // the closing quote + break; + if (c == '\\') + { // handle any escaped characters + c2 = *(++in_str); + switch (c2) + { + case '\"': + sb.append ('\"'); + break; + case '\'': + sb.append ('\''); + break; + case '\\': + sb.append ('\\'); + break; + case 't': + sb.append ('\t'); + break; + case 'r': + sb.append ('\r'); + break; + case 'b': + sb.append ('\b'); + break; + case 'f': + sb.append ('\f'); + break; + case 'n': + sb.append ('\n'); + break; + default: + if ((c2 >= '0') && (c2 <= '9')) + { + for (i = 0; i < MAXPATHLEN; i++) + { + if (((c2 < '0') || (c2 > '9')) && (c2 != 'x') && + ((c2 < 'a') || (c2 > 'f')) && + ((c2 < 'A') || (c2 > 'F'))) + { + csnum[i] = '\0'; + --in_str; + break; + } + else + { + csnum[i] = c2; + c2 = *(++in_str); + } + } + sb.append ((char) strtoul (csnum, endptr, 0)); + } + else + sb.append (c2); + break; + } + } + else + sb.append (c); + } + if (c == term && gtxt && *in_str == ')') + in_str++; + if (*in_str == '\0') + *endptr = in_str; + else + *endptr = in_str + 1; + return sb.toString (); +} + +// parse a file name of the form name`name2` +// returns name +// stores the pointer to named in fcontext +// returns NULL if the string is not properly formatted +char * +parse_fname (char *in_str, char **fcontext) +{ + *fcontext = NULL; + int ch = '`'; + if (in_str == NULL) + return NULL; + char *copy = strdup (in_str); + char *p = strchr (copy, ch); + if (p != NULL) + { + // yes, there's an embedded file name + *p = '\0'; + p++; + // now find the terminating single quote + char *p1 = strchr (p, ch); + if (p1 == NULL) + { + // if we don't have the closing `, the format is incorrect + free (copy); + return NULL; + } + //remove the closing quote + *p1 = '\0'; + // see if there's anything following it + if (*(p1 + 1) != 0) + { + // error in format + free (copy); + return NULL; + } + free (*fcontext); + *fcontext = strdup (p); + } + return copy; +} + +int +get_paren (const char *name) +{ + char buf[8192]; + char *ptr; + int temp_level1, temp_level2; + + temp_level1 = temp_level2 = 0; + snprintf (buf, sizeof (buf), NTXT ("%s"), name); + while ((ptr = strrpbrk (buf, "><)(")) != NULL) + { + if (*ptr == '>') + temp_level1++; + else if (*ptr == '<') + temp_level1--; + else if (*ptr == ')') + temp_level2++; + else + { + temp_level2--; + if (temp_level1 <= 0 && temp_level2 <= 0) + return (int) (ptr - buf); + } + *ptr = '\0'; + } + return -1; +} + +// CRC-64 based on x^64 + x^11 + x^2 + x + 1 polynomial. +// This algorithm doesn't perform well but is short and +// readable. We currently use it for a small amount of +// short strings. Should this change, another algorithm +// with better performance is to be used instead. +static uint64_t masks[256] = { + /* 0 */ 0x000000, 0x000807, 0x00100e, 0x001809, 0x00201c, 0x00281b, + /* 6 */ 0x003012, 0x003815, 0x004038, 0x00483f, 0x005036, 0x005831, + /* 12 */ 0x006024, 0x006823, 0x00702a, 0x00782d, 0x008070, 0x008877, + /* 18 */ 0x00907e, 0x009879, 0x00a06c, 0x00a86b, 0x00b062, 0x00b865, + /* 24 */ 0x00c048, 0x00c84f, 0x00d046, 0x00d841, 0x00e054, 0x00e853, + /* 30 */ 0x00f05a, 0x00f85d, 0x0100e0, 0x0108e7, 0x0110ee, 0x0118e9, + /* 36 */ 0x0120fc, 0x0128fb, 0x0130f2, 0x0138f5, 0x0140d8, 0x0148df, + /* 42 */ 0x0150d6, 0x0158d1, 0x0160c4, 0x0168c3, 0x0170ca, 0x0178cd, + /* 48 */ 0x018090, 0x018897, 0x01909e, 0x019899, 0x01a08c, 0x01a88b, + /* 54 */ 0x01b082, 0x01b885, 0x01c0a8, 0x01c8af, 0x01d0a6, 0x01d8a1, + /* 60 */ 0x01e0b4, 0x01e8b3, 0x01f0ba, 0x01f8bd, 0x0201c0, 0x0209c7, + /* 66 */ 0x0211ce, 0x0219c9, 0x0221dc, 0x0229db, 0x0231d2, 0x0239d5, + /* 72 */ 0x0241f8, 0x0249ff, 0x0251f6, 0x0259f1, 0x0261e4, 0x0269e3, + /* 78 */ 0x0271ea, 0x0279ed, 0x0281b0, 0x0289b7, 0x0291be, 0x0299b9, + /* 84 */ 0x02a1ac, 0x02a9ab, 0x02b1a2, 0x02b9a5, 0x02c188, 0x02c98f, + /* 90 */ 0x02d186, 0x02d981, 0x02e194, 0x02e993, 0x02f19a, 0x02f99d, + /* 96 */ 0x030120, 0x030927, 0x03112e, 0x031929, 0x03213c, 0x03293b, + /* 102 */ 0x033132, 0x033935, 0x034118, 0x03491f, 0x035116, 0x035911, + /* 108 */ 0x036104, 0x036903, 0x03710a, 0x03790d, 0x038150, 0x038957, + /* 114 */ 0x03915e, 0x039959, 0x03a14c, 0x03a94b, 0x03b142, 0x03b945, + /* 120 */ 0x03c168, 0x03c96f, 0x03d166, 0x03d961, 0x03e174, 0x03e973, + /* 126 */ 0x03f17a, 0x03f97d, 0x040380, 0x040b87, 0x04138e, 0x041b89, + /* 132 */ 0x04239c, 0x042b9b, 0x043392, 0x043b95, 0x0443b8, 0x044bbf, + /* 138 */ 0x0453b6, 0x045bb1, 0x0463a4, 0x046ba3, 0x0473aa, 0x047bad, + /* 144 */ 0x0483f0, 0x048bf7, 0x0493fe, 0x049bf9, 0x04a3ec, 0x04abeb, + /* 150 */ 0x04b3e2, 0x04bbe5, 0x04c3c8, 0x04cbcf, 0x04d3c6, 0x04dbc1, + /* 156 */ 0x04e3d4, 0x04ebd3, 0x04f3da, 0x04fbdd, 0x050360, 0x050b67, + /* 162 */ 0x05136e, 0x051b69, 0x05237c, 0x052b7b, 0x053372, 0x053b75, + /* 168 */ 0x054358, 0x054b5f, 0x055356, 0x055b51, 0x056344, 0x056b43, + /* 174 */ 0x05734a, 0x057b4d, 0x058310, 0x058b17, 0x05931e, 0x059b19, + /* 180 */ 0x05a30c, 0x05ab0b, 0x05b302, 0x05bb05, 0x05c328, 0x05cb2f, + /* 186 */ 0x05d326, 0x05db21, 0x05e334, 0x05eb33, 0x05f33a, 0x05fb3d, + /* 192 */ 0x060240, 0x060a47, 0x06124e, 0x061a49, 0x06225c, 0x062a5b, + /* 198 */ 0x063252, 0x063a55, 0x064278, 0x064a7f, 0x065276, 0x065a71, + /* 204 */ 0x066264, 0x066a63, 0x06726a, 0x067a6d, 0x068230, 0x068a37, + /* 210 */ 0x06923e, 0x069a39, 0x06a22c, 0x06aa2b, 0x06b222, 0x06ba25, + /* 216 */ 0x06c208, 0x06ca0f, 0x06d206, 0x06da01, 0x06e214, 0x06ea13, + /* 222 */ 0x06f21a, 0x06fa1d, 0x0702a0, 0x070aa7, 0x0712ae, 0x071aa9, + /* 228 */ 0x0722bc, 0x072abb, 0x0732b2, 0x073ab5, 0x074298, 0x074a9f, + /* 234 */ 0x075296, 0x075a91, 0x076284, 0x076a83, 0x07728a, 0x077a8d, + /* 240 */ 0x0782d0, 0x078ad7, 0x0792de, 0x079ad9, 0x07a2cc, 0x07aacb, + /* 246 */ 0x07b2c2, 0x07bac5, 0x07c2e8, 0x07caef, 0x07d2e6, 0x07dae1, + /* 252 */ 0x07e2f4, 0x07eaf3, 0x07f2fa, 0x07fafd +}; + +uint64_t +crc64 (const char *str, size_t len) +{ + uint64_t res = 0LL; + for (size_t i = 0; i < len; i++) + { + unsigned char b = (unsigned char) ((res >> 56) ^ *str++); + res = res << 8; + res ^= masks [b]; + } + return res; +} + +/** + * Canonize path inside the string provided by the argument + * @param path + * @return path + */ +char * +canonical_path (char *path) +{ + char *s1, *s2; + if (!path) + return path; + s1 = path; + s2 = path; + while (*s1) + { + if (*s1 == '.' && s1[1] == '/') + { // remove ./// + for (s1++; *s1; s1++) + if (*s1 != '/') + break; + } + else if (*s1 == '/') + { // replace /// with / + *(s2++) = *s1; + for (s1++; *s1; s1++) + if (*s1 != '/') + break; + } + else + { + while (*s1) + { // copy file or directory name + if (*s1 == '/') + break; + *(s2++) = *(s1++); + } + } + } + *s2 = 0; + if (s2 != path && (s2 - 1) != path && s2[-1] == '/') // remove last / + *(s2 - 1) = 0; + return path; +} + +char * +get_relative_path (char *name) +{ + if (*name == '/' && theApplication) + { + char *cwd = theApplication->get_cur_dir (); + if (cwd) + { + size_t len = strlen (cwd); + if (len > 0 && len < strlen (name) && name[len] == '/' + && strncmp (cwd, name, len) == 0) + { + for (name += len + 1; *name == '/'; name++) + ; + return name; + } + } + } + return name; +} + +/** + * Generate a relative link name from path_from to path_to + * Example: + * path_from=a/b/c/d + * path_to=a/b/e/f/g + * lname=../../e/f/g + * @param path_to + * @param path_from + * @return lname - relative link + */ +char * +get_relative_link (const char *path_from, const char *path_to) +{ + if (!path_to) + path_to = "."; + if (!path_from) + path_from = "."; + char *s1 = dbe_strdup (path_to); + s1 = canonical_path (s1); + char *s2 = dbe_strdup (path_from); + s2 = canonical_path (s2); + long l = dbe_sstrlen (s1); + // try to find common directories + int common_slashes = 0; + int last_common_slash = -1; + for (int i = 0; i < l; i++) + { + if (s1[i] != s2[i]) break; + if (s1[i] == 0) break; + if (s1[i] == '/') + { + common_slashes++; + last_common_slash = i; + } + } + // find slashes in remaining path_to + int slashes = 0; + for (int i = last_common_slash + 1; i < l; i++) + { + if (s1[i] == '/') + { + // Exclude "/./" case + if (i > last_common_slash + 2) + { + if (s1[i - 1] == '.' && s1[i - 2] == '/') + continue; + } + else if (i > 0 && s1[i - 1] == '.') + continue; + slashes++; + } + } + // generate relative path + StringBuilder sb; + for (int i = 0; i < slashes; i++) + sb.append ("../"); + sb.append (s2 + last_common_slash + 1); + char *lname = sb.toString (); + free (s1); + free (s2); + return lname; +} + +char * +get_prog_name (int basename) +{ + char *nm = NULL; + if (theApplication) + { + nm = theApplication->get_name (); + if (nm && basename) + nm = get_basename (nm); + } + return nm; +} + +char * +dbe_strndup (const char *str, size_t len) +{ + if (str == NULL) + return NULL; + char *s = (char *) malloc (len + 1); + strncpy (s, str, len); + s[len] = '\0'; + return s; +} + +char * +dbe_sprintf (const char *fmt, ...) +{ + char buffer[256]; + int buf_size; + va_list vp; + + va_start (vp, fmt); + buf_size = vsnprintf (buffer, sizeof (buffer), fmt, vp) + 1; + va_end (vp); + if (buf_size < (int) sizeof (buffer)) + { + if (buf_size <= 1) + buffer[0] = 0; + return strdup (buffer); + } + + va_start (vp, fmt); + char *buf = (char *) malloc (buf_size); + vsnprintf (buf, buf_size, fmt, vp); + va_end (vp); + return buf; +} + +ssize_t +dbe_write (int f, const char *fmt, ...) +{ + char buffer[256]; + int buf_size; + va_list vp; + + va_start (vp, fmt); + buf_size = vsnprintf (buffer, sizeof (buffer), fmt, vp) + 1; + va_end (vp); + if (buf_size < (int) sizeof (buffer)) + { + if (buf_size <= 1) + buffer[0] = 0; + return write (f, buffer, strlen (buffer)); + } + + va_start (vp, fmt); + char *buf = (char *) malloc (buf_size); + vsnprintf (buf, buf_size, fmt, vp); + va_end (vp); + ssize_t val = write (f, buf, strlen (buf)); + free (buf); + return val; +} + +/* Worker Threads to avoid hanging on file servers */ + +/* + * Thread states + */ +enum +{ + THREAD_START, + THREAD_STARTED, + THREAD_CANCEL, + THREAD_CANCELED, + THREAD_CREATE, + THREAD_NOT_CREATED, + THREAD_FINISHED +}; + +/* + * Communication structure + */ +struct worker_thread_info +{ + pthread_t thread_id; /* ID returned by pthread_create() */ + int thread_num; /* Application-defined thread # */ + volatile int control; /* Thread state */ + volatile int result; /* Return status */ + struct stat64 statbuf; /* File info from stat64() */ + const char *path; /* File */ +}; + +static pthread_mutex_t worker_thread_lock = PTHREAD_MUTEX_INITIALIZER; +static int worker_thread_number = 0; +/** + * Call stat64() on current worker thread + * Check if control is not THREAD_CANCEL + * If control is THREAD_CANCEL return (exit thread) + * @param *wt_info + */ +static void * +dbe_stat_on_thread (void *arg) +{ + struct worker_thread_info *wt_info = (struct worker_thread_info *) arg; + pthread_mutex_lock (&worker_thread_lock); + { + if (wt_info->control != THREAD_START) + { + // Already too late + pthread_mutex_unlock (&worker_thread_lock); + return 0; + } + wt_info->control = THREAD_STARTED; + } + pthread_mutex_unlock (&worker_thread_lock); + const char * path = wt_info->path; + int st = stat64 (path, &(wt_info->statbuf)); + pthread_mutex_lock (&worker_thread_lock); + { + if (wt_info->control == THREAD_CANCEL) + { + // Too late. + pthread_mutex_unlock (&worker_thread_lock); + free (wt_info); + return 0; + } + wt_info->result = st; + wt_info->control = THREAD_FINISHED; + } + pthread_mutex_unlock (&worker_thread_lock); + return 0; +} + +/** + * Create a worker thread to call specified function + * Wait for its result, but not longer than 5 seconds + * If the timeout happens, tell the thread to cancel + * @param path + * @param wt_info + * @return thread state + */ +static int +dbe_dispatch_on_thread (const char *path, struct worker_thread_info *wt_info) +{ + wt_info->result = 0; + wt_info->control = THREAD_START; + pthread_attr_t attr; + /* Initialize thread creation attributes */ + int res = pthread_attr_init (&attr); + if (res != 0) + { + wt_info->control = THREAD_NOT_CREATED; + return THREAD_NOT_CREATED; + } + wt_info->thread_id = 0; + wt_info->path = path; + // Lock + pthread_mutex_lock (&worker_thread_lock); + worker_thread_number++; + wt_info->thread_num = worker_thread_number; + // Unlock + pthread_mutex_unlock (&worker_thread_lock); + // Create thread + res = pthread_create (&wt_info->thread_id, &attr, &dbe_stat_on_thread, wt_info); + if (res != 0) + { + wt_info->control = THREAD_NOT_CREATED; + pthread_attr_destroy (&attr); + return THREAD_NOT_CREATED; + } + // Wait for the thread to finish + res = 0; + useconds_t maxusec = 5000000; // 5 seconds + useconds_t deltausec = 1000; // 1 millisecond + int max = maxusec / deltausec; + for (int i = 0; i < max; i++) + { + if (THREAD_FINISHED == wt_info->control) + break; // We are done + usleep (deltausec); + } + // Lock + pthread_mutex_lock (&worker_thread_lock); + if (THREAD_FINISHED != wt_info->control) + { + // Cancel thread + wt_info->control = THREAD_CANCEL; // Cannot use wt_info after that! + res = THREAD_CANCEL; + } + // Unlock + pthread_mutex_unlock (&worker_thread_lock); + // Destroy the thread attributes object, since it is no longer needed + pthread_attr_destroy (&attr); + // Report that thread was canceled + if (THREAD_CANCEL == res) + return res; /* Cannot free memory allocated by thread */ + // Free all thread resources + void *resources = 0; + res = pthread_join (wt_info->thread_id, &resources); + free (resources); /* Free memory allocated by thread */ + return THREAD_FINISHED; +} + +static pthread_mutex_t dirnames_lock = PTHREAD_MUTEX_INITIALIZER; +static Map<const char*, int> *dirnamesMap = NULL; + +#define DIR_STATUS_EXISTS 0 +#define DIR_STATUS_UNKNOWN 2 + +/** + * Check if this directory name is known + * Return: + * @param path + * 0 - known, exists + * 1 - known, does not exist + * 2 - not known + */ +static int +check_dirname (const char *path) +{ + pthread_mutex_lock (&dirnames_lock); + if (NULL == dirnamesMap) + dirnamesMap = new StringMap<int>(128, 128); + pthread_mutex_unlock (&dirnames_lock); + int res = DIR_STATUS_UNKNOWN; + if (path && *path) + { + char *fn = dbe_strdup (path); + char *dn = dirname (fn); + if (dn && *dn) + res = dirnamesMap->get (dn); + free (fn); + } + return res; +} + +/** + * Save directory name and its status + * @param path + * @param status + * @return + */ +static void +extract_and_save_dirname (const char *path, int status) +{ + pthread_mutex_lock (&dirnames_lock); + if (NULL == dirnamesMap) + dirnamesMap = new StringMap<int>(128, 128); + pthread_mutex_unlock (&dirnames_lock); + char *fn = dbe_strdup (path); + if (fn && *fn != 0) + { + char *dn = dirname (fn); + if (dn && (*dn != 0)) + { + int st = 0; // exists + if (0 != status) + st = 1; // does not exist + dirnamesMap->put (dn, st); + } + } + free (fn); +} + +// get status for specified file +static int +dbe_stat_internal (const char *path, struct stat64 *sbuf, bool file_only) +{ + struct stat64 statbuf; + int dir_status = check_dirname (path); + if (dir_status == DIR_STATUS_UNKNOWN) + { + // Try to use a worker thread + if (theApplication->get_number_of_worker_threads () > 0) + { + struct worker_thread_info *wt_info; + wt_info = (worker_thread_info *) calloc (1, sizeof (worker_thread_info)); + if (wt_info != NULL) + { + int res = dbe_dispatch_on_thread (path, wt_info); + if (THREAD_FINISHED == res) + { + int st = wt_info->result; + extract_and_save_dirname (path, st); + if (st == 0 && file_only) + if (S_ISREG ((wt_info->statbuf).st_mode) == 0) + st = -1; // It is not a regular file + if (sbuf != NULL) + *sbuf = wt_info->statbuf; + free (wt_info); + return st; + } + else + { + if (THREAD_CANCEL == res) + { + // Worker thread hung. Cannot free wt_info. + // Allocated memory will be freed by worker thread. + // save directory + extract_and_save_dirname (path, 1); + return 1; // stat64 failed + } + else // THREAD_NOT_CREATED - continue on current thread + free (wt_info); + } + } + } + } + else if (dir_status != DIR_STATUS_EXISTS) + return -1; // does not exist + if (sbuf == NULL) + sbuf = &statbuf; + int st = stat64 (path, sbuf); + Dprintf (DEBUG_DBE_FILE, NTXT ("dbe_stat %d '%s'\n"), st, path); + if (st == -1) + return -1; + else if (file_only && S_ISREG (sbuf->st_mode) == 0) + return -1; // It is not ordinary file + return st; +} + +// get status for the regular file + +int +dbe_stat_file (const char *path, struct stat64 *sbuf) +{ + int res = dbe_stat_internal (path, sbuf, true); + return res; +} + +// get status for specified file + +int +dbe_stat (const char *path, struct stat64 *sbuf) +{ + int res = dbe_stat_internal (path, sbuf, false); + return res; +} + +/** + * Reads directory and prepares list of files according to the specified format + * Supported formats: + * "/bin/ls -a" - see 'man ls' for details + * "/bin/ls -aF" - see 'man ls' for details + * @param path + * @param format + * @return char * files + */ +char * +dbe_read_dir (const char *path, const char *format) +{ + StringBuilder sb; + DIR *dir = opendir (path); + if (dir == NULL) + return sb.toString (); + int format_aF = 0; + if (!strcmp (format, NTXT ("/bin/ls -aF"))) + format_aF = 1; + struct dirent *entry = NULL; + if (format != NULL) + { + while ((entry = readdir (dir)) != NULL) + { + sb.append (entry->d_name); + if (format_aF) + { + const char *attr = NTXT ("@"); // Link + struct stat64 sbuf; + sbuf.st_mode = 0; + char filename[MAXPATHLEN + 1]; + snprintf (filename, sizeof (filename), NTXT ("%s/%s"), path, entry->d_name); + dbe_stat (filename, &sbuf); + if (S_IREAD & sbuf.st_mode) + { // Readable + if (S_ISDIR (sbuf.st_mode) != 0) // Directory + attr = NTXT ("/"); + else if (S_ISREG (sbuf.st_mode) != 0) // Regular file + attr = NTXT (""); + } + sb.append (attr); + } + sb.append (NTXT ("\n")); + } + } + closedir (dir); + return sb.toString (); +} + +/** + * Gets list of processes according to the specified format + * Supported formats: + * "/bin/ps -ef" - see 'man ps' for details + * @param format + * @return char * processes + */ +char * +dbe_get_processes (const char *format) +{ + StringBuilder sb; + if (!strcmp (format, NTXT ("/bin/ps -ef"))) + { + char buf[BUFSIZ]; + FILE *ptr = popen (format, "r"); + if (ptr != NULL) + { + while (fgets (buf, BUFSIZ, ptr) != NULL) + sb.append (buf); + pclose (ptr); + } + } + return sb.toString (); +} + +/** + * Creates the directory named by the specified path name, including any + * necessary but nonexistent parent directories. + * Uses system utility "/bin/mkdir -p" + * Temporary limitation: path name should not contain spaces. + * Returns message from "/bin/mkdir -p" + * @param pathname + * @return result + */ +char * +dbe_create_directories (const char *pathname) +{ + StringBuilder sb; + char *makedir = dbe_sprintf (NTXT ("/bin/mkdir -p %s 2>&1"), pathname); + char out[BUFSIZ]; + FILE *ptr = popen (makedir, "r"); + if (ptr != NULL) + { + while (fgets (out, BUFSIZ, ptr) != NULL) + sb.append (out); + pclose (ptr); + } + free (makedir); + DIR *dir = opendir (pathname); + if (dir != NULL) + { + closedir (dir); + return NULL; // success + } + else + sb.append (NTXT ("\nError: Cannot open directory\n")); // DEBUG + return sb.toString (); // error +} + +/** + * Deletes the file or the directory named by the specified path name. + * If this pathname denotes a directory, then the directory must be empty in order to be deleted. + * Uses system utility "/bin/rm" or "/bin/rmdir" + * Temporary limitation: path name should not contain spaces. + * Returns error message from system utility + * @param pathname + * @return result + */ +char * +dbe_delete_file (const char *pathname) +{ + StringBuilder sb; + char *cmd = NULL; + struct stat64 sbuf; + sbuf.st_mode = 0; + int st = dbe_stat (pathname, &sbuf); + if (st == 0) + { // Exists + if (S_ISDIR (sbuf.st_mode) != 0) // Directory + cmd = dbe_sprintf (NTXT ("/bin/rmdir %s 2>&1"), pathname); + else if (S_ISREG (sbuf.st_mode) != 0) // Regular file + cmd = dbe_sprintf (NTXT ("/bin/rm %s 2>&1"), pathname); + } + else + return NULL; // Nothing to remove + if (cmd != NULL) + { + char out[BUFSIZ]; + FILE *ptr = popen (cmd, "r"); + if (ptr != NULL) + { + while (fgets (out, BUFSIZ, ptr) != NULL) + sb.append (out); + pclose (ptr); + } + free (cmd); + } + else + sb.sprintf (NTXT ("Error: cannot remove %s - not a regular file and not a directory\n"), pathname); + return sb.toString (); +} + +char * +dbe_xml2str (const char *s) +{ + if (s == NULL) + return NULL; + StringBuilder sb; + while (*s) + { + if (*s == '&') + { + if (strncmp (s, NTXT (" "), 6) == 0) + { + sb.append (' '); + s += 6; + continue; + } + else if (strncmp (s, NTXT ("""), 6) == 0) + { + sb.append ('"'); + s += 6; + continue; + } + else if (strncmp (s, NTXT ("&"), 5) == 0) + { + sb.append ('&'); + s += 5; + continue; + } + else if (strncmp (s, NTXT ("<"), 4) == 0) + { + sb.append ('<'); + s += 4; + continue; + } + else if (strncmp (s, NTXT (">"), 4) == 0) + { + sb.append ('>'); + s += 4; + continue; + } + } + sb.append (*s); + s++; + } + return sb.toString (); +} + +void +swapByteOrder (void *p, size_t sz) +{ + if (sz == 8) + { + uint64_t *pv = (uint64_t *) p; + uint64_t v = *pv; + v = ((v & 0x00000000FF000000) << 8) | ((v >> 8) & 0x00000000FF000000) | + ((v & 0x0000000000FF0000) << 24) | ((v >> 24) & 0x0000000000FF0000) | + ((v & 0x000000000000FF00) << 40) | ((v >> 40) & 0x000000000000FF00) | + (v >> 56) | (v << 56); + *pv = v; + } + else if (sz == 4) + { + uint32_t *pv = (uint32_t *) p; + uint32_t v = *pv; + v = (v >> 24) | (v << 24) | ((v & 0x0000FF00) << 8) | ((v >> 8) & 0x0000FF00); + *pv = v; + } + else if (sz == 2) + { + uint16_t *pv = (uint16_t *) p; + uint16_t v = *pv; + v = (v >> 8) | (v << 8); + *pv = v; + } +} + +void +destroy (void *vec) +{ + if (vec == NULL) + return; + Vector<void*> *array = (Vector<void*>*)vec; + switch (array->type ()) + { + case VEC_STRING: + ((Vector<char *>*)array)->destroy (); + break; + case VEC_VOIDARR: + case VEC_STRINGARR: + case VEC_INTARR: + case VEC_BOOLARR: + case VEC_LLONGARR: + case VEC_DOUBLEARR: + for (long i = 0; i < array->size (); i++) + destroy (array->fetch (i)); + break; + case VEC_INTEGER: + case VEC_CHAR: + case VEC_BOOL: + case VEC_DOUBLE: + case VEC_LLONG: + default: + break; + } + delete array; +} + +int64_t +read_from_file (int fd, void *buffer, int64_t nbyte) +{ + int64_t cnt = 0; + char *buf = (char *) buffer; + while (nbyte > 0) + { // Sometimes system cannot read 'nbyte' + ssize_t n = read (fd, (void *) (buf + cnt), (size_t) nbyte); + if (n <= 0) + break; + nbyte -= n; + cnt += n; + } + return cnt; +} + +/** + * Create symbolic link to the path + * @param path - path with spaces + * @param dir - directory where the link should be created + * @return symbolic link + */ +char * +dbe_create_symlink_to_path (const char *path, const char *dir) +{ + char *symbolic_link = NULL; + if (NULL == path || NULL == dir) + return NULL; + int res = mkdir (dir, 0777); + if (res != 0 && dbe_stat (dir, NULL) != 0) + return NULL; // Cannot create directory + long len = dbe_sstrlen (path); + if (len <= 4) + return NULL; // Unknown situation + if (strcmp ((path + len - 4), "/bin") != 0) // Unknown situation + return NULL; + int max = 99; // Just an arbitrary number + for (int i = 1; i <= max; i++) + { + // Try to create symbolic link + char *d = dbe_sprintf ("%s/%d", dir, i); + if (NULL == d) + return NULL; + res = mkdir (d, 0777); + symbolic_link = dbe_sprintf ("%s/%s", d, "bin"); + free (d); + if (NULL == symbolic_link) // Not enough memory + return NULL; + res = symlink (path, symbolic_link); + if (res == 0) // Link is created - use it. + break; + // Check if such link already exists + int e = errno; + char buf[MAXPATHLEN + 1]; + memset (buf, 0, MAXPATHLEN + 1); + ssize_t n = readlink (symbolic_link, buf, MAXPATHLEN); + if (n == len && strcmp (path, buf) == 0) // Link is correct - use it. + break; + if (i == max) + { // report the error + fprintf (stderr, GTXT ("Error: symlink(%s, %s) returned error: %d\n"), path, symbolic_link, res); + fprintf (stderr, GTXT ("Error: errno=%d (%s)\n"), e, strerror (e)); + fflush (stderr); + } + free (symbolic_link); + symbolic_link = NULL; + } + return symbolic_link; +} + +// Compute checksum for specified file. +// This code is from usr/src/cmd/cksum.c, adapted for us +// crcposix -- compute posix.2 compatable 32 bit CRC +// +// The POSIX.2 (draft 10) CRC algorithm. +// This is a 32 bit CRC with polynomial +// x**32 + x**26 + x**23 + x**22 + x**16 + x**12 + x**11 + x**10 + +// x**8 + x**7 + x**5 + x**4 + x**2 + x**1 + x**0 +// +// layout is from the POSIX.2 Rationale + +static uint32_t crctab_posix[256] = { + 0x00000000L, + 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, 0x130476DCL, 0x17C56B6BL, + 0x1A864DB2L, 0x1E475005L, 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, + 0x2B4BCB61L, 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, + 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, 0x5F15ADACL, + 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, 0x6A1936C8L, 0x6ED82B7FL, + 0x639B0DA6L, 0x675A1011L, 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, + 0x745E66CDL, 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, + 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, 0xBE2B5B58L, + 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, 0xAD2F2D84L, 0xA9EE3033L, + 0xA4AD16EAL, 0xA06C0B5DL, 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, + 0xD9714B49L, 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, + 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, 0xE13EF6F4L, + 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, 0x34867077L, 0x30476DC0L, + 0x3D044B19L, 0x39C556AEL, 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, + 0x2AC12072L, 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, + 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, 0x7897AB07L, + 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, 0x6B93DDDBL, 0x6F52C06CL, + 0x6211E6B5L, 0x66D0FB02L, 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, + 0x53DC6066L, 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, + 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, 0xBFA1B04BL, + 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, 0x8AAD2B2FL, 0x8E6C3698L, + 0x832F1041L, 0x87EE0DF6L, 0x99A95DF3L, 0x9D684044L, 0x902B669DL, + 0x94EA7B2AL, 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, + 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, 0xC6BCF05FL, + 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, 0xD5B88683L, 0xD1799B34L, + 0xDC3ABDEDL, 0xD8FBA05AL, 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, + 0x644FC637L, 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, + 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, 0x5C007B8AL, + 0x58C1663DL, 0x558240E4L, 0x51435D53L, 0x251D3B9EL, 0x21DC2629L, + 0x2C9F00F0L, 0x285E1D47L, 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, + 0x3B5A6B9BL, 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, + 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, 0xF12F560EL, + 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, 0xE22B20D2L, 0xE6EA3D65L, + 0xEBA91BBCL, 0xEF68060BL, 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, + 0xDA649D6FL, 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, + 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, 0xAE3AFBA2L, + 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, 0x9B3660C6L, 0x9FF77D71L, + 0x92B45BA8L, 0x9675461FL, 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, + 0x857130C3L, 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, + 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, 0x7B827D21L, + 0x7F436096L, 0x7200464FL, 0x76C15BF8L, 0x68860BFDL, 0x6C47164AL, + 0x61043093L, 0x65C52D24L, 0x119B4BE9L, 0x155A565EL, 0x18197087L, + 0x1CD86D30L, 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, + 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, 0x2497D08DL, + 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, 0xC5A92679L, 0xC1683BCEL, + 0xCC2B1D17L, 0xC8EA00A0L, 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, + 0xDBEE767CL, 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, + 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, 0x89B8FD09L, + 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, 0x9ABC8BD5L, 0x9E7D9662L, + 0x933EB0BBL, 0x97FFAD0CL, 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, + 0xA2F33668L, 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L +}; + +static void +m_crcposix (uint32_t *crcp, unsigned char *bp, uint32_t n) +{ + while (n-- > 0) + *crcp = (*crcp << 8) ^ crctab_posix[(unsigned char) ((*crcp >> 24)^*bp++)]; +} + +// Do CRC-POSIX function by calling a library entry point that has a +// slightly different calling sequence. +static uint32_t +docrcposix (uint32_t crcval, unsigned char *bp, uint32_t n) +{ + m_crcposix (&crcval, bp, n); + return (crcval); +} + +// Sum algorithms require various kinds of post-processing. +// The 'S' and 'R' variables are from the POSIX.2 (Draft 8?) description +// of the "sum" utility. +static uint32_t +postprocess (uint32_t S, long long n) +{ + // POSIX tacks on significant bytes of the length so that + // different length sequences of '\0' have different sums; + // then it complements sum. + unsigned char char_n[sizeof (n)]; + uint32_t i; + for (i = 0; n != 0; n >>= 8, ++i) + char_n[i] = (unsigned char) (n & 0xFF); + return (~docrcposix (S, char_n, i)); +} + +uint32_t +get_cksum (const char * pathname, char ** errmsg) +{ + int fd = open (pathname, O_RDONLY); + if (fd < 0) + { + if (errmsg) + *errmsg = dbe_sprintf (GTXT ("*** Warning: Error opening file for reading: %s"), pathname); + return 0; // error + } + uint32_t crcval = 0; + long long bytes = 0; + int64_t n; + unsigned char buf[4096]; + while ((n = read_from_file (fd, (char *) buf, sizeof (buf))) > 0) + { + bytes += n; + crcval = docrcposix (crcval, buf, n); + } + close (fd); + crcval = postprocess (crcval, bytes); + return crcval; +} diff --git a/gprofng/src/util.h b/gprofng/src/util.h new file mode 100644 index 0000000..0d1b8bc --- /dev/null +++ b/gprofng/src/util.h @@ -0,0 +1,185 @@ +/* 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. */ + +#ifndef _PERFAN_UTIL_H +#define _PERFAN_UTIL_H + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <stdint.h> + +#include "gp-defs.h" +#include "gp-time.h" +#include "i18n.h" +#include "debug.h" + +#define SWAP_ENDIAN(x) swapByteOrder((void *) (&(x)), sizeof(x)) +#define AppendString(len, arr, ...) len += snprintf(arr + len, sizeof(arr) - len, __VA_ARGS__) +#define ARR_SIZE(x) (sizeof (x) / sizeof (*(x))) + +// Utility routines. + +// +// Inline functions +// +// max(a, b) - Return the maximum of two values +inline int +max (int a, int b) +{ + return (a >= b) ? a : b; +} + +// min(a, b) - Return the minimum of two values +inline int +min (int a, int b) +{ + return (a <= b) ? a : b; +} + +// streq(s1, s2) - Returns 1 if strings are the same, 0 otherwise +inline int +streq (const char *s1, const char *s2) +{ + return strcmp (s1, s2) == 0; +} + +// StrChr(str, ch) - Rerurn 'str' if 'ch' does not occur in 'str' or +// a pointer to the next symbol after the first occurrence of 'ch' in 'str' +inline char * +StrChr (char *str, char ch) +{ + char *s = strchr (str, ch); + return s ? (s + 1) : str; +} + +// StrRchr(str, ch) - Rerurn 'str' if 'ch' does not occur in 'str' or +// a pointer to the next symbol after the last occurrence of 'ch' in 'str' +inline char * +StrRchr (char *str, char ch) +{ + char *s = strrchr (str, ch); + return s ? (s + 1) : str; +} + +inline char* +STR (const char *s) +{ + return s ? (char*) s : (char*) NTXT ("NULL"); +} + +inline char* +get_str (const char *s, const char *s1) +{ + return s ? (char*) s : (char*) s1; +} + +inline char * +get_basename (const char* name) +{ + return StrRchr ((char*) name, '/'); +} + +inline char * +dbe_strdup (const char *str) +{ + return str ? strdup (str) : NULL; +} + +inline long +dbe_sstrlen (const char *str) +{ + return str ? (long) strlen (str) : 0; +} + +inline int +dbe_strcmp (const char *s1, const char *s2) +{ + return s1 ? (s2 ? strcmp (s1, s2) : 1) : (s2 ? -1 : 0); +} + +// tstodouble(t) - Return timestruc_t in (double) seconds +inline double +tstodouble (timestruc_t t) +{ + return (double) t.tv_sec + (double) (t.tv_nsec / 1000000000.0); +} + +inline void +hr2timestruc (timestruc_t *d, hrtime_t s) +{ + d->tv_sec = (long) (s / NANOSEC); + d->tv_nsec = (long) (s % NANOSEC); +} + +inline hrtime_t +timestruc2hr (timestruc_t *s) +{ + return (hrtime_t) s->tv_sec * NANOSEC + (hrtime_t) s->tv_nsec; +} + +struct stat64; + +#if defined(__cplusplus) +extern "C" +{ +#endif + // + // Declaration of utility functions + // + void tsadd (timestruc_t *result, timestruc_t *time); + void tssub (timestruc_t *result, timestruc_t *time1, timestruc_t *time2); + int tscmp (timestruc_t *time1, timestruc_t *time2); + void int_max (int *maximum, int count); + char *strstr_r (char *s1, const char *s2); + char *strrpbrk (const char *string, const char *brkset); + char *read_line (FILE *); + char *parse_qstring (char *in_str, char **endptr); + char *parse_fname (char *in_str, char **fcontext); + int get_paren (const char *name); + + uint64_t crc64 (const char *str, size_t len); + char *canonical_path (char *path); + char *get_relative_path (char *name); + char *get_relative_link (const char *path_to, const char *path_from); + char *get_prog_name (int basename); + char *dbe_strndup (const char *str, size_t len); + int dbe_stat (const char *path, struct stat64 *sbuf); + int dbe_stat_file (const char *path, struct stat64 *sbuf); + char *dbe_read_dir (const char *path, const char *format); + char *dbe_get_processes (const char *format); + char *dbe_create_directories (const char *pathname); + char *dbe_delete_file (const char *pathname); + char *dbe_xml2str (const char *s); + void swapByteOrder (void *p, size_t sz); + char *dbe_sprintf (const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); + ssize_t dbe_write (int f, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + char *dbe_create_symlink_to_path (const char *path, const char *dir); + int64_t read_from_file (int fd, void *buffer, int64_t nbyte); + uint32_t get_cksum (const char * pathname, char ** errmsg); + +#ifdef __cplusplus +} +int catch_out_of_memory (int (*real_main)(int, char*[]), int argc, char *argv[]); +#endif + + +#endif /* _UTIL_H */ diff --git a/gprofng/src/vec.h b/gprofng/src/vec.h new file mode 100644 index 0000000..28b1800c --- /dev/null +++ b/gprofng/src/vec.h @@ -0,0 +1,524 @@ +/* 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. */ + +#ifndef _PERFAN_VEC_H +#define _PERFAN_VEC_H + +#include <assert.h> +#include <inttypes.h> +#include <string.h> +#include <stdlib.h> + +// This package implements a vector of items. + +#define Destroy(x) if (x) { (x)->destroy(); delete (x); (x) = NULL; } +#define VecSize(x) ((x) ? (x)->size() : 0) + +void destroy (void *vec); // Free up the "two-dimension" Vectors + +typedef int (*CompareFunc)(const void*, const void*); +typedef int (*ExtCompareFunc)(const void*, const void*, const void*); +typedef int (*SearchFunc)(char*, char*); + +extern "C" +{ + typedef int (*StdCompareFunc)(const void*, const void*); +} + +enum Search_type +{ + LINEAR, + BINARY, + HASH +}; + +enum Direction +{ + FORWARD, + REVERSE +}; + +enum VecType +{ + VEC_VOID = 0, + VEC_INTEGER, + VEC_CHAR, + VEC_BOOL, + VEC_DOUBLE, + VEC_LLONG, + VEC_VOIDARR, + VEC_STRING, + VEC_INTARR, + VEC_BOOLARR, + VEC_LLONGARR, + VEC_STRINGARR, + VEC_DOUBLEARR +}; + +template <class ITEM> void +qsort (ITEM *, size_t, ExtCompareFunc, void *); + +template <typename ITEM> class Vector +{ +public: + + Vector () + { + count = 0; + data = NULL; + limit = 0; + sorted = false; + }; + + Vector (long sz); + + virtual + ~Vector () + { + free (data); + } + + void append (const ITEM item); + void addAll (Vector<ITEM> *vec); + Vector<ITEM> *copy (); // Return a copy of "this". + + ITEM + fetch (long index) + { + return data[index]; + } + + ITEM + get (long index) + { + return data[index]; + } + + // Return the first index in "this" that equals "item". + // Return -1 if "item" is not found. + long find (const ITEM item); + long find_r (const ITEM item); + + // Insert "item" into "index"'th slot of "this", + // moving everything over by 1. + void insert (long index, const ITEM item); + + // Insert "item" after locating its appropriate index + void incorporate (const ITEM item, CompareFunc func); + + // Remove and return the "index"'th item from "this", + // moving everything over by 1. + ITEM remove (long index); + + // Swap two items in "this", + void swap (long index1, long index2); + + long + size () + { + return count; + } + + // Store "item" into the "index"'th slot of "this". + void store (long index, const ITEM item); + + void + put (long index, const ITEM item) + { + store (index, item); + } + + // Sort the vector according to compare + void + sort (CompareFunc compare, void *arg = NULL) + { + qsort (data, count, (ExtCompareFunc) compare, arg); + sorted = true; + } + + // Binary search, vector must be sorted + long bisearch (long start, long end, void *key, CompareFunc func); + void destroy (); // delete all vector elements (must be pointers!) + + void + reset () + { + count = 0; + sorted = false; + } + + bool + is_sorted () + { + return sorted; + } + + virtual VecType + type () + { + return VEC_VOID; + } + + virtual void + dump (const char * /* msg */) + { + return; + } + +private: + + void resize (long index); + + ITEM *data; // Pointer to data vector + long count; // Number of items + long limit; // Vector length (power of 2) + bool sorted; +}; + +template<> VecType Vector<int>::type (); +template<> VecType Vector<unsigned>::type (); +template<> VecType Vector<char>::type (); +template<> VecType Vector<bool>::type (); +template<> VecType Vector<double>::type (); +template<> VecType Vector<long long>::type (); +template<> VecType Vector<uint64_t>::type (); +template<> VecType Vector<void*>::type (); +template<> VecType Vector<char*>::type (); +template<> VecType Vector<Vector<int>*>::type (); +template<> VecType Vector<Vector<char*>*>::type (); +template<> VecType Vector<Vector<long long>*>::type (); +template<> void Vector<char *>::destroy (); + +#define KILOCHUNK 1024 +#define MEGACHUNK 1024*1024 +#define GIGACHUNK 1024*1024*1024 + +// A standard looping construct: +#define Vec_loop(ITEM, vec, index, item) \ +if (vec != NULL) \ + for (index = 0, item = ((vec)->size() > 0) ? (vec)->fetch(0) : (ITEM)0; \ + index < (vec)->size(); \ + item = (++index < (vec)->size()) ? (vec)->fetch(index) : (ITEM)0) + +template <typename ITEM> +Vector<ITEM>::Vector (long sz) +{ + count = 0; + limit = sz > 0 ? sz : KILOCHUNK; // was 0; + data = limit ? (ITEM *) malloc (sizeof (ITEM) * limit) : NULL; + sorted = false; +} + +template <typename ITEM> void +Vector<ITEM> +::resize (long index) +{ + if (index < limit) + return; + if (limit < 16) + limit = 16; + while (index >= limit) + { + if (limit > GIGACHUNK) + limit += GIGACHUNK; // Deoptimization for large experiments + else + limit = limit * 2; + } + data = (ITEM *) realloc (data, limit * sizeof (ITEM)); +} + +template <typename ITEM> void +Vector<ITEM>::append (const ITEM item) +{ + // This routine will append "item" to the end of "this". + if (count >= limit) + resize (count); + data[count++] = item; +} + +template <typename ITEM> void +Vector<ITEM>::addAll (Vector<ITEM> *vec) +{ + if (vec) + for (int i = 0, sz = vec->size (); i < sz; i++) + append (vec->fetch (i)); +} + +template <typename ITEM> Vector<ITEM> * +Vector<ITEM>::copy () +{ + // This routine will return a copy of "this". + Vector<ITEM> *vector; + vector = new Vector<ITEM>; + vector->count = count; + vector->limit = limit; + vector->data = (ITEM *) malloc (sizeof (ITEM) * limit); + (void) memcpy ((char *) vector->data, (char *) data, sizeof (ITEM) * count); + return vector; +} + +template <typename ITEM> long +Vector<ITEM>::find (const ITEM match_item) +{ + for (long i = 0; i < size (); i++) + if (match_item == get (i)) + return i; + return -1; +} + +template <typename ITEM> long +Vector<ITEM>::find_r (const ITEM match_item) +{ + for (long i = size () - 1; i >= 0; i--) + if (match_item == get (i)) + return i; + return -1; +} + +template <typename ITEM> void +Vector<ITEM>::insert (long index, const ITEM item) +{ + // This routine will insert "item" into the "index"'th slot of "this". + // An error occurs if "index" > size(). + // "index" is allowed to be equal to "count" in the case that + // you are inserting past the last element of the vector. + // In that case, the bcopy below becomes a no-op. + assert (index >= 0); + assert (index <= count); + append (item); + (void) memmove (((char *) (&data[index + 1])), (char *) (&data[index]), + (count - index - 1) * sizeof (ITEM)); + data[index] = item; +} + +template <typename ITEM> ITEM +Vector<ITEM>::remove (long index) +{ + // This routine will remove the "index"'th item from "this" and + // return it. An error occurs if "index" >= size();. + assert (index >= 0); + assert (index < count); + ITEM item = data[index]; + for (long i = index + 1; i < count; i++) + data[i - 1] = data[i]; + count--; + // Bad code that works good when ITEM is a pointer type + data[count] = item; + return data[count]; +} + +template <typename ITEM> void +Vector<ITEM>::swap (long index1, long index2) +{ + ITEM item; + item = data[index1]; + data[index1] = data[index2]; + data[index2] = item; +} + +template <typename ITEM> void +Vector<ITEM>::store (long index, const ITEM item) +{ + if (index >= count) + { + resize (index); + memset (&data[count], 0, (index - count) * sizeof (ITEM)); + count = index + 1; + } + data[index] = item; +} + +// This routine performs a binary search across +// the entire vector, with "start" being the low boundary. +// It is assumed that the vector is SORTED in +// ASCENDING ORDER by the same criteria as the +// compare function. +// If no match is found, -1 is returned. +template <typename ITEM> long +Vector<ITEM>::bisearch (long start, long end, void *key, CompareFunc compare) +{ + ITEM *itemp; + if (end == -1) + end = count; + if (start >= end) + return -1; // start exceeds limit + itemp = (ITEM *) bsearch ((char *) key, (char *) &data[start], + end - start, sizeof (ITEM), (StdCompareFunc) compare); + if (itemp == (ITEM *) 0) + return -1; // not found + return (long) (itemp - data); +} + +template <typename ITEM> void +Vector<ITEM>::incorporate (const ITEM item, CompareFunc compare) +{ + long lt = 0; + long rt = count - 1; + while (lt <= rt) + { + long md = (lt + rt) / 2; + if (compare (data[md], item) < 0) + lt = md + 1; + else + rt = md - 1; + } + if (lt == count) + append (item); + else + insert (lt, item); +} + +#define QSTHRESH 6 + +template <typename ITEM> void +qsort (ITEM *base, size_t nelem, ExtCompareFunc qcmp, void *arg) +{ + for (;;) + { + // For small arrays use insertion sort + if (nelem < QSTHRESH) + { + for (size_t i = 1; i < nelem; i++) + { + ITEM *p = base + i; + ITEM *q = p - 1; + if (qcmp (q, p, arg) > 0) + { + ITEM t = *p; + *p = *q; + while (q > base && qcmp (q - 1, &t, arg) > 0) + { + *q = *(q - 1); + --q; + } + *q = t; + } + } + return; + } + + ITEM *last = base + nelem - 1; + ITEM *mid = base + nelem / 2; + // Sort the first, middle, and last elements + ITEM *a1 = base, *a2, *a3; + if (qcmp (base, mid, arg) > 0) + { + if (qcmp (mid, last, arg) > 0) + { // l-m-b + a2 = last; + a3 = last; + } + else if (qcmp (base, last, arg) > 0) + { // l-b-m + a2 = mid; + a3 = last; + } + else + { // m-b-l + a2 = mid; + a3 = mid; + } + } + else if (qcmp (mid, last, arg) > 0) + { + a1 = mid; + a3 = last; + if (qcmp (base, last, arg) > 0) // m-l-b + a2 = base; + else // b-l-m + a2 = a3; + } + else // b-m-l + a3 = a2 = a1; + if (a1 != a2) + { + ITEM t = *a1; + *a1 = *a2; + if (a2 != a3) + *a2 = *a3; + *a3 = t; + } + + // Partition + ITEM *i = base + 1; + ITEM *j = last - 1; + for (;;) + { + while (i < mid && qcmp (i, mid, arg) <= 0) + i++; + while (j > mid && qcmp (mid, j, arg) <= 0) + j--; + if (i == j) + break; + ITEM t = *i; + *i = *j; + *j = t; + if (i == mid) + { + mid = j; + i++; + } + else if (j == mid) + { + mid = i; + j--; + } + else + { + i++; + j--; + } + } + + // Compare two partitions. Do the smaller one by recursion + // and loop over the larger one. + size_t nleft = mid - base; + size_t nright = nelem - nleft - 1; + if (nleft <= nright) + { + qsort (base, nleft, qcmp, arg); + base = mid + 1; + nelem = nright; + } + else + { + qsort (mid + 1, nright, qcmp, arg); + nelem = nleft; + } + } +} + +template<> inline void +Vector<char*>::destroy () +{ + for (long i = 0; i < count; i++) + free (data[i]); + count = 0; +} + +template <typename ITEM> inline void +Vector<ITEM>::destroy () +{ + for (long i = 0; i < count; i++) + delete data[i]; + count = 0; +} + +#endif /* _VEC_H */ |