1/*
2 * Copyright (c) 2014, 2017, 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) 2002-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 *          Dave Greene
42 *          Andreas Sandberg
43 */
44
45#ifndef __BASE_LOGGING_HH__
46#define __BASE_LOGGING_HH__
47
48#include <cassert>
49#include <sstream>
50#include <utility>
51
52#include "base/compiler.hh"
53#include "base/cprintf.hh"
54
55class Logger
56{
57  public:
58
59    // Get a Logger for the specified type of message.
60    static Logger &getPanic();
61    static Logger &getFatal();
62    static Logger &getWarn();
63    static Logger &getInfo();
64    static Logger &getHack();
65
66    enum LogLevel {
67        PANIC, FATAL, WARN, INFO, HACK,
68        NUM_LOG_LEVELS,
69    };
70
71    static void
72    setLevel(LogLevel ll)
73    {
74        getPanic().enabled = (ll >= PANIC);
75        getFatal().enabled = (ll >= FATAL);
76        getWarn().enabled = (ll >= WARN);
77        getInfo().enabled = (ll >= INFO);
78        getHack().enabled = (ll >= HACK);
79    }
80
81    struct Loc
82    {
83        Loc(const char *file, int line) : file(file), line(line) {}
84        const char *file;
85        int line;
86    };
87
88    Logger(const char *prefix) : enabled(true), prefix(prefix)
89    {
90        assert(prefix);
91    }
92
93    virtual ~Logger() {};
94
95    void
96    print(const Loc &loc, const std::string &str)
97    {
98        std::stringstream ss;
99        ss << prefix << str;
100        if (str.length() && str.back() != '\n' && str.back() != '\r')
101            ss << std::endl;
102        if (!enabled)
103            return;
104        log(loc, ss.str());
105    }
106
107    template<typename ...Args> void
108    print(const Loc &loc, const char *format, const Args &...args)
109    {
110        std::stringstream ss;
111        ccprintf(ss, format, args...);
112        print(loc, ss.str());
113    }
114
115    template<typename ...Args> void
116    print(const Loc &loc, const std::string &format, const Args &...args)
117    {
118        print(loc, format.c_str(), args...);
119    }
120
121    // This helper is necessary since noreturn isn't inherited by virtual
122    // functions, and gcc will get mad if a function calls panic and then
123    // doesn't return.
124    void exit_helper() M5_ATTR_NORETURN { exit(); ::abort(); }
125
126  protected:
127    bool enabled;
128
129    virtual void log(const Loc &loc, std::string s) = 0;
130    virtual void exit() { /* Fall through to the abort in exit_helper. */ }
131
132    const char *prefix;
133};
134
135
136#define base_message(logger, ...) \
137    logger.print(::Logger::Loc(__FILE__, __LINE__), __VA_ARGS__)
138
139/*
140 * Only print the message the first time this expression is
141 * encountered.  i.e.  This doesn't check the string itself and
142 * prevent duplicate strings, this prevents the statement from
143 * happening more than once. So, even if the arguments change and that
144 * would have resulted in a different message thoes messages would be
145 * supressed.
146 */
147#define base_message_once(...) do {                     \
148        static bool once = false;                       \
149        if (!once) {                                    \
150            base_message(__VA_ARGS__);                  \
151            once = true;                                \
152        }                                               \
153    } while (0)
154
155#define exit_message(logger, ...)                       \
156    do {                                                \
157        base_message(logger, __VA_ARGS__);              \
158        logger.exit_helper();                           \
159    } while (0)
160
161/**
162 * This implements a cprintf based panic() function.  panic() should
163 * be called when something happens that should never ever happen
164 * regardless of what the user does (i.e., an acutal m5 bug).  panic()
165 * might call abort which can dump core or enter the debugger.
166 */
167#define panic(...) exit_message(::Logger::getPanic(), __VA_ARGS__)
168
169/**
170 * This implements a cprintf based fatal() function.  fatal() should
171 * be called when the simulation cannot continue due to some condition
172 * that is the user's fault (bad configuration, invalid arguments,
173 * etc.) and not a simulator bug.  fatal() might call exit, unlike panic().
174 */
175#define fatal(...) exit_message(::Logger::getFatal(), __VA_ARGS__)
176
177/**
178 * Conditional panic macro that checks the supplied condition and only panics
179 * if the condition is true and allows the programmer to specify diagnostic
180 * printout.  Useful to replace if + panic, or if + print + assert, etc.
181 *
182 * @param cond Condition that is checked; if true -> panic
183 * @param ...  Printf-based format string with arguments, extends printout.
184 */
185#define panic_if(cond, ...)                                  \
186    do {                                                     \
187        if ((cond)) {                                        \
188            panic("panic condition " # cond " occurred: %s", \
189                  csprintf(__VA_ARGS__));                    \
190        }                                                    \
191    } while (0)
192
193
194/**
195 * Conditional fatal macro that checks the supplied condition and only causes a
196 * fatal error if the condition is true and allows the programmer to specify
197 * diagnostic printout.  Useful to replace if + fatal, or if + print + assert,
198 * etc.
199 *
200 * @param cond Condition that is checked; if true -> fatal
201 * @param ...  Printf-based format string with arguments, extends printout.
202 */
203#define fatal_if(cond, ...)                                     \
204    do {                                                        \
205        if ((cond)) {                                           \
206            fatal("fatal condition " # cond " occurred: %s",    \
207                  csprintf(__VA_ARGS__));                       \
208        }                                                       \
209    } while (0)
210
211
212#define warn(...) base_message(::Logger::getWarn(), __VA_ARGS__)
213#define inform(...) base_message(::Logger::getInfo(), __VA_ARGS__)
214#define hack(...) base_message(::Logger::getHack(), __VA_ARGS__)
215
216#define warn_once(...) base_message_once(::Logger::getWarn(), __VA_ARGS__)
217#define inform_once(...) base_message_once(::Logger::getInfo(), __VA_ARGS__)
218#define hack_once(...) base_message_once(::Logger::getHack(), __VA_ARGS__)
219
220/**
221 * Conditional warning macro that checks the supplied condition and
222 * only prints a warning if the condition is true. Useful to replace
223 * if + warn.
224 *
225 * @param cond Condition that is checked; if true -> warn
226 * @param ...  Printf-based format string with arguments, extends printout.
227 */
228#define warn_if(cond, ...) \
229    do { \
230        if ((cond)) \
231            warn(__VA_ARGS__); \
232    } while (0)
233
234#define warn_if_once(cond, ...) \
235    do { \
236        if ((cond)) \
237            warn_once(__VA_ARGS__); \
238    } while (0)
239
240/**
241 * The chatty assert macro will function like a normal assert, but will allow
242 * the specification of additional, helpful material to aid debugging why the
243 * assertion actually failed.  Like the normal assertion, the chatty_assert
244 * will not be active in fast builds.
245 *
246 * @param cond Condition that is checked; if false -> assert
247 * @param ...  Printf-based format string with arguments, extends printout.
248 */
249#ifdef NDEBUG
250#define chatty_assert(cond, ...)
251#else //!NDEBUG
252#define chatty_assert(cond, ...)                                        \
253    do {                                                                \
254        if (!(cond))                                                    \
255            panic("assert(" # cond ") failed: %s", csprintf(__VA_ARGS__)); \
256    } while (0)
257#endif // NDEBUG
258#endif // __BASE_LOGGING_HH__
259