1
0
Fork 0

progress report and windows minidump

This commit is contained in:
Frank Celler 2016-04-28 18:48:28 +02:00
parent 190dd674d8
commit 6248f419f1
5 changed files with 165 additions and 37 deletions

View File

@ -146,10 +146,11 @@ bool ApplicationServer::isRequired(std::string const& name) const {
// signal. after that, it will shutdown all features
void ApplicationServer::run(int argc, char* argv[]) {
LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::run";
// collect options from all features
// in this phase, all features are order-independent
_state = ServerState::IN_COLLECT_OPTIONS;
reportServerProgress(_state);
collectOptions();
// setup dependency, but ignore any failure for now
@ -164,6 +165,7 @@ void ApplicationServer::run(int argc, char* argv[]) {
// validate options of all features
_state = ServerState::IN_VALIDATE_OPTIONS;
reportServerProgress(_state);
validateOptions();
// enable automatic features
@ -181,24 +183,30 @@ void ApplicationServer::run(int argc, char* argv[]) {
// if they want other features to access them, or if they want to access
// these files with dropped privileges
_state = ServerState::IN_PREPARE;
reportServerProgress(_state);
prepare();
// permanently drop the privileges
dropPrivilegesPermanently();
// start features. now features are allowed to start threads, write files etc.
_state = ServerState::IN_START;
reportServerProgress(_state);
start();
// wait until we get signaled the shutdown request
_state = ServerState::IN_WAIT;
reportServerProgress(_state);
wait();
// stop all features
_state = ServerState::IN_STOP;
reportServerProgress(_state);
stop();
// stopped
_state = ServerState::STOPPED;
reportServerProgress(_state);
}
// signal the server to shut down
@ -253,6 +261,7 @@ void ApplicationServer::collectOptions() {
apply([this](ApplicationFeature* feature) {
LOG_TOPIC(TRACE, Logger::STARTUP) << feature->name() << "::loadOptions";
feature->collectOptions(_options);
reportFeatureProgress(_state, feature->name());
}, true);
}
@ -302,11 +311,13 @@ void ApplicationServer::parseOptions(int argc, char* argv[]) {
void ApplicationServer::validateOptions() {
LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::validateOptions";
for (auto it = _orderedFeatures.begin(); it != _orderedFeatures.end(); ++it) {
if ((*it)->isEnabled()) {
LOG_TOPIC(TRACE, Logger::STARTUP) << (*it)->name() << "::validateOptions";
(*it)->validateOptions(_options);
(*it)->state(FeatureState::VALIDATED);
for (auto feature : _orderedFeatures) {
if (feature->isEnabled()) {
LOG_TOPIC(TRACE, Logger::STARTUP) << feature->name()
<< "::validateOptions";
feature->validateOptions(_options);
feature->state(FeatureState::VALIDATED);
reportFeatureProgress(_state, feature->name());
}
}
}
@ -383,7 +394,7 @@ void ApplicationServer::setupDependencies(bool failOnMissing) {
}
insert = j - 1;
}
if (insert != i ){
if (insert != i) {
for (size_t j = i; j > insert; --j) {
features[j] = features[j - 1];
}
@ -437,9 +448,9 @@ void ApplicationServer::prepare() {
// we start with elevated privileges
bool privilegesElevated = true;
for (auto it = _orderedFeatures.begin(); it != _orderedFeatures.end(); ++it) {
if ((*it)->isEnabled()) {
bool const requiresElevated = (*it)->requiresElevatedPrivileges();
for (auto feature : _orderedFeatures) {
if (feature->isEnabled()) {
bool const requiresElevated = feature->requiresElevatedPrivileges();
if (requiresElevated != privilegesElevated) {
// must change privileges for the feature
@ -453,9 +464,9 @@ void ApplicationServer::prepare() {
}
try {
LOG_TOPIC(TRACE, Logger::STARTUP) << (*it)->name() << "::prepare";
(*it)->prepare();
(*it)->state(FeatureState::PREPARED);
LOG_TOPIC(TRACE, Logger::STARTUP) << feature->name() << "::prepare";
feature->prepare();
feature->state(FeatureState::PREPARED);
} catch (...) {
// restore original privileges
if (!privilegesElevated) {
@ -463,6 +474,8 @@ void ApplicationServer::prepare() {
}
throw;
}
reportFeatureProgress(_state, feature->name());
}
}
}
@ -470,21 +483,22 @@ void ApplicationServer::prepare() {
void ApplicationServer::start() {
LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::start";
for (auto it = _orderedFeatures.begin(); it != _orderedFeatures.end(); ++it) {
LOG_TOPIC(TRACE, Logger::STARTUP) << (*it)->name() << "::start";
(*it)->start();
(*it)->state(FeatureState::STARTED);
for (auto feature : _orderedFeatures) {
LOG_TOPIC(TRACE, Logger::STARTUP) << feature->name() << "::start";
feature->start();
feature->state(FeatureState::STARTED);
reportFeatureProgress(_state, feature->name());
}
}
void ApplicationServer::stop() {
LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::stop";
for (auto it = _orderedFeatures.rbegin(); it != _orderedFeatures.rend();
++it) {
LOG_TOPIC(TRACE, Logger::STARTUP) << (*it)->name() << "::stop";
(*it)->stop();
(*it)->state(FeatureState::STOPPED);
for (auto feature : _orderedFeatures) {
LOG_TOPIC(TRACE, Logger::STARTUP) << feature->name() << "::stop";
feature->stop();
feature->state(FeatureState::STOPPED);
reportFeatureProgress(_state, feature->name());
}
}
@ -538,3 +552,16 @@ void ApplicationServer::dropPrivilegesPermanently() {
_privilegesDropped = true;
}
void ApplicationServer::reportServerProgress(ServerState state) {
for (auto reporter : _progressReports) {
reporter._state(state);
}
}
void ApplicationServer::reportFeatureProgress(ServerState state,
std::string const& name) {
for (auto reporter : _progressReports) {
reporter._feature(state, name);
}
}

View File

@ -36,6 +36,23 @@ class ProgramOptions;
namespace application_features {
class ApplicationFeature;
enum class ServerState {
UNINITIALIZED,
IN_COLLECT_OPTIONS,
IN_VALIDATE_OPTIONS,
IN_PREPARE,
IN_START,
IN_WAIT,
IN_STOP,
STOPPED
};
class ProgressHandler {
public:
std::function<void(ServerState)> _state;
std::function<void(ServerState, std::string const& featureName)> _feature;
};
// the following phases exists:
//
// `collectOptions`
@ -91,16 +108,6 @@ class ApplicationServer {
ApplicationServer& operator=(ApplicationServer const&) = delete;
public:
enum class ServerState {
UNINITIALIZED,
IN_COLLECT_OPTIONS,
IN_VALIDATE_OPTIONS,
IN_PREPARE,
IN_START,
IN_STOP,
STOPPED
};
enum class FeatureState {
UNINITIALIZED,
INITIALIZED,
@ -116,6 +123,7 @@ class ApplicationServer {
}
static bool isPrepared() {
return server != nullptr && (server->_state == ServerState::IN_START ||
server->_state == ServerState::IN_WAIT ||
server->_state == ServerState::IN_STOP);
}
@ -189,6 +197,10 @@ class ApplicationServer {
// return the server state
ServerState state() const { return _state; }
void addReporter(ProgressHandler reporter) {
_progressReports.emplace_back(reporter);
}
private:
// look up a feature and return a pointer to it. may be nullptr
static ApplicationFeature* lookupFeature(std::string const&);
@ -244,6 +256,9 @@ class ApplicationServer {
void dropPrivilegesTemporarily();
void dropPrivilegesPermanently();
void reportServerProgress(ServerState);
void reportFeatureProgress(ServerState, std::string const&);
private:
// the current state
ServerState _state = ServerState::UNINITIALIZED;
@ -265,6 +280,9 @@ class ApplicationServer {
// whether or not to dump dependencies
bool _dumpDependencies = false;
// reporter for progress
std::vector<ProgressHandler> _progressReports;
};
}
}

View File

@ -22,6 +22,7 @@
#include "ApplicationFeatures/TempFeature.h"
#include "Basics/ArangoGlobalContext.h"
#include "Basics/files.h"
#include "Logger/Logger.h"
#include "ProgramOptions/ProgramOptions.h"
@ -58,4 +59,11 @@ void TempFeature::start() {
// must be used after drop privileges and be called to set it to avoid raise
// conditions
TRI_GetTempPath();
// signal that the temp path is available
auto context = ArangoGlobalContext::CONTEXT;
if (context != nullptr) {
context->tempPathAvailable();
}
}

View File

@ -22,6 +22,10 @@
#include "ArangoGlobalContext.h"
#ifdef _WIN32
#include <DbgHelp.h>
#endif
#include "Basics/debugging.h"
#include "Basics/files.h"
#include "Logger/LogAppender.h"
@ -30,10 +34,6 @@
using namespace arangodb;
#ifndef _WIN32
static void ReopenLog(int) { LogAppender::reopen(); }
#endif
static void AbortHandler(int signum) {
TRI_PrintBacktrace();
#ifdef _WIN32
@ -44,11 +44,77 @@ static void AbortHandler(int signum) {
#endif
}
#ifndef _WIN32
static void ReopenLog(int) { LogAppender::reopen(); }
#endif
#ifdef _WIN32
static std::string miniDumpFilename = "c:\\arangodpanic.dmp";
LONG CALLBACK unhandledExceptionHandler(EXCEPTION_POINTERS* e) {
#if ARANGODB_ENABLE_BACKTRACE
if ((e != nullptr) && (e->ExceptionRecord != nullptr)) {
LOG_FATAL_WINDOWS("Unhandled exception: %d",
(int)e->ExceptionRecord->ExceptionCode);
} else {
LOG_FATAL_WINDOWS("Unhandled exception without ExceptionCode!");
}
std::string bt;
TRI_GetBacktrace(bt);
std::cerr << bt << std::endl;
LOG_FATAL_WINDOWS(bt.c_str());
HANDLE hFile =
CreateFile(miniDumpFilename.c_str(), GENERIC_WRITE, FILE_SHARE_READ, 0,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (hFile == INVALID_HANDLE_VALUE) {
LOG_FATAL_WINDOWS("could not open minidump file : %lu", GetLastError());
return EXCEPTION_CONTINUE_SEARCH;
}
MINIDUMP_EXCEPTION_INFORMATION exceptionInfo;
exceptionInfo.ThreadId = GetCurrentThreadId();
exceptionInfo.ExceptionPointers = e;
exceptionInfo.ClientPointers = FALSE;
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
MINIDUMP_TYPE(MiniDumpWithIndirectlyReferencedMemory |
MiniDumpScanMemory | MiniDumpWithFullMemory),
e ? &exceptionInfo : nullptr, nullptr, nullptr);
if (hFile) {
CloseHandle(hFile);
hFile = nullptr;
}
LOG_FATAL_WINDOWS("wrote minidump: %s", miniDumpFilename.c_str());
#endif
if ((e != nullptr) && (e->ExceptionRecord != nullptr)) {
LOG_FATAL_WINDOWS("Unhandled exception: %d - will crash now.",
(int)e->ExceptionRecord->ExceptionCode);
} else {
LOG_FATAL_WINDOWS(
"Unhandled exception without ExceptionCode - will crash now.!");
}
return EXCEPTION_CONTINUE_SEARCH;
}
#endif
ArangoGlobalContext* ArangoGlobalContext::CONTEXT = nullptr;
ArangoGlobalContext::ArangoGlobalContext(int argc, char* argv[])
: _binaryName(TRI_BinaryName(argv[0])), _ret(EXIT_FAILURE) {
ADB_WindowsEntryFunction();
#ifdef _WIN32
SetUnhandledExceptionFilter(unhandledExceptionHandler);
#endif
TRIAGENS_REST_INITIALIZE();
CONTEXT = this;
}
@ -184,3 +250,11 @@ void ArangoGlobalContext::runStartupChecks() {
#endif
}
void ArangoGlobalContext::tempPathAvailable() {
#ifdef _WIN32
miniDumpFilename = TRI_GetTempPath();
miniDumpFilename +=
"\\minidump_" + std::to_string(GetCurrentProcessId()) + ".dmp";
#endif
}

View File

@ -42,6 +42,7 @@ class ArangoGlobalContext {
void maskAllSignals();
void unmaskStandardSignals();
void runStartupChecks();
void tempPathAvailable();
private:
std::string _binaryName;