aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/linux_crash_logger/linux_crash_logger.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/linux_crash_logger/linux_crash_logger.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/linux_crash_logger/linux_crash_logger.cpp')
-rw-r--r--linden/indra/linux_crash_logger/linux_crash_logger.cpp573
1 files changed, 573 insertions, 0 deletions
diff --git a/linden/indra/linux_crash_logger/linux_crash_logger.cpp b/linden/indra/linux_crash_logger/linux_crash_logger.cpp
new file mode 100644
index 0000000..a332ccc
--- /dev/null
+++ b/linden/indra/linux_crash_logger/linux_crash_logger.cpp
@@ -0,0 +1,573 @@
1/**
2 * @file linux_crash_logger.cpp
3 * @brief Linux crash logger implementation
4 *
5 * Copyright (c) 2003-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "linden_common.h"
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <time.h>
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <unistd.h>
36
37#include <curl/curl.h>
38
39#if LL_GTK
40# include "gtk/gtk.h"
41#endif // LL_GTK
42
43#include "indra_constants.h" // CRASH_BEHAVIOR_ASK
44#include "llerror.h"
45#include "lltimer.h"
46#include "lldir.h"
47
48#include "llstring.h"
49
50
51// These need to be localized.
52static const char dialog_text[] =
53"Second Life appears to have crashed.\n"
54"This crash reporter collects information about your computer's hardware, operating system, and some Second Life logs, which are used for debugging purposes only.\n"
55"Sending crash reports is the best way to help us improve the quality of Second Life.\n"
56"If you continue to experience this problem, please try one of the following:\n"
57"- Contact support by email at support@EXAMPLE.com\n"
58"- If you can log-in, please contact Live Help by using menu Help > Live Help.\n"
59"- Search the Second Life Knowledge Base at http://secondlife.com/knowledgebase/\n"
60"\n"
61"Send crash report?";
62
63static const char dialog_title[] =
64"Second Life Crash Logger";
65
66
67class LLFileEncoder
68{
69public:
70 LLFileEncoder(const char *formname, const char *filename, bool isCrashLog = false);
71
72 BOOL isValid() const { return mIsValid; }
73 LLString encodeURL(const S32 max_length = 0);
74public:
75 BOOL mIsValid;
76 LLString mFilename;
77 LLString mFormname;
78 LLString mBuf;
79};
80
81LLString encode_string(const char *formname, const LLString &str);
82
83LLString gServerResponse;
84BOOL gSendReport = FALSE;
85LLString gUserserver;
86LLString gUserText;
87BOOL gCrashInPreviousExec = FALSE;
88time_t gLaunchTime;
89
90static size_t curl_download_callback(void *data, size_t size, size_t nmemb,
91 void *user_data)
92{
93 S32 bytes = size * nmemb;
94 char *cdata = (char *) data;
95 for (int i =0; i < bytes; i += 1)
96 {
97 gServerResponse += (cdata[i]);
98 }
99 return bytes;
100}
101
102#if LL_GTK
103static void response_callback (GtkDialog *dialog,
104 gint arg1,
105 gpointer user_data)
106{
107 gint *response = (gint*)user_data;
108 *response = arg1;
109 gtk_widget_destroy(GTK_WIDGET(dialog));
110 gtk_main_quit();
111}
112#endif // LL_GTK
113
114static BOOL do_ask_dialog(void)
115{
116#if LL_GTK
117 gtk_disable_setlocale();
118 if (!gtk_init_check(NULL, NULL)) {
119 llinfos << "Could not initialize GTK for 'ask to send crash report' dialog; not sending report." << llendl;
120 return FALSE;
121 }
122
123 GtkWidget *win = NULL;
124 GtkDialogFlags flags = GTK_DIALOG_MODAL;
125 GtkMessageType messagetype = GTK_MESSAGE_QUESTION;
126 GtkButtonsType buttons = GTK_BUTTONS_YES_NO;
127 gint response = GTK_RESPONSE_NONE;
128
129 win = gtk_message_dialog_new(NULL,
130 flags, messagetype, buttons,
131 dialog_text);
132 gtk_window_set_type_hint(GTK_WINDOW(win),
133 GDK_WINDOW_TYPE_HINT_DIALOG);
134 gtk_window_set_title(GTK_WINDOW(win), dialog_title);
135 g_signal_connect (win,
136 "response",
137 G_CALLBACK (response_callback),
138 &response);
139 gtk_widget_show_all (win);
140 gtk_main();
141
142 return (GTK_RESPONSE_OK == response ||
143 GTK_RESPONSE_YES == response ||
144 GTK_RESPONSE_APPLY == response);
145#else
146 return FALSE;
147#endif // LL_GTK
148}
149
150
151int main(int argc, char **argv)
152{
153 const S32 BT_MAX_SIZE = 100000; // Maximum size to transmit of the backtrace file
154 const S32 SL_MAX_SIZE = 100000; // Maximum size of the Second Life log file.
155 int i;
156 S32 crash_behavior = CRASH_BEHAVIOR_ALWAYS_SEND;
157
158 time(&gLaunchTime);
159
160 llinfos << "Starting Second Life Viewer Crash Reporter" << llendl;
161
162 for(i=1; i<argc; i++)
163 {
164 if(!strcmp(argv[i], "-dialog"))
165 {
166 llinfos << "Show the user dialog" << llendl;
167 crash_behavior = CRASH_BEHAVIOR_ASK;
168 }
169 if(!strcmp(argv[i], "-previous"))
170 {
171 gCrashInPreviousExec = TRUE;
172 }
173 if(!strcmp(argv[i], "-user"))
174 {
175 if ((i + 1) < argc)
176 {
177 i++;
178 gUserserver = argv[i];
179 llinfos << "Got userserver " << gUserserver << llendl;
180 }
181 }
182 }
183
184 if( gCrashInPreviousExec )
185 {
186 llinfos << "Previous execution did not remove SecondLife.exec_marker" << llendl;
187 }
188
189 if(!gCrashInPreviousExec)
190 {
191 // Wait a while to let the crashed client finish exiting,
192 // freeing up the screen/etc.
193 sleep(5);
194 }
195
196 // *FIX: do some dialog stuff here?
197 if (CRASH_BEHAVIOR_ALWAYS_SEND == crash_behavior)
198 {
199 gSendReport = TRUE;
200 }
201 else if (CRASH_BEHAVIOR_ASK == crash_behavior)
202 {
203 gSendReport = do_ask_dialog();
204 }
205
206 if(!gSendReport)
207 {
208 // Only send the report if the user agreed to it.
209 llinfos << "User cancelled, not sending report" << llendl;
210
211 return(0);
212 }
213
214 // We assume that all the logs we're looking for reside on the current drive
215 gDirUtilp->initAppDirs("SecondLife");
216
217 // Lots of silly variable, replicated for each log file.
218 LLString db_file_name;
219 LLString sl_file_name;
220 LLString bt_file_name; // stack_trace.log file
221 LLString st_file_name; // stats.log file
222 LLString si_file_name; // settings.xml file
223
224 LLFileEncoder *db_filep = NULL;
225 LLFileEncoder *sl_filep = NULL;
226 LLFileEncoder *st_filep = NULL;
227 LLFileEncoder *bt_filep = NULL;
228 LLFileEncoder *si_filep = NULL;
229
230 ///////////////////////////////////
231 //
232 // We do the parsing for the debug_info file first, as that will
233 // give us the location of the SecondLife.log file.
234 //
235
236 // Figure out the filename of the debug log
237 db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log").c_str();
238 db_filep = new LLFileEncoder("DB", db_file_name.c_str());
239
240 // Get the filename of the SecondLife.log file
241 // *TODO tofu - get right MAX_PATH.
242 // *FIX: What's up with this? This #define just can't be safe.
243#define MAX_PATH PATH_MAX
244 char tmp_sl_name[MAX_PATH];
245 tmp_sl_name[0] = '\0';
246 char tmp_space[256];
247 tmp_space[0] = '\0';
248
249 // Look for it in the debug_info.log file
250 if (db_filep->isValid())
251 {
252 // This was originally scanning for "SL Log: %[^\r\n]", which happily skipped to the next line
253 // on debug logs (which don't have anything after "SL Log:" and tried to open a nonsensical filename.
254 sscanf(db_filep->mBuf.c_str(), "SL Log:%[ ]%[^\r\n]", tmp_space, tmp_sl_name);
255 }
256 else
257 {
258 delete db_filep;
259 db_filep = NULL;
260 }
261
262 // If we actually have a legitimate file name, use it.
263 if (gCrashInPreviousExec)
264 {
265 // If we froze, the crash log this time around isn't useful.
266 // Use the old one.
267 sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.old");
268 }
269 else if (tmp_sl_name[0])
270 {
271 sl_file_name = tmp_sl_name;
272 llinfos << "Using log file from debug log: " << sl_file_name << llendl;
273 }
274 else
275 {
276 // Figure out the filename of the second life log
277 sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log").c_str();
278 }
279
280 // Now we get the SecondLife.log file if it's there, and recent enough...
281 sl_filep = new LLFileEncoder("SL", sl_file_name.c_str());
282 if (!sl_filep->isValid())
283 {
284 delete sl_filep;
285 sl_filep = NULL;
286 }
287
288 st_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log").c_str();
289 st_filep = new LLFileEncoder("ST", st_file_name.c_str());
290 if (!st_filep->isValid())
291 {
292 delete st_filep;
293 st_filep = NULL;
294 }
295
296 si_file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml").c_str();
297 si_filep = new LLFileEncoder("SI", si_file_name.c_str());
298 if (!si_filep->isValid())
299 {
300 delete si_filep;
301 si_filep = NULL;
302 }
303
304 // encode this as if it were a 'Dr Watson' plain-text backtrace
305 bt_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log").c_str();
306 bt_filep = new LLFileEncoder("DW", bt_file_name.c_str());
307 if (!bt_filep->isValid())
308 {
309 delete bt_filep;
310 bt_filep = NULL;
311 }
312
313 LLString post_data;
314 LLString tmp_url_buf;
315
316 // Append the userserver
317 tmp_url_buf = encode_string("USER", gUserserver);
318 post_data += tmp_url_buf;
319 llinfos << "PostData:" << post_data << llendl;
320
321 if (gCrashInPreviousExec)
322 {
323 post_data.append("&");
324 tmp_url_buf = encode_string("EF", "Y");
325 post_data += tmp_url_buf;
326 }
327
328 if (db_filep)
329 {
330 post_data.append("&");
331 tmp_url_buf = db_filep->encodeURL();
332 post_data += tmp_url_buf;
333 llinfos << "Sending DB log file" << llendl;
334 }
335 else
336 {
337 llinfos << "Not sending DB log file" << llendl;
338 }
339
340 if (sl_filep)
341 {
342 post_data.append("&");
343 tmp_url_buf = sl_filep->encodeURL(SL_MAX_SIZE);
344 post_data += tmp_url_buf;
345 llinfos << "Sending SL log file" << llendl;
346 }
347 else
348 {
349 llinfos << "Not sending SL log file" << llendl;
350 }
351
352 if (st_filep)
353 {
354 post_data.append("&");
355 tmp_url_buf = st_filep->encodeURL(SL_MAX_SIZE);
356 post_data += tmp_url_buf;
357 llinfos << "Sending stats log file" << llendl;
358 }
359 else
360 {
361 llinfos << "Not sending stats log file" << llendl;
362 }
363
364 if (bt_filep)
365 {
366 post_data.append("&");
367 tmp_url_buf = bt_filep->encodeURL(BT_MAX_SIZE);
368 post_data += tmp_url_buf;
369 llinfos << "Sending crash log file" << llendl;
370 }
371 else
372 {
373 llinfos << "Not sending crash log file" << llendl;
374 }
375
376 if (si_filep)
377 {
378 post_data.append("&");
379 tmp_url_buf = si_filep->encodeURL();
380 post_data += tmp_url_buf;
381 llinfos << "Sending settings log file" << llendl;
382 }
383 else
384 {
385 llinfos << "Not sending settings.xml file" << llendl;
386 }
387
388 if (gUserText.size())
389 {
390 post_data.append("&");
391 tmp_url_buf = encode_string("UN", gUserText);
392 post_data += tmp_url_buf;
393 }
394
395 delete db_filep;
396 db_filep = NULL;
397 delete sl_filep;
398 sl_filep = NULL;
399 delete bt_filep;
400 bt_filep = NULL;
401
402 // Debugging spam
403#if 0
404 printf("Crash report post data:\n--------\n");
405 printf("%s", post_data.getString());
406 printf("\n--------\n");
407#endif
408
409 // Send the report. Yes, it's this easy.
410 {
411 CURL *curl = curl_easy_init();
412
413 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
414 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback);
415 curl_easy_setopt(curl, CURLOPT_POST, 1);
416 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data.c_str());
417 curl_easy_setopt(curl, CURLOPT_URL, "http://EXAMPLE.com/cgi-bin/viewer_crash_reporter2");
418
419 llinfos << "Connecting to crash report server" << llendl;
420 CURLcode result = curl_easy_perform(curl);
421
422 curl_easy_cleanup(curl);
423
424 if(result != CURLE_OK)
425 {
426 llinfos << "Couldn't talk to crash report server" << llendl;
427 }
428 else
429 {
430 llinfos << "Response from crash report server:" << llendl;
431 llinfos << gServerResponse << llendl;
432 }
433 }
434
435 return 0;
436}
437
438LLFileEncoder::LLFileEncoder(const char *form_name, const char *filename, bool isCrashLog)
439{
440 mFormname = form_name;
441 mFilename = filename;
442 mIsValid = FALSE;
443
444 int res;
445
446 struct stat stat_data;
447 res = stat(mFilename.c_str(), &stat_data);
448 if (res)
449 {
450 llwarns << "File " << mFilename << " is missing!" << llendl;
451 return;
452 }
453 else
454 {
455 // Debugging spam
456// llinfos << "File " << mFilename << " is present..." << llendl;
457
458 if(!gCrashInPreviousExec && isCrashLog)
459 {
460 // Make sure the file isn't too old.
461 double age = difftime(gLaunchTime, stat_data.st_mtim.tv_sec);
462
463// llinfos << "age is " << age << llendl;
464
465 if(age > 60.0)
466 {
467 // The file was last modified more than 60 seconds before the crash reporter was launched. Assume it's stale.
468 llwarns << "File " << mFilename << " is too old!" << llendl;
469 return;
470 }
471 }
472
473 }
474
475 S32 buf_size = stat_data.st_size;
476 FILE *fp = fopen(mFilename.c_str(), "rb");
477 U8 *buf = new U8[buf_size + 1];
478 fread(buf, 1, buf_size, fp);
479 fclose(fp);
480 buf[buf_size] = 0;
481
482 mBuf = (char *)buf;
483
484 if(isCrashLog)
485 {
486 // Crash logs consist of a number of entries, one per crash.
487 // Each entry is preceeded by "**********" on a line by itself.
488 // We want only the most recent (i.e. last) one.
489 const char *sep = "**********";
490 const char *start = mBuf.c_str();
491 const char *cur = start;
492 const char *temp = strstr(cur, sep);
493
494 while(temp != NULL)
495 {
496 // Skip past the marker we just found
497 cur = temp + strlen(sep);
498
499 // and try to find another
500 temp = strstr(cur, sep);
501 }
502
503 // If there's more than one entry in the log file, strip all but the last one.
504 if(cur != start)
505 {
506 mBuf.erase(0, cur - start);
507 }
508 }
509
510 mIsValid = TRUE;
511 delete[] buf;
512}
513
514LLString LLFileEncoder::encodeURL(const S32 max_length)
515{
516 LLString result = mFormname;
517 result.append("=");
518
519 S32 i = 0;
520
521 if (max_length)
522 {
523 if ((S32)mBuf.size() > max_length)
524 {
525 i = mBuf.size() - max_length;
526 }
527 }
528
529#if 0
530 // Plain text version for debugging
531 result.append(mBuf);
532#else
533 // Not using LLString because of bad performance issues
534 S32 buf_size = mBuf.size();
535 S32 url_buf_size = 3*mBuf.size() + 1;
536 char *url_buf = new char[url_buf_size];
537
538 S32 cur_pos = 0;
539 for (; i < buf_size; i++)
540 {
541 sprintf(url_buf + cur_pos, "%%%02x", mBuf[i]);
542 cur_pos += 3;
543 }
544 url_buf[i*3] = 0;
545
546 result.append(url_buf);
547 delete[] url_buf;
548#endif
549 return result;
550}
551
552LLString encode_string(const char *formname, const LLString &str)
553{
554 LLString result = formname;
555 result.append("=");
556 // Not using LLString because of bad performance issues
557 S32 buf_size = str.size();
558 S32 url_buf_size = 3*str.size() + 1;
559 char *url_buf = new char[url_buf_size];
560
561 S32 cur_pos = 0;
562 S32 i;
563 for (i = 0; i < buf_size; i++)
564 {
565 sprintf(url_buf + cur_pos, "%%%02x", str[i]);
566 cur_pos += 3;
567 }
568 url_buf[i*3] = 0;
569
570 result.append(url_buf);
571 delete[] url_buf;
572 return result;
573}