1/* Copyright (C) 2021 Free Software Foundation, Inc.
2   Contributed by Oracle.
3
4   This file is part of GNU Binutils.
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 3, or (at your option)
9   any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, 51 Franklin Street - Fifth Floor, Boston,
19   MA 02110-1301, USA.  */
20
21#include "config.h"
22#include "util.h"
23#include "DbeSession.h"
24#include "DbeView.h"
25#include "IndexObject.h"
26#include "StringBuilder.h"
27
28IndexObject::IndexObject (int _indextype, uint64_t _index)
29{
30  indextype = _indextype;
31  obj = NULL;
32  id = _index;
33  name = NULL;
34  nameIsFinal = false;
35}
36
37IndexObject::IndexObject (int _indextype, Histable *_obj)
38{
39  indextype = _indextype;
40  obj = _obj;
41  id = obj ? obj->id : (uint64_t) - 1;
42  name = NULL;
43  nameIsFinal = false;
44}
45
46void
47IndexObject::set_name (char * other_name)
48{
49  if (name == NULL)
50    {
51      name = other_name;
52      nameIsFinal = true;
53    }
54}
55
56static uint64_t
57extractExpgrid (uint64_t id)
58{
59  return (id >> IndexObject::INDXOBJ_EXPGRID_SHIFT)
60	  & IndexObject::INDXOBJ_EXPGRID_MASK;
61}
62
63static uint64_t
64extractExpid (uint64_t id)
65{
66  return (id >> IndexObject::INDXOBJ_EXPID_SHIFT)
67	  & IndexObject::INDXOBJ_EXPID_MASK;
68}
69
70static uint64_t
71extractPayload (uint64_t id)
72{
73  return (id >> IndexObject::INDXOBJ_PAYLOAD_SHIFT)
74	  & IndexObject::INDXOBJ_PAYLOAD_MASK;
75}
76
77static void
78printCompareLabel (StringBuilder *sb, uint64_t grpId);
79
80static bool
81printThread (StringBuilder *sbIn, Expression::Context * ctx, uint64_t id)
82{
83  uint64_t proc = extractExpid (id);
84  uint64_t thrid = extractPayload (id);
85  bool isFinal = true;
86  bool hasJava = false;
87  bool javaThread = false;
88  if (ctx)
89    {
90      if (ctx->dview && ctx->dview->getProp (PROP_JTHREAD))
91	{
92	  hasJava = true;
93	  uint64_t tstamp = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId);
94	  JThread *jthread = ctx->exp->map_pckt_to_Jthread (thrid, tstamp);
95	  if (jthread != JTHREAD_NONE && jthread != JTHREAD_DEFAULT)
96	    {
97	      sbIn->appendf (GTXT ("Process %llu, Thread %llu, JThread %llu \'%s\', Group \'%s\', Parent \'%s\'"),
98			     (unsigned long long) proc,
99			     (unsigned long long) thrid,
100			     (unsigned long long) jthread->jthr_id,
101			     get_str(jthread->name, ""),
102			     get_str(jthread->group_name, ""),
103			     get_str(jthread->parent_name, ""));
104	      javaThread = true;
105	    }
106	}
107    }
108  if (!javaThread)
109    {
110      sbIn->appendf (GTXT ("Process %llu, Thread %llu"),
111		     (unsigned long long) proc, (unsigned long long) thrid);
112      if (hasJava)
113	// sometimes threads start as native and later become Java; keep checking
114	isFinal = false;
115    }
116  if (ctx && ctx->dbev && ctx->dbev->comparingExperiments ())
117    {
118      Vector <Histable *> *v = ctx->exp->get_comparable_objs ();
119      int st = 0;
120      for (long i = 0, sz = VecSize (v); i < sz; i++)
121	{
122	  Experiment *exp = (Experiment *) v->get (i);
123	  if (exp)
124	    {
125	      if (st == 0)
126		{
127		  st = 1;
128		  continue;
129		}
130	      sbIn->appendf (GTXT (" [ Group %llu  Process %llu ]"),
131			     (unsigned long long) exp->groupId - 1,
132			     (unsigned long long) exp->getUserExpId ());
133	    }
134	}
135    }
136  return isFinal;
137}
138
139static bool
140printProcess (StringBuilder *sbIn, Expression::Context * ctx, uint64_t id)
141{
142  uint64_t proc = id;
143  if (ctx && ctx->exp)
144    {
145      int st = 0;
146      if (ctx->dbev && ctx->dbev->comparingExperiments ())
147	{
148	  Vector <Histable *> *v = ctx->exp->get_comparable_objs ();
149	  for (long i = 0, sz = VecSize (v); i < sz; i++)
150	    {
151	      Experiment *exp = (Experiment *) v->get (i);
152	      if (exp)
153		{
154		  if (st == 0)
155		    {
156		      st = 1;
157		      sbIn->appendf (GTXT ("%s, Process %3llu, PID %llu"),
158				 get_str (exp->utargname, GTXT ("(unknown)")),
159				     (unsigned long long) proc,
160				     (unsigned long long) exp->getPID ());
161		      continue;
162		    }
163		  sbIn->appendf (GTXT (" [ Group %llu,  Process %llu, PID %llu ]"),
164				 (unsigned long long) exp->groupId - 1,
165				 (unsigned long long) exp->getUserExpId (),
166				 (unsigned long long) exp->getPID ());
167		}
168	    }
169	}
170      if (st == 0)
171	sbIn->appendf (GTXT ("%s, Process %3llu, PID %llu"),
172		       get_str (ctx->exp->utargname, GTXT ("(unknown)")),
173		       (unsigned long long) proc,
174		       (unsigned long long) ctx->exp->getPID ());
175    }
176  else
177    sbIn->appendf (GTXT ("Process %3llu"), (unsigned long long) proc);
178  return true; //name is final
179}
180
181static bool
182printExperiment (StringBuilder *sbIn, Expression::Context * ctx, uint64_t id)
183{
184  uint64_t grpId = extractExpgrid (id);
185  uint64_t expid = extractExpid (id);
186  if (ctx && ctx->dbev->comparingExperiments ())
187    printCompareLabel (sbIn, grpId);
188  if (ctx)
189    {
190      Experiment *hasFounder = ctx->exp->founder_exp;
191      int pid = ctx->exp->getPID ();
192      uint64_t founderExpid;
193      if (hasFounder)
194	founderExpid = hasFounder->getUserExpId ();
195      else
196	founderExpid = expid;
197      sbIn->appendf (GTXT ("Base Experiment %llu, Process %llu, PID %llu, %s"),
198		     (unsigned long long) founderExpid,
199		     (unsigned long long) expid,
200		     (unsigned long long) pid,
201		     get_str (ctx->exp->utargname, GTXT ("(unknown)")));
202    }
203  else
204    sbIn->appendf (GTXT ("Process %llu"), (unsigned long long) expid);
205  return true; // name is final
206}
207
208void
209IndexObject::set_name_from_context (Expression::Context * ctx)
210{
211  if (name != NULL)
212    if (nameIsFinal && strstr (name, GTXT ("(unknown)")) == NULL)
213      return;
214  if (ctx == NULL || ctx->dview == NULL || ctx->dbev == NULL)
215    return;
216  StringBuilder sb;
217  switch (indextype)
218    {
219    case INDEX_THREADS:
220      nameIsFinal = printThread (&sb, ctx, id);
221      break;
222    case INDEX_PROCESSES:
223      nameIsFinal = printProcess (&sb, ctx, id);
224      break;
225    case INDEX_EXPERIMENTS:
226      nameIsFinal = printExperiment (&sb, ctx, id);
227      break;
228    default:
229      name = NULL;
230      return;
231    }
232  if (sb.length ())
233    name = sb.toString ();
234}
235
236static void
237printCompareLabel (StringBuilder *sbIn, uint64_t grpId)
238{
239  static const char *labels[] = {"", GTXT ("Baseline"), GTXT ("Comparison")};
240  static int length;
241  if (!length)
242    {
243      length = strlen (labels[1]);
244      int length2 = strlen (labels[2]);
245      if (length < length2)
246	length = length2;
247      length += 5; // for open/close brace and grpId number and spaces
248    }
249  char *s = NULL;
250  if (grpId != 0)
251    {
252      if (grpId <= 2)
253	s = dbe_sprintf ("[%s]", labels[grpId]);
254      else
255	s = dbe_sprintf ("[%s-%llu]", labels[2],
256			 (unsigned long long) (grpId - 1));
257    }
258  sbIn->appendf ("%-*s", length, get_str (s, ""));
259  free (s);
260}
261
262char *
263IndexObject::get_name (NameFormat fmt)
264{
265  if (name == NULL)
266    {
267      StringBuilder sb;
268      int64_t upper;
269      int64_t num1;
270      int64_t num2;
271      switch (indextype)
272	{
273	case INDEX_THREADS:
274	  printThread (&sb, NULL, id);
275	  break;
276
277	case INDEX_CPUS:
278	  sb.sprintf (GTXT ("CPU %llu"), (unsigned long long) id);
279	  break;
280
281	case INDEX_SAMPLES:
282	  sb.sprintf (GTXT ("Sample %llu"), (unsigned long long) id);
283	  break;
284
285	case INDEX_GCEVENTS:
286	  if (id == 0)
287	    {
288	      sb.sprintf (GTXT ("Not in any GCEvent"));
289	    }
290	  else
291	    {
292	      sb.sprintf (GTXT ("GCEvent %llu"), (unsigned long long) id);
293	    }
294	  break;
295
296	case INDEX_SECONDS:
297	  sb.sprintf (GTXT ("Second of execution %llu"), (unsigned long long) id);
298	  break;
299
300	case INDEX_PROCESSES:
301	  printProcess (&sb, NULL, id);
302	  break;
303
304	case INDEX_EXPERIMENTS:
305	  printExperiment (&sb, NULL, id);
306	  break;
307	case INDEX_BYTES:
308	  upper = id;
309	  if (id == -1)
310	    {
311	      break;
312	    }
313	  if (id % 2 == 1 && id > 1)
314	    {
315	      upper = id - 1;
316	      if (upper >= 1099511627776)
317		{
318		  num1 = upper / 1099511627776;
319		  sb.sprintf (GTXT (">= %3llu TB"), (unsigned long long) num1);
320		}
321	      else
322		{
323		  // XXXX do nothing, this should not happen
324		}
325	    }
326	  else
327	    {
328	      if (upper >= 1099511627776)
329		{
330		  num1 = upper / 1099511627776;
331		  num2 = num1 / 4;
332		  if (num2)
333		    {
334		      sb.sprintf (GTXT ("%3lluTB < n <= %3lluTB"), (unsigned long long) num2, (unsigned long long) num1);
335		    }
336		  else
337		    {
338		      sb.sprintf (GTXT ("256GB < n <= %3lluTB"), (unsigned long long) num1);
339		    }
340		}
341	      else if (upper >= 1073741824)
342		{
343		  num1 = upper / 1073741824;
344		  num2 = num1 / 4;
345		  if (num2)
346		    {
347		      sb.sprintf (GTXT ("%3lluGB < n <= %3lluGB"), (unsigned long long) num2, (unsigned long long) num1);
348		    }
349		  else
350		    {
351		      sb.sprintf (GTXT ("256MB < n <= %3lluGB"), (unsigned long long) num1);
352		    }
353		}
354	      else if (upper >= 1048576)
355		{
356		  num1 = upper / 1048576;
357		  num2 = num1 / 4;
358		  if (num2)
359		    {
360		      sb.sprintf (GTXT ("%3lluMB < n <= %3lluMB"), (unsigned long long) num2, (unsigned long long) num1);
361		    }
362		  else
363		    {
364		      sb.sprintf (GTXT ("256KB < n <= %3lluMB"), (unsigned long long) num1);
365		    }
366		}
367	      else if (upper >= 1024)
368		{
369		  num1 = upper / 1024;
370		  num2 = num1 / 4;
371		  if (num2)
372		    {
373		      sb.sprintf (GTXT ("%3lluKB < n <= %3lluKB"), (unsigned long long) num2, (unsigned long long) num1);
374		    }
375		  else
376		    {
377		      sb.sprintf (GTXT ("  256 < n <= %3lluKB"), (unsigned long long) num1);
378		    }
379		}
380	      else if (upper > 0)
381		{
382		  num1 = upper;
383		  num2 = num1 / 4;
384		  if (num1 == 1)
385		    {
386		      sb.sprintf (GTXT ("    1 Byte"));
387		    }
388		  else
389		    {
390		      sb.sprintf (GTXT ("%5llu < n <= %5llu Bytes"), (unsigned long long) num2, (unsigned long long) num1);
391		    }
392		}
393	      else if (upper == 0)
394		{
395		  sb.sprintf (GTXT ("    0 Bytes"));
396		}
397	      else
398		{
399		  sb.sprintf (GTXT ("<No Data>"));
400		}
401	    }
402	  break;
403	case INDEX_DURATION:
404	  if (id == -1)
405	    {
406	      break;
407	    }
408
409	  if (id > 10000000000000)
410	    {
411	      sb.sprintf (GTXT ("n > 10000s"));
412	    }
413	  else if (id > 1000000000000)
414	    {
415	      sb.sprintf (GTXT ("1000s < n <= 10000s"));
416	    }
417	  else if (id > 100000000000)
418	    {
419	      sb.sprintf (GTXT (" 100s < n <= 1000s"));
420	    }
421	  else if (id > 10000000000)
422	    {
423	      sb.sprintf (GTXT ("  10s < n <=  100s"));
424	    }
425	  else if (id > 1000000000)
426	    {
427	      sb.sprintf (GTXT ("   1s < n <=   10s"));
428	    }
429	  else if (id > 100000000)
430	    {
431	      sb.sprintf (GTXT ("100ms < n <=    1s"));
432	    }
433	  else if (id > 10000000)
434	    {
435	      sb.sprintf (GTXT (" 10ms < n <= 100ms"));
436	    }
437	  else if (id > 1000000)
438	    {
439	      sb.sprintf (GTXT ("  1ms < n <=  10ms"));
440	    }
441	  else if (id > 100000)
442	    {
443	      sb.sprintf (GTXT ("100us < n <=   1ms"));
444	    }
445	  else if (id > 10000)
446	    {
447	      sb.sprintf (GTXT (" 10us < n <= 100us"));
448	    }
449	  else if (id > 1000)
450	    {
451	      sb.sprintf (GTXT ("  1us < n <=  10us"));
452	    }
453	  else if (id > 0)
454	    {
455	      sb.sprintf (GTXT ("   0s < n <=   1us"));
456	    }
457	  else if (id == 0)
458	    {
459	      sb.sprintf (GTXT ("   0s"));
460	    }
461	  else
462	    {
463	      sb.sprintf (GTXT ("<No Data>"));
464	    }
465	  break;
466
467	  // Custom index objects
468	default:
469	  if (obj)
470	      sb.sprintf (GTXT ("%s from %s"),
471			  dbeSession->getIndexSpaceDescr (indextype), obj->get_name (fmt));
472	  else
473	    {
474	      IndexObjType_t *indexObj = dbeSession->getIndexSpace (indextype);
475	      if (indexObj->memObj)
476		{
477		  if (strcasecmp (indexObj->name, NTXT ("Memory_page_size")) == 0)
478		    {
479		      if (id == 0)
480			  sb.append (GTXT ("<Unknown>"));
481		      else
482			  sb.sprintf (NTXT ("%s 0x%16.16llx (%llu)"), indexObj->name,
483				      (unsigned long long) id, (unsigned long long) id);
484		    }
485		  else if (strcasecmp (indexObj->name, NTXT ("Memory_in_home_lgrp")) == 0)
486		    {
487		      if (id == 0 || id == 1)
488			  sb.sprintf (NTXT ("%s: %s"), indexObj->name,
489				      id == 1 ? GTXT ("True") : GTXT ("False"));
490		      else
491			  sb.sprintf (NTXT ("%s %s (0x%llx"), indexObj->name,
492				      GTXT ("<Unknown>"), (unsigned long long) id);
493		    }
494		  else if (strcasecmp (indexObj->name, NTXT ("Memory_lgrp")) == 0)
495		    {
496		      if (id == 0)
497			  sb.append (GTXT ("<Unknown>"));
498		      else
499			  sb.sprintf (NTXT ("%s %llu"), indexObj->name, (unsigned long long) id);
500		    }
501		  else
502		      sb.sprintf (NTXT ("%s 0x%16.16llx"), indexObj->name, (unsigned long long) id);
503		}
504	      else
505		  sb.sprintf ("%s 0x%16.16llx (%llu)", indexObj->name,
506			      (unsigned long long) id, (unsigned long long) id);
507	    }
508	}
509      name = sb.toString ();
510      nameIsFinal = true;
511    }
512  return name;
513}
514
515bool
516IndexObject::requires_string_sort ()
517{
518  if (indextype == INDEX_PROCESSES || indextype >= INDEX_LAST)
519    return true;
520  return false;
521}
522
523Histable *
524IndexObject::convertto (Histable_type type, Histable *ext)
525{
526  if (type == INDEXOBJ)
527    return this;
528  if (obj)
529    return obj->convertto (type, ext);
530  return NULL;
531}
532
533IndexObjType_t::IndexObjType_t ()
534{
535  type = 0;
536  name = NULL;
537  i18n_name = NULL;
538  index_expr_str = NULL;
539  index_expr = NULL;
540  mnemonic = 0;
541  short_description = NULL;
542  long_description = NULL;
543  memObj = NULL;
544}
545
546IndexObjType_t::~IndexObjType_t ()
547{
548  free (name);
549  free (i18n_name);
550  free (index_expr_str);
551  delete index_expr;
552  free (short_description);
553  free (long_description);
554}
555