llcrashlogger.cpp

Go to the documentation of this file.
00001 
00031 #include <cstdio>
00032 #include <cstdlib>
00033 #include <sstream>
00034 #include <map>
00035 
00036 #include "llcrashlogger.h"
00037 #include "linden_common.h"
00038 #include "llstring.h"
00039 #include "indra_constants.h"    // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
00040 #include "llerror.h"
00041 #include "lltimer.h"
00042 #include "lldir.h"
00043 #include "llsdserialize.h"
00044 #include "lliopipe.h"
00045 #include "llpumpio.h"
00046 #include "llhttpclient.h"
00047 #include "llsdserialize.h"
00048 
00049 LLPumpIO* gServicePump;
00050 BOOL gBreak = false;
00051 BOOL gSent = false;
00052 
00053 class LLCrashLoggerResponder : public LLHTTPClient::Responder
00054 {
00055 public:
00056         LLCrashLoggerResponder() 
00057         {
00058         }
00059 
00060         virtual void error(U32 status, const std::string& reason)
00061         {
00062                 gBreak = true;          
00063         }
00064 
00065         virtual void result(const LLSD& content)
00066         {       
00067                 gBreak = true;
00068                 gSent = true;
00069         }
00070 };
00071 
00072 bool LLCrashLoggerText::mainLoop()
00073 {
00074         std::cout << "Entering main loop" << std::endl;
00075         sendCrashLogs();
00076         return true;    
00077 }
00078 
00079 void LLCrashLoggerText::updateApplication(LLString message)
00080 {
00081         LLCrashLogger::updateApplication(message);
00082         std::cout << message << std::endl;
00083 }
00084 
00085 LLCrashLogger::LLCrashLogger() :
00086         mCrashBehavior(CRASH_BEHAVIOR_ASK),
00087         mCrashInPreviousExec(false),
00088         mSentCrashLogs(false),
00089         mCrashHost("")
00090 {
00091 
00092 }
00093 
00094 LLCrashLogger::~LLCrashLogger()
00095 {
00096 
00097 }
00098 
00099 void LLCrashLogger::gatherFiles()
00100 {
00101 
00102         /*
00103         //TODO:This function needs to be reimplemented somewhere in here...
00104         if(!previous_crash && is_crash_log)
00105         {
00106                 // Make sure the file isn't too old.
00107                 double age = difftime(gLaunchTime, stat_data.st_mtimespec.tv_sec);
00108                 
00109                 //                      llinfos << "age is " << age << llendl;
00110                 
00111                 if(age > 60.0)
00112                 {
00113                                 // The file was last modified more than 60 seconds before the crash reporter was launched.  Assume it's stale.
00114                         llwarns << "File " << mFilename << " is too old!" << llendl;
00115                         return;
00116                 }
00117         }
00118         */
00119 
00120         updateApplication("Gathering logs...");
00121 
00122         // Figure out the filename of the debug log
00123         std::string db_file_name = gDirUtilp->getExpandedFilename(
00124                 LL_PATH_LOGS,
00125                 "debug_info.log");
00126         llifstream debug_log_file(db_file_name.c_str());
00127 
00128         // Look for it in the debug_info.log file
00129         if (debug_log_file.is_open())
00130         {               
00131                 LLSDSerialize::fromXML(mDebugLog, debug_log_file);
00132                 mFileMap["SecondLifeLog"] = mDebugLog["SLLog"].asString();
00133                 mFileMap["SettingsXml"] = mDebugLog["SettingsFilename"].asString();
00134                 LLCurl::setCAFile(mDebugLog["CAFilename"].asString());
00135                 llinfos << "Using log file from debug log " << mFileMap["SecondLifeLog"] << llendl;
00136                 llinfos << "Using settings file from debug log " << mFileMap["SettingsXml"] << llendl;
00137         }
00138         else
00139         {
00140                 // Figure out the filename of the second life log
00141                 LLCurl::setCAFile(gDirUtilp->getCAFile());
00142                 mFileMap["SecondLifeLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log");
00143                 mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml");
00144         }
00145 
00146         gatherPlatformSpecificFiles();
00147 
00148         //Use the debug log to reconstruct the URL to send the crash report to
00149         if(mDebugLog.has("CurrentSimHost"))
00150         {
00151                 mCrashHost = "https://";
00152                 mCrashHost += mDebugLog["CurrentSimHost"].asString();
00153                 mCrashHost += ":12043/crash/report";
00154         }
00155         // Use login servers as the alternate, since they are already load balanced and have a known name
00156         mAltCrashHost = "https://login.agni.lindenlab.com:12043/crash/report";
00157 
00158         mCrashInfo["DebugLog"] = mDebugLog;
00159         mFileMap["StatsLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log");
00160         mFileMap["StackTrace"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
00161         
00162         updateApplication("Encoding files...");
00163 
00164         for(std::map<LLString, LLString>::iterator itr = mFileMap.begin(); itr != mFileMap.end(); ++itr)
00165         {
00166                 std::ifstream f((*itr).second.c_str());
00167                 if(!f.is_open())
00168                 {
00169                         std::cout << "Can't find file " << (*itr).second.c_str() << std::endl;
00170                         continue;
00171                 }
00172                 std::stringstream s;
00173                 s << f.rdbuf();
00174                 mCrashInfo[(*itr).first] = s.str();
00175         }
00176 }
00177 
00178 LLSD LLCrashLogger::constructPostData()
00179 {
00180         LLSD ret;
00181 
00182         if(mCrashInPreviousExec)
00183         {
00184                 mCrashInfo["CrashInPreviousExecution"] = "Y";
00185         }
00186 
00187         return mCrashInfo;
00188 }
00189 
00190 S32 LLCrashLogger::loadCrashBehaviorSetting()
00191 {
00192         std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
00193 
00194         mCrashSettings.loadFromFile(filename);
00195                 
00196         S32 value = mCrashSettings.getS32(CRASH_BEHAVIOR_SETTING);
00197         
00198         if (value < CRASH_BEHAVIOR_ASK || CRASH_BEHAVIOR_NEVER_SEND < value) return CRASH_BEHAVIOR_ASK;
00199 
00200         return value;
00201 }
00202 
00203 bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior)
00204 {
00205         if (crash_behavior != CRASH_BEHAVIOR_ASK && crash_behavior != CRASH_BEHAVIOR_ALWAYS_SEND) return false;
00206 
00207         mCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, crash_behavior);
00208         std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
00209 
00210         mCrashSettings.saveToFile(filename, FALSE);
00211 
00212         return true;
00213 }
00214 
00215 bool LLCrashLogger::runCrashLogPost(LLString host, LLSD data, LLString msg, int retries, int timeout)
00216 {
00217         gBreak = false;
00218         LLString status_message;
00219         for(int i = 0; i < retries; ++i)
00220         {
00221                 status_message = llformat("%s, try %d...", msg.c_str(), i+1);
00222                 LLHTTPClient::post(host, data, new LLCrashLoggerResponder(), timeout);
00223                 while(!gBreak)
00224                 {
00225                         updateApplication(status_message);
00226                 }
00227                 if(gSent)
00228                 {
00229                         return gSent;
00230                 }
00231         }
00232         return gSent;
00233 }
00234 
00235 bool LLCrashLogger::sendCrashLogs()
00236 {
00237         gatherFiles();
00238 
00239         LLSD post_data;
00240         post_data = constructPostData();
00241 
00242         updateApplication("Sending reports...");
00243 
00244         std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
00245                                                                                                                            "SecondLifeCrashReport");
00246         std::string report_file = dump_path + ".log";
00247 
00248         std::ofstream out_file(report_file.c_str());
00249         LLSDSerialize::toPrettyXML(post_data, out_file);
00250         out_file.close();
00251 
00252         bool sent = false;
00253 
00254         if(mCrashHost != "")
00255         {
00256                 sent = runCrashLogPost(mCrashHost, post_data, "Sending to server", 3, 5);
00257         }
00258 
00259         if(!sent)
00260         {
00261                 sent = runCrashLogPost(mAltCrashHost, post_data, "Sending to alternate server", 3, 5);
00262         }
00263         
00264         mSentCrashLogs = sent;
00265 
00266         return true;
00267 }
00268 
00269 void LLCrashLogger::updateApplication(LLString message)
00270 {
00271         gServicePump->pump();
00272     gServicePump->callback();
00273 }
00274 
00275 bool LLCrashLogger::init()
00276 {
00277         // We assume that all the logs we're looking for reside on the current drive
00278         gDirUtilp->initAppDirs("SecondLife");
00279 
00280         // Default to the product name "Second Life" (this is overridden by the -name argument)
00281         mProductName = "Second Life";
00282         
00283         mCrashSettings.declareS32(CRASH_BEHAVIOR_SETTING, CRASH_BEHAVIOR_ASK, "Controls behavior when viewer crashes "
00284                 "(0 = ask before sending crash report, 1 = always send crash report, 2 = never send crash report)");
00285 
00286         llinfos << "Loading crash behavior setting" << llendl;
00287         mCrashBehavior = loadCrashBehaviorSetting();
00288 
00289         //Run through command line options
00290         if(getOption("previous").isDefined())
00291         {
00292                 llinfos << "Previous execution did not remove SecondLife.exec_marker" << llendl;
00293                 mCrashInPreviousExec = TRUE;
00294         }
00295 
00296         if(getOption("dialog").isDefined())
00297         {
00298                 llinfos << "Show the user dialog" << llendl;
00299                 mCrashBehavior = CRASH_BEHAVIOR_ASK;
00300         }
00301         
00302         LLSD name = getOption("name");
00303         if(name.isDefined())
00304         {       
00305                 mProductName = name.asString();
00306         }
00307 
00308         // If user doesn't want to send, bail out
00309         if (mCrashBehavior == CRASH_BEHAVIOR_NEVER_SEND)
00310         {
00311                 llinfos << "Crash behavior is never_send, quitting" << llendl;
00312                 return false;
00313         }
00314 
00315         gServicePump = new LLPumpIO(gAPRPoolp);
00316         gServicePump->prime(gAPRPoolp);
00317         LLHTTPClient::setPump(*gServicePump);
00318 
00319         //If we've opened the crash logger, assume we can delete the marker file if it exists   
00320         if( gDirUtilp )
00321         {
00322                 LLString marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.exec_marker");
00323                 ll_apr_file_remove( marker_file );
00324         }
00325         
00326         return true;
00327 }

Generated on Fri May 16 08:32:10 2008 for SecondLife by  doxygen 1.5.5