1/****************************************************************************
2 * Copyright (C) 2014-2015 Intel Corporation.   All Rights Reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 ****************************************************************************/
23
24#include "common/os.h"
25#include <stdarg.h>
26#include <stdio.h>
27#include <assert.h>
28#include <algorithm>
29#include <mutex>
30
31#if SWR_ENABLE_ASSERTS || SWR_ENABLE_REL_ASSERTS
32
33#if defined(_WIN32)
34#pragma comment(lib, "user32.lib")
35#endif // _WIN32
36
37namespace ConsoleUtils
38{
39    enum class TextColor
40    {
41        BLACK = 0,
42#if defined(_WIN32)
43        RED   = 4,
44        GREEN = 2,
45        BLUE  = 1,
46#else
47        RED   = 1,
48        GREEN = 2,
49        BLUE  = 4,
50#endif // _WIN32
51        PURPLE = static_cast<uint32_t>(RED) | static_cast<uint32_t>(BLUE),
52        CYAN   = static_cast<uint32_t>(GREEN) | static_cast<uint32_t>(BLUE),
53        YELLOW = static_cast<uint32_t>(RED) | static_cast<uint32_t>(GREEN),
54        WHITE =
55            static_cast<uint32_t>(RED) | static_cast<uint32_t>(GREEN) | static_cast<uint32_t>(BLUE),
56    };
57
58    enum class TextStyle
59    {
60        NORMAL    = 0,
61        INTENSITY = 1,
62    };
63
64    void SetTextColor(FILE*     stream,
65                      TextColor color = TextColor::WHITE,
66                      TextStyle style = TextStyle::NORMAL)
67    {
68#if defined(_WIN32)
69
70        HANDLE hConsoleHandle = nullptr;
71        if (stream == stderr)
72        {
73            hConsoleHandle = GetStdHandle(STD_ERROR_HANDLE);
74        }
75        else if (stream == stdout)
76        {
77            hConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
78        }
79        else
80        {
81            // Not a console stream, do nothing
82            return;
83        }
84
85        WORD textAttributes = static_cast<WORD>(color);
86        if (style == TextStyle::INTENSITY)
87        {
88            textAttributes |= FOREGROUND_INTENSITY;
89        }
90        SetConsoleTextAttribute(hConsoleHandle, textAttributes);
91
92#else // !_WIN32
93
94        // Print ANSI codes
95        uint32_t cc =
96            30 + ((style == TextStyle::INTENSITY) ? 60 : 0) + static_cast<uint32_t>(color);
97        fprintf(stream, "\033[0m\033[%d;%dm", static_cast<uint32_t>(style), cc);
98
99#endif
100    }
101
102    void ResetTextColor(FILE* stream)
103    {
104#if defined(_WIN32)
105
106        SetTextColor(stream);
107
108#else // !_WIN32
109
110        // Print ANSI codes
111        fprintf(stream, "\033[0m");
112
113#endif
114    }
115
116    static std::mutex g_stderrMutex;
117} // namespace ConsoleUtils
118
119bool SwrAssert(bool        chkDebugger,
120               bool&       enabled,
121               const char* pExpression,
122               const char* pFileName,
123               uint32_t    lineNum,
124               const char* pFunction,
125               const char* pFmtString,
126               ...)
127{
128    using namespace ConsoleUtils;
129    std::lock_guard<std::mutex> l(g_stderrMutex);
130
131    SetTextColor(stderr, TextColor::CYAN, TextStyle::NORMAL);
132
133    fprintf(stderr, "%s(%d): ", pFileName, lineNum);
134
135    SetTextColor(stderr, TextColor::RED, TextStyle::INTENSITY);
136
137    fprintf(stderr, "ASSERT: %s\n", pExpression);
138
139    SetTextColor(stderr, TextColor::CYAN, TextStyle::INTENSITY);
140    fprintf(stderr, "\t%s\n", pFunction);
141
142    if (pFmtString)
143    {
144        SetTextColor(stderr, TextColor::YELLOW, TextStyle::INTENSITY);
145        fprintf(stderr, "\t");
146        va_list args;
147        va_start(args, pFmtString);
148        vfprintf(stderr, pFmtString, args);
149        va_end(args);
150        fprintf(stderr, "\n");
151    }
152    ResetTextColor(stderr);
153    fflush(stderr);
154
155#if defined(_WIN32)
156    static const int MAX_MESSAGE_LEN = 2048;
157    char             msgBuf[MAX_MESSAGE_LEN];
158
159    sprintf_s(msgBuf, "%s(%d): ASSERT: %s\n", pFileName, lineNum, pExpression);
160    msgBuf[MAX_MESSAGE_LEN - 2] = '\n';
161    msgBuf[MAX_MESSAGE_LEN - 1] = 0;
162    OutputDebugStringA(msgBuf);
163
164    sprintf_s(msgBuf, "\t%s\n", pFunction);
165    msgBuf[MAX_MESSAGE_LEN - 2] = '\n';
166    msgBuf[MAX_MESSAGE_LEN - 1] = 0;
167    OutputDebugStringA(msgBuf);
168
169    int offset = 0;
170
171    if (pFmtString)
172    {
173        va_list args;
174        va_start(args, pFmtString);
175        offset = _vsnprintf_s(msgBuf, sizeof(msgBuf), sizeof(msgBuf), pFmtString, args);
176        va_end(args);
177
178        if (offset < 0)
179        {
180            return true;
181        }
182
183        OutputDebugStringA("\t");
184        OutputDebugStringA(msgBuf);
185        OutputDebugStringA("\n");
186    }
187
188    if (enabled && KNOB_ENABLE_ASSERT_DIALOGS)
189    {
190        int retval = sprintf_s(&msgBuf[offset],
191                               MAX_MESSAGE_LEN - offset,
192                               "\n\n"
193                               "File: %s\n"
194                               "Line: %d\n"
195                               "\n"
196                               "Expression: %s\n\n"
197                               "Cancel: Disable this assert for the remainder of the process\n"
198                               "Try Again: Break into the debugger\n"
199                               "Continue: Continue execution (but leave assert enabled)",
200                               pFileName,
201                               lineNum,
202                               pExpression);
203
204        if (retval < 0)
205        {
206            return true;
207        }
208
209        offset += retval;
210
211        if (!IsDebuggerPresent())
212        {
213            sprintf_s(&msgBuf[offset],
214                      MAX_MESSAGE_LEN - offset,
215                      "\n\n*** NO DEBUGGER DETECTED ***\n\nPressing \"Try Again\" will cause a "
216                      "program crash!");
217        }
218
219        retval = MessageBoxA(nullptr,
220                             msgBuf,
221                             "Assert Failed",
222                             MB_CANCELTRYCONTINUE | MB_ICONEXCLAMATION | MB_SETFOREGROUND);
223
224        switch (retval)
225        {
226        case IDCANCEL:
227            enabled = false;
228            return false;
229
230        case IDTRYAGAIN:
231            return true;
232
233        case IDCONTINUE:
234            return false;
235        }
236    }
237    else
238    {
239        return (IsDebuggerPresent() || !chkDebugger) && enabled;
240    }
241#endif // _WIN32
242
243    return enabled;
244}
245
246void SwrTrace(
247    const char* pFileName, uint32_t lineNum, const char* pFunction, const char* pFmtString, ...)
248{
249    using namespace ConsoleUtils;
250    std::lock_guard<std::mutex> l(g_stderrMutex);
251
252    SetTextColor(stderr, TextColor::CYAN, TextStyle::NORMAL);
253
254    fprintf(stderr, "%s(%d): TRACE in %s:\n", pFileName, lineNum, pFunction);
255
256    if (pFmtString)
257    {
258        SetTextColor(stderr, TextColor::PURPLE, TextStyle::INTENSITY);
259        fprintf(stderr, "\t");
260        va_list args;
261        va_start(args, pFmtString);
262        vfprintf(stderr, pFmtString, args);
263        va_end(args);
264        fprintf(stderr, "\n");
265    }
266    ResetTextColor(stderr);
267    fflush(stderr);
268
269#if defined(_WIN32)
270    static const int MAX_MESSAGE_LEN = 2048;
271    char             msgBuf[MAX_MESSAGE_LEN];
272
273    sprintf_s(msgBuf, "%s(%d): TRACE in %s\n", pFileName, lineNum, pFunction);
274    msgBuf[MAX_MESSAGE_LEN - 2] = '\n';
275    msgBuf[MAX_MESSAGE_LEN - 1] = 0;
276    OutputDebugStringA(msgBuf);
277
278    int offset = 0;
279
280    if (pFmtString)
281    {
282        va_list args;
283        va_start(args, pFmtString);
284        offset = _vsnprintf_s(msgBuf, sizeof(msgBuf), sizeof(msgBuf), pFmtString, args);
285        va_end(args);
286
287        if (offset < 0)
288        {
289            return;
290        }
291
292        OutputDebugStringA("\t");
293        OutputDebugStringA(msgBuf);
294        OutputDebugStringA("\n");
295    }
296#endif // _WIN32
297}
298
299#endif // SWR_ENABLE_ASSERTS
300