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( _RELEASE_INTERNAL ) && !defined( _WIN32 ) && !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