static void ConvertProcessStateToJSON(const ProcessState& aProcessState, Json::Value& aRoot) { // We use this map to get the index of a module when listed by address OrderedModulesMap orderedModules; // Crash info Json::Value crashInfo; int requestingThread = aProcessState.requesting_thread(); if (aProcessState.crashed()) { crashInfo["type"] = aProcessState.crash_reason(); crashInfo["address"] = ToHex(aProcessState.crash_address()); if (requestingThread != -1) { crashInfo["crashing_thread"] = requestingThread; } } else { crashInfo["type"] = Json::Value(Json::nullValue); // Add assertion info, if available string assertion = aProcessState.assertion(); if (!assertion.empty()) { crashInfo["assertion"] = assertion; } } aRoot["crash_info"] = crashInfo; // Modules Json::Value modules(Json::arrayValue); int mainModule = ConvertModulesToJSON(aProcessState, orderedModules, modules); if (mainModule != -1) { aRoot["main_module"] = mainModule; } aRoot["modules"] = modules; // Threads Json::Value threads(Json::arrayValue); int threadCount = aProcessState.threads()->size(); for (int threadIndex = 0; threadIndex < threadCount; ++threadIndex) { Json::Value thread; Json::Value stack(Json::arrayValue); const CallStack* rawStack = aProcessState.threads()->at(threadIndex); ConvertStackToJSON(aProcessState, orderedModules, rawStack, stack); thread["frames"] = stack; threads.append(thread); } aRoot["threads"] = threads; }
int main(int argc, char *argv[]) { BPLOG_INIT(&argc, &argv); if (argc <= 1) { std::cerr << "Usage: " << argv[0] << " <config file path>" << std::endl; return 1; } std::ifstream configFile; configFile.open(argv[1]); if (!configFile.is_open()) { std::cerr << "Failed to open config file for reading: " << argv[1] << std::endl; return 1; } json config; configFile >> config; configFile.close(); const auto &beanstalkHost = config["beanstalk"]["host"]; const auto &beanstalkPort = config["beanstalk"]["port"]; const auto &beanstalkQueue = config["beanstalk"]["queue"]; Client queue(beanstalkHost, beanstalkPort); queue.watch(beanstalkQueue); BPLOG(INFO) << "Connected to beanstalkd @ " << beanstalkHost << ":" << beanstalkPort << " (queue: " << beanstalkQueue << ")"; const auto &mysqlHost = config["mysql"]["host"]; const auto &mysqlPort = config["mysql"]["port"]; const auto &mysqlUser = config["mysql"]["user"]; const auto &mysqlPassword = config["mysql"]["password"]; const auto &mysqlDatabase = config["mysql"]["database"]; Connection mysql( mysqlDatabase.get<string>().c_str(), mysqlHost.is_null() ? nullptr :mysqlHost.get<string>().c_str(), mysqlUser.is_null() ? nullptr :mysqlUser.get<string>().c_str(), mysqlPassword.is_null() ? nullptr :mysqlPassword.get<string>().c_str(), mysqlPort); BPLOG(INFO) << "Connected to MySQL @ " << mysql.ipc_info() << " (database: " << mysqlDatabase << ")"; const std::vector<string> &symbolPaths = config["breakpad"]["symbols"]; CompressedSymbolSupplier symbolSupplier(symbolPaths); const string &minidumpDirectory = config["breakpad"]["minidumps"]; Job job; while (true) { if (!queue.reserve(job) || !job) { BPLOG(ERROR) << "Failed to reserve job."; break; } const json body = json::parse(job.body()); const string &id = body["id"]; BPLOG(INFO) << id << " " << body["ip"] << " " << body["owner"]; auto start = std::chrono::steady_clock::now(); // Create this inside the loop to avoid keeping a global symbol cache. RepoSourceLineResolver resolver; MinidumpProcessor minidumpProcessor(&symbolSupplier, &resolver); std::string minidumpFile = minidumpDirectory + "/" + id.substr(0, 2) + "/" + id + ".dmp"; ProcessState processState; ProcessResult processResult = minidumpProcessor.Process(minidumpFile, &processState); if (processResult == google_breakpad::PROCESS_ERROR_MINIDUMP_NOT_FOUND) { queue.del(job); continue; } if (processResult != google_breakpad::PROCESS_OK) { BPLOG(ERROR) << "MinidumpProcessor::Process failed"; queue.bury(job); continue; } //////////////////////////////////////////////////////////////////////// #if 0 // If there is no requesting thread, print the main thread. int requestingThread = processState.requesting_thread(); if (requestingThread == -1) { requestingThread = 0; } const CallStack *stack = processState.threads()->at(requestingThread); if (!stack) { BPLOG(ERROR) << "Missing stack for thread " << requestingThread; queue.bury(job); continue; } int frameCount = stack->frames()->size(); for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { const StackFrame *frame = stack->frames()->at(frameIndex); std::cout << frameIndex << ": "; std::cout << std::hex; if (frame->module) { std::cout << PathnameStripper::File(frame->module->code_file()); if (!frame->function_name.empty()) { std::cout << "!" << frame->function_name; if (!frame->source_file_name.empty()) { std::cout << " [" << PathnameStripper::File(frame->source_file_name) << ":" << std::dec << frame->source_line << std::hex << " + 0x" << frame->ReturnAddress() - frame->source_line_base << "]"; } else { std::cout << " + 0x" << frame->ReturnAddress() - frame->function_base; } } else { std::cout << " + 0x" << frame->ReturnAddress() - frame->module->base_address(); } } else { std::cout << "0x" << frame->ReturnAddress(); } std::cout << std::dec; std::string repoUrl = resolver.LookupRepoUrl(frame); if (!repoUrl.empty()) { std::cout << " <" << repoUrl << ">"; } std::cout << std::endl; } #endif //////////////////////////////////////////////////////////////////////// //__asm__("int3"); auto end = std::chrono::steady_clock::now(); double elapsedSeconds = ((end - start).count()) * std::chrono::steady_clock::period::num / static_cast<double>(std::chrono::steady_clock::period::den); //std::cout << "Processing Time: " << elapsedSeconds << "s" << std::endl; json serialized = SerializeProcessState(&processState, &resolver); serialized["id"] = id; if (!body["ip"].is_null()) serialized["ip"] = body["ip"]; if (!body["owner"].is_null()) serialized["owner"] = body["owner"]; serialized["processing_time"] = elapsedSeconds; std::cout << serialized << std::endl; queue.del(job); bool processSingleJob = config["processSingleJob"]; if (processSingleJob) { break; } } return 0; }