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"
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
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120 updateApplication("Gathering logs...");
00121
00122
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
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
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
00149 if(mDebugLog.has("CurrentSimHost"))
00150 {
00151 mCrashHost = "https://";
00152 mCrashHost += mDebugLog["CurrentSimHost"].asString();
00153 mCrashHost += ":12043/crash/report";
00154 }
00155
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
00278 gDirUtilp->initAppDirs("SecondLife");
00279
00280
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
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
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
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 }