1 /*==============================================================================
2 Copyright(c) 2017 Intel Corporation
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 shall be included
12 in all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 OTHER DEALINGS IN THE SOFTWARE.
21 ============================================================================*/
22 /*
23
24 File Name: AssertTracer.cpp
25
26 Abstract:
27 These functions enables reporting asserts to system log in the debug
28 driver build.
29
30 Notes:
31
32 \*****************************************************************************/
33
34 #if defined( _WIN32 ) && (defined( _DEBUG ) || defined( _RELEASE_INTERNAL ))
35 #include "AssertTracer.h"
36 #ifndef NOMINMAX
37 #define NOMINMAX
38 #endif
39 #include <windows.h>
40
41 // Windows.h defines MemoryFence as _mm_mfence, but this conflicts with llvm::sys::MemoryFence
42 #undef MemoryFence
43 #include <stdio.h>
44 #include <stdlib.h>
45
46 #if defined( __GMM_KMD__ ) || defined( STATIC_DRIVER_MODEL )
47 #include"BufferedDbg.h"
48 #include "igdKrnlEtwMacros.h"
49 #endif //__GMM_KMD__ || STATIC_DRIVER_MODEL
50
51 #if _DEBUG
52 /*****************************************************************************\
53
54 Function:
55 ReportAsserts
56
57 Description:
58 Sends message to the system log.
59
60 Input:
61 const char *expr -
62 The expression passed to function.
63 const char *file -
64 The name of the file that is the origin of the expression.
65 const char *func -
66 The function from which call to this function was made.
67 const unsigned long line -
68 The line number from file, which caused this function call.
69 const char *msg -
70 Message passed to the system log.
71 Output:
72 void - None.
73
74 \*****************************************************************************/
ReportAssert(const char * expr,const char * file,const char * func,const unsigned long line,const char * msg)75 void __stdcall ReportAssert(
76 const char *expr,
77 const char *file,
78 const char *func,
79 const unsigned long line,
80 const char *msg )
81 {
82 #if !defined( __GMM_KMD__ ) && !defined( STATIC_DRIVER_MODEL )//We are in UMD, use the UMD implementation
83
84 #if 0
85
86 HANDLE hEventLog = RegisterEventSourceA( NULL, "GFX Driver AssertTracer" );
87 char *wideMessage;
88 if ( hEventLog != NULL )
89 {
90 // Calculate length with hard coded max unsigned long length plus some safe space.
91 size_t length = strlen( expr ) + strlen( file ) + strlen( func ) + strlen( msg ) + 15;
92
93 // Checks against maximum string size for ReportEvent lpStrings parameter.
94 // Cuts string to the limit size.
95 uint32_t maxSizeReached = 0;
96 if ( length > 31839 )
97 {
98 length = 31839;
99 maxSizeReached = 1;
100 }
101
102 wideMessage = ( char * ) malloc( sizeof( char ) * length );
103 if( wideMessage != NULL )
104 {
105 // snprintf spec: "The resulting character string will be terminated with a null character"
106 _snprintf_s( wideMessage, length, length / sizeof( char ), "%s:%lu\n%s\n%s\n%s", file, line, expr, func, msg);
107
108 ReportEventA( hEventLog, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ( LPCSTR* ) &wideMessage, NULL );
109 free( wideMessage );
110 }
111
112 DeregisterEventSource( hEventLog );
113 }
114
115 #endif //!define( WINDOWS_MOBILE )
116
117 // Windows Mobile has no implementation. Reference the parameters to remove the unreference param warnings.
118 (void)expr;
119 (void)file;
120 (void)func;
121 (void)line;
122 (void)msg;
123
124 #else //We are in KMD, use the KMD implementation
125 BufDbgPrint("ASSERT_TRACE: %s::%s:%i : %s : %s\n", file, func, line, expr, msg);
126 #endif //!defined( __GMM_KMD__ ) && !defined( STATIC_DRIVER_MODEL )
127 }
128 #endif
129
130 /*****************************************************************************\
131
132 Function:
133 ReportAssertsETW
134
135 Description:
136 Sends message to the system log.
137
138 Input:
139 const unsigned long ComponentMask
140 Contains the component id for which raised assert (KMD, MINIPORT..)
141 const char *expr -
142 The expression passed to function.
143 const char *file -
144 The name of the file that is the origin of the expression.
145 const char *func -
146 The function from which call to this function was made.
147 const unsigned long line -
148 The line number from file, which caused this function call.
149 const char *msg -
150 Message passed to the system log.
151 Output:
152 void - None.
153
154 \*****************************************************************************/
155
ReportAssertETW(const unsigned short compId,const unsigned long compMsk,const char * expr,const char * file,const char * func,const unsigned long line,const char * msg)156 void __stdcall ReportAssertETW( const unsigned short compId,
157 const unsigned long compMsk,
158 const char *expr,
159 const char *file,
160 const char *func,
161 const unsigned long line,
162 const char *msg)
163 {
164 #if !defined( __GMM_KMD__ ) && !defined( STATIC_DRIVER_MODEL ) //We are in UMD, use the UMD implementation
165 // TODO: Add UMD code for ETW here.
166 // Reference the parameters to remove the unreference param warnings.
167 (void)compId;
168 (void)compMsk;
169 (void)expr;
170 (void)file;
171 (void)func;
172 (void)line;
173 (void)msg;
174 #else //We are in KMD, use the KMD implementation
175 // Log event if ETW session is active
176 if (g_ulHDGraphics_SessionActive)
177 {
178 EtwAssertPrint(compId, compMsk, expr, file, func, line, msg);
179 }
180 #endif
181 }
182
183 #elif defined( __linux__ ) && defined( _RELEASE_INTERNAL ) && !defined( __ANDROID__ )
184 #include <algorithm>
185 #include <syslog.h>
186 #include <execinfo.h>
187 #include <string>
188 #include <cxxabi.h>
189 #include <cstdlib>
190 #include <cstring>
191 #include <sstream>
192 #include <fstream>
193 #define CALL_STACK_REPORT_SIZE 50 // maximal limit of call stack to be reported in case of assertion
194 #define MAX_FUNCTION_NAME_LENGTH 100 // no worries it can be extended (relocated by driver if needed)
195 #define MAX_SYSLOG_ENTRY_LENGTH 1000 // Syslog protocol recommends minimum entry length 1KB (ubuntu rsyslog message limit is about 2K)
LogAssertion(const char * function_name,const char * file_name,unsigned int line_number,const char * expr)196 void LogAssertion( const char *function_name, const char *file_name, unsigned int line_number, const char *expr )
197 {
198 std::string stack;
199 std::string part1;
200 std::string part2;
201 std::size_t pos;
202 void *buffer[CALL_STACK_REPORT_SIZE];
203 char *function_name_buffer = NULL;
204 int nframes; // numer of frames to be returned
205 char **strings;
206 int i;
207 int status;
208 size_t length;
209 const size_t stringLength = 4096;
210 std::string oclAppCmdLine;
211
212 nframes = backtrace( buffer, CALL_STACK_REPORT_SIZE );
213
214 strings = backtrace_symbols( buffer, nframes );
215 if( strings == NULL )
216 {
217 perror( "Getting backtrace symbols error:" );
218 nframes = 0;
219 }
220 else
221 {
222 // Get Commandline of process (in that case OCL app)
223 // from process info itself eg. /proc/self/cmdline
224
225 oclAppCmdLine.reserve( stringLength );
226 std::ifstream fileCmdline( "/proc/self/cmdline", std::ifstream::binary );
227 if( fileCmdline.is_open() )
228 {
229 oclAppCmdLine = std::string( std::istreambuf_iterator< char >( fileCmdline ), std::istreambuf_iterator< char >() );
230 if( oclAppCmdLine.size() > 0 )
231 {
232 // Trim last \0 character
233 oclAppCmdLine.resize( oclAppCmdLine.size() - 1 );
234 // Format of /proc/self/cmdline is that args are separated with '\0'
235 // so for nicer printing we replace zeros with spaces (without terminating 0)
236 std::replace( oclAppCmdLine.begin(), oclAppCmdLine.end(), '\0', ' ' );
237 }
238 else
239 {
240 fprintf( stderr, "Getting Commandline of OCL app error: Error reading /proc/self/cmdline\n" );
241 }
242 }
243 else
244 {
245 fprintf( stderr, "Getting Commandline of OCL app error:" );
246 }
247 }
248
249 // allocation by malloc is suggessted by documentation as abi function may do some relocation
250 function_name_buffer = ( char * )malloc( MAX_FUNCTION_NAME_LENGTH );
251 if( function_name_buffer == NULL )
252 {
253 // Not enough memory to get small allocation then do not print stack
254 nframes = 0;
255 }
256 else
257 {
258 length = MAX_FUNCTION_NAME_LENGTH;
259 memset( function_name_buffer, 0, length );
260 }
261
262 for( i = 0; i < nframes; ++i )
263 {
264 // Generate signature of given stack frame eg. #0 #1 etc...
265 std::stringstream framePromptBuilder;
266 framePromptBuilder << "#" << i << " ";
267 const std::string &framePrompt = framePromptBuilder.str();
268 // demangle name eg. split stack frame into two pieces
269 part1 = strings[i];
270 pos = part1.find( "(" );
271 if( pos != std::string::npos )
272 {
273 // For final level instead of binary print whole commandline
274 // if were able to get one
275 if( ( i == nframes - 1 ) && ( !oclAppCmdLine.empty() ) )
276 {
277 //..call stack's part1 contains "(" so we add it here manualy
278 stack.append( ( oclAppCmdLine.insert( 0, "COMMANDLINE(" ) ).c_str() );
279 }
280 else
281 {
282 // part1 contains everything before section to be demangled
283 part1 = part1.substr( 0, pos + 1 );
284 stack.append(framePrompt);
285 stack.append( part1 );
286 }
287 // part2 contains string to be demangled
288 part2 = strings[i];
289 part2 = part2.substr( pos + 1 );
290 pos = part2.find( "+" );
291 // Final level may not have any function (static functions are not exposed)
292 if( pos != std::string::npos )
293 {
294 part2 = part2.substr( 0, pos );
295 function_name_buffer = abi::__cxa_demangle( part2.c_str(), function_name_buffer, &length, &status );
296 // in case of error during demangling attach mangled name
297 if( status != 0 )
298 {
299 stack.append( part2 );
300 }
301 else
302 {
303 stack.append( function_name_buffer );
304 }
305 stack.append( "())" );
306 free( function_name_buffer );
307 function_name_buffer = NULL;
308
309 }
310 else
311 {
312 // if there was no function then attach ")"
313 stack.append( ")" );
314 }
315 }
316 else
317 {
318 if( ( i == nframes - 1 ) && ( !oclAppCmdLine.empty() ) )
319 {
320 stack.append( ( oclAppCmdLine.insert( 0, "COMMANDLINE(" ) ).c_str() );
321 stack.append( ")" );
322 }
323 else
324 {
325 stack.append(framePrompt);
326 stack.append( strings[i] );
327 }
328 }
329 stack.append( " " );
330 }
331 //Compose full message..
332 std::string fullMsg = "File: ";
333 std::string syslogEntry;
334 fullMsg += file_name;
335 fullMsg += " line: ";
336 std::stringstream lineStr;
337 lineStr << line_number;
338 fullMsg += lineStr.str();
339 fullMsg += " function: ";
340 fullMsg += function_name;
341 fullMsg += " expr: ";
342 fullMsg += expr;
343 fullMsg += " ";
344 fullMsg += stack.c_str();
345
346 // split it into chunks we can send
347 openlog( "OpenCL", LOG_PID, LOG_USER );
348 pos = 0;
349 int numberOfChunks = ( fullMsg.length() / MAX_SYSLOG_ENTRY_LENGTH ) + 1;
350 while( pos < fullMsg.length() )
351 {
352 syslogEntry = fullMsg.substr( pos, MAX_SYSLOG_ENTRY_LENGTH );
353 // Add chunk ID / part number and send to syslog
354 syslog( LOG_MAKEPRI( LOG_USER,
355 LOG_ERR ), "[%zd/%d]%s", ( pos / MAX_SYSLOG_ENTRY_LENGTH + 1 ), numberOfChunks,
356 syslogEntry.c_str() );
357 pos += MAX_SYSLOG_ENTRY_LENGTH;
358 }
359 closelog();
360
361 // backtrace_symbols allocates memory in a malloc like way sop it should be freed
362 free( strings );
363 strings = NULL;
364 }
365 #endif //defined( _WIN32 ) && defined( _DEBUG )
366