bsod.cpp: skip path prefix up to /usr
[enigma2.git] / main / bsod.cpp
1 #include <string.h>
2 #include <signal.h>
3 #include <asm/ptrace.h>
4
5 #include <lib/base/eerror.h>
6 #include <lib/base/smartptr.h>
7 #include <lib/gdi/grc.h>
8 #include <lib/gdi/gfbdc.h>
9 #ifdef WITH_SDL
10 #include <lib/gdi/sdl.h>
11 #endif
12
13 #include "version.h"
14
15 /************************************************/
16
17 #define CRASH_EMAILADDR "crashlog@dream-multimedia-tv.de"
18
19 #define RINGBUFFER_SIZE 16384
20 static char ringbuffer[RINGBUFFER_SIZE];
21 static int ringbuffer_head;
22
23 static void addToLogbuffer(const char *data, int len)
24 {
25         while (len)
26         {
27                 int remaining = RINGBUFFER_SIZE - ringbuffer_head;
28         
29                 if (remaining > len)
30                         remaining = len;
31         
32                 memcpy(ringbuffer + ringbuffer_head, data, remaining);
33                 len -= remaining;
34                 data += remaining;
35                 ringbuffer_head += remaining;
36                 if (ringbuffer_head >= RINGBUFFER_SIZE)
37                         ringbuffer_head = 0;
38         }
39 }
40
41 static std::string getLogBuffer()
42 {
43         int begin = ringbuffer_head;
44         while (ringbuffer[begin] == 0)
45         {
46                 ++begin;
47                 if (begin == RINGBUFFER_SIZE)
48                         begin = 0;
49                 if (begin == ringbuffer_head)
50                         return "";
51         }
52         if (begin < ringbuffer_head)
53                 return std::string(ringbuffer + begin, ringbuffer_head - begin);
54         else
55         {
56                 return std::string(ringbuffer + begin, RINGBUFFER_SIZE - begin) + std::string(ringbuffer, ringbuffer_head);
57         }
58 }
59
60 static void addToLogbuffer(int level, const std::string &log)
61 {
62         addToLogbuffer(log.c_str(), log.size());
63 }
64
65
66 extern std::string getLogBuffer();
67
68 #define INFOFILE "/maintainer.info"
69
70 void bsodFatal(const char *component)
71 {
72         char logfile[128];
73         sprintf(logfile, "/media/hdd/enigma2_crash_%u.log", (unsigned int)time(0));
74         FILE *f = fopen(logfile, "wb");
75         
76         std::string lines = getLogBuffer();
77         
78                 /* find python-tracebacks, and extract "  File "-strings */
79         size_t start = 0;
80         
81         char crash_emailaddr[256] = CRASH_EMAILADDR;
82         char crash_component[256] = "enigma2";
83
84         if (component)
85                 snprintf(crash_component, 256, component);
86         else
87         {
88                 while ((start = lines.find("\n  File \"", start)) != std::string::npos)
89                 {
90                         start += 9;
91                         size_t end = lines.find("\"", start);
92                         if (end == std::string::npos)
93                                 break;
94                         end = lines.rfind("/", end);
95                                 /* skip a potential prefix to the path */
96                         unsigned int path_prefix = lines.find("/usr/", start);
97                         if (path_prefix != std::string::npos && path_prefix < end)
98                                 start = path_prefix;
99
100                         if (end == std::string::npos)
101                                 break;
102                         if (end - start >= (256 - strlen(INFOFILE)))
103                                 continue;
104                         char filename[256];
105                         snprintf(filename, 256, "%s%s", lines.substr(start, end - start).c_str(), INFOFILE);
106                         FILE *cf = fopen(filename, "r");
107                         if (cf)
108                         {
109                                 fgets(crash_emailaddr, sizeof crash_emailaddr, cf);
110                                 if (*crash_emailaddr && crash_emailaddr[strlen(crash_emailaddr)-1] == '\n')
111                                         crash_emailaddr[strlen(crash_emailaddr)-1] = 0;
112
113                                 fgets(crash_component, sizeof crash_component, cf);
114                                 if (*crash_component && crash_component[strlen(crash_component)-1] == '\n')
115                                         crash_component[strlen(crash_component)-1] = 0;
116                                 fclose(cf);
117                         }
118                 }
119         }
120
121         if (f)
122         {
123                 time_t t = time(0);
124                 fprintf(f, "enigma2 crashed on %s", ctime(&t));
125 #ifdef ENIGMA2_CHECKOUT_TAG
126                 fprintf(f, "enigma2 CVS TAG: " ENIGMA2_CHECKOUT_TAG "\n");
127 #else
128                 fprintf(f, "enigma2 compiled on " __DATE__ "\n");
129 #endif
130 #ifdef ENIGMA2_CHECKOUT_ROOT
131                 fprintf(f, "enigma2 checked out from " ENIGMA2_CHECKOUT_ROOT "\n");
132 #endif
133                 fprintf(f, "please email this file to %s\n", crash_emailaddr);
134                 std::string buffer = getLogBuffer();
135                 fwrite(buffer.c_str(), buffer.size(), 1, f);
136                 fclose(f);
137                 
138                 char cmd[256];
139                 sprintf(cmd, "find /usr/lib/enigma2/python/ -name \"*.py\" | xargs md5sum >> %s", logfile);
140                 system(cmd);
141         }
142         
143 #ifdef WITH_SDL
144         ePtr<gSDLDC> my_dc;
145         gSDLDC::getInstance(my_dc);
146 #else
147         ePtr<gFBDC> my_dc;
148         gFBDC::getInstance(my_dc);
149 #endif
150         
151         {
152                 gPainter p(my_dc);
153                 p.resetOffset();
154                 p.resetClip(eRect(ePoint(0, 0), my_dc->size()));
155 #ifdef ENIGMA2_CHECKOUT_TAG
156                 if (ENIGMA2_CHECKOUT_TAG[0] == 'T') /* tagged checkout (release) */
157                         p.setBackgroundColor(gRGB(0x0000C0));
158                 else if (ENIGMA2_CHECKOUT_TAG[0] == 'D') /* dated checkout (daily experimental build) */
159                 {
160                         srand(time(0));
161                         int r = rand();
162                         unsigned int col = 0;
163                         if (r & 1)
164                                 col |= 0x800000;
165                         if (r & 2)
166                                 col |= 0x008000;
167                         if (r & 4)
168                                 col |= 0x0000c0;
169                         p.setBackgroundColor(gRGB(col));
170                 }
171 #else
172                         p.setBackgroundColor(gRGB(0x008000));
173 #endif
174
175                 p.setForegroundColor(gRGB(0xFFFFFF));
176         
177                 ePtr<gFont> font = new gFont("Regular", 20);
178                 p.setFont(font);
179                 p.clear();
180         
181                 eRect usable_area = eRect(100, 70, my_dc->size().width() - 150, 100);
182                 
183                 char text[512];
184                 snprintf(text, 512, "We are really sorry. Your Dreambox encountered "
185                         "a software problem, and needs to be restarted. "
186                         "Please send the logfile created in /hdd/ to %s.\n"
187                         "Your Dreambox restarts in 10 seconds!\n"
188                         "Component: %s",
189                         crash_emailaddr, crash_component);
190         
191                 p.renderText(usable_area, text, gPainter::RT_WRAP|gPainter::RT_HALIGN_LEFT);
192         
193                 usable_area = eRect(100, 170, my_dc->size().width() - 180, my_dc->size().height() - 20);
194         
195                 int i;
196         
197                 size_t start = std::string::npos + 1;
198                 for (i=0; i<20; ++i)
199                 {
200                         start = lines.rfind('\n', start - 1);
201                         if (start == std::string::npos)
202                         {
203                                 start = 0;
204                                 break;
205                         }
206                 }
207         
208                 font = new gFont("Regular", 14);
209                 p.setFont(font);
210         
211                 p.renderText(usable_area, 
212                         lines.substr(start), gPainter::RT_HALIGN_LEFT);
213                 sleep(10);
214         }
215
216         raise(SIGKILL);
217 }
218
219 #if defined(__MIPSEL__)
220 void oops(const mcontext_t &context, int dumpcode)
221 {
222         eDebug("PC: %08lx", (unsigned long)context.pc);
223         int i;
224         for (i=0; i<32; ++i)
225         {
226                 eDebugNoNewLine(" %08x", (int)context.gregs[i]);
227                 if ((i&3) == 3)
228                         eDebug("");
229         }
230                 /* this is temporary debug stuff. */
231         if (dumpcode && ((unsigned long)context.pc) > 0x10000) /* not a zero pointer */
232         {
233                 eDebug("As a final action, i will try to dump a bit of code.");
234                 eDebug("I just hope that this won't crash.");
235                 int i;
236                 eDebugNoNewLine("%08lx:", (unsigned long)context.pc);
237                 for (i=0; i<0x20; ++i)
238                         eDebugNoNewLine(" %02x", ((unsigned char*)context.pc)[i]);
239                 eDebug(" (end)");
240         }
241 }
242 #else
243 #warning "no oops support!"
244 #define NO_OOPS_SUPPORT
245 #endif
246
247 void handleFatalSignal(int signum, siginfo_t *si, void *ctx)
248 {
249         ucontext_t *uc = (ucontext_t*)ctx;
250         eDebug("KILLED BY signal %d", signum);
251 #ifndef NO_OOPS_SUPPORT
252         oops(uc->uc_mcontext, signum == SIGSEGV || signum == SIGABRT);
253 #endif
254         eDebug("-------");
255         bsodFatal("enigma2, signal");
256 }
257
258 void bsodCatchSignals()
259 {
260         struct sigaction act;
261         act.sa_handler = SIG_DFL;
262         act.sa_sigaction = handleFatalSignal;
263         act.sa_flags = SA_RESTART | SA_SIGINFO;
264         if (sigemptyset(&act.sa_mask) == -1)
265                 perror("sigemptyset");
266         
267                 /* start handling segfaults etc. */
268         sigaction(SIGSEGV, &act, 0);
269         sigaction(SIGILL, &act, 0);
270         sigaction(SIGBUS, &act, 0);
271         sigaction(SIGABRT, &act, 0);
272 }
273
274 void bsodLogInit()
275 {
276         logOutput.connect(addToLogbuffer);
277 }