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(_MSC_VER)
34 #pragma comment(lib, "user32.lib")
35 #endif // _WIN32
36 
37 namespace 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 
SetTextColor(FILE * stream,TextColor color=TextColor::WHITE,TextStyle style=TextStyle::NORMAL)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 
ResetTextColor(FILE * stream)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 
SwrAssert(bool chkDebugger,bool & enabled,const char * pExpression,const char * pFileName,uint32_t lineNum,const char * pFunction,const char * pFmtString,...)119 bool 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 
SwrTrace(const char * pFileName,uint32_t lineNum,const char * pFunction,const char * pFmtString,...)246 void 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