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{
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();
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
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
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)) {
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{
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
793