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 <strings.h>
23#include "DerivedMetrics.h"
24#include "util.h"
25
26enum opType
27{
28  opNULL,
29  opPrimitive,
30  opDivide
31};
32
33class definition
34{
35public:
36  definition();
37  ~definition();
38  char *name;
39  char *def;
40  opType op;
41  definition *arg1;
42  definition *arg2;
43  int index;
44};
45
46definition::definition ()
47{
48  name = def = NULL;
49  arg1 = arg2 = NULL;
50}
51
52definition::~definition ()
53{
54  free (name);
55  free (def);
56}
57
58DerivedMetrics::DerivedMetrics ()
59{
60  items = new Vector<definition*>;
61}
62
63DerivedMetrics::~DerivedMetrics ()
64{
65  Destroy (items);
66}
67
68definition *
69DerivedMetrics::add_definition (char *_name, char *_username, char *_def)
70{
71  definition *p;
72
73  // if the name doesn't matter, maybe there is a duplicate we can use
74  if (_name == NULL)
75    {
76      int i;
77      Vec_loop (definition*, items, i, p)
78      {
79	if (strcmp (p->def, _def) == 0)
80	  return p;
81      }
82    }
83
84  p = new definition;
85  p->name = dbe_strdup (_name);
86  p->def = dbe_strdup (_def);
87
88  // parse the definition
89  if (strchr (_def, '/') == NULL)
90    {
91      // it's a primitive metric
92      p->op = opPrimitive;
93      p->arg1 = p->arg2 = NULL;
94
95    }
96  else
97    {
98      // it's some operation on arguments
99      p->op = opDivide;
100      char *op_ptr = strchr (p->def, '/');
101      *op_ptr = 0;
102      p->arg1 = add_definition (NULL, NULL, p->def);
103      *op_ptr = '/';
104      p->arg2 = add_definition (NULL, NULL, op_ptr + 1);
105    }
106  p->index = items->size ();
107  items->append (p);
108  return p;
109}
110
111int *
112DerivedMetrics::construct_map (Vector<Metric*> *mitems, BaseMetric::SubType st, char *expr_spec)
113{
114  if (items == NULL)
115    return NULL;
116  int ndm = items->size ();
117  if (ndm == 0)
118    return NULL;
119  int nmetrics = mitems->size ();
120
121  // allocate arrays for the mapping between derived metrics and requested values
122  int *map = (int *) malloc (ndm * sizeof (int));
123
124  // map derived metrics to requested metrics    // EUGENE explain this more clearly
125  //   0  means not mapped
126  //  >0  means primitive metric maps to map-1
127  //  <0  means  derived  metric maps to 1-map
128  int ndm_requested = 0;
129  for (int idm = 0; idm < ndm; idm++)
130    {
131      definition *defdm = items->fetch (idm);
132      map[idm] = 0;
133
134      // figure out what name to use for this derived metric
135      char *dname;
136      if (defdm->op == opPrimitive)
137	dname = defdm->def;
138      else
139	{
140	  dname = defdm->name;
141	  if (dname == NULL) break;
142	}
143
144      // look for this name among metrics
145      int im;
146      for (im = 0; im < nmetrics; im++)
147	{
148	  Metric *m = mitems->fetch (im);
149	  if (strcmp (dname, m->get_cmd ()) == 0 && m->get_subtype () == st)
150	    // apparent match, but let's check comparison mode
151	    if (dbe_strcmp (expr_spec, m->get_expr_spec ()) == 0)
152	      break;
153	}
154
155      // encode the mapping
156      if (im >= nmetrics)
157	map[idm] = 0; // does not map to requested metrics
158      else if (defdm->op == opPrimitive)
159	map[idm] = +1 + im; // encode as a positive index
160      else
161	{
162	  map[idm] = -1 - im; // encode as a negative index
163	  ndm_requested++;
164	}
165    }
166  if (ndm_requested == 0)
167    {
168      free (map);
169      map = NULL;
170    }
171  return map;
172}
173
174void
175DerivedMetrics::fill_dependencies (definition *def, int *vec)
176{
177  switch (def->op)
178    {
179    case opPrimitive:
180      vec[def->index] = 1;
181      break;
182    case opDivide:
183      fill_dependencies (def->arg1, vec);
184      fill_dependencies (def->arg2, vec);
185      break;
186    default:
187      break;
188    }
189}
190
191Vector<definition*> *
192DerivedMetrics::get_dependencies (definition *def)
193{
194  int n = items->size ();
195
196  // zero out a vector representing definitions
197  int *vec = (int *) malloc (n * sizeof (int));
198  for (int i = 0; i < n; i++)
199    vec[i] = 0;
200  fill_dependencies (def, vec);
201
202  // construct the dependency vector
203  Vector<definition*> *dependencies = new Vector<definition*>;
204  for (int i = 0; i < n; i++)
205    if (vec[i] == 1)
206      dependencies->append (items->fetch (i));
207  free (vec);
208  return dependencies;
209}
210
211void
212DerivedMetrics::dump (FILE *dis_file, int verbosity)
213{
214  int i;
215  definition *item;
216
217  // deal with the possibility that names might be NULL
218  const char *UNNAMED = "(unnamed)";
219#define NAME(x) ( (x) ? (x) : UNNAMED)
220
221  Vec_loop (definition*, items, i, item)
222  {
223    // at low verbosity, skip over some items
224    if (verbosity == 0)
225      {
226	if (item->name == NULL)
227	  continue;
228	if (strcmp (item->name, item->def) && item->op == opPrimitive)
229	  continue;
230      }
231
232    // dump the definition
233    switch (item->op)
234      {
235      case opPrimitive:
236	fprintf (dis_file, "%s [%s] is a primitive metric\n", NAME (item->name),
237		 item->def);
238	break;
239      case opDivide:
240	fprintf (dis_file, "%s [%s] = %s [%s] / %s [%s]\n", NAME (item->name),
241		 item->def, NAME (item->arg1->name), item->arg1->def,
242		 NAME (item->arg2->name), item->arg2->def);
243	break;
244      default:
245	fprintf (dis_file, "%s [%s] has an unrecognized op %d\n",
246		 NAME (item->name), item->def, item->op);
247	break;
248      }
249  }
250}
251
252double
253DerivedMetrics::eval_one_item (definition *def, int *map, double *values)
254{
255  switch (def->op)
256    {
257    case opNULL:
258      fprintf (stderr, GTXT ("cannot eval NULL expression\n"));
259      return 0.;
260    case opPrimitive:
261      {
262	int ival = map[def->index];
263	if (ival <= 0) return 0.;
264	ival--;
265	return values[ival];
266      }
267    case opDivide:
268      {
269	double x1 = eval_one_item (def->arg1, map, values);
270	double x2 = eval_one_item (def->arg2, map, values);
271	if (x2 == 0) return 0.;
272	return (x1 / x2);
273      }
274    default:
275      fprintf (stderr, GTXT ("unknown expression\n"));
276      return 0.;
277    }
278}
279
280int
281DerivedMetrics::eval (int *map, double *values)
282{
283  for (int i = 0, n = items->size (); i < n; i++)
284    {
285      if (map[i] < 0)
286	{
287	  int ival = -1 - map[i];
288	  values[ival] = eval_one_item (items->fetch (i), map, values);
289	}
290    }
291  return 0;
292}
293
294