From 420b91db29485df39fd6e724e782c449158811cb Mon Sep 17 00:00:00 2001 From: James Cook Date: Tue, 2 Jan 2007 08:33:20 +0000 Subject: Print done when done. --- indra/mac_crash_logger/mac_crash_logger.cpp | 669 ++++++++++++++++++++++++++++ 1 file changed, 669 insertions(+) create mode 100644 indra/mac_crash_logger/mac_crash_logger.cpp (limited to 'indra/mac_crash_logger/mac_crash_logger.cpp') diff --git a/indra/mac_crash_logger/mac_crash_logger.cpp b/indra/mac_crash_logger/mac_crash_logger.cpp new file mode 100644 index 0000000000..5e6d9ba2f0 --- /dev/null +++ b/indra/mac_crash_logger/mac_crash_logger.cpp @@ -0,0 +1,669 @@ +/** + * @file mac_crash_logger.cpp + * @brief Mac OSX crash logger implementation + * + * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" + +#include +#include +//#include +#include +#include +#include +#include + +#include + +#include "llerror.h" +#include "lltimer.h" +#include "lldir.h" + +#include "llstring.h" + +class LLFileEncoder +{ +public: + LLFileEncoder(const char *formname, const char *filename, bool isCrashLog = false); + + BOOL isValid() const { return mIsValid; } + LLString encodeURL(const S32 max_length = 0); +public: + BOOL mIsValid; + LLString mFilename; + LLString mFormname; + LLString mBuf; +}; + +LLString encode_string(const char *formname, const LLString &str); + +#include + +LLString gServerResponse; +BOOL gSendReport = FALSE; +LLString gUserserver; +LLString gUserText; +WindowRef gWindow = NULL; +EventHandlerRef gEventHandler = NULL; +BOOL gCrashInPreviousExec = FALSE; +time_t gLaunchTime; + +size_t curl_download_callback(void *data, size_t size, size_t nmemb, + void *user_data) +{ + S32 bytes = size * nmemb; + char *cdata = (char *) data; + for (int i =0; i < bytes; i += 1) + { + gServerResponse += (cdata[i]); + } + return bytes; +} + +OSStatus dialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata) +{ + OSStatus result = eventNotHandledErr; + OSStatus err; + UInt32 evtClass = GetEventClass(event); + UInt32 evtKind = GetEventKind(event); + + if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess)) + { + HICommand cmd; + err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd); + + if(err == noErr) + { + switch(cmd.commandID) + { + case kHICommandOK: + { + char buffer[65535]; + Size size = sizeof(buffer) - 1; + ControlRef textField = NULL; + ControlID id; + + id.signature = 'text'; + id.id = 0; + + err = GetControlByID(gWindow, &id, &textField); + if(err == noErr) + { + // Get the user response text + err = GetControlData(textField, kControlNoPart, kControlEditTextTextTag, size, (Ptr)buffer, &size); + } + if(err == noErr) + { + // Make sure the string is terminated. + buffer[size] = 0; + gUserText = buffer; + llinfos << buffer << llendl; + } + + // Send the report. + gSendReport = TRUE; + + QuitAppModalLoopForWindow(gWindow); + result = noErr; + } + break; + + case kHICommandCancel: + QuitAppModalLoopForWindow(gWindow); + result = noErr; + break; + } + } + } + + return(result); +} + +int main(int argc, char **argv) +{ + const S32 DW_MAX_SIZE = 100000; // Maximum size to transmit of the Dr. Watson log file + const S32 SL_MAX_SIZE = 100000; // Maximum size of the Second Life log file. + int i; + + time(&gLaunchTime); + + llinfos << "Starting Second Life Viewer Crash Reporter" << llendl; + + for(i=1; iinitAppDirs("SecondLife"); + + int res; + + // Lots of silly variable, replicated for each log file. + LLString db_file_name; + LLString sl_file_name; + LLString dw_file_name; // DW file name is a hack for now... + LLString st_file_name; // stats.log file + LLString si_file_name; // settings.ini file + + LLFileEncoder *db_filep = NULL; + LLFileEncoder *sl_filep = NULL; + LLFileEncoder *st_filep = NULL; + LLFileEncoder *dw_filep = NULL; + LLFileEncoder *si_filep = NULL; + + /////////////////////////////////// + // + // We do the parsing for the debug_info file first, as that will + // give us the location of the SecondLife.log file. + // + + // Figure out the filename of the debug log + db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log").c_str(); + db_filep = new LLFileEncoder("DB", db_file_name.c_str()); + + // Get the filename of the SecondLife.log file + char tmp_sl_name[MAX_PATH]; + tmp_sl_name[0] = '\0'; + char tmp_space[256]; + tmp_space[0] = '\0'; + + // Look for it in the debug_info.log file + if (db_filep->isValid()) + { + // This was originally scanning for "SL Log: %[^\r\n]", which happily skipped to the next line + // on debug logs (which don't have anything after "SL Log:" and tried to open a nonsensical filename. + sscanf(db_filep->mBuf.c_str(), "SL Log:%[ ]%[^\r\n]", tmp_space, tmp_sl_name); + } + else + { + delete db_filep; + db_filep = NULL; + } + + // If we actually have a legitimate file name, use it. + if (tmp_sl_name[0]) + { + sl_file_name = tmp_sl_name; + llinfos << "Using log file from debug log " << sl_file_name << llendl; + } + else + { + // Figure out the filename of the second life log + sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log").c_str(); + } + + // Now we get the SecondLife.log file if it's there, and recent enough... + sl_filep = new LLFileEncoder("SL", sl_file_name.c_str()); + if (!sl_filep->isValid()) + { + delete sl_filep; + sl_filep = NULL; + } + + st_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log").c_str(); + st_filep = new LLFileEncoder("ST", st_file_name.c_str()); + if (!st_filep->isValid()) + { + delete st_filep; + st_filep = NULL; + } + + si_file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.ini").c_str(); + si_filep = new LLFileEncoder("SI", si_file_name.c_str()); + if (!si_filep->isValid()) + { + delete si_filep; + si_filep = NULL; + } + + // MBW -- This needs to go find "~/Library/Logs/CrashReporter/Second Life.crash.log" on 10.3 + // or "~/Library/Logs/Second Life.crash.log" on 10.2. + { + char path[MAX_PATH]; + FSRef folder; + + if(FSFindFolder(kUserDomain, kLogsFolderType, false, &folder) == noErr) + { + // folder is an FSRef to ~/Library/Logs/ + if(FSRefMakePath(&folder, (UInt8*)&path, sizeof(path)) == noErr) + { + struct stat dw_stat; +// printf("path is %s\n", path); + + // Try the 10.3 path first... + dw_file_name = LLString(path) + LLString("/CrashReporter/Second Life.crash.log"); + res = stat(dw_file_name.c_str(), &dw_stat); + + if (res) + { + // Try the 10.2 one next... + dw_file_name = LLString(path) + LLString("/Second Life.crash.log"); + res = stat(dw_file_name.c_str(), &dw_stat); + } + + if (!res) + { + dw_filep = new LLFileEncoder("DW", dw_file_name.c_str(), true); + if (!dw_filep->isValid()) + { + delete dw_filep; + dw_filep = NULL; + } + } + else + { + llwarns << "Couldn't find any CrashReporter files..." << llendl; + } + } + } + } + + LLString post_data; + LLString tmp_url_buf; + + // Append the userserver + tmp_url_buf = encode_string("USER", gUserserver); + post_data += tmp_url_buf; + llinfos << "PostData:" << post_data << llendl; + + if (gCrashInPreviousExec) + { + post_data.append("&"); + tmp_url_buf = encode_string("EF", "Y"); + post_data += tmp_url_buf; + } + + if (db_filep) + { + post_data.append("&"); + tmp_url_buf = db_filep->encodeURL(); + post_data += tmp_url_buf; + llinfos << "Sending DB log file" << llendl; + } + else + { + llinfos << "Not sending DB log file" << llendl; + } + + if (sl_filep) + { + post_data.append("&"); + tmp_url_buf = sl_filep->encodeURL(SL_MAX_SIZE); + post_data += tmp_url_buf; + llinfos << "Sending SL log file" << llendl; + } + else + { + llinfos << "Not sending SL log file" << llendl; + } + + if (st_filep) + { + post_data.append("&"); + tmp_url_buf = st_filep->encodeURL(SL_MAX_SIZE); + post_data += tmp_url_buf; + llinfos << "Sending stats log file" << llendl; + } + else + { + llinfos << "Not sending stats log file" << llendl; + } + + if (dw_filep) + { + post_data.append("&"); + tmp_url_buf = dw_filep->encodeURL(DW_MAX_SIZE); + post_data += tmp_url_buf; + } + else + { + llinfos << "Not sending crash log file" << llendl; + } + + if (si_filep) + { + post_data.append("&"); + tmp_url_buf = si_filep->encodeURL(); + post_data += tmp_url_buf; + llinfos << "Sending settings log file" << llendl; + } + else + { + llinfos << "Not sending settings.ini file" << llendl; + } + + if (gUserText.size()) + { + post_data.append("&"); + tmp_url_buf = encode_string("UN", gUserText); + post_data += tmp_url_buf; + } + + delete db_filep; + db_filep = NULL; + delete sl_filep; + sl_filep = NULL; + delete dw_filep; + dw_filep = NULL; + + // Debugging spam +#if 0 + printf("Crash report post data:\n--------\n"); + printf("%s", post_data.getString()); + printf("\n--------\n"); +#endif + + // Send the report. Yes, it's this easy. + { + CURL *curl = curl_easy_init(); + + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback); + curl_easy_setopt(curl, CURLOPT_POST, 1); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data.c_str()); + curl_easy_setopt(curl, CURLOPT_URL, "http://secondlife.com/cgi-bin/viewer_crash_reporter2"); + + llinfos << "Connecting to crash report server" << llendl; + CURLcode result = curl_easy_perform(curl); + + curl_easy_cleanup(curl); + + if(result != CURLE_OK) + { + llinfos << "Couldn't talk to crash report server" << llendl; + } + else + { + llinfos << "Response from crash report server:" << llendl; + llinfos << gServerResponse << llendl; + } + } + + return 0; +} + +LLFileEncoder::LLFileEncoder(const char *form_name, const char *filename, bool isCrashLog) +{ + mFormname = form_name; + mFilename = filename; + mIsValid = FALSE; + + int res; + + struct stat stat_data; + res = stat(mFilename.c_str(), &stat_data); + if (res) + { + llwarns << "File " << mFilename << " is missing!" << llendl; + return; + } + else + { + // Debugging spam +// llinfos << "File " << mFilename << " is present..." << llendl; + + if(!gCrashInPreviousExec && isCrashLog) + { + // Make sure the file isn't too old. + double age = difftime(gLaunchTime, stat_data.st_mtimespec.tv_sec); + +// llinfos << "age is " << age << llendl; + + if(age > 60.0) + { + // The file was last modified more than 60 seconds before the crash reporter was launched. Assume it's stale. + llwarns << "File " << mFilename << " is too old!" << llendl; + return; + } + } + + } + + S32 buf_size = stat_data.st_size; + FILE *fp = fopen(mFilename.c_str(), "rb"); + U8 *buf = new U8[buf_size + 1]; + fread(buf, 1, buf_size, fp); + fclose(fp); + buf[buf_size] = 0; + + mBuf = (char *)buf; + + if(isCrashLog) + { + // Crash logs consist of a number of entries, one per crash. + // Each entry is preceeded by "**********" on a line by itself. + // We want only the most recent (i.e. last) one. + const char *sep = "**********"; + const char *start = mBuf.c_str(); + const char *cur = start; + const char *temp = strstr(cur, sep); + + while(temp != NULL) + { + // Skip past the marker we just found + cur = temp + strlen(sep); + + // and try to find another + temp = strstr(cur, sep); + } + + // If there's more than one entry in the log file, strip all but the last one. + if(cur != start) + { + mBuf.erase(0, cur - start); + } + } + + mIsValid = TRUE; + delete[] buf; +} + +LLString LLFileEncoder::encodeURL(const S32 max_length) +{ + LLString result = mFormname; + result.append("="); + + S32 i = 0; + + if (max_length) + { + if (mBuf.size() > max_length) + { + i = mBuf.size() - max_length; + } + } + +#if 0 + // Plain text version for debugging + result.append(mBuf); +#else + // Not using LLString because of bad performance issues + S32 buf_size = mBuf.size(); + S32 url_buf_size = 3*mBuf.size() + 1; + char *url_buf = new char[url_buf_size]; + + S32 cur_pos = 0; + for (; i < buf_size; i++) + { + sprintf(url_buf + cur_pos, "%%%02x", mBuf[i]); + cur_pos += 3; + } + url_buf[i*3] = 0; + + result.append(url_buf); + delete[] url_buf; +#endif + return result; +} + +LLString encode_string(const char *formname, const LLString &str) +{ + LLString result = formname; + result.append("="); + // Not using LLString because of bad performance issues + S32 buf_size = str.size(); + S32 url_buf_size = 3*str.size() + 1; + char *url_buf = new char[url_buf_size]; + + S32 cur_pos = 0; + S32 i; + for (i = 0; i < buf_size; i++) + { + sprintf(url_buf + cur_pos, "%%%02x", str[i]); + cur_pos += 3; + } + url_buf[i*3] = 0; + + result.append(url_buf); + delete[] url_buf; + return result; +} -- cgit v1.2.3