1/*
2 * Copyright (c) 2019 Arm Limited
3 * All rights reserved.
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2004-2005 The Regents of The University of Michigan
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions are
19 * met: redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer;
21 * redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution;
24 * neither the name of the copyright holders nor the names of its
25 * contributors may be used to endorse or promote products derived from
26 * this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 *
40 * Authors: Nathan Binkert
41 */
42
43#if defined(__APPLE__)
44#define _GLIBCPP_USE_C99 1
45#endif
46
47#if defined(__sun)
48#include <cmath>
49
50#endif
51
52#include <cassert>
53
54#ifdef __SUNPRO_CC
55#include <cmath>
56
57#endif
58#include "base/stats/text.hh"
59
60#include <cmath>
61#include <fstream>
62#include <iostream>
63#include <sstream>
64#include <string>
65
66#include "base/cast.hh"
67#include "base/logging.hh"
68#include "base/stats/info.hh"
69#include "base/str.hh"
70
71using namespace std;
72
73#ifndef NAN
74float __nan();
75/** Define Not a number. */
76#define NAN (__nan())
77/** Need to define __nan() */
78#define __M5_NAN
79#endif
80
81#ifdef __M5_NAN
82float
83__nan()
84{
85 union {
86 uint32_t ui;
87 float f;
88 } nan;
89
90 nan.ui = 0x7fc00000;
91 return nan.f;
92}
93#endif
94
95namespace Stats {
96
97std::list<Info *> &statsList();
98
99Text::Text()
100 : mystream(false), stream(NULL), descriptions(false)
101{
102}
103
104Text::Text(std::ostream &stream)
105 : mystream(false), stream(NULL), descriptions(false)
106{
107 open(stream);
108}
109
110Text::Text(const std::string &file)
111 : mystream(false), stream(NULL), descriptions(false)
112{
113 open(file);
114}
115
116
117Text::~Text()
118{
119 if (mystream) {
120 assert(stream);
121 delete stream;
122 }
123}
124
125void
126Text::open(std::ostream &_stream)
127{
128 if (stream)
129 panic("stream already set!");
130
131 mystream = false;
132 stream = &_stream;
133 if (!valid())
134 fatal("Unable to open output stream for writing\n");
135}
136
137void
138Text::open(const std::string &file)
139{
140 if (stream)
141 panic("stream already set!");
142
143 mystream = true;
144 stream = new ofstream(file.c_str(), ios::trunc);
145 if (!valid())
146 fatal("Unable to open statistics file for writing\n");
147}
148
149bool
150Text::valid() const
151{
152 return stream != NULL && stream->good();
153}
154
155void
156Text::begin()
157{
158 ccprintf(*stream, "\n---------- Begin Simulation Statistics ----------\n");
159}
160
161void
162Text::end()
163{
164 ccprintf(*stream, "\n---------- End Simulation Statistics ----------\n");
165 stream->flush();
166}
167
168std::string
169Text::statName(const std::string &name) const
170{
171 if (path.empty())
172 return name;
173 else
174 return csprintf("%s.%s", path.top(), name);
175}
176
177void
178Text::beginGroup(const char *name)
179{
180 if (path.empty()) {
181 path.push(name);
182 } else {
183 path.push(csprintf("%s.%s", path.top(), name));
184 }
185}
186
187void
188Text::endGroup()
189{
190 assert(!path.empty());
191 path.pop();
192}
193
194bool
195Text::noOutput(const Info &info)
196{
197 if (!info.flags.isSet(display))
198 return true;
199
200 if (info.prereq && info.prereq->zero())
201 return true;
202
203 return false;
204}
205
206string
207ValueToString(Result value, int precision)
208{
209 stringstream val;
210
211 if (!std::isnan(value)) {
212 if (precision != -1)
213 val.precision(precision);
214 else if (value == rint(value))
215 val.precision(0);
216
217 val.unsetf(ios::showpoint);
218 val.setf(ios::fixed);
219 val << value;
220 } else {
221 val << "nan";
222 }
223
224 return val.str();
225}
226
227struct ScalarPrint
228{
229 Result value;
230 string name;
231 string desc;
232 Flags flags;
233 bool descriptions;
234 int precision;
235 Result pdf;
236 Result cdf;
237
238 void update(Result val, Result total);
239 void operator()(ostream &stream, bool oneLine = false) const;
240};
241
242void
243ScalarPrint::update(Result val, Result total)
244{
245 value = val;
246 if (total) {
247 pdf = val / total;
248 cdf += pdf;
249 }
250}
251
252void
253ScalarPrint::operator()(ostream &stream, bool oneLine) const
254{
255 if ((flags.isSet(nozero) && (!oneLine) && value == 0.0) ||
256 (flags.isSet(nonan) && std::isnan(value)))
257 return;
258
259 stringstream pdfstr, cdfstr;
260
261 if (!std::isnan(pdf))
262 ccprintf(pdfstr, "%.2f%%", pdf * 100.0);
263
264 if (!std::isnan(cdf))
265 ccprintf(cdfstr, "%.2f%%", cdf * 100.0);
266
267 if (oneLine) {
268 ccprintf(stream, " |%12s %10s %10s",
269 ValueToString(value, precision), pdfstr.str(), cdfstr.str());
270 } else {
271 ccprintf(stream, "%-40s %12s %10s %10s", name,
272 ValueToString(value, precision), pdfstr.str(), cdfstr.str());
273
274 if (descriptions) {
275 if (!desc.empty())
276 ccprintf(stream, " # %s", desc);
277 }
278 stream << endl;
279 }
280}
281
282struct VectorPrint
283{
284 string name;
285 string separatorString;
286 string desc;
287 vector<string> subnames;
288 vector<string> subdescs;
289 Flags flags;
290 bool descriptions;
291 int precision;
292 VResult vec;
293 Result total;
294 bool forceSubnames;
295
296 void operator()(ostream &stream) const;
297};
298
299void
300VectorPrint::operator()(std::ostream &stream) const
301{
302 size_type _size = vec.size();
303 Result _total = 0.0;
304
305 if (flags.isSet(pdf | cdf)) {
306 for (off_type i = 0; i < _size; ++i) {
307 _total += vec[i];
308 }
309 }
310
311 string base = name + separatorString;
312
313 ScalarPrint print;
314 print.name = name;
315 print.desc = desc;
316 print.precision = precision;
317 print.descriptions = descriptions;
318 print.flags = flags;
319 print.pdf = _total ? 0.0 : NAN;
320 print.cdf = _total ? 0.0 : NAN;
321
322 bool havesub = !subnames.empty();
323
324 if (_size == 1) {
325 // If forceSubnames is set, get the first subname (or index in
326 // the case where there are no subnames) and append it to the
327 // base name.
328 if (forceSubnames)
329 print.name = base + (havesub ? subnames[0] : std::to_string(0));
330 print.value = vec[0];
331 print(stream);
332 return;
333 }
334
335 if ((!flags.isSet(nozero)) || (total != 0)) {
336 if (flags.isSet(oneline)) {
337 ccprintf(stream, "%-40s", name);
338 print.flags = print.flags & (~nozero);
339 }
340
341 for (off_type i = 0; i < _size; ++i) {
342 if (havesub && (i >= subnames.size() || subnames[i].empty()))
343 continue;
344
345 print.name = base + (havesub ? subnames[i] : std::to_string(i));
346 print.desc = subdescs.empty() ? desc : subdescs[i];
347
348 print.update(vec[i], _total);
349 print(stream, flags.isSet(oneline));
350 }
351
352 if (flags.isSet(oneline)) {
353 if (descriptions) {
354 if (!desc.empty())
355 ccprintf(stream, " # %s", desc);
356 }
357 stream << endl;
358 }
359 }
360
361 if (flags.isSet(::Stats::total)) {
362 print.pdf = NAN;
363 print.cdf = NAN;
364 print.name = base + "total";
365 print.desc = desc;
366 print.value = total;
367 print(stream);
368 }
369}
370
371struct DistPrint
372{
373 string name;
374 string separatorString;
375 string desc;
376 Flags flags;
377 bool descriptions;
378 int precision;
379
380 const DistData &data;
381
382 DistPrint(const Text *text, const DistInfo &info);
383 DistPrint(const Text *text, const VectorDistInfo &info, int i);
384 void init(const Text *text, const Info &info);
385 void operator()(ostream &stream) const;
386};
387
388DistPrint::DistPrint(const Text *text, const DistInfo &info)
389 : data(info.data)
390{
391 init(text, info);
392}
393
394DistPrint::DistPrint(const Text *text, const VectorDistInfo &info, int i)
395 : data(info.data[i])
396{
397 init(text, info);
398
399 name = info.name + "_" +
400 (info.subnames[i].empty() ? (std::to_string(i)) : info.subnames[i]);
401
402 if (!info.subdescs[i].empty())
403 desc = info.subdescs[i];
404}
405
406void
407DistPrint::init(const Text *text, const Info &info)
408{
371 name = info.name;
409 name = text->statName(info.name);
410 separatorString = info.separatorString;
411 desc = info.desc;
412 flags = info.flags;
413 precision = info.precision;
414 descriptions = text->descriptions;
415}
416
417void
418DistPrint::operator()(ostream &stream) const
419{
420 if (flags.isSet(nozero) && data.samples == 0) return;
421 string base = name + separatorString;
422
423 ScalarPrint print;
424 print.precision = precision;
425 print.flags = flags;
426 print.descriptions = descriptions;
427 print.desc = desc;
428 print.pdf = NAN;
429 print.cdf = NAN;
430
431 if (flags.isSet(oneline)) {
432 print.name = base + "bucket_size";
433 print.value = data.bucket_size;
434 print(stream);
435
436 print.name = base + "min_bucket";
437 print.value = data.min;
438 print(stream);
439
440 print.name = base + "max_bucket";
441 print.value = data.max;
442 print(stream);
443 }
444
445 print.name = base + "samples";
446 print.value = data.samples;
447 print(stream);
448
449 print.name = base + "mean";
450 print.value = data.samples ? data.sum / data.samples : NAN;
451 print(stream);
452
453 if (data.type == Hist) {
454 print.name = base + "gmean";
455 print.value = data.samples ? exp(data.logs / data.samples) : NAN;
456 print(stream);
457 }
458
459 Result stdev = NAN;
460 if (data.samples)
461 stdev = sqrt((data.samples * data.squares - data.sum * data.sum) /
462 (data.samples * (data.samples - 1.0)));
463 print.name = base + "stdev";
464 print.value = stdev;
465 print(stream);
466
467 if (data.type == Deviation)
468 return;
469
470 size_t size = data.cvec.size();
471
472 Result total = 0.0;
473 if (data.type == Dist && data.underflow != NAN)
474 total += data.underflow;
475 for (off_type i = 0; i < size; ++i)
476 total += data.cvec[i];
477 if (data.type == Dist && data.overflow != NAN)
478 total += data.overflow;
479
480 if (total) {
481 print.pdf = 0.0;
482 print.cdf = 0.0;
483 }
484
485 if (data.type == Dist && data.underflow != NAN) {
486 print.name = base + "underflows";
487 print.update(data.underflow, total);
488 print(stream);
489 }
490
491 if (flags.isSet(oneline)) {
492 ccprintf(stream, "%-40s", name);
493 }
494
495 for (off_type i = 0; i < size; ++i) {
496 stringstream namestr;
497 namestr << base;
498
499 Counter low = i * data.bucket_size + data.min;
500 Counter high = ::min(low + data.bucket_size - 1.0, data.max);
501 namestr << low;
502 if (low < high)
503 namestr << "-" << high;
504
505 print.name = namestr.str();
506 print.update(data.cvec[i], total);
507 print(stream, flags.isSet(oneline));
508 }
509
510 if (flags.isSet(oneline)) {
511 if (descriptions) {
512 if (!desc.empty())
513 ccprintf(stream, " # %s", desc);
514 }
515 stream << endl;
516 }
517
518 if (data.type == Dist && data.overflow != NAN) {
519 print.name = base + "overflows";
520 print.update(data.overflow, total);
521 print(stream);
522 }
523
524 print.pdf = NAN;
525 print.cdf = NAN;
526
527 if (data.type == Dist && data.min_val != NAN) {
528 print.name = base + "min_value";
529 print.value = data.min_val;
530 print(stream);
531 }
532
533 if (data.type == Dist && data.max_val != NAN) {
534 print.name = base + "max_value";
535 print.value = data.max_val;
536 print(stream);
537 }
538
539 print.name = base + "total";
540 print.value = total;
541 print(stream);
542}
543
544void
545Text::visit(const ScalarInfo &info)
546{
547 if (noOutput(info))
548 return;
549
550 ScalarPrint print;
551 print.value = info.result();
514 print.name = info.name;
552 print.name = statName(info.name);
553 print.desc = info.desc;
554 print.flags = info.flags;
555 print.descriptions = descriptions;
556 print.precision = info.precision;
557 print.pdf = NAN;
558 print.cdf = NAN;
559
560 print(*stream);
561}
562
563void
564Text::visit(const VectorInfo &info)
565{
566 if (noOutput(info))
567 return;
568
569 size_type size = info.size();
570 VectorPrint print;
571
534 print.name = info.name;
572 print.name = statName(info.name);
573 print.separatorString = info.separatorString;
574 print.desc = info.desc;
575 print.flags = info.flags;
576 print.descriptions = descriptions;
577 print.precision = info.precision;
578 print.vec = info.result();
579 print.total = info.total();
580 print.forceSubnames = false;
581
582 if (!info.subnames.empty()) {
583 for (off_type i = 0; i < size; ++i) {
584 if (!info.subnames[i].empty()) {
585 print.subnames = info.subnames;
586 print.subnames.resize(size);
587 for (off_type i = 0; i < size; ++i) {
588 if (!info.subnames[i].empty() &&
589 !info.subdescs[i].empty()) {
590 print.subdescs = info.subdescs;
591 print.subdescs.resize(size);
592 break;
593 }
594 }
595 break;
596 }
597 }
598 }
599
600 print(*stream);
601}
602
603void
604Text::visit(const Vector2dInfo &info)
605{
606 if (noOutput(info))
607 return;
608
609 bool havesub = false;
610 VectorPrint print;
611
612 if (!info.y_subnames.empty()) {
613 for (off_type i = 0; i < info.y; ++i) {
614 if (!info.y_subnames[i].empty()) {
615 print.subnames = info.y_subnames;
616 break;
617 }
618 }
619 }
620 print.flags = info.flags;
621 print.separatorString = info.separatorString;
622 print.descriptions = descriptions;
623 print.precision = info.precision;
624 print.forceSubnames = true;
625
626 if (!info.subnames.empty()) {
627 for (off_type i = 0; i < info.x; ++i)
628 if (!info.subnames[i].empty())
629 havesub = true;
630 }
631
632 VResult tot_vec(info.y);
633 for (off_type i = 0; i < info.x; ++i) {
634 if (havesub && (i >= info.subnames.size() || info.subnames[i].empty()))
635 continue;
636
637 off_type iy = i * info.y;
638 VResult yvec(info.y);
639
640 Result total = 0.0;
641 for (off_type j = 0; j < info.y; ++j) {
642 yvec[j] = info.cvec[iy + j];
643 tot_vec[j] += yvec[j];
644 total += yvec[j];
645 }
646
609 print.name = info.name + "_" +
610 (havesub ? info.subnames[i] : std::to_string(i));
647 print.name = statName(
648 info.name + "_" +
649 (havesub ? info.subnames[i] : std::to_string(i)));
650 print.desc = info.desc;
651 print.vec = yvec;
652 print.total = total;
653 print(*stream);
654 }
655
656 // Create a subname for printing the total
657 vector<string> total_subname;
658 total_subname.push_back("total");
659
660 if (info.flags.isSet(::Stats::total) && (info.x > 1)) {
622 print.name = info.name;
661 print.name = statName(info.name);
662 print.subnames = total_subname;
663 print.desc = info.desc;
664 print.vec = VResult(1, info.total());
665 print.flags = print.flags & ~total;
666 print(*stream);
667 }
668}
669
670void
671Text::visit(const DistInfo &info)
672{
673 if (noOutput(info))
674 return;
675
676 DistPrint print(this, info);
677 print(*stream);
678}
679
680void
681Text::visit(const VectorDistInfo &info)
682{
683 if (noOutput(info))
684 return;
685
686 for (off_type i = 0; i < info.size(); ++i) {
687 DistPrint print(this, info, i);
688 print(*stream);
689 }
690}
691
692void
693Text::visit(const FormulaInfo &info)
694{
695 visit((const VectorInfo &)info);
696}
697
698/*
699 This struct implements the output methods for the sparse
700 histogram stat
701*/
702struct SparseHistPrint
703{
704 string name;
705 string separatorString;
706 string desc;
707 Flags flags;
708 bool descriptions;
709 int precision;
710
711 const SparseHistData &data;
712
713 SparseHistPrint(const Text *text, const SparseHistInfo &info);
714 void init(const Text *text, const Info &info);
715 void operator()(ostream &stream) const;
716};
717
718/* Call initialization function */
719SparseHistPrint::SparseHistPrint(const Text *text, const SparseHistInfo &info)
720 : data(info.data)
721{
722 init(text, info);
723}
724
725/* Initialization function */
726void
727SparseHistPrint::init(const Text *text, const Info &info)
728{
690 name = info.name;
729 name = text->statName(info.name);
730 separatorString = info.separatorString;
731 desc = info.desc;
732 flags = info.flags;
733 precision = info.precision;
734 descriptions = text->descriptions;
735}
736
737/* Grab data from map and write to output stream */
738void
739SparseHistPrint::operator()(ostream &stream) const
740{
741 string base = name + separatorString;
742
743 ScalarPrint print;
744 print.precision = precision;
745 print.flags = flags;
746 print.descriptions = descriptions;
747 print.desc = desc;
748 print.pdf = NAN;
749 print.cdf = NAN;
750
751 print.name = base + "samples";
752 print.value = data.samples;
753 print(stream);
754
755 MCounter::const_iterator it;
756 for (it = data.cmap.begin(); it != data.cmap.end(); it++) {
757 stringstream namestr;
758 namestr << base;
759
760 namestr <<(*it).first;
761 print.name = namestr.str();
762 print.value = (*it).second;
763 print(stream);
764 }
765}
766
767void
768Text::visit(const SparseHistInfo &info)
769{
770 if (noOutput(info))
771 return;
772
773 SparseHistPrint print(this, info);
774 print(*stream);
775}
776
777Output *
778initText(const string &filename, bool desc)
779{
780 static Text text;
781 static bool connected = false;
782
783 if (!connected) {
784 text.open(*simout.findOrCreate(filename)->stream());
785 text.descriptions = desc;
786 connected = true;
787 }
788
789 return &text;
790}
791
792} // namespace Stats