1 /* Jitter: profiling subsystem.
2 
3    Copyright (C) 2020 Luca Saiu
4    Written by Luca Saiu
5 
6    This file is part of Jitter.
7 
8    Jitter is free software: you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation, either version 3 of the License, or
11    (at your option) any later version.
12 
13    Jitter is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with Jitter.  If not, see <http://www.gnu.org/licenses/>. */
20 
21 
22 #include <jitter/jitter.h>
23 
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include <jitter/jitter-arithmetic.h>
28 #include <jitter/jitter-human-prefix.h>
29 #include <jitter/jitter-profile.h>
30 #include <jitter/jitter-malloc.h>
31 
32 
33 
34 
35 /* Runtime data structures.
36  * ************************************************************************** */
37 
38 void
jitter_profile_runtime_initialize(const struct jitter_vm * vm,struct jitter_profile_runtime * prd)39 jitter_profile_runtime_initialize (const struct jitter_vm *vm,
40                                    struct jitter_profile_runtime *prd)
41 {
42   struct jitter_count_profile_runtime *count_rdp
43     = & prd->count_profile_runtime;
44   struct jitter_sample_profile_runtime *sample_rdp
45     = & prd->sample_profile_runtime;
46   if (vm->configuration->instrumentation & jitter_vm_instrumentation_count)
47     count_rdp->counts
48       = jitter_xmalloc (sizeof (uint64_t) * vm->specialized_instruction_no);
49   else
50     count_rdp->counts = NULL; /* Out of defensiveness. */
51 
52   if (vm->configuration->instrumentation & jitter_vm_instrumentation_sample)
53     sample_rdp->counts
54       = jitter_xmalloc (sizeof (uint32_t) * vm->specialized_instruction_no);
55   else
56     sample_rdp->counts = NULL; /* Out of defensiveness. */
57 
58   /* Set all the counts to zero. */
59   jitter_profile_runtime_clear (vm, prd);
60 }
61 
62 void
jitter_profile_runtime_finalize(const struct jitter_vm * vm,struct jitter_profile_runtime * prd)63 jitter_profile_runtime_finalize (const struct jitter_vm *vm,
64                                  struct jitter_profile_runtime *prd)
65 {
66   struct jitter_count_profile_runtime *count_rdp
67     = & prd->count_profile_runtime;
68   struct jitter_sample_profile_runtime *sample_rdp
69     = & prd->sample_profile_runtime;
70   if (vm->configuration->instrumentation & jitter_vm_instrumentation_count)
71     free (count_rdp->counts);
72   if (vm->configuration->instrumentation & jitter_vm_instrumentation_sample)
73     free (sample_rdp->counts);
74 }
75 
76 struct jitter_profile_runtime *
jitter_profile_runtime_make(const struct jitter_vm * vm)77 jitter_profile_runtime_make (const struct jitter_vm *vm)
78 {
79   struct jitter_profile_runtime *res
80     = jitter_xmalloc (sizeof (struct jitter_profile_runtime));
81   jitter_profile_runtime_initialize (vm, res);
82   return res;
83 }
84 
85 void
jitter_profile_runtime_destroy(const struct jitter_vm * vm,struct jitter_profile_runtime * pd)86 jitter_profile_runtime_destroy (const struct jitter_vm *vm,
87                                 struct jitter_profile_runtime *pd)
88 {
89   jitter_profile_runtime_finalize (vm, pd);
90   free (pd);
91 }
92 
93 void
jitter_profile_runtime_clear(const struct jitter_vm * vm,struct jitter_profile_runtime * prd)94 jitter_profile_runtime_clear (const struct jitter_vm *vm,
95                               struct jitter_profile_runtime *prd)
96 {
97   struct jitter_count_profile_runtime *count_rdp
98     = & prd->count_profile_runtime;
99   struct jitter_sample_profile_runtime *sample_rdp
100     = & prd->sample_profile_runtime;
101   int i;
102   if (vm->configuration->instrumentation & jitter_vm_instrumentation_count)
103     for (i = 0; i < vm->specialized_instruction_no; i ++)
104       count_rdp->counts [i] = 0;
105   if (vm->configuration->instrumentation & jitter_vm_instrumentation_sample)
106     for (i = 0; i < vm->specialized_instruction_no; i ++)
107       sample_rdp->counts [i] = 0;
108   sample_rdp->current_specialized_instruction_opcode
109     = -1 /* Out of defensiveness. */;
110   sample_rdp->sample_no = 0;
111 }
112 
113 void
jitter_profile_runtime_merge_from(const struct jitter_vm * vm,struct jitter_profile_runtime * to,const struct jitter_profile_runtime * from)114 jitter_profile_runtime_merge_from (const struct jitter_vm *vm,
115                                    struct jitter_profile_runtime *to,
116                                    const struct jitter_profile_runtime *from)
117 {
118   struct jitter_count_profile_runtime *to_count_rdp
119     = & to->count_profile_runtime;
120   struct jitter_sample_profile_runtime *to_sample_rdp
121     = & to->sample_profile_runtime;
122   const struct jitter_count_profile_runtime *from_count_rdp
123     = & from->count_profile_runtime;
124   const struct jitter_sample_profile_runtime *from_sample_rdp
125     = & from->sample_profile_runtime;
126   int i;
127   if (vm->configuration->instrumentation & jitter_vm_instrumentation_count)
128     for (i = 0; i < vm->specialized_instruction_no; i ++)
129       to_count_rdp->counts [i]
130         += from_count_rdp->counts [i];
131   if (vm->configuration->instrumentation & jitter_vm_instrumentation_sample)
132     for (i = 0; i < vm->specialized_instruction_no; i ++)
133       to_sample_rdp->counts [i]
134         += from_sample_rdp->counts [i];
135   to_sample_rdp->current_specialized_instruction_opcode
136     = -1 /* Out of defensiveness. */;
137   to_sample_rdp->sample_no
138     += from_sample_rdp->sample_no;
139 }
140 
141 
142 
143 
144 /* Processed data structures.
145  * ************************************************************************** */
146 
147 /* A comparison function between struct jitter_profile_item objects, for
148    qsort. */
149 static int
jitter_profile_item_compare(const void * pa,const void * pb)150 jitter_profile_item_compare (const void *pa, const void *pb)
151 {
152   const struct jitter_profile_item *ia = pa;
153   const struct jitter_profile_item *ib = pb;
154 
155   /* Notice that we sort in descending order.
156      It would be nice to be able to just return the result of a subtraction, but
157      here there is concrete danger of affecting the sign bit by truncating a
158      wider number into an int. */
159   if (ia->total_run_time_in_seconds > ib->total_run_time_in_seconds)
160     return -1;
161   else if (ia->total_run_time_in_seconds < ib->total_run_time_in_seconds)
162     return 1;
163   else
164     {
165       /* Secondary sort criterion */
166       if (ia->execution_count > ib->execution_count)
167         return -1;
168       else if (ia->execution_count < ib->execution_count)
169         return 1;
170       else
171         /* Tertiary sort criterion */
172         return strcmp (ia->name, ib->name);
173     }
174 }
175 
176 /* Sort the item of the given profile as per the comment before the data
177    structure. */
178 static void
jitter_profile_sort(const struct jitter_vm * vm,struct jitter_profile * p)179 jitter_profile_sort (const struct jitter_vm *vm, struct jitter_profile *p)
180 {
181   qsort (p->items, p->item_no, sizeof (struct jitter_profile_item),
182          jitter_profile_item_compare);
183 }
184 
185 /* Return a fresh well-initialised but empty profile. */
186 static struct jitter_profile *
jitter_profile_make_empty(void)187 jitter_profile_make_empty (void)
188 {
189   struct jitter_profile *res = jitter_xmalloc (sizeof (struct jitter_profile));
190   res->items = NULL;
191   res->item_no = 0;
192   return res;
193 }
194 
195 /* Common code factoring jitter_profile_specialized_from_runtime and
196    jitter_profile_unspecialized_from_runtime . */
197 static struct jitter_profile *
jitter_profile_from_runtime(const struct jitter_vm * vm,const struct jitter_profile_runtime * rp,bool specialized)198 jitter_profile_from_runtime (const struct jitter_vm *vm,
199                              const struct jitter_profile_runtime *rp,
200                              bool specialized)
201 {
202   /* If the runtime profile was NULL, which means that the VM was not configured
203      for profiling, return an empty profile as the result. */
204   if (rp == NULL)
205     return jitter_profile_make_empty ();
206 
207   /* The structure in struct jitter_profile_runtime is not really appropriate
208      for unspecialised instructions.  In the case of unspecialised instructions
209      we will build a structure of the same shape, but with information about
210      each specialisation of each unspecialised instruction joined together. */
211   struct jitter_profile_runtime *origin;
212   size_t in_element_no;
213   if (specialized)
214     {
215       in_element_no = vm->specialized_instruction_no;
216       origin = (struct jitter_profile_runtime *) rp;
217     }
218   else
219     {
220       in_element_no
221         = vm->meta_instruction_no + 1 /* One more element for the case of
222                                          specialised instructions with no
223                                          matching unspecialised version. */;
224       /* Make a new profile runtime.  Its content will be blank, and we will
225          rely on that by *adding* to the current value of each element, rather
226          than simply setting.  Its indices will be unspecialised instruction
227          opcodes *plus one*; the index 0 is for specialised instructions with
228          no unspecialised counterpart, which are recorded as having opcode -1
229          in vm->specialized_instruction_to_unspecialized_instruction . */
230       origin = jitter_profile_runtime_make (vm);
231       int specialized_i;
232       for (specialized_i = 0;
233            specialized_i < vm->specialized_instruction_no;
234            specialized_i ++)
235         {
236           int unspecidalized_opcode
237             = vm->specialized_instruction_to_unspecialized_instruction
238                  [specialized_i];
239           int index = unspecidalized_opcode + 1 /* See the comment above. */;
240           if (vm->configuration->instrumentation
241               & jitter_vm_instrumentation_count)
242             origin->count_profile_runtime.counts [index]
243               += rp->count_profile_runtime.counts [specialized_i];
244           if (vm->configuration->instrumentation
245               & jitter_vm_instrumentation_sample)
246           origin->sample_profile_runtime.counts [index]
247             += rp->sample_profile_runtime.counts [specialized_i];
248         }
249     }
250   struct jitter_count_profile_runtime *count_rdp
251     = & origin->count_profile_runtime;
252   struct jitter_sample_profile_runtime *sample_rdp
253     = & origin->sample_profile_runtime;
254 
255   struct jitter_profile *res = jitter_xmalloc (sizeof (struct jitter_profile));
256   res->items
257     = jitter_xmalloc (/* It is harmless to allocate more space than we need. */
258                       vm->specialized_instruction_no
259                       * sizeof (struct jitter_profile_item));
260 
261   int ini, outi = 0;
262   for (ini = 0; ini < in_element_no; ini ++)
263     {
264       bool interesting = false;
265       if (vm->configuration->instrumentation & jitter_vm_instrumentation_count
266           && count_rdp->counts [ini] > 0)
267         interesting = true;
268       if (vm->configuration->instrumentation & jitter_vm_instrumentation_sample
269           && sample_rdp->counts [ini] > 0)
270         interesting = true;
271       if (interesting)
272         {
273           const char *name;
274           if (specialized)
275             name = vm->specialized_instruction_names [ini];
276           else if (ini == 0)
277             name = "<specialized only>";
278           else
279             name = vm->meta_instructions [ini - 1].name;
280           res->items [outi].name = name;
281           if (vm->configuration->instrumentation
282               & jitter_vm_instrumentation_count)
283             res->items [outi].execution_count = count_rdp->counts [ini];
284           else
285             res->items [outi].execution_count = 0;
286           if (vm->configuration->instrumentation
287               & jitter_vm_instrumentation_sample)
288             res->items [outi].total_run_time_in_seconds
289               = (sample_rdp->counts [ini]
290                  * (JITTER_PROFILE_SAMPLE_PERIOD_IN_MILLISECONDS / 1000.0));
291           else
292             res->items [outi].total_run_time_in_seconds = 0;
293           outi ++;
294         }
295     }
296   res->item_no = outi;
297 
298   /* If we used a temporary structure for unspecialised instructions we can
299      dispose of it now. */
300   if (! specialized)
301     jitter_profile_runtime_destroy (vm, origin);
302 
303   /* Sort the output structure as per the specification in the header. */
304   jitter_profile_sort (vm, res);
305   return res;
306 }
307 
308 struct jitter_profile *
jitter_profile_specialized_from_runtime(const struct jitter_vm * vm,const struct jitter_profile_runtime * rp)309 jitter_profile_specialized_from_runtime (const struct jitter_vm *vm,
310                                          const struct jitter_profile_runtime *rp)
311 {
312   return jitter_profile_from_runtime (vm, rp, true);
313 }
314 
315 struct jitter_profile *
jitter_profile_unspecialized_from_runtime(const struct jitter_vm * vm,const struct jitter_profile_runtime * rp)316 jitter_profile_unspecialized_from_runtime (const struct jitter_vm *vm,
317                                            const struct jitter_profile_runtime
318                                            *rp)
319 {
320   return jitter_profile_from_runtime (vm, rp, false);
321 }
322 
323 void
jitter_profile_destroy(struct jitter_profile * p)324 jitter_profile_destroy (struct jitter_profile *p)
325 {
326   free (p->items);
327   free (p);
328 }
329 
330 
331 
332 
333 /* Printing.
334  * ************************************************************************** */
335 
336 /* Like jitter_mutable_routine_begin_class. */
337 static void
jitter_profile_begin_class(jitter_print_context ctx,const struct jitter_vm * vm,const char * suffix)338 jitter_profile_begin_class (jitter_print_context ctx,
339                             const struct jitter_vm *vm,
340                             const char *suffix)
341 {
342   char *prefix = vm->configuration->lower_case_prefix;
343   size_t size = strlen (prefix) + 1 + strlen (suffix) + 1;
344   char *buffer = jitter_xmalloc (size);
345   sprintf (buffer, "%s-%s", prefix, suffix);
346   jitter_print_begin_class (ctx, buffer);
347   free (buffer);
348 }
349 
350 /* Print a warning saying that the VM is not instrumented for profiling. */
351 static void
jitter_profile_print_uninstrumented(jitter_print_context ct,const struct jitter_vm * vm)352 jitter_profile_print_uninstrumented (jitter_print_context ct,
353                                      const struct jitter_vm *vm)
354 {
355   jitter_profile_begin_class (ct, vm, "warning");
356   jitter_print_char_star (ct, "[Profiling instrumentation not enabled for ");
357   jitter_print_char_star (ct, vm->configuration->lower_case_prefix);
358   jitter_print_char_star (ct, "; you may want to recompile with the ");
359   jitter_print_char_star (ct, "macros JITTER_PROFILE_COUNT and ");
360   jitter_print_char_star (ct, "JITTER_PROFILE_SAMPLE defined.]\n");
361   jitter_print_end_class (ct);
362 }
363 
364 /* The common logic factoring jitter_maximal_name_length_in and
365    jitter_maximal_count_width_in . */
366 #define JITTER_MAXIMUM_IN_ITEMS(the_items, expression, the_element_no)   \
367   do                                                                     \
368     {                                                                    \
369       const struct jitter_profile_item *_jitter_items = (the_items);     \
370       size_t _jitter_element_no = (the_element_no);                      \
371       size_t _jitter_candidate_maximal_length = 0;                       \
372       int _jitter_i;                                                     \
373       for (_jitter_i = 0; _jitter_i < _jitter_element_no; _jitter_i ++)  \
374         {                                                                \
375           size_t _jitter_length = (expression);                          \
376           if (_jitter_length > _jitter_candidate_maximal_length)         \
377             _jitter_candidate_maximal_length = _jitter_length;           \
378         }                                                                \
379       return _jitter_candidate_maximal_length;                           \
380     }                                                                    \
381   while (false)
382 
383 /* Return the maximum name length in the pointed array of struct
384    jitter_profile_item values. */
385 static
jitter_maximal_name_length_in(const struct jitter_profile_item * items,size_t element_no)386 size_t jitter_maximal_name_length_in (const struct jitter_profile_item *items,
387                                       size_t element_no)
388 {
389   JITTER_MAXIMUM_IN_ITEMS (items,
390                            strlen (_jitter_items [_jitter_i].name),
391                            element_no);
392 }
393 
394 /* Return the maximum count width in the pointed array of struct
395    jitter_profile_item values. */
396 static
jitter_maximal_count_width_in(const struct jitter_profile_item * items,size_t element_no)397 size_t jitter_maximal_count_width_in (const struct jitter_profile_item *items,
398                                       size_t element_no)
399 {
400   JITTER_MAXIMUM_IN_ITEMS (items,
401                            jitter_digit_no_unsigned_radix_10
402                               (_jitter_items [_jitter_i].execution_count),
403                            element_no);
404 }
405 
406 void
jitter_profile_runtime_print_specialized(jitter_print_context ct,const struct jitter_vm * vm,const struct jitter_profile_runtime * prd)407 jitter_profile_runtime_print_specialized (jitter_print_context ct,
408                                           const struct jitter_vm *vm,
409                                           const struct jitter_profile_runtime
410                                           *prd)
411 {
412   struct jitter_profile *p = jitter_profile_specialized_from_runtime (vm, prd);
413   jitter_profile_print (ct, vm, p);
414   jitter_profile_destroy (p);
415 }
416 
417 void
jitter_profile_runtime_print_unspecialized(jitter_print_context ct,const struct jitter_vm * vm,const struct jitter_profile_runtime * prd)418 jitter_profile_runtime_print_unspecialized (jitter_print_context ct,
419                                             const struct jitter_vm *vm,
420                                             const struct jitter_profile_runtime
421                                             *prd)
422 {
423   struct jitter_profile *p = jitter_profile_unspecialized_from_runtime (vm, prd);
424   jitter_profile_print (ct, vm, p);
425   jitter_profile_destroy (p);
426 }
427 
428 void
jitter_profile_print(jitter_print_context ct,const struct jitter_vm * vm,const struct jitter_profile * p)429 jitter_profile_print (jitter_print_context ct,
430                       const struct jitter_vm *vm,
431                       const struct jitter_profile *p)
432 {
433   /* If the code was not instrumented print a warning and do nothing else. */
434   if (vm->configuration->instrumentation == jitter_vm_instrumentation_none)
435     {
436       jitter_profile_print_uninstrumented (ct, vm);
437       return;
438     }
439 
440   size_t element_no = p->item_no;
441   struct jitter_profile_item *items = p->items;
442 
443   uint64_t count_sum = 0;
444   double time_sum = 0;
445   int i;
446   for (i = 0; i < element_no; i ++)
447     {
448       count_sum += items [i].execution_count;
449       time_sum += items [i].total_run_time_in_seconds;
450     }
451   size_t maximal_name_length = jitter_maximal_name_length_in (items, element_no);
452   size_t maximal_count_width = jitter_maximal_count_width_in (items, element_no);
453   for (i = 0; i < element_no; i ++)
454     {
455       struct jitter_profile_item *item = items + i;
456 
457       jitter_profile_begin_class (ct, vm, "profile-instruction");
458       jitter_print_char_star (ct, item->name);
459       jitter_print_end_class (ct);
460 
461       /* The printed ratio may be a run time ratio, available if sampling, of
462          otherwise an execution count ratio. */
463       double ratio;
464       if (vm->configuration->instrumentation & jitter_vm_instrumentation_sample)
465         ratio = item->total_run_time_in_seconds / time_sum;
466       else
467         ratio = item->execution_count / count_sum;
468 
469       /* Print enough additional spaces to get to the width of the longest
470          instruction name, plus one space for separation, plus enough additional
471          spaces so that, adding the count width, we get to the maximum width. */
472       size_t name_length = strlen (item->name);
473       size_t count_width = jitter_digit_no_radix_10 (item->execution_count);
474       int j;
475       for (j = name_length;
476            j < maximal_name_length + 1 + maximal_count_width - count_width;
477            j ++)
478         jitter_print_char (ct, ' ');
479       jitter_print_char (ct, ' ');
480 
481       /* Print the execution count, if available. */
482       if (vm->configuration->instrumentation & jitter_vm_instrumentation_count)
483         {
484           jitter_profile_begin_class (ct, vm, "profile-execution-count");
485           jitter_print_ulong_long (ct, 10, item->execution_count);
486           jitter_print_end_class (ct);
487           jitter_print_char (ct, ' ');
488         }
489 
490       /* Print the run time if available and greater than zero; if the execution
491          count is also available print the average execution time, otherwise the
492          total. */
493       if (item->total_run_time_in_seconds > 0)
494         {
495           /* Print the number, aligned to the right. */
496           double run_time_ = item->total_run_time_in_seconds;
497           if (vm->configuration->instrumentation
498               & jitter_vm_instrumentation_count)
499             /* This is safe: it is impossible, if an instruction was executed at
500                least once (its run time is positive) and we have execution
501                counters available, which are exact, for the count to be zero. */
502             run_time_ /= item->execution_count;
503           JITTER_HUMAN_READABLE_ (run_time, run_time_, false);
504           char run_time_text [100];
505           sprintf (run_time_text, "%8.3f%1ss", run_time, run_time_prefix);
506           jitter_profile_begin_class (ct, vm, "profile-run-time");
507           jitter_print_char_star (ct, run_time_text);
508           jitter_print_end_class (ct);
509           jitter_print_char (ct, ' ');
510 
511           /* Print the ratio as a percentage, aligned to the right. */
512           char percentage [10];
513           sprintf (percentage, "%5.1f%%", ratio * 100);
514           jitter_profile_begin_class (ct, vm, "profile-time-ratio");
515           jitter_print_end_class (ct);
516           jitter_print_char_star (ct, percentage);
517         }
518       jitter_print_char (ct, '\n');
519     }
520 }
521