aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/mac_crash_logger
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:45:19 -0500
committerJacek Antonelli2008-08-15 23:45:19 -0500
commitb235c59d60472f818a9142c0886b95a0ff4191d7 (patch)
treed323c55587584b19cc43a03f58a178823f12d3cd /linden/indra/mac_crash_logger
parentSecond Life viewer sources 1.18.5.3 (diff)
downloadmeta-impy-b235c59d60472f818a9142c0886b95a0ff4191d7.zip
meta-impy-b235c59d60472f818a9142c0886b95a0ff4191d7.tar.gz
meta-impy-b235c59d60472f818a9142c0886b95a0ff4191d7.tar.bz2
meta-impy-b235c59d60472f818a9142c0886b95a0ff4191d7.tar.xz
Second Life viewer sources 1.18.6.0-RC
Diffstat (limited to 'linden/indra/mac_crash_logger')
-rw-r--r--linden/indra/mac_crash_logger/llcrashloggermac.cpp342
-rw-r--r--linden/indra/mac_crash_logger/llcrashloggermac.h51
-rw-r--r--linden/indra/mac_crash_logger/mac_crash_logger.cpp668
3 files changed, 400 insertions, 661 deletions
diff --git a/linden/indra/mac_crash_logger/llcrashloggermac.cpp b/linden/indra/mac_crash_logger/llcrashloggermac.cpp
new file mode 100644
index 0000000..3d8abe5
--- /dev/null
+++ b/linden/indra/mac_crash_logger/llcrashloggermac.cpp
@@ -0,0 +1,342 @@
1/**
2 * @file llcrashloggermac.cpp
3 * @brief Mac OSX crash logger implementation
4 *
5 * $LicenseInfo:firstyear=2003&license=viewergpl$
6 *
7 * Copyright (c) 2003-2007, Linden Research, Inc.
8 *
9 * Second Life Viewer Source Code
10 * The source code in this file ("Source Code") is provided by Linden Lab
11 * to you under the terms of the GNU General Public License, version 2.0
12 * ("GPL"), unless you have obtained a separate licensing agreement
13 * ("Other License"), formally executed by you and Linden Lab. Terms of
14 * the GPL can be found in doc/GPL-license.txt in this distribution, or
15 * online at http://secondlife.com/developers/opensource/gplv2
16 *
17 * There are special exceptions to the terms and conditions of the GPL as
18 * it is applied to this Source Code. View the full text of the exception
19 * in the file doc/FLOSS-exception.txt in this software distribution, or
20 * online at http://secondlife.com/developers/opensource/flossexception
21 *
22 * By copying, modifying or distributing this software, you acknowledge
23 * that you have read and understood your obligations described above,
24 * and agree to abide by those obligations.
25 *
26 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
27 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
28 * COMPLETENESS OR PERFORMANCE.
29 * $/LicenseInfo$
30 */
31
32
33#include "llcrashloggermac.h"
34
35#include <Carbon/Carbon.h>
36#include <iostream>
37#include <sstream>
38
39#include "boost/tokenizer.hpp"
40
41#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
42#include "llerror.h"
43#include "llfile.h"
44#include "lltimer.h"
45#include "llstring.h"
46#include "lldir.h"
47#include "llsdserialize.h"
48
49#define MAX_LOADSTRING 100
50const char* const SETTINGS_FILE_HEADER = "version";
51const S32 SETTINGS_FILE_VERSION = 101;
52
53// Windows Message Handlers
54
55BOOL gFirstDialog = TRUE; // Are we currently handling the Send/Don't Send dialog?
56FILE *gDebugFile = NULL;
57
58WindowRef gWindow = NULL;
59EventHandlerRef gEventHandler = NULL;
60LLString gUserNotes = "";
61bool gSendReport = false;
62bool gRememberChoice = false;
63IBNibRef nib = NULL;
64
65OSStatus dialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata)
66{
67 OSStatus result = eventNotHandledErr;
68 OSStatus err;
69 UInt32 evtClass = GetEventClass(event);
70 UInt32 evtKind = GetEventKind(event);
71 if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess))
72 {
73 HICommand cmd;
74 err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd);
75
76
77
78 if(err == noErr)
79 {
80 //Get the value of the checkbox
81 ControlID id;
82 ControlRef checkBox = NULL;
83 id.signature = 'remb';
84 id.id = 0;
85 err = GetControlByID(gWindow, &id, &checkBox);
86
87 if(err == noErr)
88 {
89 if(GetControl32BitValue(checkBox) == kControlCheckBoxCheckedValue)
90 {
91 gRememberChoice = true;
92 }
93 else
94 {
95 gRememberChoice = false;
96 }
97 }
98 switch(cmd.commandID)
99 {
100 case kHICommandOK:
101 {
102 char buffer[65535]; /* Flawfinder: ignore */
103 Size size = sizeof(buffer) - 1;
104 ControlRef textField = NULL;
105
106 id.signature = 'text';
107 id.id = 0;
108
109 err = GetControlByID(gWindow, &id, &textField);
110 if(err == noErr)
111 {
112 // Get the user response text
113 err = GetControlData(textField, kControlNoPart, kControlEditTextTextTag, size, (Ptr)buffer, &size);
114 }
115 if(err == noErr)
116 {
117 // Make sure the string is terminated.
118 buffer[size] = 0;
119 gUserNotes = buffer;
120
121 llinfos << buffer << llendl;
122 }
123
124 // Send the report.
125
126 QuitAppModalLoopForWindow(gWindow);
127 gSendReport = true;
128 result = noErr;
129 }
130 break;
131
132 case kHICommandCancel:
133 QuitAppModalLoopForWindow(gWindow);
134 result = noErr;
135 break;
136 }
137 }
138 }
139
140 return(result);
141}
142
143
144LLCrashLoggerMac::LLCrashLoggerMac(void)
145{
146}
147
148LLCrashLoggerMac::~LLCrashLoggerMac(void)
149{
150}
151
152bool LLCrashLoggerMac::init(void)
153{
154 bool ok = LLCrashLogger::init();
155 if(!ok) return false;
156 if(mCrashBehavior != CRASH_BEHAVIOR_ASK) return true;
157
158 // Real UI...
159 OSStatus err;
160
161 err = CreateNibReference(CFSTR("CrashReporter"), &nib);
162
163 if(err == noErr)
164 {
165 err = CreateWindowFromNib(nib, CFSTR("CrashReporter"), &gWindow);
166 }
167
168 if(err == noErr)
169 {
170 // Set focus to the edit text area
171 ControlRef textField = NULL;
172 ControlID id;
173
174 id.signature = 'text';
175 id.id = 0;
176
177 // Don't set err if any of this fails, since it's non-critical.
178 if(GetControlByID(gWindow, &id, &textField) == noErr)
179 {
180 SetKeyboardFocus(gWindow, textField, kControlFocusNextPart);
181 }
182 }
183
184 if(err == noErr)
185 {
186 ShowWindow(gWindow);
187 }
188
189 if(err == noErr)
190 {
191 // Set up an event handler for the window.
192 EventTypeSpec handlerEvents[] =
193 {
194 { kEventClassCommand, kEventCommandProcess }
195 };
196
197 InstallWindowEventHandler(
198 gWindow,
199 NewEventHandlerUPP(dialogHandler),
200 GetEventTypeCount (handlerEvents),
201 handlerEvents,
202 0,
203 &gEventHandler);
204 }
205 return true;
206}
207
208void LLCrashLoggerMac::gatherPlatformSpecificFiles()
209{
210 updateApplication("Gathering hardware information...");
211 char path[MAX_PATH];
212 FSRef folder;
213
214 if(FSFindFolder(kUserDomain, kLogsFolderType, false, &folder) == noErr)
215 {
216 // folder is an FSRef to ~/Library/Logs/
217 if(FSRefMakePath(&folder, (UInt8*)&path, sizeof(path)) == noErr)
218 {
219 struct stat dw_stat;
220 LLString mBuf;
221 bool isLeopard = false;
222 // Try the 10.3 path first...
223 LLString dw_file_name = LLString(path) + LLString("/CrashReporter/Second Life.crash.log");
224 int res = stat(dw_file_name.c_str(), &dw_stat);
225
226 if (res)
227 {
228 // Try the 10.2 one next...
229 dw_file_name = LLString(path) + LLString("/Second Life.crash.log");
230 res = stat(dw_file_name.c_str(), &dw_stat);
231 }
232
233 if(res)
234 {
235 //10.5: Like 10.3+, except it puts the crash time in the file instead of dividing it up
236 //using asterisks. Get a directory listing, search for files starting with second life,
237 //use the last one found.
238 LLString old_file_name, current_file_name, pathname, mask;
239 pathname = LLString(path) + LLString("/CrashReporter/");
240 mask = "Second Life*";
241 while(gDirUtilp->getNextFileInDir(pathname, mask, current_file_name, false))
242 {
243 old_file_name = current_file_name;
244 }
245 if(old_file_name != "")
246 {
247 dw_file_name = pathname + old_file_name;
248 res=stat(dw_file_name.c_str(), &dw_stat);
249 isLeopard = true;
250 }
251 }
252
253 if (!res)
254 {
255 std::ifstream fp(dw_file_name.c_str());
256 std::stringstream str;
257 if(!fp.is_open()) return;
258 str << fp.rdbuf();
259 mBuf = str.str();
260
261 if(!isLeopard)
262 {
263 // Crash logs consist of a number of entries, one per crash.
264 // Each entry is preceeded by "**********" on a line by itself.
265 // We want only the most recent (i.e. last) one.
266 const char *sep = "**********";
267 const char *start = mBuf.c_str();
268 const char *cur = start;
269 const char *temp = strstr(cur, sep);
270
271 while(temp != NULL)
272 {
273 // Skip past the marker we just found
274 cur = temp + strlen(sep); /* Flawfinder: ignore */
275
276 // and try to find another
277 temp = strstr(cur, sep);
278 }
279
280 // If there's more than one entry in the log file, strip all but the last one.
281 if(cur != start)
282 {
283 mBuf.erase(0, cur - start);
284 }
285 }
286 mCrashInfo["CrashLog"] = mBuf;
287 }
288 else
289 {
290 llwarns << "Couldn't find any CrashReporter files..." << llendl;
291 }
292 }
293 }
294}
295
296bool LLCrashLoggerMac::mainLoop()
297{
298 OSStatus err = noErr;
299
300 if(err == noErr && mCrashBehavior == CRASH_BEHAVIOR_ASK)
301 {
302 RunAppModalLoopForWindow(gWindow);
303 }
304 else if (mCrashBehavior == CRASH_BEHAVIOR_ALWAYS_SEND)
305 {
306 gSendReport = true;
307 }
308
309 if(gRememberChoice)
310 {
311 if(gSendReport) saveCrashBehaviorSetting(CRASH_BEHAVIOR_ALWAYS_SEND);
312 else saveCrashBehaviorSetting(CRASH_BEHAVIOR_NEVER_SEND);
313 }
314
315 if(gSendReport)
316 {
317 setUserText(gUserNotes);
318 sendCrashLogs();
319 }
320
321 if(gWindow != NULL)
322 {
323 DisposeWindow(gWindow);
324 }
325
326 if(nib != NULL)
327 {
328 DisposeNibReference(nib);
329 }
330
331 return true;
332}
333
334void LLCrashLoggerMac::updateApplication(LLString message)
335{
336 LLCrashLogger::updateApplication();
337}
338
339bool LLCrashLoggerMac::cleanup()
340{
341 return true;
342}
diff --git a/linden/indra/mac_crash_logger/llcrashloggermac.h b/linden/indra/mac_crash_logger/llcrashloggermac.h
new file mode 100644
index 0000000..cf4e766
--- /dev/null
+++ b/linden/indra/mac_crash_logger/llcrashloggermac.h
@@ -0,0 +1,51 @@
1/**
2 * @file llcrashloggermac.h
3 * @brief Mac OSX crash logger definition
4 *
5 * $LicenseInfo:firstyear=2003&license=viewergpl$
6 *
7 * Copyright (c) 2003-2007, Linden Research, Inc.
8 *
9 * Second Life Viewer Source Code
10 * The source code in this file ("Source Code") is provided by Linden Lab
11 * to you under the terms of the GNU General Public License, version 2.0
12 * ("GPL"), unless you have obtained a separate licensing agreement
13 * ("Other License"), formally executed by you and Linden Lab. Terms of
14 * the GPL can be found in doc/GPL-license.txt in this distribution, or
15 * online at http://secondlife.com/developers/opensource/gplv2
16 *
17 * There are special exceptions to the terms and conditions of the GPL as
18 * it is applied to this Source Code. View the full text of the exception
19 * in the file doc/FLOSS-exception.txt in this software distribution, or
20 * online at http://secondlife.com/developers/opensource/flossexception
21 *
22 * By copying, modifying or distributing this software, you acknowledge
23 * that you have read and understood your obligations described above,
24 * and agree to abide by those obligations.
25 *
26 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
27 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
28 * COMPLETENESS OR PERFORMANCE.
29 * $/LicenseInfo$
30 */
31
32#ifndef LLCRASHLOGGERMAC_H
33#define LLCRASHLOGGERMAC_H
34
35#include "linden_common.h"
36#include "llcrashlogger.h"
37#include "llstring.h"
38
39class LLCrashLoggerMac : public LLCrashLogger
40{
41public:
42 LLCrashLoggerMac(void);
43 ~LLCrashLoggerMac(void);
44 virtual bool init();
45 virtual bool mainLoop();
46 virtual void updateApplication(LLString message = "");
47 virtual bool cleanup();
48 virtual void gatherPlatformSpecificFiles();
49};
50
51#endif
diff --git a/linden/indra/mac_crash_logger/mac_crash_logger.cpp b/linden/indra/mac_crash_logger/mac_crash_logger.cpp
index 2501b4a..bf3151a 100644
--- a/linden/indra/mac_crash_logger/mac_crash_logger.cpp
+++ b/linden/indra/mac_crash_logger/mac_crash_logger.cpp
@@ -31,675 +31,21 @@
31 31
32#include "linden_common.h" 32#include "linden_common.h"
33 33
34#include <sys/types.h> 34#include "llcrashloggermac.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]; /* Flawfinder: ignore */
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 35
144int main(int argc, char **argv) 36int main(int argc, char **argv)
145{ 37{
146 const S32 DW_MAX_SIZE = 100000; // Maximum size to transmit of the Dr. Watson log file 38 //time(&gLaunchTime);
147 const S32 SL_MAX_SIZE = 100000; // Maximum size of the Second Life log file.
148 int i;
149
150 time(&gLaunchTime);
151 39
152 llinfos << "Starting Second Life Viewer Crash Reporter" << llendl; 40 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
334 // *NOTE: changing the size of either of these buffers will
335 // require changing the sscanf() format string to correctly
336 // account for it.
337 char tmp_sl_name[LL_MAX_PATH]; /* Flawfinder: ignore */
338 tmp_sl_name[0] = '\0';
339 char tmp_space[MAX_STRING]; /* Flawfinder: ignore */
340 tmp_space[0] = '\0';
341
342 // Look for it in the debug_info.log file
343 if (db_filep->isValid())
344 {
345 // This was originally scanning for "SL Log: %[^\r\n]", which happily skipped to the next line
346 // on debug logs (which don't have anything after "SL Log:" and tried to open a nonsensical filename.
347 sscanf(
348 db_filep->mBuf.c_str(),
349 "SL Log:%254[ ]%1023[^\r\n]",
350 tmp_space,
351 tmp_sl_name);
352 }
353 else
354 {
355 delete db_filep;
356 db_filep = NULL;
357 }
358
359 // If we actually have a legitimate file name, use it.
360 if (tmp_sl_name[0])
361 {
362 sl_file_name = tmp_sl_name;
363 llinfos << "Using log file from debug log " << sl_file_name << llendl;
364 }
365 else
366 {
367 // Figure out the filename of the second life log
368 sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log").c_str();
369 }
370
371 // Now we get the SecondLife.log file if it's there, and recent enough...
372 sl_filep = new LLFileEncoder("SL", sl_file_name.c_str());
373 if (!sl_filep->isValid())
374 {
375 delete sl_filep;
376 sl_filep = NULL;
377 }
378
379 st_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log").c_str();
380 st_filep = new LLFileEncoder("ST", st_file_name.c_str());
381 if (!st_filep->isValid())
382 {
383 delete st_filep;
384 st_filep = NULL;
385 }
386
387 si_file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.ini").c_str();
388 si_filep = new LLFileEncoder("SI", si_file_name.c_str());
389 if (!si_filep->isValid())
390 {
391 delete si_filep;
392 si_filep = NULL;
393 }
394
395 // MBW -- This needs to go find "~/Library/Logs/CrashReporter/Second Life.crash.log" on 10.3
396 // or "~/Library/Logs/Second Life.crash.log" on 10.2.
397 {
398 char path[MAX_PATH]; /* Flawfinder: ignore */
399 FSRef folder;
400
401 if(FSFindFolder(kUserDomain, kLogsFolderType, false, &folder) == noErr)
402 {
403 // folder is an FSRef to ~/Library/Logs/
404 if(FSRefMakePath(&folder, (UInt8*)&path, sizeof(path)) == noErr)
405 {
406 struct stat dw_stat;
407// printf("path is %s\n", path);
408
409 // Try the 10.3 path first...
410 dw_file_name = LLString(path) + LLString("/CrashReporter/Second Life.crash.log");
411 res = stat(dw_file_name.c_str(), &dw_stat);
412
413 if (res)
414 {
415 // Try the 10.2 one next...
416 dw_file_name = LLString(path) + LLString("/Second Life.crash.log");
417 res = stat(dw_file_name.c_str(), &dw_stat);
418 }
419
420 if (!res)
421 {
422 dw_filep = new LLFileEncoder("DW", dw_file_name.c_str(), true);
423 if (!dw_filep->isValid())
424 {
425 delete dw_filep;
426 dw_filep = NULL;
427 }
428 }
429 else
430 {
431 llwarns << "Couldn't find any CrashReporter files..." << llendl;
432 }
433 }
434 }
435 }
436
437 LLString post_data;
438 LLString tmp_url_buf;
439
440 // Append the userserver
441 tmp_url_buf = encode_string("USER", gUserserver);
442 post_data += tmp_url_buf;
443 llinfos << "PostData:" << post_data << llendl;
444
445 if (gCrashInPreviousExec)
446 {
447 post_data.append("&");
448 tmp_url_buf = encode_string("EF", "Y");
449 post_data += tmp_url_buf;
450 }
451
452 if (db_filep)
453 {
454 post_data.append("&");
455 tmp_url_buf = db_filep->encodeURL();
456 post_data += tmp_url_buf;
457 llinfos << "Sending DB log file" << llendl;
458 }
459 else
460 {
461 llinfos << "Not sending DB log file" << llendl;
462 }
463
464 if (sl_filep)
465 {
466 post_data.append("&");
467 tmp_url_buf = sl_filep->encodeURL(SL_MAX_SIZE);
468 post_data += tmp_url_buf;
469 llinfos << "Sending SL log file" << llendl;
470 }
471 else
472 {
473 llinfos << "Not sending SL log file" << llendl;
474 }
475
476 if (st_filep)
477 {
478 post_data.append("&");
479 tmp_url_buf = st_filep->encodeURL(SL_MAX_SIZE);
480 post_data += tmp_url_buf;
481 llinfos << "Sending stats log file" << llendl;
482 }
483 else
484 {
485 llinfos << "Not sending stats log file" << llendl;
486 }
487 41
488 if (dw_filep) 42 LLCrashLoggerMac app;
43 app.parseCommandOptions(argc, argv);
44 if(!app.init())
489 { 45 {
490 post_data.append("&"); 46 return 0;
491 tmp_url_buf = dw_filep->encodeURL(DW_MAX_SIZE);
492 post_data += tmp_url_buf;
493 } 47 }
494 else 48 app.mainLoop();
495 {
496 llinfos << "Not sending crash log file" << llendl;
497 }
498
499 if (si_filep)
500 {
501 post_data.append("&");
502 tmp_url_buf = si_filep->encodeURL();
503 post_data += tmp_url_buf;
504 llinfos << "Sending settings log file" << llendl;
505 }
506 else
507 {
508 llinfos << "Not sending settings.ini file" << llendl;
509 }
510
511 if (gUserText.size())
512 {
513 post_data.append("&");
514 tmp_url_buf = encode_string("UN", gUserText);
515 post_data += tmp_url_buf;
516 }
517
518 delete db_filep;
519 db_filep = NULL;
520 delete sl_filep;
521 sl_filep = NULL;
522 delete dw_filep;
523 dw_filep = NULL;
524
525 // Debugging spam
526#if 0
527 printf("Crash report post data:\n--------\n");
528 printf("%s", post_data.getString());
529 printf("\n--------\n");
530#endif
531
532 // Send the report. Yes, it's this easy.
533 {
534 CURL *curl = curl_easy_init();
535
536 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
537 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback);
538 curl_easy_setopt(curl, CURLOPT_POST, 1);
539 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data.c_str());
540 curl_easy_setopt(curl, CURLOPT_URL, "http://secondlife.com/cgi-bin/viewer_crash_reporter2");
541
542 llinfos << "Connecting to crash report server" << llendl;
543 CURLcode result = curl_easy_perform(curl);
544
545 curl_easy_cleanup(curl);
546 49
547 if(result != CURLE_OK)
548 {
549 llinfos << "Couldn't talk to crash report server" << llendl;
550 }
551 else
552 {
553 llinfos << "Response from crash report server:" << llendl;
554 llinfos << gServerResponse << llendl;
555 }
556 }
557
558 return 0; 50 return 0;
559} 51}
560
561LLFileEncoder::LLFileEncoder(const char *form_name, const char *filename, bool isCrashLog)
562{
563 mFormname = form_name;
564 mFilename = filename;
565 mIsValid = FALSE;
566
567 int res;
568
569 struct stat stat_data;
570 res = stat(mFilename.c_str(), &stat_data);
571 if (res)
572 {
573 llwarns << "File " << mFilename << " is missing!" << llendl;
574 return;
575 }
576 else
577 {
578 // Debugging spam
579// llinfos << "File " << mFilename << " is present..." << llendl;
580
581 if(!gCrashInPreviousExec && isCrashLog)
582 {
583 // Make sure the file isn't too old.
584 double age = difftime(gLaunchTime, stat_data.st_mtimespec.tv_sec);
585
586// llinfos << "age is " << age << llendl;
587
588 if(age > 60.0)
589 {
590 // The file was last modified more than 60 seconds before the crash reporter was launched. Assume it's stale.
591 llwarns << "File " << mFilename << " is too old!" << llendl;
592 return;
593 }
594 }
595
596 }
597
598 S32 buf_size = stat_data.st_size;
599 FILE* fp = fopen(mFilename.c_str(), "rb"); /* Flawfinder: ignore */
600 U8 *buf = new U8[buf_size + 1];
601 fread(buf, 1, buf_size, fp);
602 fclose(fp);
603 buf[buf_size] = 0;
604
605 mBuf = (char *)buf;
606
607 if(isCrashLog)
608 {
609 // Crash logs consist of a number of entries, one per crash.
610 // Each entry is preceeded by "**********" on a line by itself.
611 // We want only the most recent (i.e. last) one.
612 const char *sep = "**********";
613 const char *start = mBuf.c_str();
614 const char *cur = start;
615 const char *temp = strstr(cur, sep);
616
617 while(temp != NULL)
618 {
619 // Skip past the marker we just found
620 cur = temp + strlen(sep); /* Flawfinder: ignore */
621
622 // and try to find another
623 temp = strstr(cur, sep);
624 }
625
626 // If there's more than one entry in the log file, strip all but the last one.
627 if(cur != start)
628 {
629 mBuf.erase(0, cur - start);
630 }
631 }
632
633 mIsValid = TRUE;
634 delete[] buf;
635}
636
637LLString LLFileEncoder::encodeURL(const S32 max_length)
638{
639 LLString result = mFormname;
640 result.append("=");
641
642 S32 i = 0;
643
644 if (max_length)
645 {
646 if (mBuf.size() > max_length)
647 {
648 i = mBuf.size() - max_length;
649 }
650 }
651
652#if 0
653 // Plain text version for debugging
654 result.append(mBuf);
655#else
656 // Not using LLString because of bad performance issues
657 S32 buf_size = mBuf.size();
658 S32 url_buf_size = 3*mBuf.size() + 1;
659 char *url_buf = new char[url_buf_size];
660 if (url_buf == NULL)
661 {
662 llerrs << "Memory Allocation Failed" << llendl;
663 return result;
664 }
665 S32 cur_pos = 0;
666 for (; i < buf_size; i++)
667 {
668 sprintf(url_buf + cur_pos, "%%%02x", mBuf[i]); /* Flawfinder: ignore */
669 cur_pos += 3;
670 }
671 url_buf[i*3] = 0;
672
673 result.append(url_buf);
674 delete[] url_buf;
675#endif
676 return result;
677}
678
679LLString encode_string(const char *formname, const LLString &str)
680{
681 LLString result = formname;
682 result.append("=");
683 // Not using LLString because of bad performance issues
684 S32 buf_size = str.size();
685 S32 url_buf_size = 3*str.size() + 1;
686 char *url_buf = new char[url_buf_size];
687 if (url_buf == NULL)
688 {
689 llerrs << "Memory Allocation Failed" << llendl;
690 return result;
691 }
692
693 S32 cur_pos = 0;
694 S32 i;
695 for (i = 0; i < buf_size; i++)
696 {
697 sprintf(url_buf + cur_pos, "%%%02x", str[i]); /* Flawfinder: ignore */
698 cur_pos += 3;
699 }
700 url_buf[i*3] = 0;
701
702 result.append(url_buf);
703 delete[] url_buf;
704 return result;
705}