LCOV - code coverage report
Current view: top level - src - debug.cpp (source / functions) Hit Total Coverage
Test: report Lines: 79 135 58.5 %
Date: 2015-07-11 18:23:49 Functions: 13 20 65.0 %

          Line data    Source code
       1             : /*
       2             : Minetest
       3             : Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
       4             : 
       5             : This program is free software; you can redistribute it and/or modify
       6             : it under the terms of the GNU Lesser General Public License as published by
       7             : the Free Software Foundation; either version 2.1 of the License, or
       8             : (at your option) any later version.
       9             : 
      10             : This program is distributed in the hope that it will be useful,
      11             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             : GNU Lesser General Public License for more details.
      14             : 
      15             : You should have received a copy of the GNU Lesser General Public License along
      16             : with this program; if not, write to the Free Software Foundation, Inc.,
      17             : 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      18             : */
      19             : 
      20             : 
      21             : #include "porting.h"
      22             : #include "debug.h"
      23             : #include "exceptions.h"
      24             : #include "threads.h"
      25             : #include <stdio.h>
      26             : #include <stdlib.h>
      27             : #include <cstring>
      28             : #include <map>
      29             : #include "jthread/jmutex.h"
      30             : #include "jthread/jmutexautolock.h"
      31             : #include "config.h"
      32             : 
      33             : #ifdef _MSC_VER
      34             :         #include <dbghelp.h>
      35             :         #include "version.h"
      36             :         #include "filesys.h"
      37             : #endif
      38             : 
      39             : /*
      40             :         Debug output
      41             : */
      42             : 
      43             : #define DEBUGSTREAM_COUNT 2
      44             : 
      45           1 : FILE *g_debugstreams[DEBUGSTREAM_COUNT] = {stderr, NULL};
      46             : 
      47             : #define DEBUGPRINT(...)\
      48             : {\
      49             :         for(int i=0; i<DEBUGSTREAM_COUNT; i++)\
      50             :         {\
      51             :                 if(g_debugstreams[i] != NULL){\
      52             :                         fprintf(g_debugstreams[i], __VA_ARGS__);\
      53             :                         fflush(g_debugstreams[i]);\
      54             :                 }\
      55             :         }\
      56             : }
      57             : 
      58           1 : void debugstreams_init(bool disable_stderr, const char *filename)
      59             : {
      60           1 :         if(disable_stderr)
      61           0 :                 g_debugstreams[0] = NULL;
      62             :         else
      63           1 :                 g_debugstreams[0] = stderr;
      64             : 
      65           1 :         if(filename)
      66           1 :                 g_debugstreams[1] = fopen(filename, "a");
      67             : 
      68           1 :         if(g_debugstreams[1])
      69             :         {
      70           1 :                 fprintf(g_debugstreams[1], "\n\n-------------\n");
      71           1 :                 fprintf(g_debugstreams[1],     "  Separator  \n");
      72           1 :                 fprintf(g_debugstreams[1],     "-------------\n\n");
      73             :         }
      74           1 : }
      75             : 
      76           1 : void debugstreams_deinit()
      77             : {
      78           1 :         if(g_debugstreams[1] != NULL)
      79           1 :                 fclose(g_debugstreams[1]);
      80           1 : }
      81             : 
      82           2 : class Debugbuf : public std::streambuf
      83             : {
      84             : public:
      85           2 :         Debugbuf(bool disable_stderr)
      86           2 :         {
      87           2 :                 m_disable_stderr = disable_stderr;
      88           2 :         }
      89             : 
      90          18 :         int overflow(int c)
      91             :         {
      92          54 :                 for(int i=0; i<DEBUGSTREAM_COUNT; i++)
      93             :                 {
      94          36 :                         if(g_debugstreams[i] == stderr && m_disable_stderr)
      95           6 :                                 continue;
      96          30 :                         if(g_debugstreams[i] != NULL)
      97          21 :                                 (void)fwrite(&c, 1, 1, g_debugstreams[i]);
      98             :                         //TODO: Is this slow?
      99          30 :                         fflush(g_debugstreams[i]);
     100             :                 }
     101             : 
     102          18 :                 return c;
     103             :         }
     104          27 :         std::streamsize xsputn(const char *s, std::streamsize n)
     105             :         {
     106             : #ifdef __ANDROID__
     107             :                 __android_log_print(ANDROID_LOG_VERBOSE, PROJECT_NAME, "%s", s);
     108             : #endif
     109          81 :                 for(int i=0; i<DEBUGSTREAM_COUNT; i++)
     110             :                 {
     111          54 :                         if(g_debugstreams[i] == stderr && m_disable_stderr)
     112           6 :                                 continue;
     113          48 :                         if(g_debugstreams[i] != NULL)
     114          33 :                                 (void)fwrite(s, 1, n, g_debugstreams[i]);
     115             :                         //TODO: Is this slow?
     116          48 :                         fflush(g_debugstreams[i]);
     117             :                 }
     118             : 
     119          27 :                 return n;
     120             :         }
     121             : 
     122             : private:
     123             :         bool m_disable_stderr;
     124             : };
     125             : 
     126           1 : Debugbuf debugbuf(false);
     127           1 : std::ostream dstream(&debugbuf);
     128           1 : Debugbuf debugbuf_no_stderr(true);
     129           1 : std::ostream dstream_no_stderr(&debugbuf_no_stderr);
     130           1 : Nullstream dummyout;
     131             : 
     132             : /*
     133             :         Assert
     134             : */
     135             : 
     136           0 : void sanity_check_fn(const char *assertion, const char *file,
     137             :                 unsigned int line, const char *function)
     138             : {
     139           0 :         DEBUGPRINT("\nIn thread %lx:\n"
     140             :                         "%s:%u: %s: An engine assumption '%s' failed.\n",
     141             :                         (unsigned long)get_current_thread_id(),
     142             :                         file, line, function, assertion);
     143             : 
     144           0 :         debug_stacks_print();
     145             : 
     146           0 :         if(g_debugstreams[1])
     147           0 :                 fclose(g_debugstreams[1]);
     148             : 
     149           0 :         abort();
     150             : }
     151             : 
     152           0 : void fatal_error_fn(const char *msg, const char *file,
     153             :                 unsigned int line, const char *function)
     154             : {
     155           0 :         DEBUGPRINT("\nIn thread %lx:\n"
     156             :                         "%s:%u: %s: A fatal error occurred: %s\n",
     157             :                         (unsigned long)get_current_thread_id(),
     158             :                         file, line, function, msg);
     159             : 
     160           0 :         debug_stacks_print();
     161             : 
     162           0 :         if(g_debugstreams[1])
     163           0 :                 fclose(g_debugstreams[1]);
     164             : 
     165           0 :         abort();
     166             : }
     167             : 
     168             : /*
     169             :         DebugStack
     170             : */
     171             : 
     172             : struct DebugStack
     173             : {
     174             :         DebugStack(threadid_t id);
     175             :         void print(FILE *file, bool everything);
     176             :         void print(std::ostream &os, bool everything);
     177             : 
     178             :         threadid_t threadid;
     179             :         char stack[DEBUG_STACK_SIZE][DEBUG_STACK_TEXT_SIZE];
     180             :         int stack_i; // Points to the lowest empty position
     181             :         int stack_max_i; // Highest i that was seen
     182             : };
     183             : 
     184           3 : DebugStack::DebugStack(threadid_t id)
     185             : {
     186           3 :         threadid = id;
     187           3 :         stack_i = 0;
     188           3 :         stack_max_i = 0;
     189           3 :         memset(stack, 0, DEBUG_STACK_SIZE*DEBUG_STACK_TEXT_SIZE);
     190           3 : }
     191             : 
     192           0 : void DebugStack::print(FILE *file, bool everything)
     193             : {
     194             :         fprintf(file, "DEBUG STACK FOR THREAD %lx:\n",
     195           0 :                         (unsigned long)threadid);
     196             : 
     197           0 :         for(int i=0; i<stack_max_i; i++)
     198             :         {
     199           0 :                 if(i == stack_i && everything == false)
     200           0 :                         break;
     201             : 
     202           0 :                 if(i < stack_i)
     203           0 :                         fprintf(file, "#%d  %s\n", i, stack[i]);
     204             :                 else
     205           0 :                         fprintf(file, "(Leftover data: #%d  %s)\n", i, stack[i]);
     206             :         }
     207             : 
     208           0 :         if(stack_i == DEBUG_STACK_SIZE)
     209           0 :                 fprintf(file, "Probably overflown.\n");
     210           0 : }
     211             : 
     212           0 : void DebugStack::print(std::ostream &os, bool everything)
     213             : {
     214           0 :         os<<"DEBUG STACK FOR THREAD "<<(unsigned long)threadid<<": "<<std::endl;
     215             : 
     216           0 :         for(int i=0; i<stack_max_i; i++)
     217             :         {
     218           0 :                 if(i == stack_i && everything == false)
     219           0 :                         break;
     220             : 
     221           0 :                 if(i < stack_i)
     222           0 :                         os<<"#"<<i<<"  "<<stack[i]<<std::endl;
     223             :                 else
     224           0 :                         os<<"(Leftover data: #"<<i<<"  "<<stack[i]<<")"<<std::endl;
     225             :         }
     226             : 
     227           0 :         if(stack_i == DEBUG_STACK_SIZE)
     228           0 :                 os<<"Probably overflown."<<std::endl;
     229           0 : }
     230             : 
     231           1 : std::map<threadid_t, DebugStack*> g_debug_stacks;
     232           1 : JMutex g_debug_stacks_mutex;
     233             : 
     234           1 : void debug_stacks_init()
     235             : {
     236           1 : }
     237             : 
     238           0 : void debug_stacks_print_to(std::ostream &os)
     239             : {
     240           0 :         JMutexAutoLock lock(g_debug_stacks_mutex);
     241             : 
     242           0 :         os<<"Debug stacks:"<<std::endl;
     243             : 
     244           0 :         for(std::map<threadid_t, DebugStack*>::iterator
     245           0 :                         i = g_debug_stacks.begin();
     246           0 :                         i != g_debug_stacks.end(); ++i)
     247             :         {
     248           0 :                 i->second->print(os, false);
     249             :         }
     250           0 : }
     251             : 
     252           0 : void debug_stacks_print()
     253             : {
     254           0 :         JMutexAutoLock lock(g_debug_stacks_mutex);
     255             : 
     256           0 :         DEBUGPRINT("Debug stacks:\n");
     257             : 
     258           0 :         for(std::map<threadid_t, DebugStack*>::iterator
     259           0 :                         i = g_debug_stacks.begin();
     260           0 :                         i != g_debug_stacks.end(); ++i)
     261             :         {
     262           0 :                 DebugStack *stack = i->second;
     263             : 
     264           0 :                 for(int i=0; i<DEBUGSTREAM_COUNT; i++)
     265             :                 {
     266           0 :                         if(g_debugstreams[i] != NULL)
     267           0 :                                 stack->print(g_debugstreams[i], true);
     268             :                 }
     269             :         }
     270           0 : }
     271             : 
     272       17788 : DebugStacker::DebugStacker(const char *text)
     273             : {
     274       17788 :         threadid_t threadid = get_current_thread_id();
     275             : 
     276       35576 :         JMutexAutoLock lock(g_debug_stacks_mutex);
     277             : 
     278       17788 :         std::map<threadid_t, DebugStack*>::iterator n;
     279       17788 :         n = g_debug_stacks.find(threadid);
     280       17788 :         if(n != g_debug_stacks.end())
     281             :         {
     282       17785 :                 m_stack = n->second;
     283             :         }
     284             :         else
     285             :         {
     286             :                 /*DEBUGPRINT("Creating new debug stack for thread %x\n",
     287             :                                 (unsigned int)threadid);*/
     288           3 :                 m_stack = new DebugStack(threadid);
     289           3 :                 g_debug_stacks[threadid] = m_stack;
     290             :         }
     291             : 
     292       17788 :         if(m_stack->stack_i >= DEBUG_STACK_SIZE)
     293             :         {
     294           0 :                 m_overflowed = true;
     295             :         }
     296             :         else
     297             :         {
     298       17788 :                 m_overflowed = false;
     299             : 
     300       17788 :                 snprintf(m_stack->stack[m_stack->stack_i],
     301       17788 :                                 DEBUG_STACK_TEXT_SIZE, "%s", text);
     302       17788 :                 m_stack->stack_i++;
     303       17788 :                 if(m_stack->stack_i > m_stack->stack_max_i)
     304           8 :                         m_stack->stack_max_i = m_stack->stack_i;
     305             :         }
     306       17788 : }
     307             : 
     308       35574 : DebugStacker::~DebugStacker()
     309             : {
     310       35574 :         JMutexAutoLock lock(g_debug_stacks_mutex);
     311             : 
     312       17787 :         if(m_overflowed == true)
     313           0 :                 return;
     314             : 
     315       17787 :         m_stack->stack_i--;
     316             : 
     317       17787 :         if(m_stack->stack_i == 0)
     318             :         {
     319           2 :                 threadid_t threadid = m_stack->threadid;
     320             :                 /*DEBUGPRINT("Deleting debug stack for thread %x\n",
     321             :                                 (unsigned int)threadid);*/
     322           2 :                 delete m_stack;
     323           2 :                 g_debug_stacks.erase(threadid);
     324             :         }
     325       17787 : }
     326             : 
     327             : #ifdef _MSC_VER
     328             : 
     329             : const char *Win32ExceptionCodeToString(DWORD exception_code)
     330             : {
     331             :         switch (exception_code) {
     332             :         case EXCEPTION_ACCESS_VIOLATION:
     333             :                 return "Access violation";
     334             :         case EXCEPTION_DATATYPE_MISALIGNMENT:
     335             :                 return "Misaligned data access";
     336             :         case EXCEPTION_BREAKPOINT:
     337             :                 return "Breakpoint reached";
     338             :         case EXCEPTION_SINGLE_STEP:
     339             :                 return "Single debug step";
     340             :         case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
     341             :                 return "Array access out of bounds";
     342             :         case EXCEPTION_FLT_DENORMAL_OPERAND:
     343             :                 return "Denormal floating point operand";
     344             :         case EXCEPTION_FLT_DIVIDE_BY_ZERO:
     345             :                 return "Floating point division by zero";
     346             :         case EXCEPTION_FLT_INEXACT_RESULT:
     347             :                 return "Inaccurate floating point result";
     348             :         case EXCEPTION_FLT_INVALID_OPERATION:
     349             :                 return "Invalid floating point operation";
     350             :         case EXCEPTION_FLT_OVERFLOW:
     351             :                 return "Floating point exponent overflow";
     352             :         case EXCEPTION_FLT_STACK_CHECK:
     353             :                 return "Floating point stack overflow or underflow";
     354             :         case EXCEPTION_FLT_UNDERFLOW:
     355             :                 return "Floating point exponent underflow";
     356             :         case EXCEPTION_INT_DIVIDE_BY_ZERO:
     357             :                 return "Integer division by zero";
     358             :         case EXCEPTION_INT_OVERFLOW:
     359             :                 return "Integer overflow";
     360             :         case EXCEPTION_PRIV_INSTRUCTION:
     361             :                 return "Privileged instruction executed";
     362             :         case EXCEPTION_IN_PAGE_ERROR:
     363             :                 return "Could not access or load page";
     364             :         case EXCEPTION_ILLEGAL_INSTRUCTION:
     365             :                 return "Illegal instruction encountered";
     366             :         case EXCEPTION_NONCONTINUABLE_EXCEPTION:
     367             :                 return "Attempted to continue after fatal exception";
     368             :         case EXCEPTION_STACK_OVERFLOW:
     369             :                 return "Stack overflow";
     370             :         case EXCEPTION_INVALID_DISPOSITION:
     371             :                 return "Invalid disposition returned to the exception dispatcher";
     372             :         case EXCEPTION_GUARD_PAGE:
     373             :                 return "Attempted guard page access";
     374             :         case EXCEPTION_INVALID_HANDLE:
     375             :                 return "Invalid handle";
     376             :         }
     377             : 
     378             :         return "Unknown exception";
     379             : }
     380             : 
     381             : long WINAPI Win32ExceptionHandler(struct _EXCEPTION_POINTERS *pExceptInfo)
     382             : {
     383             :         char buf[512];
     384             :         MINIDUMP_EXCEPTION_INFORMATION mdei;
     385             :         MINIDUMP_USER_STREAM_INFORMATION mdusi;
     386             :         MINIDUMP_USER_STREAM mdus;
     387             :         bool minidump_created = false;
     388             : 
     389             :         std::string dumpfile = porting::path_user + DIR_DELIM PROJECT_NAME ".dmp";
     390             : 
     391             :         std::string version_str(PROJECT_NAME " ");
     392             :         version_str += g_version_hash;
     393             : 
     394             :         HANDLE hFile = CreateFileA(dumpfile.c_str(), GENERIC_WRITE,
     395             :                 FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
     396             :         if (hFile == INVALID_HANDLE_VALUE)
     397             :                 goto minidump_failed;
     398             : 
     399             :         if (SetEndOfFile(hFile) == FALSE)
     400             :                 goto minidump_failed;
     401             : 
     402             :         mdei.ClientPointers        = NULL;
     403             :         mdei.ExceptionPointers = pExceptInfo;
     404             :         mdei.ThreadId              = GetCurrentThreadId();
     405             : 
     406             :         mdus.Type       = CommentStreamA;
     407             :         mdus.BufferSize = version_str.size();
     408             :         mdus.Buffer     = (PVOID)version_str.c_str();
     409             : 
     410             :         mdusi.UserStreamArray = &mdus;
     411             :         mdusi.UserStreamCount = 1;
     412             : 
     413             :         if (MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
     414             :                         MiniDumpNormal, &mdei, &mdusi, NULL) == FALSE)
     415             :                 goto minidump_failed;
     416             : 
     417             :         minidump_created = true;
     418             : 
     419             : minidump_failed:
     420             : 
     421             :         CloseHandle(hFile);
     422             : 
     423             :         DWORD excode = pExceptInfo->ExceptionRecord->ExceptionCode;
     424             :         _snprintf(buf, sizeof(buf),
     425             :                 " >> === FATAL ERROR ===\n"
     426             :                 " >> %s (Exception 0x%08X) at 0x%p\n",
     427             :                 Win32ExceptionCodeToString(excode), excode,
     428             :                 pExceptInfo->ExceptionRecord->ExceptionAddress);
     429             :         dstream << buf;
     430             : 
     431             :         if (minidump_created)
     432             :                 dstream << " >> Saved dump to " << dumpfile << std::endl;
     433             :         else
     434             :                 dstream << " >> Failed to save dump" << std::endl;
     435             : 
     436             :         return EXCEPTION_EXECUTE_HANDLER;
     437             : }
     438             : 
     439             : #endif
     440             : 
     441           1 : void debug_set_exception_handler()
     442             : {
     443             : #ifdef _MSC_VER
     444             :         SetUnhandledExceptionFilter(Win32ExceptionHandler);
     445             : #endif
     446           4 : }
     447             : 

Generated by: LCOV version 1.11