|  | /* 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); | 
|  | } |