aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/mac_crash_logger
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/mac_crash_logger
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/mac_crash_logger')
-rw-r--r--linden/indra/mac_crash_logger/mac_crash_logger.cpp688
1 files changed, 688 insertions, 0 deletions
diff --git a/linden/indra/mac_crash_logger/mac_crash_logger.cpp b/linden/indra/mac_crash_logger/mac_crash_logger.cpp
new file mode 100644
index 0000000..177a4b5
--- /dev/null
+++ b/linden/indra/mac_crash_logger/mac_crash_logger.cpp
@@ -0,0 +1,688 @@
1/**
2 * @file mac_crash_logger.cpp
3 * @brief Mac OSX 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 <direct.h>
33#include <time.h>
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <unistd.h>
37
38#include <curl/curl.h>
39
40#include "llerror.h"
41#include "lltimer.h"
42#include "lldir.h"
43
44#include "llstring.h"
45
46class LLFileEncoder
47{
48public:
49 LLFileEncoder(const char *formname, const char *filename, bool isCrashLog = false);
50
51 BOOL isValid() const { return mIsValid; }
52 LLString encodeURL(const S32 max_length = 0);
53public:
54 BOOL mIsValid;
55 LLString mFilename;
56 LLString mFormname;
57 LLString mBuf;
58};
59
60LLString encode_string(const char *formname, const LLString &str);
61
62#include <Carbon/Carbon.h>
63
64LLString gServerResponse;
65BOOL gSendReport = FALSE;
66LLString gUserserver;
67LLString gUserText;
68WindowRef gWindow = NULL;
69EventHandlerRef gEventHandler = NULL;
70BOOL gCrashInPreviousExec = FALSE;
71time_t gLaunchTime;
72
73size_t curl_download_callback(void *data, size_t size, size_t nmemb,
74 void *user_data)
75{
76 S32 bytes = size * nmemb;
77 char *cdata = (char *) data;
78 for (int i =0; i < bytes; i += 1)
79 {
80 gServerResponse += (cdata[i]);
81 }
82 return bytes;
83}
84
85OSStatus dialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata)
86{
87 OSStatus result = eventNotHandledErr;
88 OSStatus err;
89 UInt32 evtClass = GetEventClass(event);
90 UInt32 evtKind = GetEventKind(event);
91
92 if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess))
93 {
94 HICommand cmd;
95 err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd);
96
97 if(err == noErr)
98 {
99 switch(cmd.commandID)
100 {
101 case kHICommandOK:
102 {
103 char buffer[65535];
104 Size size = sizeof(buffer) - 1;
105 ControlRef textField = NULL;
106 ControlID id;
107
108 id.signature = 'text';
109 id.id = 0;
110
111 err = GetControlByID(gWindow, &id, &textField);
112 if(err == noErr)
113 {
114 // Get the user response text
115 err = GetControlData(textField, kControlNoPart, kControlEditTextTextTag, size, (Ptr)buffer, &size);
116 }
117 if(err == noErr)
118 {
119 // Make sure the string is terminated.
120 buffer[size] = 0;
121 gUserText = buffer;
122 llinfos << buffer << llendl;
123 }
124
125 // Send the report.
126 gSendReport = TRUE;
127
128 QuitAppModalLoopForWindow(gWindow);
129 result = noErr;
130 }
131 break;
132
133 case kHICommandCancel:
134 QuitAppModalLoopForWindow(gWindow);
135 result = noErr;
136 break;
137 }
138 }
139 }
140
141 return(result);
142}
143
144int main(int argc, char **argv)
145{
146 const S32 DW_MAX_SIZE = 100000; // Maximum size to transmit of the Dr. Watson log file
147 const S32 SL_MAX_SIZE = 100000; // Maximum size of the Second Life log file.
148 int i;
149
150 time(&gLaunchTime);
151
152 llinfos << "Starting Second Life Viewer Crash Reporter" << llendl;
153
154 for(i=1; i<argc; i++)
155 {
156 if(!strcmp(argv[i], "-previous"))
157 {
158 gCrashInPreviousExec = TRUE;
159 }
160 if(!strcmp(argv[i], "-user"))
161 {
162 if ((i + 1) < argc)
163 {
164 i++;
165 gUserserver = argv[i];
166 llinfos << "Got userserver " << gUserserver << llendl;
167 }
168 }
169 }
170
171 if( gCrashInPreviousExec )
172 {
173 llinfos << "Previous execution did not remove SecondLife.exec_marker" << llendl;
174 }
175
176 if(!gCrashInPreviousExec)
177 {
178 // Delay five seconds to let CrashReporter do its thing.
179 sleep(5);
180 }
181
182#if 1
183 // Real UI...
184 OSStatus err;
185 IBNibRef nib = NULL;
186
187 err = CreateNibReference(CFSTR("CrashReporter"), &nib);
188
189 if(err == noErr)
190 {
191 if(gCrashInPreviousExec)
192 {
193 err = CreateWindowFromNib(nib, CFSTR("CrashReporterDelayed"), &gWindow);
194 }
195 else
196 {
197 err = CreateWindowFromNib(nib, CFSTR("CrashReporter"), &gWindow);
198 }
199 }
200
201 if(err == noErr)
202 {
203 // Set focus to the edit text area
204 ControlRef textField = NULL;
205 ControlID id;
206
207 id.signature = 'text';
208 id.id = 0;
209
210 // Don't set err if any of this fails, since it's non-critical.
211 if(GetControlByID(gWindow, &id, &textField) == noErr)
212 {
213 SetKeyboardFocus(gWindow, textField, kControlFocusNextPart);
214 }
215 }
216
217 if(err == noErr)
218 {
219 ShowWindow(gWindow);
220 }
221
222 if(err == noErr)
223 {
224 // Set up an event handler for the window.
225 EventTypeSpec handlerEvents[] =
226 {
227 { kEventClassCommand, kEventCommandProcess }
228 };
229
230 InstallWindowEventHandler(
231 gWindow,
232 NewEventHandlerUPP(dialogHandler),
233 GetEventTypeCount (handlerEvents),
234 handlerEvents,
235 0,
236 &gEventHandler);
237 }
238
239 if(err == noErr)
240 {
241 RunAppModalLoopForWindow(gWindow);
242 }
243
244 if(gWindow != NULL)
245 {
246 DisposeWindow(gWindow);
247 }
248
249 if(nib != NULL)
250 {
251 DisposeNibReference(nib);
252 }
253#else
254 // Cheap version -- just use the standard system alert.
255 SInt16 itemHit = 0;
256 AlertStdCFStringAlertParamRec params;
257 OSStatus err = noErr;
258 DialogRef alert = NULL;
259
260 params.version = kStdCFStringAlertVersionOne;
261 params.movable = false;
262 params.helpButton = false;
263 params.defaultText = CFSTR("Send Report");
264 params.cancelText = CFSTR("Don't Send Report");
265 params.otherText = 0;
266 params.defaultButton = kAlertStdAlertOKButton;
267 params.cancelButton = kAlertStdAlertCancelButton;
268 params.position = kWindowDefaultPosition;
269 params.flags = 0;
270
271 err = CreateStandardAlert(
272 kAlertCautionAlert,
273 CFSTR("Second Life appears to have crashed."),
274 CFSTR(
275 "To help us debug the problem, you can send a crash report to Linden Lab. "
276 "The report contains information about your microprocessor type, graphics card, "
277 "memory, things that happened during the last run of the program, and the Crash Log "
278 "of where the crash occurred.\r"
279 "\r"
280 "You may also report crashes in the forums, or by sending e-mail to peter@lindenlab.com"),
281 &params,
282 &alert);
283
284 if(err == noErr)
285 {
286 err = RunStandardAlert(
287 alert,
288 NULL,
289 &itemHit);
290 }
291
292 if(itemHit == kAlertStdAlertOKButton)
293 gSendReport = TRUE;
294#endif
295
296 if(!gSendReport)
297 {
298 // Only send the report if the user agreed to it.
299 llinfos << "User cancelled, not sending report" << llendl;
300
301 return(0);
302 }
303
304 // We assume that all the logs we're looking for reside on the current drive
305 gDirUtilp->initAppDirs("SecondLife");
306
307 int res;
308
309 // Lots of silly variable, replicated for each log file.
310 LLString db_file_name;
311 LLString sl_file_name;
312 LLString dw_file_name; // DW file name is a hack for now...
313 LLString st_file_name; // stats.log file
314 LLString si_file_name; // settings.ini file
315
316 LLFileEncoder *db_filep = NULL;
317 LLFileEncoder *sl_filep = NULL;
318 LLFileEncoder *st_filep = NULL;
319 LLFileEncoder *dw_filep = NULL;
320 LLFileEncoder *si_filep = NULL;
321
322 ///////////////////////////////////
323 //
324 // We do the parsing for the debug_info file first, as that will
325 // give us the location of the SecondLife.log file.
326 //
327
328 // Figure out the filename of the debug log
329 db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log").c_str();
330 db_filep = new LLFileEncoder("DB", db_file_name.c_str());
331
332 // Get the filename of the SecondLife.log file
333 char tmp_sl_name[MAX_PATH];
334 tmp_sl_name[0] = '\0';
335 char tmp_space[256];
336 tmp_space[0] = '\0';
337
338 // Look for it in the debug_info.log file
339 if (db_filep->isValid())
340 {
341 // This was originally scanning for "SL Log: %[^\r\n]", which happily skipped to the next line
342 // on debug logs (which don't have anything after "SL Log:" and tried to open a nonsensical filename.
343 sscanf(db_filep->mBuf.c_str(), "SL Log:%[ ]%[^\r\n]", tmp_space, tmp_sl_name);
344 }
345 else
346 {
347 delete db_filep;
348 db_filep = NULL;
349 }
350
351 // If we actually have a legitimate file name, use it.
352 if (tmp_sl_name[0])
353 {
354 sl_file_name = tmp_sl_name;
355 llinfos << "Using log file from debug log " << sl_file_name << llendl;
356 }
357 else
358 {
359 // Figure out the filename of the second life log
360 sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log").c_str();
361 }
362
363 // Now we get the SecondLife.log file if it's there, and recent enough...
364 sl_filep = new LLFileEncoder("SL", sl_file_name.c_str());
365 if (!sl_filep->isValid())
366 {
367 delete sl_filep;
368 sl_filep = NULL;
369 }
370
371 st_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log").c_str();
372 st_filep = new LLFileEncoder("ST", st_file_name.c_str());
373 if (!st_filep->isValid())
374 {
375 delete st_filep;
376 st_filep = NULL;
377 }
378
379 si_file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.ini").c_str();
380 si_filep = new LLFileEncoder("SI", si_file_name.c_str());
381 if (!si_filep->isValid())
382 {
383 delete si_filep;
384 si_filep = NULL;
385 }
386
387 // MBW -- This needs to go find "~/Library/Logs/CrashReporter/Second Life.crash.log" on 10.3
388 // or "~/Library/Logs/Second Life.crash.log" on 10.2.
389 {
390 char path[MAX_PATH];
391 FSRef folder;
392
393 if(FSFindFolder(kUserDomain, kLogsFolderType, false, &folder) == noErr)
394 {
395 // folder is an FSRef to ~/Library/Logs/
396 if(FSRefMakePath(&folder, (UInt8*)&path, sizeof(path)) == noErr)
397 {
398 struct stat dw_stat;
399// printf("path is %s\n", path);
400
401 // Try the 10.3 path first...
402 dw_file_name = LLString(path) + LLString("/CrashReporter/Second Life.crash.log");
403 res = stat(dw_file_name.c_str(), &dw_stat);
404
405 if (res)
406 {
407 // Try the 10.2 one next...
408 dw_file_name = LLString(path) + LLString("/Second Life.crash.log");
409 res = stat(dw_file_name.c_str(), &dw_stat);
410 }
411
412 if (!res)
413 {
414 dw_filep = new LLFileEncoder("DW", dw_file_name.c_str(), true);
415 if (!dw_filep->isValid())
416 {
417 delete dw_filep;
418 dw_filep = NULL;
419 }
420 }
421 else
422 {
423 llwarns << "Couldn't find any CrashReporter files..." << llendl;
424 }
425 }
426 }
427 }
428
429 LLString post_data;
430 LLString tmp_url_buf;
431
432 // Append the userserver
433 tmp_url_buf = encode_string("USER", gUserserver);
434 post_data += tmp_url_buf;
435 llinfos << "PostData:" << post_data << llendl;
436
437 if (gCrashInPreviousExec)
438 {
439 post_data.append("&");
440 tmp_url_buf = encode_string("EF", "Y");
441 post_data += tmp_url_buf;
442 }
443
444 if (db_filep)
445 {
446 post_data.append("&");
447 tmp_url_buf = db_filep->encodeURL();
448 post_data += tmp_url_buf;
449 llinfos << "Sending DB log file" << llendl;
450 }
451 else
452 {
453 llinfos << "Not sending DB log file" << llendl;
454 }
455
456 if (sl_filep)
457 {
458 post_data.append("&");
459 tmp_url_buf = sl_filep->encodeURL(SL_MAX_SIZE);
460 post_data += tmp_url_buf;
461 llinfos << "Sending SL log file" << llendl;
462 }
463 else
464 {
465 llinfos << "Not sending SL log file" << llendl;
466 }
467
468 if (st_filep)
469 {
470 post_data.append("&");
471 tmp_url_buf = st_filep->encodeURL(SL_MAX_SIZE);
472 post_data += tmp_url_buf;
473 llinfos << "Sending stats log file" << llendl;
474 }
475 else
476 {
477 llinfos << "Not sending stats log file" << llendl;
478 }
479
480 if (dw_filep)
481 {
482 post_data.append("&");
483 tmp_url_buf = dw_filep->encodeURL(DW_MAX_SIZE);
484 post_data += tmp_url_buf;
485 }
486 else
487 {
488 llinfos << "Not sending crash log file" << llendl;
489 }
490
491 if (si_filep)
492 {
493 post_data.append("&");
494 tmp_url_buf = si_filep->encodeURL();
495 post_data += tmp_url_buf;
496 llinfos << "Sending settings log file" << llendl;
497 }
498 else
499 {
500 llinfos << "Not sending settings.ini file" << llendl;
501 }
502
503 if (gUserText.size())
504 {
505 post_data.append("&");
506 tmp_url_buf = encode_string("UN", gUserText);
507 post_data += tmp_url_buf;
508 }
509
510 delete db_filep;
511 db_filep = NULL;
512 delete sl_filep;
513 sl_filep = NULL;
514 delete dw_filep;
515 dw_filep = NULL;
516
517 // Debugging spam
518#if 0
519 printf("Crash report post data:\n--------\n");
520 printf("%s", post_data.getString());
521 printf("\n--------\n");
522#endif
523
524 // Send the report. Yes, it's this easy.
525 {
526 CURL *curl = curl_easy_init();
527
528 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
529 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback);
530 curl_easy_setopt(curl, CURLOPT_POST, 1);
531 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data.c_str());
532 curl_easy_setopt(curl, CURLOPT_URL, "http://secondlife.com/cgi-bin/viewer_crash_reporter2");
533
534 llinfos << "Connecting to crash report server" << llendl;
535 CURLcode result = curl_easy_perform(curl);
536
537 curl_easy_cleanup(curl);
538
539 if(result != CURLE_OK)
540 {
541 llinfos << "Couldn't talk to crash report server" << llendl;
542 }
543 else
544 {
545 llinfos << "Response from crash report server:" << llendl;
546 llinfos << gServerResponse << llendl;
547 }
548 }
549
550 return 0;
551}
552
553LLFileEncoder::LLFileEncoder(const char *form_name, const char *filename, bool isCrashLog)
554{
555 mFormname = form_name;
556 mFilename = filename;
557 mIsValid = FALSE;
558
559 int res;
560
561 struct stat stat_data;
562 res = stat(mFilename.c_str(), &stat_data);
563 if (res)
564 {
565 llwarns << "File " << mFilename << " is missing!" << llendl;
566 return;
567 }
568 else
569 {
570 // Debugging spam
571// llinfos << "File " << mFilename << " is present..." << llendl;
572
573 if(!gCrashInPreviousExec && isCrashLog)
574 {
575 // Make sure the file isn't too old.
576 double age = difftime(gLaunchTime, stat_data.st_mtimespec.tv_sec);
577
578// llinfos << "age is " << age << llendl;
579
580 if(age > 60.0)
581 {
582 // The file was last modified more than 60 seconds before the crash reporter was launched. Assume it's stale.
583 llwarns << "File " << mFilename << " is too old!" << llendl;
584 return;
585 }
586 }
587
588 }
589
590 S32 buf_size = stat_data.st_size;
591 FILE *fp = fopen(mFilename.c_str(), "rb");
592 U8 *buf = new U8[buf_size + 1];
593 fread(buf, 1, buf_size, fp);
594 fclose(fp);
595 buf[buf_size] = 0;
596
597 mBuf = (char *)buf;
598
599 if(isCrashLog)
600 {
601 // Crash logs consist of a number of entries, one per crash.
602 // Each entry is preceeded by "**********" on a line by itself.
603 // We want only the most recent (i.e. last) one.
604 const char *sep = "**********";
605 const char *start = mBuf.c_str();
606 const char *cur = start;
607 const char *temp = strstr(cur, sep);
608
609 while(temp != NULL)
610 {
611 // Skip past the marker we just found
612 cur = temp + strlen(sep);
613
614 // and try to find another
615 temp = strstr(cur, sep);
616 }
617
618 // If there's more than one entry in the log file, strip all but the last one.
619 if(cur != start)
620 {
621 mBuf.erase(0, cur - start);
622 }
623 }
624
625 mIsValid = TRUE;
626 delete[] buf;
627}
628
629LLString LLFileEncoder::encodeURL(const S32 max_length)
630{
631 LLString result = mFormname;
632 result.append("=");
633
634 S32 i = 0;
635
636 if (max_length)
637 {
638 if (mBuf.size() > max_length)
639 {
640 i = mBuf.size() - max_length;
641 }
642 }
643
644#if 0
645 // Plain text version for debugging
646 result.append(mBuf);
647#else
648 // Not using LLString because of bad performance issues
649 S32 buf_size = mBuf.size();
650 S32 url_buf_size = 3*mBuf.size() + 1;
651 char *url_buf = new char[url_buf_size];
652
653 S32 cur_pos = 0;
654 for (; i < buf_size; i++)
655 {
656 sprintf(url_buf + cur_pos, "%%%02x", mBuf[i]);
657 cur_pos += 3;
658 }
659 url_buf[i*3] = 0;
660
661 result.append(url_buf);
662 delete[] url_buf;
663#endif
664 return result;
665}
666
667LLString encode_string(const char *formname, const LLString &str)
668{
669 LLString result = formname;
670 result.append("=");
671 // Not using LLString because of bad performance issues
672 S32 buf_size = str.size();
673 S32 url_buf_size = 3*str.size() + 1;
674 char *url_buf = new char[url_buf_size];
675
676 S32 cur_pos = 0;
677 S32 i;
678 for (i = 0; i < buf_size; i++)
679 {
680 sprintf(url_buf + cur_pos, "%%%02x", str[i]);
681 cur_pos += 3;
682 }
683 url_buf[i*3] = 0;
684
685 result.append(url_buf);
686 delete[] url_buf;
687 return result;
688}