diff options
author | Jacek Antonelli | 2008-08-15 23:45:19 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:45:19 -0500 |
commit | b235c59d60472f818a9142c0886b95a0ff4191d7 (patch) | |
tree | d323c55587584b19cc43a03f58a178823f12d3cd /linden/indra/mac_crash_logger | |
parent | Second Life viewer sources 1.18.5.3 (diff) | |
download | meta-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.cpp | 342 | ||||
-rw-r--r-- | linden/indra/mac_crash_logger/llcrashloggermac.h | 51 | ||||
-rw-r--r-- | linden/indra/mac_crash_logger/mac_crash_logger.cpp | 668 |
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 | ||
50 | const char* const SETTINGS_FILE_HEADER = "version"; | ||
51 | const S32 SETTINGS_FILE_VERSION = 101; | ||
52 | |||
53 | // Windows Message Handlers | ||
54 | |||
55 | BOOL gFirstDialog = TRUE; // Are we currently handling the Send/Don't Send dialog? | ||
56 | FILE *gDebugFile = NULL; | ||
57 | |||
58 | WindowRef gWindow = NULL; | ||
59 | EventHandlerRef gEventHandler = NULL; | ||
60 | LLString gUserNotes = ""; | ||
61 | bool gSendReport = false; | ||
62 | bool gRememberChoice = false; | ||
63 | IBNibRef nib = NULL; | ||
64 | |||
65 | OSStatus 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 | |||
144 | LLCrashLoggerMac::LLCrashLoggerMac(void) | ||
145 | { | ||
146 | } | ||
147 | |||
148 | LLCrashLoggerMac::~LLCrashLoggerMac(void) | ||
149 | { | ||
150 | } | ||
151 | |||
152 | bool 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 | |||
208 | void 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 | |||
296 | bool 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 | |||
334 | void LLCrashLoggerMac::updateApplication(LLString message) | ||
335 | { | ||
336 | LLCrashLogger::updateApplication(); | ||
337 | } | ||
338 | |||
339 | bool 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 | |||
39 | class LLCrashLoggerMac : public LLCrashLogger | ||
40 | { | ||
41 | public: | ||
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 | |||
46 | class LLFileEncoder | ||
47 | { | ||
48 | public: | ||
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); | ||
53 | public: | ||
54 | BOOL mIsValid; | ||
55 | LLString mFilename; | ||
56 | LLString mFormname; | ||
57 | LLString mBuf; | ||
58 | }; | ||
59 | |||
60 | LLString encode_string(const char *formname, const LLString &str); | ||
61 | |||
62 | #include <Carbon/Carbon.h> | ||
63 | |||
64 | LLString gServerResponse; | ||
65 | BOOL gSendReport = FALSE; | ||
66 | LLString gUserserver; | ||
67 | LLString gUserText; | ||
68 | WindowRef gWindow = NULL; | ||
69 | EventHandlerRef gEventHandler = NULL; | ||
70 | BOOL gCrashInPreviousExec = FALSE; | ||
71 | time_t gLaunchTime; | ||
72 | |||
73 | size_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 | |||
85 | OSStatus 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 | ||
144 | int main(int argc, char **argv) | 36 | int 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 | ¶ms, | ||
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 | |||
561 | LLFileEncoder::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 | |||
637 | LLString 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 | |||
679 | LLString 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 | } | ||