blob: 98d36bb6435cdabd31f7e36e2acf3c0887ff8801 [file] [log] [blame]
Elliott Hughesed398002017-06-21 14:41:24 -07001
2/*--------------------------------------------------------------------*/
3/*--- An xtree, tree of stacktraces with data m_xtree.c ---*/
4/*--------------------------------------------------------------------*/
5
6/*
7 This file is part of Valgrind, a dynamic binary instrumentation
8 framework.
9
10 Copyright (C) 2016-2017 Philippe Waroquiers
11
12 This code generalises the XTree idea that was implemented in
13 the massif tool in Valgrind versions <= 3.12, which is
14 Copyright (C) 2005-2017 Nicholas Nethercote
15 njn@valgrind.org
16
17 The XTree implementation in this file is however implemented completely
18 differently. Some code has been re-used for the production of the
19 massif file header (e.g. FP_cmd function).
20
21 This program is free software; you can redistribute it and/or
22 modify it under the terms of the GNU General Public License as
23 published by the Free Software Foundation; either version 2 of the
24 License, or (at your option) any later version.
25
26 This program is distributed in the hope that it will be useful, but
27 WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
29 General Public License for more details.
30
31 You should have received a copy of the GNU General Public License
32 along with this program; if not, write to the Free Software
33 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
34 02111-1307, USA.
35
36 The GNU General Public License is contained in the file COPYING.
37*/
38
39#include "pub_core_basics.h"
40#include "pub_core_debuglog.h"
41#include "pub_core_clientstate.h"
42#include "pub_core_stacktrace.h"
43#include "pub_core_execontext.h"
44#include "pub_core_libcbase.h"
45#include "pub_core_libcassert.h"
46#include "pub_core_libcfile.h"
47#include "pub_core_libcprint.h"
48#include "pub_core_libcproc.h"
49#include "pub_core_hashtable.h"
50#include "pub_core_mallocfree.h"
51#include "pub_core_options.h"
52#include "pub_core_debuginfo.h"
53#include "pub_core_deduppoolalloc.h"
54#include "pub_core_xtree.h" /* self */
55
56#define DMSG(level, ...) (level <= VG_(debugLog_getLevel)() ? \
57 VG_(dmsg)(__VA_ARGS__) \
58 : 0)
59
60/* Defines the relevant part of an ec. This is shared between an xt
61 and its snapshots (see XT_shared XArray of xec). */
62typedef
63 struct _xec {
64 ExeContext* ec;
65 UShort top; // The first ips of ec to take into account.
66 UShort n_ips_sel; // The nr of ips from top to take into account.
67 }
68 xec;
69
70/* XT_shared maintains the information shared between an XT and all
71 its snapshots. */
72typedef
73 struct _XT_shared {
74 UWord nrRef; /* nr of XTrees referencing this shared memory. */
75
76 Alloc_Fn_t alloc_fn; /* alloc fn (nofail) */
77 const HChar* cc; /* cost centre for alloc */
78 Free_Fn_t free_fn; /* free fn */
79
80 /* The data associated to each ec is stored in 2 arrays:
81 an xec array, shared between an xt and all its snapshots.
82 a data array, private to each XTree.
83 For an ec with an ECU ecu, d4ecu2xecu[ecu/4] gives the offset in
84 xec and data arrays where the ec information is located (this
85 indirection is used to avoid huge xec and data arrays, in
86 case an XTree contains data only for a small number of ec.
87 The offset in the xec and data array is used as xtree ec unique
88 id i.e. an xecu. */
89
90 UInt d4ecu2xecu_sz; /* size of d4ecu2xecu (in nr of elements). */
91 UInt* d4ecu2xecu;
92
93 /* ec information common to an xt and its snapshots. */
94 XArray* xec; /* XArray of xec, indexed by xecu (== d4ecu2xecu[ecu/4]). */
95
96 /* XArray of xecu, sorted by StackTrace ips[top..top+n_ips_sel-1].
97 See ips_order_cmp. */
98 XArray* ips_order_xecu;
99 } XT_shared;
100
101/* NO_OFFSET indicates in d4ecu2xecu there is no data (yet) for this ec
102 (with the index ecu/4). */
103#define NO_OFFSET 0xffffffff
104
105static XT_shared* new_XT_shared (Alloc_Fn_t alloc_fn,
106 const HChar* cc,
107 void (*free_fn)(void*))
108{
109 XT_shared* shared;
110
111 vg_assert(alloc_fn);
112 vg_assert(cc);
113 vg_assert(free_fn);
114 shared = alloc_fn(cc, sizeof(*shared));
115 shared->nrRef = 0;
116 shared->alloc_fn = alloc_fn;
117 shared->cc = cc;
118 shared->free_fn = free_fn;
119
120 shared->d4ecu2xecu_sz = 0;
121 shared->d4ecu2xecu = NULL;
122 shared->xec = VG_(newXA)(alloc_fn, cc, free_fn, sizeof(xec));
123 shared->ips_order_xecu = NULL; // Allocated when needed.
124
125 return shared;
126}
127
128static void delete_XT_shared (XT_shared* shared)
129{
130 vg_assert(shared->nrRef == 0);
131 shared->free_fn(shared->d4ecu2xecu);
132 VG_(deleteXA)(shared->xec);
133 if (shared->ips_order_xecu != NULL)
134 VG_(deleteXA)(shared->ips_order_xecu);
135 shared->free_fn(shared);
136}
137
138/* Compare 2 entries in ips_order_xecu by StackTrace elements.
139 In case stack traces are of different length, an 'absent' ips is
140 considered smaller than any other address. */
141static XArray* xec_data_for_sort; // Needed to translate an xecu into an xec
142static Int ips_order_cmp(const void* vleft, const void* vright)
143{
144 const Xecu left_xecu = *(const Xecu*)vleft;
145 const Xecu right_xecu = *(const Xecu*)vright;
146 const xec* left = VG_(indexXA)(xec_data_for_sort, left_xecu);
147 const xec* right = VG_(indexXA)(xec_data_for_sort, right_xecu);
148 const StackTrace left_ips = VG_(get_ExeContext_StackTrace)(left->ec)
149 + left->top;
150 const StackTrace right_ips = VG_(get_ExeContext_StackTrace)(right->ec)
151 + right->top;
152 UInt i;
153
154 const UInt c_n_ips_sel = left->n_ips_sel < right->n_ips_sel
155 ? left->n_ips_sel : right->n_ips_sel;
156
157 // First see if we have a difference on the common nr of ips selected
158 for (i = 0; i < c_n_ips_sel; i++) {
159 if (left_ips[i] == right_ips[i]) continue;
160 if (left_ips[i] < right_ips[i]) return -1;
161 return 1;
162 }
163 // Common part is equal => compare lengths.
164 if (left->n_ips_sel < right->n_ips_sel) return -1;
165 if (left->n_ips_sel > right->n_ips_sel) return 1;
166 return 0;
167}
168
169// If needed, build or refresh shared->ips_order_xecu
170static void ensure_ips_order_xecu_valid(XT_shared* shared)
171{
172 UInt i;
173 UInt n_xecu;
174
175 if (shared->ips_order_xecu == NULL) {
176 shared->ips_order_xecu = VG_(newXA)(shared->alloc_fn, shared->cc,
177 shared->free_fn, sizeof(Xecu));
178 VG_(hintSizeXA)(shared->ips_order_xecu, VG_(sizeXA)(shared->xec));
179 VG_(setCmpFnXA)(shared->ips_order_xecu, ips_order_cmp);
180 }
181
182 if (VG_(sizeXA)(shared->xec) == VG_(sizeXA)(shared->ips_order_xecu))
183 return;
184
185 n_xecu = VG_(sizeXA)(shared->xec);
186 for (i = VG_(sizeXA)(shared->ips_order_xecu); i < n_xecu; i++)
187 VG_(addToXA)(shared->ips_order_xecu, &i);
188
189 xec_data_for_sort = shared->xec;
190 VG_(sortXA)(shared->ips_order_xecu);
191}
192
193static void addRef_XT_shared (XT_shared* shared)
194{
195 shared->nrRef++;
196}
197
198static UWord release_XT_shared(XT_shared* shared)
199{
200 UWord nrRef;
201
202 vg_assert(shared->nrRef > 0);
203 nrRef = --shared->nrRef;
204 if (nrRef == 0)
205 delete_XT_shared(shared);
206 return nrRef;
207}
208
209
210struct _XTree {
211 Alloc_Fn_t alloc_fn; /* alloc fn (nofail) */
212 const HChar* cc; /* cost centre for alloc */
213 Free_Fn_t free_fn; /* free fn */
214 Word dataSzB; /* data size in bytes */
215 XT_init_data_t init_data_fn;
216 XT_add_data_t add_data_fn;
217 XT_sub_data_t sub_data_fn;
218 XT_filter_IPs_t filter_IPs_fn;
219
220 XT_shared* shared;
221
222 HChar* tmp_data; /* temporary buffer, to insert new elements. */
223 XArray* data; /* of elements of size dataSzB */
224};
225
226
227XTree* VG_(XT_create) ( Alloc_Fn_t alloc_fn,
228 const HChar* cc,
229 Free_Fn_t free_fn,
230 Word dataSzB,
231 XT_init_data_t init_data_fn,
232 XT_add_data_t add_data_fn,
233 XT_sub_data_t sub_data_fn,
234 XT_filter_IPs_t filter_IPs_fn)
235{
236 XTree* xt;
237
238 /* check user-supplied info .. */
239 vg_assert(alloc_fn);
240 vg_assert(free_fn);
241 vg_assert(dataSzB >= 0);
242 vg_assert(init_data_fn);
243 vg_assert(add_data_fn);
244 vg_assert(sub_data_fn);
245
246 xt = alloc_fn(cc, sizeof(struct _XTree) );
247 xt->alloc_fn = alloc_fn;
248 xt->cc = cc;
249 xt->free_fn = free_fn;
250 xt->dataSzB = dataSzB;
251 xt->init_data_fn = init_data_fn;
252 xt->add_data_fn = add_data_fn;
253 xt->sub_data_fn = sub_data_fn;
254 xt->filter_IPs_fn = filter_IPs_fn;
255
256 xt->shared = new_XT_shared(alloc_fn, cc, free_fn);
257 addRef_XT_shared(xt->shared);
258 xt->tmp_data = alloc_fn(cc, xt->dataSzB);
259 xt->data = VG_(newXA)(alloc_fn, cc, free_fn, dataSzB);
260
261 return xt;
262}
263
264XTree* VG_(XT_snapshot)(XTree* xt)
265{
266 XTree* nxt;
267
268 vg_assert(xt);
269
270 nxt = xt->alloc_fn(xt->cc, sizeof(struct _XTree) );
271
272 *nxt = *xt;
273 addRef_XT_shared(nxt->shared);
274 nxt->tmp_data = nxt->alloc_fn(nxt->cc, nxt->dataSzB);
275 nxt->data = VG_(cloneXA)(nxt->cc, xt->data);
276
277 return nxt;
278}
279
280void VG_(XT_delete) ( XTree* xt )
281{
282 vg_assert(xt);
283
284 release_XT_shared(xt->shared);
285 xt->free_fn(xt->tmp_data);
286 VG_(deleteXA)(xt->data);
287 xt->free_fn(xt);
288}
289
290static Xecu find_or_insert (XTree* xt, ExeContext* ec)
291{
292
293 const UInt d4ecu = VG_(get_ECU_from_ExeContext)(ec) / 4;
294 XT_shared* shared = xt->shared;
295
296 /* First grow the d4ecu2xecu array if needed. */
297 if (d4ecu >= shared->d4ecu2xecu_sz) {
298 UInt old_sz = shared->d4ecu2xecu_sz;
299 UInt new_sz = (3 * d4ecu) / 2;
300
301 if (new_sz < 1000)
302 new_sz = 1000;
303 shared->d4ecu2xecu = VG_(realloc)(xt->cc, shared->d4ecu2xecu,
304 new_sz * sizeof(UInt));
305 shared->d4ecu2xecu_sz = new_sz;
306 for (UInt i = old_sz; i < new_sz; i++)
307 shared->d4ecu2xecu[i] = NO_OFFSET;
308 }
309
310 if (shared->d4ecu2xecu[d4ecu] == NO_OFFSET) {
311 xec xe;
312
313 xe.ec = ec;
314 if (xt->filter_IPs_fn == NULL) {
315 xe.top = 0;
316 xe.n_ips_sel = (UShort)VG_(get_ExeContext_n_ips)(xe.ec);
317 } else {
318 UInt top;
319 UInt n_ips_sel = VG_(get_ExeContext_n_ips)(xe.ec);
320 xt->filter_IPs_fn(VG_(get_ExeContext_StackTrace)(xe.ec), n_ips_sel,
321 &top, &n_ips_sel);
322 xe.top = (UShort)top;
323 xe.n_ips_sel = (UShort)n_ips_sel;
324 }
325 xt->init_data_fn(xt->tmp_data);
326 VG_(addToXA)(shared->xec, &xe);
327 shared->d4ecu2xecu[d4ecu] = (UInt)VG_(addToXA)(xt->data, xt->tmp_data);
328 }
329
330 return shared->d4ecu2xecu[d4ecu];
331}
332
333Xecu VG_(XT_add_to_ec) (XTree* xt, ExeContext* ec, const void* value)
334{
335 Xecu xecu = find_or_insert(xt, ec);
336 void* data = VG_(indexXA)(xt->data, xecu);
337
338 xt->add_data_fn(data, value);
339 return xecu;
340}
341
342Xecu VG_(XT_sub_from_ec) (XTree* xt, ExeContext* ec, const void* value)
343{
344 Xecu xecu = find_or_insert(xt, ec);
345 void* data = VG_(indexXA)(xt->data, xecu);
346
347 xt->sub_data_fn(data, value);
348 return xecu;
349}
350
351void VG_(XT_add_to_xecu) (XTree* xt, Xecu xecu, const void* value)
352{
353 void* data = VG_(indexXA)(xt->data, xecu);
354 xt->add_data_fn(data, value);
355}
356
357void VG_(XT_sub_from_xecu) (XTree* xt, Xecu xecu, const void* value)
358{
359 void* data = VG_(indexXA)(xt->data, xecu);
360 xt->sub_data_fn(data, value);
361}
362
363UInt VG_(XT_n_ips_sel) (XTree* xt, Xecu xecu)
364{
365 xec* xe = (xec*)VG_(indexXA)(xt->shared->xec, xecu);
366 return (UInt)xe->n_ips_sel;
367}
368
369ExeContext* VG_(XT_get_ec_from_xecu) (XTree* xt, Xecu xecu)
370{
371 xec* xe = (xec*)VG_(indexXA)(xt->shared->xec, xecu);
372 return xe->ec;
373}
374
375static VgFile* xt_open (const HChar* outfilename)
376{
377 VgFile* fp;
378
379 fp = VG_(fopen)(outfilename, VKI_O_CREAT|VKI_O_WRONLY|VKI_O_TRUNC,
380 VKI_S_IRUSR|VKI_S_IWUSR);
381 if (fp == NULL) {
382 VG_(message)(Vg_UserMsg,
383 "Error: can not open xtree output file `%s'\n",
384 outfilename);
385 }
386 return fp;
387}
388
389#define FP(format, args...) ({ VG_(fprintf)(fp, format, ##args); })
390
391// Print "cmd:" line.
392static void FP_cmd(VgFile* fp)
393{
394 UInt i;
395
396 FP("cmd: ");
397 FP("%s", VG_(args_the_exename));
398 for (i = 0; i < VG_(sizeXA)(VG_(args_for_client)); i++) {
399 HChar* arg = * (HChar**) VG_(indexXA)(VG_(args_for_client), i);
400 FP(" %s", arg);
401 }
402 FP("\n");
403}
404
405/* ----------- Callgrind output ------------------------------------------- */
406
407/* Output a callgrind format element in compressed format:
408 "name=(pos)" or "name=(pos) value" (if value_new)
409 or not compressed format: "name=value"
410 VG_(clo_xtree_compress_strings) indicates if the compressed format is used.
411 name is the format element (e.g. fl, fn, cfi, cfn, ...).
412 pos is the value dictionary position, used for compressed format.
413 value_new is True if this is the first usage of value. */
414static void FP_pos_str(VgFile* fp, const HChar* name, UInt pos,
415 const HChar* value, Bool value_new)
416{
417 if (!VG_(clo_xtree_compress_strings))
418 FP("%s=%s\n", name, value);
419 else if (value_new)
420 FP("%s=(%d) %s\n", name, pos, value);
421 else
422 FP("%s=(%d)\n", name, pos);
423}
424
425void VG_(XT_callgrind_print)
426 (XTree* xt,
427 const HChar* outfilename,
428 const HChar* events,
429 const HChar* (*img_value)(const void* value))
430{
431 UInt n_xecu;
432 XT_shared* shared = xt->shared;
433 VgFile* fp = xt_open(outfilename);
434 DedupPoolAlloc* fnname_ddpa;
435 DedupPoolAlloc* filename_ddpa;
436 HChar* filename_buf = NULL;
437 UInt filename_buf_size = 0;
438 const HChar* filename_dir;
439 const HChar* filename_name;
440
441 if (fp == NULL)
442 return;
443
444 fnname_ddpa = VG_(newDedupPA)(16000, 1, xt->alloc_fn,
445 "XT_callgrind_print.fn", xt->free_fn);
446 filename_ddpa = VG_(newDedupPA)(16000, 1, xt->alloc_fn,
447 "XT_callgrind_print.fl", xt->free_fn);
448
449 FP("# callgrind format\n");
450 FP("version: 1\n");
451 FP("creator: xtree-1\n");
452 FP("pid: %d\n", VG_(getpid)());
453 FP_cmd(fp);
454
455 /* Currently, we only need/support line positions. */
456 FP("\npositions:%s\n", " line");
457
458 /* Produce one "event:" line for each event, and the "events:" line. */
459 {
460 HChar strtok_events[VG_(strlen)(events)+1];
461 HChar* e;
462 HChar* ssaveptr;
463 HChar* p;
464
465 VG_(strcpy)(strtok_events, events);
466 for (e = VG_(strtok_r)(strtok_events, ",", &ssaveptr);
467 e != NULL;
468 e = VG_(strtok_r)(NULL, ",", &ssaveptr))
469 FP("event: %s\n", e);
470 FP("events:");
471 VG_(strcpy)(strtok_events, events);
472 for (e = VG_(strtok_r)(strtok_events, ",", &ssaveptr);
473 e != NULL;
474 e = VG_(strtok_r)(NULL, ",", &ssaveptr)) {
475 p = e;
476 while (*p) {
477 if (*p == ':')
478 *p = 0;
479 p++;
480 }
481 FP(" %s", e);
482 }
483 FP("\n");
484 }
485 xt->init_data_fn(xt->tmp_data); // to compute totals
486
487 n_xecu = VG_(sizeXA)(xt->data);
488 vg_assert (n_xecu <= VG_(sizeXA)(shared->xec));
489 for (Xecu xecu = 0; xecu < n_xecu; xecu++) {
490 xec* xe = (xec*)VG_(indexXA)(shared->xec, xecu);
491 if (xe->n_ips_sel == 0)
492 continue;
493
494 const HChar* img = img_value(VG_(indexXA)(xt->data, xecu));
495
496 // CALLED_FLF gets the Dir+Filename/Line number/Function name for ips[n]
497 // in the variables called_filename/called_linenum/called_fnname.
498 // The booleans called_filename_new/called_fnname_new are set to True
499 // the first time the called_filename/called_fnname are encountered.
500 // The called_filename_nr/called_fnname_nr are numbers identifying
501 // the strings called_filename/called_fnname.
502#define CALLED_FLF(n) \
503 if ((n) < 0 \
504 || !VG_(get_filename_linenum)(ips[(n)], \
505 &filename_name, \
506 &filename_dir, \
507 &called_linenum)) { \
508 filename_name = "UnknownFile???"; \
509 called_linenum = 0; \
510 } \
511 if ((n) < 0 \
512 || !VG_(get_fnname)(ips[(n)], &called_fnname)) { \
513 called_fnname = "UnknownFn???"; \
514 } \
515 { \
516 UInt needed_size = VG_(strlen)(filename_dir) + 1 \
517 + VG_(strlen)(filename_name) + 1; \
518 if (filename_buf_size < needed_size) { \
519 filename_buf_size = needed_size; \
520 filename_buf = VG_(realloc)(xt->cc, filename_buf, \
521 filename_buf_size); \
522 } \
523 } \
524 VG_(strcpy)(filename_buf, filename_dir); \
525 if (filename_buf[0] != '\0') { \
526 VG_(strcat)(filename_buf, "/"); \
527 } \
528 VG_(strcat)(filename_buf, filename_name); \
529 called_filename_nr = VG_(allocStrDedupPA)(filename_ddpa, \
530 filename_buf, \
531 &called_filename_new); \
532 called_filename = filename_buf; \
533 called_fnname_nr = VG_(allocStrDedupPA)(fnname_ddpa, \
534 called_fnname, \
535 &called_fnname_new);
536
537 /* Instead of unknown fnname ???, CALLED_FLF could use instead:
538 VG_(sprintf)(unknown_fn, "%p", (void*)ips[(n)]);
539 but that creates a lot of (useless) nodes at least for
540 valgrind self-hosting. */
541
542 if (img) {
543 const HChar* called_filename;
544 UInt called_filename_nr;
545 Bool called_filename_new; // True the first time we see this filename.
546 const HChar* called_fnname;
547 UInt called_fnname_nr;
548 Bool called_fnname_new; // True the first time we see this fnname.
549 UInt called_linenum;
550 UInt prev_linenum;
551
552 const Addr* ips = VG_(get_ExeContext_StackTrace)(xe->ec) + xe->top;
553 Int ips_idx = xe->n_ips_sel - 1;
554
555 if (0) {
556 VG_(printf)("entry img %s\n", img);
557 VG_(pp_ExeContext)(xe->ec);
558 VG_(printf)("\n");
559 }
560 xt->add_data_fn(xt->tmp_data, VG_(indexXA)(xt->data, xecu));
561 CALLED_FLF(ips_idx);
562 for (;
563 ips_idx >= 0;
564 ips_idx--) {
565 FP_pos_str(fp, "fl", called_filename_nr,
566 called_filename, called_filename_new);
567 FP_pos_str(fp, "fn", called_fnname_nr,
568 called_fnname, called_fnname_new);
569 if (ips_idx == 0)
570 FP("%d %s\n", called_linenum, img);
571 else
572 FP("%d\n", called_linenum); //no self cost.
573 prev_linenum = called_linenum;
574 if (ips_idx >= 1) {
575 CALLED_FLF(ips_idx-1);
576 FP_pos_str(fp, "cfi", called_filename_nr,
577 called_filename, called_filename_new);
578 FP_pos_str(fp, "cfn", called_fnname_nr,
579 called_fnname, called_fnname_new);
580 called_filename_new = False;
581 called_fnname_new = False;
582 /* Giving a call count of 0 allows kcachegrind to hide the calls
583 column. A call count of 1 means kcachegrind would show in the
584 calls column the nr of stacktrace containing this arc, which
585 is very confusing. So, the less bad is to give a 0 call
586 count. */
587 FP("calls=0 %d\n", called_linenum);
588 FP("%d %s\n", prev_linenum, img);
589 }
590 }
591 FP("\n");
592 }
593 }
594 /* callgrind format is not really fully supporting (yet?) execution trees:
595 in an execution tree, self and inclusive costs are identical, and
596 cannot be added together.
597 If no totals: line is given, callgrind_annotate calculates the addition
598 of all costs, and so gives a wrong totals.
599 Giving a totals: line solves this, but the user must give the option
600 --inclusive=yes (kind of hack) to have all the functions given
601 in the output file. */
602 FP("totals: %s\n", img_value(xt->tmp_data));
603 VG_(fclose)(fp);
604 VG_(deleteDedupPA)(fnname_ddpa);
605 VG_(deleteDedupPA)(filename_ddpa);
606 VG_(free)(filename_buf);
607}
608
609
610/* ----------- Massif output ---------------------------------------------- */
611
612/* For Massif output, some functions from the execontext are not output, a.o.
613 the allocation functions at the top of the stack and the functions below
614 main. So, the StackTrace of the execontexts in the xtree must be filtered.
615 Ms_Ec defines the subset of the stacktrace relevant for the report. */
616typedef
617 struct {
618 StackTrace ips; // ips and n_ips provides the subset of the xtree ec
619 UInt n_ips; // to use for a massif report.
620
621 SizeT report_value; // The value to report for this stack trace.
622 } Ms_Ec;
623
624/* Ms_Group defines (at a certain depth) a group of ec context that
625 have the same IPs at the given depth, and have the same 'parent'.
626 total is the sum of the values of all group elements.
627 A Ms_Group can also represent a set of ec contexts that do not
628 have the same IP, but that have each a total which is below the
629 significant size. Such a group has a NULL ms_ec, a zero group_io.
630 n_ec is the nr of insignificant ec that have been collected inside this
631 insignificant group, and total is the sum of all non significant ec
632 at the given depth. */
633typedef
634 struct {
635 Ms_Ec* ms_ec; // The group consists in ms_ec[0 .. n_ec-1]
636 Addr group_ip;
637 UInt n_ec;
638 SizeT total;
639 } Ms_Group;
640
641/* Compare 2 groups by total, to have bigger total first. */
642static Int ms_group_revcmp_total(const void* vleft, const void* vright)
643{
644 const Ms_Group* left = (const Ms_Group*)vleft;
645 const Ms_Group* right = (const Ms_Group*)vright;
646
647 // First reverse compare total
648 if (left->total > right->total) return -1;
649 if (left->total < right->total) return 1;
650
651 /* Equal size => compare IPs.
652 This (somewhat?) helps to have deterministic test results.
653 If this would change between platforms, then we should compare
654 function names/filename/linenr */
655 if (left->group_ip < right->group_ip) return -1;
656 if (left->group_ip > right->group_ip) return 1;
657 return 0;
658}
659
660/* Scan the addresses in ms_ec at the given depth.
661 On return,
662 *groups points to an array of Ms_Group sorted by total.
663 *n_groups is the nr of groups
664 The caller is responsible to free the allocated group array. */
665static void ms_make_groups (UInt depth, Ms_Ec* ms_ec, UInt n_ec, SizeT sig_sz,
666 UInt* n_groups, Ms_Group** groups)
667{
668 UInt i, g;
669 Addr cur_group_ip = 0;
670
671 *n_groups = 0;
672
673 /* Handle special case somewhat more efficiently */
674 if (n_ec == 0) {
675 *groups = NULL;
676 return;
677 }
678
679 /* Compute how many groups we have. */
680 for (i = 0; i < n_ec; i++) {
681 if (ms_ec[i].n_ips > depth
682 && (*n_groups == 0 || cur_group_ip != ms_ec[i].ips[depth])) {
683 (*n_groups)++;
684 cur_group_ip = ms_ec[i].ips[depth];
685 }
686 }
687
688 /* make the group array. */
689 *groups = VG_(malloc)("ms_make_groups", *n_groups * sizeof(Ms_Group));
690 i = 0;
691 for (g = 0; g < *n_groups; g++) {
692 while (ms_ec[i].n_ips <= depth)
693 i++;
694 cur_group_ip = ms_ec[i].ips[depth];
695 (*groups)[g].group_ip = cur_group_ip;
696 (*groups)[g].ms_ec = &ms_ec[i];
697 (*groups)[g].n_ec = 1;
698 (*groups)[g].total = ms_ec[i].report_value;
699 i++;
700 while (i < n_ec
701 && ms_ec[i].n_ips > depth
702 && cur_group_ip == ms_ec[i].ips[depth]) {
703 (*groups)[g].total += ms_ec[i].report_value;
704 i++;
705 (*groups)[g].n_ec++;
706 }
707 }
708
709 /* Search for insignificant groups, collect them all together
710 in the first insignificant group, and compact the group array. */
711 {
712 UInt insig1; // Position of first insignificant group.
713 UInt n_insig = 0; // Nr of insignificant groups found.
714
715 for (g = 0; g < *n_groups; g++) {
716 if ((*groups)[g].total < sig_sz) {
717 if (n_insig == 0) {
718 // First insig group => transform it into the special group
719 (*groups)[g].ms_ec = NULL;
720 (*groups)[g].group_ip = 0;
721 (*groups)[g].n_ec = 0;
722 // start the sum of insig total as total
723 insig1 = g;
724 } else {
725 // Add this insig group total into insig1 first group
726 (*groups)[insig1].total += (*groups)[g].total;
727 }
728 n_insig++;
729 } else {
730 if (n_insig > 1)
731 (*groups)[g - n_insig + 1] = (*groups)[g];
732 }
733 }
734 if (n_insig > 0) {
735 (*groups)[insig1].n_ec = n_insig;
736 *n_groups -= n_insig - 1;
737 }
738 DMSG(1, "depth %u n_groups %u n_insig %u\n", depth, *n_groups, n_insig);
739 }
740
741 /* Sort on total size, bigger size first. */
742 VG_(ssort)(*groups, *n_groups, sizeof(Ms_Group), ms_group_revcmp_total);
743}
744
745static void ms_output_group (VgFile* fp, UInt depth, Ms_Group* group,
746 SizeT sig_sz, double sig_pct_threshold)
747{
748 UInt i;
749 Ms_Group* groups;
750 UInt n_groups;
751
752 // If this is an insignificant group, handle it specially
753 if (group->ms_ec == NULL) {
754 const HChar* s = ( 1 == group->n_ec? "," : "s, all" );
755 vg_assert(group->group_ip == 0);
756 FP("%*sn0: %lu in %d place%s below massif's threshold (%.2f%%)\n",
757 depth+1, "", group->total, group->n_ec, s, sig_pct_threshold);
758 return;
759 }
760
761 // Normal group => output the group and its subgroups.
762 ms_make_groups(depth+1, group->ms_ec, group->n_ec, sig_sz,
763 &n_groups, &groups);
764
765 FP("%*s" "n%u: %ld %s\n",
766 depth + 1, "",
767 n_groups,
768 group->total,
769 VG_(describe_IP)(group->ms_ec->ips[depth] - 1, NULL));
770 /* XTREE??? Massif original code removes 1 to get the IP description. I am
771 wondering if this is not something that predates revision r8818,
772 which introduced a -1 in the stack unwind (see m_stacktrace.c)
773 Kept for the moment to allow exact comparison with massif output, but
774 probably we should remove this, as we very probably end up 2 bytes before
775 the RA Return Address. */
776
777 /* Output sub groups of this group. */
778 for (i = 0; i < n_groups; i++)
779 ms_output_group(fp, depth+1, &groups[i], sig_sz, sig_pct_threshold);
780
781 VG_(free)(groups);
782}
783
784/* Allocate and build an array of Ms_Ec sorted by addresses in the
785 Ms_Ec StackTrace. */
786static void prepare_ms_ec (XTree* xt,
787 ULong (*report_value)(const void* value),
788 ULong* top_total, Ms_Ec** vms_ec, UInt* vn_ec)
789{
790 XT_shared* shared = xt->shared;
791 const UInt n_xecu = VG_(sizeXA)(shared->xec);
792 const UInt n_data_xecu = VG_(sizeXA)(xt->data);
793 Ms_Ec* ms_ec = VG_(malloc)("XT_massif_print.ms_ec", n_xecu * sizeof(Ms_Ec));
794 UInt n_xecu_sel = 0; // Nr of xecu that are selected for output.
795
796 vg_assert(n_data_xecu <= n_xecu);
797
798 // Ensure we have in shared->ips_order_xecu our xecu sorted by StackTrace.
799 ensure_ips_order_xecu_valid(shared);
800
801 *top_total = 0;
802 DMSG(1, "iteration %u\n", n_xecu);
803 for (UInt i = 0; i < n_xecu; i++) {
804 Xecu xecu = *(Xecu*)VG_(indexXA)(shared->ips_order_xecu, i);
805 xec* xe = (xec*)VG_(indexXA)(shared->xec, xecu);
806
807 if (xecu >= n_data_xecu)
808 continue; // No data for this xecu in xt->data.
809 ms_ec[n_xecu_sel].n_ips = xe->n_ips_sel;
810 if (ms_ec[n_xecu_sel].n_ips == 0)
811 continue;
812
813 ms_ec[n_xecu_sel].ips = VG_(get_ExeContext_StackTrace)(xe->ec) + xe->top;
814 ms_ec[n_xecu_sel].report_value
815 = (*report_value)(VG_(indexXA)(xt->data, xecu));
816 *top_total += ms_ec[n_xecu_sel].report_value;
817
818 n_xecu_sel++;
819 }
820 vg_assert(n_xecu_sel <= n_xecu);
821
822 *vms_ec = ms_ec;
823 *vn_ec = n_xecu_sel;
824}
825
826MsFile* VG_(XT_massif_open)
827 (const HChar* outfilename,
828 const HChar* desc,
829 const XArray* desc_args,
830 const HChar* time_unit)
831{
832 UInt i;
833 VgFile* fp = xt_open(outfilename);
834
835 if (fp == NULL)
836 return NULL; // xt_open reported the error.
837
838 /* ------ file header ------------------------------- */
839 FP("desc:");
840 if (desc)
841 FP(" %s", desc);
842 i = 0;
843 if (desc_args) {
844 for (i = 0; i < VG_(sizeXA)(desc_args); i++) {
845 HChar* arg = *(HChar**)VG_(indexXA)(desc_args, i);
846 FP(" %s", arg);
847 }
848 }
849 if (0 == i && desc == NULL) FP(" (none)");
850 FP("\n");
851
852 FP_cmd(fp);
853
854 FP("time_unit: %s\n", time_unit);
855
856 return fp;
857}
858
859void VG_(XT_massif_close)(MsFile* fp)
860{
861 if (fp == NULL)
862 return; // Error should have been reported by VG_(XT_massif_open)
863
864 VG_(fclose)(fp);
865}
866
867void VG_(XT_massif_print)
868 (MsFile* fp,
869 XTree* xt,
870 const Massif_Header* header,
871 ULong (*report_value)(const void* value))
872{
873 UInt i;
874
875 if (fp == NULL)
876 return; // Normally VG_(XT_massif_open) already reported an error.
877
878 /* Compute/prepare Snapshot totals/data/... */
879 ULong top_total;
880
881 /* Following variables only used for detailed snapshot. */
882 UInt n_ec = 0;
883 Ms_Ec* ms_ec = NULL;
884 const HChar* kind =
885 header->detailed ? (header->peak ? "peak" : "detailed") : "empty";
886
887 DMSG(1, "XT_massif_print %s\n", kind);
888 if (header->detailed) {
889 /* Prepare the Ms_Ec sorted array of stacktraces and the groups
890 at level 0. */
891 prepare_ms_ec(xt, report_value, &top_total, &ms_ec, &n_ec);
892 DMSG(1, "XT_print_massif ms_ec n_ec %u\n", n_ec);
893 } else if (xt == NULL) {
894 /* Non detailed, no xt => use the sz provided in the header. */
895 top_total = header->sz_B;
896 } else {
897 /* For non detailed snapshot, compute total directly from the xec. */
898 const XT_shared* shared = xt->shared;
899 const UInt n_xecu = VG_(sizeXA)(xt->data);
900 top_total = 0;
901
902 for (UInt xecu = 0; xecu < n_xecu; xecu++) {
903 xec* xe = (xec*)VG_(indexXA)(shared->xec, xecu);
904 if (xe->n_ips_sel == 0)
905 continue;
906 top_total += (*report_value)(VG_(indexXA)(xt->data, xecu));
907 }
908 }
909
910 /* ------ snapshot header --------------------------- */
911 FP("#-----------\n");
912 FP("snapshot=%d\n", header->snapshot_n);
913 FP("#-----------\n");
914 FP("time=%lld\n", header->time);
915
916 FP("mem_heap_B=%llu\n", top_total); // without extra_B and without stacks_B
917 FP("mem_heap_extra_B=%llu\n", header->extra_B);
918 FP("mem_stacks_B=%llu\n", header->stacks_B);
919 FP("heap_tree=%s\n", kind);
920
921 /* ------ detailed snapshot data ----------------------------- */
922 if (header->detailed) {
923 UInt n_groups;
924 Ms_Group* groups;
925
926 ULong sig_sz;
927 // Work out how big a child must be to be significant. If the current
928 // top_total is zero, then we set it to 1, which means everything will be
929 // judged insignificant -- this is sensible, as there's no point showing
930 // any detail for this case. Unless they used threshold=0, in which
931 // case we show them everything because that's what they asked for.
932 //
933 // Nb: We do this once now, rather than once per child, because if we do
934 // that the cost of all the divisions adds up to something significant.
935 if (0 == top_total && 0 != header->sig_threshold)
936 sig_sz = 1;
937 else
938 sig_sz = ((top_total + header->extra_B + header->stacks_B)
939 * header->sig_threshold) / 100;
940
941 /* Produce the groups at depth 0 */
942 DMSG(1, "XT_massif_print producing depth 0 groups\n");
943 ms_make_groups(0, ms_ec, n_ec, sig_sz, &n_groups, &groups);
944
945 /* Output the top node. */
946 FP("n%u: %llu %s\n", n_groups, top_total, header->top_node_desc);
947
948 /* Output depth 0 groups. */
949 DMSG(1, "XT_massif_print outputing %u depth 0 groups\n", n_groups);
950 for (i = 0; i < n_groups; i++)
951 ms_output_group(fp, 0, &groups[i], sig_sz, header->sig_threshold);
952
953 VG_(free)(groups);
954 VG_(free)(ms_ec);
955 }
956}
957
958Int VG_(XT_offset_main_or_below_main)(Addr* ips, Int n_ips)
959{
960 /* Search for main or below main function.
961 To limit the nr of ips to examine, we maintain the deepest
962 offset where main was found, and we first search main
963 from there.
964 If no main is found, we will then do a search for main or
965 below main function till the top. */
966 static Int deepest_main = 0;
967 Vg_FnNameKind kind = Vg_FnNameNormal;
968 Int mbm = n_ips - 1; // Position of deepest main or below main.
969 Vg_FnNameKind mbmkind = Vg_FnNameNormal;
970 Int i;
971
972 for (i = n_ips - 1 - deepest_main;
973 i < n_ips;
974 i++) {
975 mbmkind = VG_(get_fnname_kind_from_IP)(ips[i]);
976 if (mbmkind != Vg_FnNameNormal) {
977 mbm = i;
978 break;
979 }
980 }
981
982 /* Search for main or below main function till top. */
983 for (i = mbm - 1;
984 i >= 0 && mbmkind != Vg_FnNameMain;
985 i--) {
986 kind = VG_(get_fnname_kind_from_IP)(ips[i]);
987 if (kind != Vg_FnNameNormal) {
988 mbm = i;
989 mbmkind = kind;
990 }
991 }
992 if (Vg_FnNameMain == mbmkind || Vg_FnNameBelowMain == mbmkind) {
993 if (mbmkind == Vg_FnNameMain && (n_ips - 1 - mbm) > deepest_main)
994 deepest_main = n_ips - 1 - mbm;
995 return mbm;
996 } else
997 return n_ips-1;
998}
999
1000void VG_(XT_filter_1top_and_maybe_below_main)
1001 (Addr* ips, Int n_ips,
1002 UInt* top, UInt* n_ips_sel)
1003{
1004 Int mbm;
1005
1006 *n_ips_sel = n_ips;
1007 if (n_ips == 0) {
1008 *top = 0;
1009 return;
1010 }
1011
1012 /* Filter top function. */
1013 *top = 1;
1014
1015 if (VG_(clo_show_below_main))
1016 mbm = n_ips - 1;
1017 else
1018 mbm = VG_(XT_offset_main_or_below_main)(ips, n_ips);
1019
1020 *n_ips_sel = mbm - *top + 1;
1021}
1022
1023void VG_(XT_filter_maybe_below_main)
1024 (Addr* ips, Int n_ips,
1025 UInt* top, UInt* n_ips_sel)
1026{
1027 Int mbm;
1028
1029 *n_ips_sel = n_ips;
1030 *top = 0;
1031 if (n_ips == 0)
1032 return;
1033
1034 if (VG_(clo_show_below_main))
1035 mbm = n_ips - 1;
1036 else
1037 mbm = VG_(XT_offset_main_or_below_main)(ips, n_ips);
1038
1039 *n_ips_sel = mbm - *top + 1;
1040}
1041
1042/*--------------------------------------------------------------------*/
1043/*--- end m_xtree.c ---*/
1044/*--------------------------------------------------------------------*/