27 #ifdef Q_OS_LINUX |
27 #ifdef Q_OS_LINUX |
28 # include <sys/prctl.h> |
28 # include <sys/prctl.h> |
29 #endif |
29 #endif |
30 |
30 |
31 // Is the crash catcher active now? |
31 // Is the crash catcher active now? |
32 static bool g_crashCatcherActive = false; |
32 static bool IsCrashCatcherActive = false; |
33 |
33 |
34 // If an assertion failed, what was it? |
34 // If an assertion failed, what was it? |
35 static QString g_assertionFailure; |
35 static QString AssertionFailureText; |
36 |
36 |
37 // List of signals to catch and crash on |
37 // List of signals to catch and crash on |
38 static QList<int> g_signalsToCatch ({ |
38 static QList<int> SignalsToCatch ({ |
39 SIGSEGV, // segmentation fault |
39 SIGSEGV, // segmentation fault |
40 SIGABRT, // abort() calls |
40 SIGABRT, // abort() calls |
41 SIGFPE, // floating point exceptions (e.g. division by zero) |
41 SIGFPE, // floating point exceptions (e.g. division by zero) |
42 SIGILL, // illegal instructions |
42 SIGILL, // illegal instructions |
43 }); |
43 }); |
44 |
44 |
|
45 // ------------------------------------------------------------------------------------------------- |
45 // |
46 // |
46 // Removes the signal handler from SIGABRT and then aborts. |
47 // Removes the signal handler from SIGABRT and then aborts. |
47 // |
48 // |
48 static void finalAbort() |
49 static void FinalAbort() |
49 { |
50 { |
50 struct sigaction sighandler; |
51 struct sigaction sighandler; |
51 sighandler.sa_handler = SIG_DFL; |
52 sighandler.sa_handler = SIG_DFL; |
52 sighandler.sa_flags = 0; |
53 sighandler.sa_flags = 0; |
53 sigaction (SIGABRT, &sighandler, 0); |
54 sigaction (SIGABRT, &sighandler, 0); |
54 abort(); |
55 abort(); |
55 } |
56 } |
56 |
57 |
57 // ============================================================================= |
58 // ------------------------------------------------------------------------------------------------- |
58 // |
59 // |
59 static void handleCrash (int sig) |
60 static void HandleCrash (int sig) |
60 { |
61 { |
61 printf ("!! Caught signal %d, launching gdb\n", sig); |
62 printf ("!! Caught signal %d, launching gdb\n", sig); |
62 |
63 |
63 if (g_crashCatcherActive) |
64 if (IsCrashCatcherActive) |
64 { |
65 { |
65 printf ("Caught signal while crash catcher is active! Execution cannot continue.\n"); |
66 printf ("Caught signal while crash catcher is active! Execution cannot continue.\n"); |
66 finalAbort(); |
67 FinalAbort(); |
67 } |
68 } |
68 |
69 |
69 const pid_t pid = getpid(); |
70 pid_t const pid (getpid()); |
70 QProcess proc; |
71 QProcess proc; |
71 QTemporaryFile commandsFile; |
72 QTemporaryFile commandsFile; |
72 |
73 |
73 g_crashCatcherActive = true; |
74 IsCrashCatcherActive = true; |
74 |
75 |
75 if (commandsFile.open()) |
76 if (commandsFile.open()) |
76 { |
77 { |
77 commandsFile.write (format ("attach %1\n", pid).toLocal8Bit()); |
78 commandsFile.write (format ("attach %1\n", pid).toLocal8Bit()); |
78 commandsFile.write (QString ("backtrace full\n").toLocal8Bit()); |
79 commandsFile.write (QString ("backtrace full\n").toLocal8Bit()); |
89 #ifdef Q_OS_LINUX |
90 #ifdef Q_OS_LINUX |
90 prctl (PR_SET_PTRACER, proc.pid(), 0, 0, 0); |
91 prctl (PR_SET_PTRACER, proc.pid(), 0, 0, 0); |
91 #endif |
92 #endif |
92 |
93 |
93 proc.waitForFinished (1000); |
94 proc.waitForFinished (1000); |
94 QString output = QString (proc.readAllStandardOutput()); |
95 QString output (proc.readAllStandardOutput()); |
95 QString err = QString (proc.readAllStandardError()); |
96 QString err (proc.readAllStandardError()); |
96 QFile f (UNIXNAME "-crash.log"); |
97 QFile f (UNIXNAME "-crash.log"); |
97 |
98 |
98 if (f.open (QIODevice::WriteOnly)) |
99 if (f.open (QIODevice::WriteOnly)) |
99 { |
100 { |
100 fprint (f, format ("=== Program crashed with signal %1 ===\n\n%2" |
101 fprint (f, format ("=== Program crashed with signal %1 ===\n\n%2" |
101 "GDB stdout:\n%3\n" |
102 "GDB stdout:\n%3\nGDB stderr:\n%4\n", sig, |
102 "GDB stderr:\n%4\n", |
103 (not AssertionFailureText.isEmpty()) ? AssertionFailureText + "\n\n" : "", |
103 sig, (not g_assertionFailure.isEmpty()) ? g_assertionFailure + "\n\n" : "", output, err)); |
104 output, err)); |
104 f.close(); |
105 f.close(); |
105 } |
106 } |
106 |
107 |
107 if (g_assertionFailure.isEmpty()) |
108 if (not AssertionFailureText.isEmpty()) |
108 { |
109 printf ("Assertion failed: \"%s\".\n", qPrintable (AssertionFailureText)); |
109 printf ("Crashlog written to " UNIXNAME "-crash.log. Aborting.\n"); |
|
110 } |
|
111 else |
|
112 { |
|
113 printf ("Assertion failed: \"%s\". Backtrace written to " UNIXNAME "-crash.log.\n", |
|
114 qPrintable (g_assertionFailure)); |
|
115 } |
|
116 |
110 |
117 finalAbort(); |
111 printf ("Backtrace written to " UNIXNAME "-crash.log. Aborting.\n"); |
|
112 FinalAbort(); |
118 } |
113 } |
119 |
114 |
|
115 // ------------------------------------------------------------------------------------------------- |
120 // |
116 // |
121 // Initializes the crash catcher. |
117 // Initializes the crash catcher. |
122 // |
118 // |
123 void initCrashCatcher() |
119 void InitCrashCatcher() |
124 { |
120 { |
125 struct sigaction sighandler; |
121 struct sigaction sighandler; |
126 sighandler.sa_handler = &handleCrash; |
122 sighandler.sa_handler = &HandleCrash; |
127 sighandler.sa_flags = 0; |
123 sighandler.sa_flags = 0; |
128 sigemptyset (&sighandler.sa_mask); |
124 sigemptyset (&sighandler.sa_mask); |
129 |
125 |
130 for (int sig : g_signalsToCatch) |
126 for (int sig : SignalsToCatch) |
131 { |
127 { |
132 if (sigaction (sig, &sighandler, null) == -1) |
128 if (sigaction (sig, &sighandler, null) == -1) |
133 { |
129 { |
134 fprint (stderr, "Couldn't set signal handler %1: %2", sig, strerror (errno)); |
130 fprint (stderr, "Couldn't set signal handler %1: %2", sig, strerror (errno)); |
135 g_signalsToCatch.removeOne (sig); |
131 SignalsToCatch.removeOne (sig); |
136 } |
132 } |
137 } |
133 } |
138 |
134 |
139 print ("Crash catcher hooked to signals: %1\n", g_signalsToCatch); |
135 print ("Crash catcher hooked to signals: %1\n", SignalsToCatch); |
140 } |
136 } |
141 |
137 |
142 #endif // #ifdef __unix__ |
138 #endif // #ifdef __unix__ |
143 |
139 |
144 // ============================================================================= |
140 // ------------------------------------------------------------------------------------------------- |
145 // |
141 // |
146 // This function must be readily available in both Windows and Linux. We display |
142 // This function catches an assertion failure. It must be readily available in both Windows and |
147 // the bomb box straight in Windows while in Linux we let abort() trigger the |
143 // Linux. We display the bomb box straight in Windows while in Linux we let abort() trigger |
148 // signal handler, which will cause the usual bomb box with GDB diagnostics. |
144 // the signal handler, which will cause the usual bomb box with GDB diagnostics. Said prompt will |
149 // Said prompt will embed the assertion failure information. |
145 // embed the assertion failure information. |
150 // |
146 // |
151 void assertionFailure (const char* file, int line, const char* funcname, const char* expr) |
147 void assertionFailure (const char* file, int line, const char* funcname, const char* expr) |
152 { |
148 { |
153 #ifdef __unix__ |
149 #ifdef __unix__ |
154 g_assertionFailure = format ("%1:%2: %3: %4", file, line, funcname, expr); |
150 AssertionFailureText = format ("%1:%2: %3: %4", file, line, funcname, expr); |
155 #else |
151 #else |
156 bombBox (format ( |
152 bombBox (format ( |
157 "<p><b>File</b>: <tt>%1</tt><br />" |
153 "<p><b>File</b>: <tt>%1</tt><br />" |
158 "<b>Line</b>: <tt>%2</tt><br />" |
154 "<b>Line</b>: <tt>%2</tt><br />" |
159 "<b>Function:</b> <tt>%3</tt></p>" |
155 "<b>Function:</b> <tt>%3</tt></p>" |