/indra/llcrashlogger/llcrashlogger.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 437 lines · 312 code · 70 blank · 55 comment · 40 complexity · 9f38b1894c00cd545df54a6d2a7c7bb2 MD5 · raw file

  1. /**
  2. * @file llcrashlogger.cpp
  3. * @brief Crash logger implementation
  4. *
  5. * $LicenseInfo:firstyear=2003&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include <cstdio>
  27. #include <cstdlib>
  28. #include <sstream>
  29. #include <map>
  30. #include "llcrashlogger.h"
  31. #include "linden_common.h"
  32. #include "llstring.h"
  33. #include "indra_constants.h" // CRASH_BEHAVIOR_...
  34. #include "llerror.h"
  35. #include "llerrorcontrol.h"
  36. #include "lltimer.h"
  37. #include "lldir.h"
  38. #include "llfile.h"
  39. #include "llsdserialize.h"
  40. #include "lliopipe.h"
  41. #include "llpumpio.h"
  42. #include "llhttpclient.h"
  43. #include "llsdserialize.h"
  44. #include "llproxy.h"
  45. LLPumpIO* gServicePump;
  46. BOOL gBreak = false;
  47. BOOL gSent = false;
  48. class LLCrashLoggerResponder : public LLHTTPClient::Responder
  49. {
  50. public:
  51. LLCrashLoggerResponder()
  52. {
  53. }
  54. virtual void error(U32 status, const std::string& reason)
  55. {
  56. gBreak = true;
  57. }
  58. virtual void result(const LLSD& content)
  59. {
  60. gBreak = true;
  61. gSent = true;
  62. }
  63. };
  64. LLCrashLogger::LLCrashLogger() :
  65. mCrashBehavior(CRASH_BEHAVIOR_ALWAYS_SEND),
  66. mCrashInPreviousExec(false),
  67. mCrashSettings("CrashSettings"),
  68. mSentCrashLogs(false),
  69. mCrashHost("")
  70. {
  71. // Set up generic error handling
  72. setupErrorHandling();
  73. }
  74. LLCrashLogger::~LLCrashLogger()
  75. {
  76. }
  77. // TRIM_SIZE must remain larger than LINE_SEARCH_SIZE.
  78. const int TRIM_SIZE = 128000;
  79. const int LINE_SEARCH_DIST = 500;
  80. const std::string SKIP_TEXT = "\n ...Skipping... \n";
  81. void trimSLLog(std::string& sllog)
  82. {
  83. if(sllog.length() > TRIM_SIZE * 2)
  84. {
  85. std::string::iterator head = sllog.begin() + TRIM_SIZE;
  86. std::string::iterator tail = sllog.begin() + sllog.length() - TRIM_SIZE;
  87. std::string::iterator new_head = std::find(head, head - LINE_SEARCH_DIST, '\n');
  88. if(new_head != head - LINE_SEARCH_DIST)
  89. {
  90. head = new_head;
  91. }
  92. std::string::iterator new_tail = std::find(tail, tail + LINE_SEARCH_DIST, '\n');
  93. if(new_tail != tail + LINE_SEARCH_DIST)
  94. {
  95. tail = new_tail;
  96. }
  97. sllog.erase(head, tail);
  98. sllog.insert(head, SKIP_TEXT.begin(), SKIP_TEXT.end());
  99. }
  100. }
  101. std::string getStartupStateFromLog(std::string& sllog)
  102. {
  103. std::string startup_state = "STATE_FIRST";
  104. std::string startup_token = "Startup state changing from ";
  105. int index = sllog.rfind(startup_token);
  106. if (index < 0 || index + startup_token.length() > sllog.length()) {
  107. return startup_state;
  108. }
  109. // find new line
  110. char cur_char = sllog[index + startup_token.length()];
  111. std::string::size_type newline_loc = index + startup_token.length();
  112. while(cur_char != '\n' && newline_loc < sllog.length())
  113. {
  114. newline_loc++;
  115. cur_char = sllog[newline_loc];
  116. }
  117. // get substring and find location of " to "
  118. std::string state_line = sllog.substr(index, newline_loc - index);
  119. std::string::size_type state_index = state_line.find(" to ");
  120. startup_state = state_line.substr(state_index + 4, state_line.length() - state_index - 4);
  121. return startup_state;
  122. }
  123. void LLCrashLogger::gatherFiles()
  124. {
  125. updateApplication("Gathering logs...");
  126. // Figure out the filename of the debug log
  127. std::string db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log");
  128. std::ifstream debug_log_file(db_file_name.c_str());
  129. // Look for it in the debug_info.log file
  130. if (debug_log_file.is_open())
  131. {
  132. LLSDSerialize::fromXML(mDebugLog, debug_log_file);
  133. mCrashInPreviousExec = mDebugLog["CrashNotHandled"].asBoolean();
  134. mFileMap["SecondLifeLog"] = mDebugLog["SLLog"].asString();
  135. mFileMap["SettingsXml"] = mDebugLog["SettingsFilename"].asString();
  136. if(mDebugLog.has("CAFilename"))
  137. {
  138. LLCurl::setCAFile(mDebugLog["CAFilename"].asString());
  139. }
  140. else
  141. {
  142. LLCurl::setCAFile(gDirUtilp->getCAFile());
  143. }
  144. llinfos << "Using log file from debug log " << mFileMap["SecondLifeLog"] << llendl;
  145. llinfos << "Using settings file from debug log " << mFileMap["SettingsXml"] << llendl;
  146. }
  147. else
  148. {
  149. // Figure out the filename of the second life log
  150. LLCurl::setCAFile(gDirUtilp->getCAFile());
  151. mFileMap["SecondLifeLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log");
  152. mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml");
  153. }
  154. if(mCrashInPreviousExec)
  155. {
  156. // Restarting after freeze.
  157. // Replace the log file ext with .old, since the
  158. // instance that launched this process has overwritten
  159. // SecondLife.log
  160. std::string log_filename = mFileMap["SecondLifeLog"];
  161. log_filename.replace(log_filename.size() - 4, 4, ".old");
  162. mFileMap["SecondLifeLog"] = log_filename;
  163. }
  164. gatherPlatformSpecificFiles();
  165. //Use the debug log to reconstruct the URL to send the crash report to
  166. if(mDebugLog.has("CrashHostUrl"))
  167. {
  168. // Crash log receiver has been manually configured.
  169. mCrashHost = mDebugLog["CrashHostUrl"].asString();
  170. }
  171. else if(mDebugLog.has("CurrentSimHost"))
  172. {
  173. mCrashHost = "https://";
  174. mCrashHost += mDebugLog["CurrentSimHost"].asString();
  175. mCrashHost += ":12043/crash/report";
  176. }
  177. else if(mDebugLog.has("GridName"))
  178. {
  179. // This is a 'little' hacky, but its the best simple solution.
  180. std::string grid_host = mDebugLog["GridName"].asString();
  181. LLStringUtil::toLower(grid_host);
  182. mCrashHost = "https://login.";
  183. mCrashHost += grid_host;
  184. mCrashHost += ".lindenlab.com:12043/crash/report";
  185. }
  186. // Use login servers as the alternate, since they are already load balanced and have a known name
  187. mAltCrashHost = "https://login.agni.lindenlab.com:12043/crash/report";
  188. mCrashInfo["DebugLog"] = mDebugLog;
  189. mFileMap["StatsLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log");
  190. updateApplication("Encoding files...");
  191. for(std::map<std::string, std::string>::iterator itr = mFileMap.begin(); itr != mFileMap.end(); ++itr)
  192. {
  193. std::ifstream f((*itr).second.c_str());
  194. if(!f.is_open())
  195. {
  196. std::cout << "Can't find file " << (*itr).second << std::endl;
  197. continue;
  198. }
  199. std::stringstream s;
  200. s << f.rdbuf();
  201. std::string crash_info = s.str();
  202. if(itr->first == "SecondLifeLog")
  203. {
  204. if(!mCrashInfo["DebugLog"].has("StartupState"))
  205. {
  206. mCrashInfo["DebugLog"]["StartupState"] = getStartupStateFromLog(crash_info);
  207. }
  208. trimSLLog(crash_info);
  209. }
  210. mCrashInfo[(*itr).first] = LLStringFn::strip_invalid_xml(rawstr_to_utf8(crash_info));
  211. }
  212. // Add minidump as binary.
  213. std::string minidump_path = mDebugLog["MinidumpPath"];
  214. if(minidump_path != "")
  215. {
  216. std::ifstream minidump_stream(minidump_path.c_str(), std::ios_base::in | std::ios_base::binary);
  217. if(minidump_stream.is_open())
  218. {
  219. minidump_stream.seekg(0, std::ios::end);
  220. size_t length = minidump_stream.tellg();
  221. minidump_stream.seekg(0, std::ios::beg);
  222. LLSD::Binary data;
  223. data.resize(length);
  224. minidump_stream.read(reinterpret_cast<char *>(&(data[0])),length);
  225. minidump_stream.close();
  226. mCrashInfo["Minidump"] = data;
  227. }
  228. }
  229. mCrashInfo["DebugLog"].erase("MinidumpPath");
  230. }
  231. LLSD LLCrashLogger::constructPostData()
  232. {
  233. LLSD ret;
  234. return mCrashInfo;
  235. }
  236. const char* const CRASH_SETTINGS_FILE = "settings_crash_behavior.xml";
  237. S32 LLCrashLogger::loadCrashBehaviorSetting()
  238. {
  239. // First check user_settings (in the user's home dir)
  240. std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
  241. if (! mCrashSettings.loadFromFile(filename))
  242. {
  243. // Next check app_settings (in the SL program dir)
  244. std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, CRASH_SETTINGS_FILE);
  245. mCrashSettings.loadFromFile(filename);
  246. }
  247. // If we didn't load any files above, this will return the default
  248. S32 value = mCrashSettings.getS32("CrashSubmitBehavior");
  249. // Whatever value we got, make sure it's valid
  250. switch (value)
  251. {
  252. case CRASH_BEHAVIOR_NEVER_SEND:
  253. return CRASH_BEHAVIOR_NEVER_SEND;
  254. case CRASH_BEHAVIOR_ALWAYS_SEND:
  255. return CRASH_BEHAVIOR_ALWAYS_SEND;
  256. }
  257. return CRASH_BEHAVIOR_ASK;
  258. }
  259. bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior)
  260. {
  261. switch (crash_behavior)
  262. {
  263. case CRASH_BEHAVIOR_ASK:
  264. case CRASH_BEHAVIOR_NEVER_SEND:
  265. case CRASH_BEHAVIOR_ALWAYS_SEND:
  266. break;
  267. default:
  268. return false;
  269. }
  270. mCrashSettings.setS32("CrashSubmitBehavior", crash_behavior);
  271. std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
  272. mCrashSettings.saveToFile(filename, FALSE);
  273. return true;
  274. }
  275. bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout)
  276. {
  277. gBreak = false;
  278. for(int i = 0; i < retries; ++i)
  279. {
  280. updateApplication(llformat("%s, try %d...", msg.c_str(), i+1));
  281. LLHTTPClient::post(host, data, new LLCrashLoggerResponder(), timeout);
  282. while(!gBreak)
  283. {
  284. updateApplication(); // No new message, just pump the IO
  285. }
  286. if(gSent)
  287. {
  288. return gSent;
  289. }
  290. }
  291. return gSent;
  292. }
  293. bool LLCrashLogger::sendCrashLogs()
  294. {
  295. gatherFiles();
  296. LLSD post_data;
  297. post_data = constructPostData();
  298. updateApplication("Sending reports...");
  299. std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
  300. "SecondLifeCrashReport");
  301. std::string report_file = dump_path + ".log";
  302. std::ofstream out_file(report_file.c_str());
  303. LLSDSerialize::toPrettyXML(post_data, out_file);
  304. out_file.close();
  305. bool sent = false;
  306. //*TODO: Translate
  307. if(mCrashHost != "")
  308. {
  309. sent = runCrashLogPost(mCrashHost, post_data, std::string("Sending to server"), 3, 5);
  310. }
  311. if(!sent)
  312. {
  313. sent = runCrashLogPost(mAltCrashHost, post_data, std::string("Sending to alternate server"), 3, 5);
  314. }
  315. mSentCrashLogs = sent;
  316. return true;
  317. }
  318. void LLCrashLogger::updateApplication(const std::string& message)
  319. {
  320. gServicePump->pump();
  321. gServicePump->callback();
  322. if (!message.empty()) llinfos << message << llendl;
  323. }
  324. bool LLCrashLogger::init()
  325. {
  326. LLCurl::initClass(false);
  327. // We assume that all the logs we're looking for reside on the current drive
  328. gDirUtilp->initAppDirs("SecondLife");
  329. LLError::initForApplication(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
  330. // Default to the product name "Second Life" (this is overridden by the -name argument)
  331. mProductName = "Second Life";
  332. // Rename current log file to ".old"
  333. std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log.old");
  334. std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log");
  335. LLFile::rename(log_file.c_str(), old_log_file.c_str());
  336. // Set the log file to crashreport.log
  337. LLError::logToFile(log_file);
  338. mCrashSettings.declareS32("CrashSubmitBehavior", CRASH_BEHAVIOR_ALWAYS_SEND,
  339. "Controls behavior when viewer crashes "
  340. "(0 = ask before sending crash report, "
  341. "1 = always send crash report, "
  342. "2 = never send crash report)");
  343. // llinfos << "Loading crash behavior setting" << llendl;
  344. // mCrashBehavior = loadCrashBehaviorSetting();
  345. // If user doesn't want to send, bail out
  346. if (mCrashBehavior == CRASH_BEHAVIOR_NEVER_SEND)
  347. {
  348. llinfos << "Crash behavior is never_send, quitting" << llendl;
  349. return false;
  350. }
  351. gServicePump = new LLPumpIO(gAPRPoolp);
  352. gServicePump->prime(gAPRPoolp);
  353. LLHTTPClient::setPump(*gServicePump);
  354. //If we've opened the crash logger, assume we can delete the marker file if it exists
  355. if( gDirUtilp )
  356. {
  357. std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
  358. "SecondLife.exec_marker");
  359. LLAPRFile::remove( marker_file );
  360. }
  361. return true;
  362. }
  363. // For cleanup code common to all platforms.
  364. void LLCrashLogger::commonCleanup()
  365. {
  366. LLProxy::cleanupClass();
  367. }