statistics.cc revision 14205:197360deaa20
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    if (!validateStatName(name))
207        panic("invalid stat name '%s'", name);
208
209    pair<NameMapType::iterator, bool> p =
210        nameMap().insert(make_pair(name, this));
211
212    Info *other = p.first->second;
213    bool result = p.second;
214
215    if (!result) {
216      // using other->name instead of just name to avoid a compiler
217      // warning.  They should be the same.
218        panic("same statistic name used twice! name=%s\n", other->name);
219    }
220
221    this->name = name;
222}
223
224bool
225Info::less(Info *stat1, Info *stat2)
226{
227    const string &name1 = stat1->name;
228    const string &name2 = stat2->name;
229
230    vector<string> v1;
231    vector<string> v2;
232
233    tokenize(v1, name1, '.');
234    tokenize(v2, name2, '.');
235
236    size_type last = min(v1.size(), v2.size()) - 1;
237    for (off_type i = 0; i < last; ++i)
238        if (v1[i] != v2[i])
239            return v1[i] < v2[i];
240
241    // Special compare for last element.
242    if (v1[last] == v2[last])
243        return v1.size() < v2.size();
244    else
245        return v1[last] < v2[last];
246
247    return false;
248}
249
250bool
251Info::baseCheck() const
252{
253    if (!(flags & Stats::init)) {
254#ifdef DEBUG
255        cprintf("this is stat number %d\n", id);
256#endif
257        panic("Not all stats have been initialized.\n"
258              "You may need to add <ParentClass>::regStats() to a"
259              " new SimObject's regStats() function. Name: %s",
260              name);
261        return false;
262    }
263
264    if ((flags & display) && name.empty()) {
265        panic("all printable stats must be named");
266        return false;
267    }
268
269    return true;
270}
271
272void
273Info::enable()
274{
275}
276
277void
278VectorInfo::enable()
279{
280    size_type s = size();
281    if (subnames.size() < s)
282        subnames.resize(s);
283    if (subdescs.size() < s)
284        subdescs.resize(s);
285}
286
287void
288VectorDistInfo::enable()
289{
290    size_type s = size();
291    if (subnames.size() < s)
292        subnames.resize(s);
293    if (subdescs.size() < s)
294        subdescs.resize(s);
295}
296
297void
298Vector2dInfo::enable()
299{
300    if (subnames.size() < x)
301        subnames.resize(x);
302    if (subdescs.size() < x)
303        subdescs.resize(x);
304    if (y_subnames.size() < y)
305        y_subnames.resize(y);
306}
307
308void
309HistStor::grow_out()
310{
311    int size = cvec.size();
312    int zero = size / 2; // round down!
313    int top_half = zero + (size - zero + 1) / 2; // round up!
314    int bottom_half = (size - zero) / 2; // round down!
315
316    // grow down
317    int low_pair = zero - 1;
318    for (int i = zero - 1; i >= bottom_half; i--) {
319        cvec[i] = cvec[low_pair];
320        if (low_pair - 1 >= 0)
321            cvec[i] += cvec[low_pair - 1];
322        low_pair -= 2;
323    }
324    assert(low_pair == 0 || low_pair == -1 || low_pair == -2);
325
326    for (int i = bottom_half - 1; i >= 0; i--)
327        cvec[i] = Counter();
328
329    // grow up
330    int high_pair = zero;
331    for (int i = zero; i < top_half; i++) {
332        cvec[i] = cvec[high_pair];
333        if (high_pair + 1 < size)
334            cvec[i] += cvec[high_pair + 1];
335        high_pair += 2;
336    }
337    assert(high_pair == size || high_pair == size + 1);
338
339    for (int i = top_half; i < size; i++)
340        cvec[i] = Counter();
341
342    max_bucket *= 2;
343    min_bucket *= 2;
344    bucket_size *= 2;
345}
346
347void
348HistStor::grow_convert()
349{
350    int size = cvec.size();
351    int half = (size + 1) / 2; // round up!
352    //bool even = (size & 1) == 0;
353
354    int pair = size - 1;
355    for (int i = size - 1; i >= half; --i) {
356        cvec[i] = cvec[pair];
357        if (pair - 1 >= 0)
358            cvec[i] += cvec[pair - 1];
359        pair -= 2;
360    }
361
362    for (int i = half - 1; i >= 0; i--)
363        cvec[i] = Counter();
364
365    min_bucket = -max_bucket;// - (even ? bucket_size : 0);
366    bucket_size *= 2;
367}
368
369void
370HistStor::grow_up()
371{
372    int size = cvec.size();
373    int half = (size + 1) / 2; // round up!
374
375    int pair = 0;
376    for (int i = 0; i < half; i++) {
377        cvec[i] = cvec[pair];
378        if (pair + 1 < size)
379            cvec[i] += cvec[pair + 1];
380        pair += 2;
381    }
382    assert(pair == size || pair == size + 1);
383
384    for (int i = half; i < size; i++)
385        cvec[i] = Counter();
386
387    max_bucket *= 2;
388    bucket_size *= 2;
389}
390
391void
392HistStor::add(HistStor *hs)
393{
394    int b_size = hs->size();
395    assert(size() == b_size);
396    assert(min_bucket == hs->min_bucket);
397
398    sum += hs->sum;
399    logs += hs->logs;
400    squares += hs->squares;
401    samples += hs->samples;
402
403    while (bucket_size > hs->bucket_size)
404        hs->grow_up();
405    while (bucket_size < hs->bucket_size)
406        grow_up();
407
408    for (uint32_t i = 0; i < b_size; i++)
409        cvec[i] += hs->cvec[i];
410}
411
412Formula::Formula(Group *parent, const char *name, const char *desc)
413    : DataWrapVec<Formula, FormulaInfoProxy>(parent, name, desc)
414
415{
416}
417
418
419
420Formula::Formula(Group *parent, const char *name, const char *desc,
421                 const Temp &r)
422    : DataWrapVec<Formula, FormulaInfoProxy>(parent, name, desc)
423{
424    *this = r;
425}
426
427const Formula &
428Formula::operator=(const Temp &r)
429{
430    assert(!root && "Can't change formulas");
431    root = r.getNodePtr();
432    setInit();
433    assert(size());
434    return *this;
435}
436
437const Formula &
438Formula::operator+=(Temp r)
439{
440    if (root)
441        root = NodePtr(new BinaryNode<std::plus<Result> >(root, r));
442    else {
443        root = r.getNodePtr();
444        setInit();
445    }
446
447    assert(size());
448    return *this;
449}
450
451const Formula &
452Formula::operator/=(Temp r)
453{
454    assert (root);
455    root = NodePtr(new BinaryNode<std::divides<Result> >(root, r));
456
457    assert(size());
458    return *this;
459}
460
461
462void
463Formula::result(VResult &vec) const
464{
465    if (root)
466        vec = root->result();
467}
468
469Result
470Formula::total() const
471{
472    return root ? root->total() : 0.0;
473}
474
475size_type
476Formula::size() const
477{
478    if (!root)
479        return 0;
480    else
481        return root->size();
482}
483
484void
485Formula::reset()
486{
487}
488
489bool
490Formula::zero() const
491{
492    VResult vec;
493    result(vec);
494    for (VResult::size_type i = 0; i < vec.size(); ++i)
495        if (vec[i] != 0.0)
496            return false;
497    return true;
498}
499
500string
501Formula::str() const
502{
503    return root ? root->str() : "";
504}
505
506Handler resetHandler = NULL;
507Handler dumpHandler = NULL;
508
509void
510registerHandlers(Handler reset_handler, Handler dump_handler)
511{
512    resetHandler = reset_handler;
513    dumpHandler = dump_handler;
514}
515
516CallbackQueue dumpQueue;
517CallbackQueue resetQueue;
518
519void
520processResetQueue()
521{
522    resetQueue.process();
523}
524
525void
526processDumpQueue()
527{
528    dumpQueue.process();
529}
530
531void
532registerResetCallback(Callback *cb)
533{
534    resetQueue.add(cb);
535}
536
537bool _enabled = false;
538
539bool
540enabled()
541{
542    return _enabled;
543}
544
545void
546enable()
547{
548    if (_enabled)
549        fatal("Stats are already enabled");
550
551    _enabled = true;
552}
553
554void
555dump()
556{
557    if (dumpHandler)
558        dumpHandler();
559    else
560        fatal("No registered Stats::dump handler");
561}
562
563void
564reset()
565{
566    if (resetHandler)
567        resetHandler();
568    else
569        fatal("No registered Stats::reset handler");
570}
571
572void
573registerDumpCallback(Callback *cb)
574{
575    dumpQueue.add(cb);
576}
577
578} // namespace Stats
579
580void
581debugDumpStats()
582{
583    Stats::dump();
584}
585