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