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) 2003-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#include "base/statistics.hh"
44
45#include <fstream>
46#include <iomanip>
47#include <list>
48#include <map>
49#include <string>
50
51#include "base/callback.hh"
52#include "base/cprintf.hh"
53#include "base/debug.hh"
54#include "base/hostinfo.hh"
55#include "base/logging.hh"
56#include "base/str.hh"
57#include "base/time.hh"
58#include "base/trace.hh"
59
60using namespace std;
61
62namespace Stats {
63
64std::string Info::separatorString = "::";
65
66// We wrap these in a function to make sure they're built in time.
67list<Info *> &
68statsList()
69{
70    static list<Info *> the_list;
71    return the_list;
72}
73
74MapType &
75statsMap()
76{
77    static MapType the_map;
78    return the_map;
79}
80
81void
82InfoAccess::setInfo(Group *parent, Info *info)
83{
84    panic_if(statsMap().find(this) != statsMap().end() ||
85             _info != nullptr,
86             "shouldn't register stat twice!");
87
88    // New-style stats are reachable through the hierarchy and
89    // shouldn't be added to the global lists.
90    if (parent) {
91        _info = info;
92        return;
93    }
94
95    statsList().push_back(info);
96
97#ifndef NDEBUG
98    pair<MapType::iterator, bool> result =
99#endif
100        statsMap().insert(make_pair(this, info));
101    assert(result.second && "this should never fail");
102    assert(statsMap().find(this) != statsMap().end());
103}
104
105void
106InfoAccess::setParams(const StorageParams *params)
107{
108    info()->storageParams = params;
109}
110
111void
112InfoAccess::setInit()
113{
114    info()->flags.set(init);
115}
116
117Info *
118InfoAccess::info()
119{
120    if (_info) {
121        // New-style stats
122        return _info;
123    } else {
124        // Legacy stats
125        MapType::const_iterator i = statsMap().find(this);
126        assert(i != statsMap().end());
127        return (*i).second;
128    }
129}
130
131const Info *
132InfoAccess::info() const
133{
134    if (_info) {
135        // New-style stats
136        return _info;
137    } else {
138        // Legacy stats
139        MapType::const_iterator i = statsMap().find(this);
140        assert(i != statsMap().end());
141        return (*i).second;
142    }
143}
144
145StorageParams::~StorageParams()
146{
147}
148
149NameMapType &
150nameMap()
151{
152    static NameMapType the_map;
153    return the_map;
154}
155
156int Info::id_count = 0;
157
158int debug_break_id = -1;
159
160Info::Info()
161    : flags(none), precision(-1), prereq(0), storageParams(NULL)
162{
163    id = id_count++;
164    if (debug_break_id >= 0 and debug_break_id == id)
165        Debug::breakpoint();
166}
167
168Info::~Info()
169{
170}
171
172bool
173validateStatName(const string &name)
174{
175    if (name.empty())
176        return false;
177
178    vector<string> vec;
179    tokenize(vec, name, '.');
180    vector<string>::const_iterator item = vec.begin();
181    while (item != vec.end()) {
182        if (item->empty())
183            return false;
184
185        string::const_iterator c = item->begin();
186
187        // The first character is different
188        if (!isalpha(*c) && *c != '_')
189            return false;
190
191        // The rest of the characters have different rules.
192        while (++c != item->end()) {
193            if (!isalnum(*c) && *c != '_')
194                return false;
195        }
196
197        ++item;
198    }
199
200    return true;
201}
202
203void
204Info::setName(const string &name)
205{
206    setName(nullptr, name);
207}
208
209void
210Info::setName(const Group *parent, const string &name)
211{
212    if (!validateStatName(name))
213        panic("invalid stat name '%s'", name);
214
215    // We only register the stat with the nameMap() if we are using
216    // old-style stats without a parent group. New-style stats should
217    // be unique since their names should correspond to a member
218    // variable.
219    if (!parent) {
220        auto p = nameMap().insert(make_pair(name, this));
221
222        if (!p.second)
223            panic("same statistic name used twice! name=%s\n",
224                  name);
225    }
226
227    this->name = name;
228}
229
230bool
231Info::less(Info *stat1, Info *stat2)
232{
233    const string &name1 = stat1->name;
234    const string &name2 = stat2->name;
235
236    vector<string> v1;
237    vector<string> v2;
238
239    tokenize(v1, name1, '.');
240    tokenize(v2, name2, '.');
241
242    size_type last = min(v1.size(), v2.size()) - 1;
243    for (off_type i = 0; i < last; ++i)
244        if (v1[i] != v2[i])
245            return v1[i] < v2[i];
246
247    // Special compare for last element.
248    if (v1[last] == v2[last])
249        return v1.size() < v2.size();
250    else
251        return v1[last] < v2[last];
252
253    return false;
254}
255
256bool
257Info::baseCheck() const
258{
259    if (!(flags & Stats::init)) {
260#ifdef DEBUG
261        cprintf("this is stat number %d\n", id);
262#endif
263        panic("Not all stats have been initialized.\n"
264              "You may need to add <ParentClass>::regStats() to a"
265              " new SimObject's regStats() function. Name: %s",
266              name);
267        return false;
268    }
269
270    if ((flags & display) && name.empty()) {
271        panic("all printable stats must be named");
272        return false;
273    }
274
275    return true;
276}
277
278void
279Info::enable()
280{
281}
282
283void
284VectorInfo::enable()
285{
286    size_type s = size();
287    if (subnames.size() < s)
288        subnames.resize(s);
289    if (subdescs.size() < s)
290        subdescs.resize(s);
291}
292
293void
294VectorDistInfo::enable()
295{
296    size_type s = size();
297    if (subnames.size() < s)
298        subnames.resize(s);
299    if (subdescs.size() < s)
300        subdescs.resize(s);
301}
302
303void
304Vector2dInfo::enable()
305{
306    if (subnames.size() < x)
307        subnames.resize(x);
308    if (subdescs.size() < x)
309        subdescs.resize(x);
310    if (y_subnames.size() < y)
311        y_subnames.resize(y);
312}
313
314void
315HistStor::grow_out()
316{
317    int size = cvec.size();
318    int zero = size / 2; // round down!
319    int top_half = zero + (size - zero + 1) / 2; // round up!
320    int bottom_half = (size - zero) / 2; // round down!
321
322    // grow down
323    int low_pair = zero - 1;
324    for (int i = zero - 1; i >= bottom_half; i--) {
325        cvec[i] = cvec[low_pair];
326        if (low_pair - 1 >= 0)
327            cvec[i] += cvec[low_pair - 1];
328        low_pair -= 2;
329    }
330    assert(low_pair == 0 || low_pair == -1 || low_pair == -2);
331
332    for (int i = bottom_half - 1; i >= 0; i--)
333        cvec[i] = Counter();
334
335    // grow up
336    int high_pair = zero;
337    for (int i = zero; i < top_half; i++) {
338        cvec[i] = cvec[high_pair];
339        if (high_pair + 1 < size)
340            cvec[i] += cvec[high_pair + 1];
341        high_pair += 2;
342    }
343    assert(high_pair == size || high_pair == size + 1);
344
345    for (int i = top_half; i < size; i++)
346        cvec[i] = Counter();
347
348    max_bucket *= 2;
349    min_bucket *= 2;
350    bucket_size *= 2;
351}
352
353void
354HistStor::grow_convert()
355{
356    int size = cvec.size();
357    int half = (size + 1) / 2; // round up!
358    //bool even = (size & 1) == 0;
359
360    int pair = size - 1;
361    for (int i = size - 1; i >= half; --i) {
362        cvec[i] = cvec[pair];
363        if (pair - 1 >= 0)
364            cvec[i] += cvec[pair - 1];
365        pair -= 2;
366    }
367
368    for (int i = half - 1; i >= 0; i--)
369        cvec[i] = Counter();
370
371    min_bucket = -max_bucket;// - (even ? bucket_size : 0);
372    bucket_size *= 2;
373}
374
375void
376HistStor::grow_up()
377{
378    int size = cvec.size();
379    int half = (size + 1) / 2; // round up!
380
381    int pair = 0;
382    for (int i = 0; i < half; i++) {
383        cvec[i] = cvec[pair];
384        if (pair + 1 < size)
385            cvec[i] += cvec[pair + 1];
386        pair += 2;
387    }
388    assert(pair == size || pair == size + 1);
389
390    for (int i = half; i < size; i++)
391        cvec[i] = Counter();
392
393    max_bucket *= 2;
394    bucket_size *= 2;
395}
396
397void
398HistStor::add(HistStor *hs)
399{
400    int b_size = hs->size();
401    assert(size() == b_size);
402    assert(min_bucket == hs->min_bucket);
403
404    sum += hs->sum;
405    logs += hs->logs;
406    squares += hs->squares;
407    samples += hs->samples;
408
409    while (bucket_size > hs->bucket_size)
410        hs->grow_up();
411    while (bucket_size < hs->bucket_size)
412        grow_up();
413
414    for (uint32_t i = 0; i < b_size; i++)
415        cvec[i] += hs->cvec[i];
416}
417
418Formula::Formula(Group *parent, const char *name, const char *desc)
419    : DataWrapVec<Formula, FormulaInfoProxy>(parent, name, desc)
420
421{
422}
423
424
425
426Formula::Formula(Group *parent, const char *name, const char *desc,
427                 const Temp &r)
428    : DataWrapVec<Formula, FormulaInfoProxy>(parent, name, desc)
429{
430    *this = r;
431}
432
433const Formula &
434Formula::operator=(const Temp &r)
435{
436    assert(!root && "Can't change formulas");
437    root = r.getNodePtr();
438    setInit();
439    assert(size());
440    return *this;
441}
442
443const Formula &
444Formula::operator+=(Temp r)
445{
446    if (root)
447        root = NodePtr(new BinaryNode<std::plus<Result> >(root, r));
448    else {
449        root = r.getNodePtr();
450        setInit();
451    }
452
453    assert(size());
454    return *this;
455}
456
457const Formula &
458Formula::operator/=(Temp r)
459{
460    assert (root);
461    root = NodePtr(new BinaryNode<std::divides<Result> >(root, r));
462
463    assert(size());
464    return *this;
465}
466
467
468void
469Formula::result(VResult &vec) const
470{
471    if (root)
472        vec = root->result();
473}
474
475Result
476Formula::total() const
477{
478    return root ? root->total() : 0.0;
479}
480
481size_type
482Formula::size() const
483{
484    if (!root)
485        return 0;
486    else
487        return root->size();
488}
489
490void
491Formula::reset()
492{
493}
494
495bool
496Formula::zero() const
497{
498    VResult vec;
499    result(vec);
500    for (VResult::size_type i = 0; i < vec.size(); ++i)
501        if (vec[i] != 0.0)
502            return false;
503    return true;
504}
505
506string
507Formula::str() const
508{
509    return root ? root->str() : "";
510}
511
512Handler resetHandler = NULL;
513Handler dumpHandler = NULL;
514
515void
516registerHandlers(Handler reset_handler, Handler dump_handler)
517{
518    resetHandler = reset_handler;
519    dumpHandler = dump_handler;
520}
521
522CallbackQueue dumpQueue;
523CallbackQueue resetQueue;
524
525void
526processResetQueue()
527{
528    resetQueue.process();
529}
530
531void
532processDumpQueue()
533{
534    dumpQueue.process();
535}
536
537void
538registerResetCallback(Callback *cb)
539{
540    resetQueue.add(cb);
541}
542
543bool _enabled = false;
544
545bool
546enabled()
547{
548    return _enabled;
549}
550
551void
552enable()
553{
554    if (_enabled)
555        fatal("Stats are already enabled");
556
557    _enabled = true;
558}
559
560void
561dump()
562{
563    if (dumpHandler)
564        dumpHandler();
565    else
566        fatal("No registered Stats::dump handler");
567}
568
569void
570reset()
571{
572    if (resetHandler)
573        resetHandler();
574    else
575        fatal("No registered Stats::reset handler");
576}
577
578void
579registerDumpCallback(Callback *cb)
580{
581    dumpQueue.add(cb);
582}
583
584} // namespace Stats
585
586void
587debugDumpStats()
588{
589    Stats::dump();
590}
591