mirror of https://gitee.com/bigwinds/arangodb
progress report and windows minidump
This commit is contained in:
parent
190dd674d8
commit
6248f419f1
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ class ArangoGlobalContext {
|
|||
void maskAllSignals();
|
||||
void unmaskStandardSignals();
|
||||
void runStartupChecks();
|
||||
void tempPathAvailable();
|
||||
|
||||
private:
|
||||
std::string _binaryName;
|
||||
|
|
Loading…
Reference in New Issue