/* Copyright (C) 2021-2024 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 #include #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<=0 && ((1<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 *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 * Definition::get_dependencies () { if (dependencies == NULL) { if (arg1 && arg1->bm && arg2 && arg2->bm) { dependencies = new Vector(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.; } }