Your IP : 18.226.180.253
/*
* Phusion Passenger - https://www.phusionpassenger.com/
* Copyright (c) 2017-2018 Phusion Holding B.V.
*
* "Passenger", "Phusion Passenger" and "Union Station" are registered
* trademarks of Phusion Holding B.V.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef _PASSENGER_SPAWNING_KIT_HANDSHAKE_JOURNEY_H_
#define _PASSENGER_SPAWNING_KIT_HANDSHAKE_JOURNEY_H_
#include <map>
#include <utility>
#include <oxt/macros.hpp>
#include <oxt/backtrace.hpp>
#include <jsoncpp/json.h>
#include <LoggingKit/LoggingKit.h>
#include <StaticString.h>
#include <SystemTools/SystemTime.h>
#include <JsonTools/JsonUtils.h>
#include <StrIntTools/StrIntUtils.h>
namespace Passenger {
namespace SpawningKit {
using namespace std;
/**
* As explained in README.md, there are three possible journeys,
* although each journey can have small variations (based on whether
* a wrapper is used or not).
*/
enum JourneyType {
SPAWN_DIRECTLY,
START_PRELOADER,
SPAWN_THROUGH_PRELOADER
};
enum JourneyStep {
// Steps in Passenger Core / SpawningKit
SPAWNING_KIT_PREPARATION,
SPAWNING_KIT_FORK_SUBPROCESS,
SPAWNING_KIT_CONNECT_TO_PRELOADER,
SPAWNING_KIT_SEND_COMMAND_TO_PRELOADER,
SPAWNING_KIT_READ_RESPONSE_FROM_PRELOADER,
SPAWNING_KIT_PARSE_RESPONSE_FROM_PRELOADER,
SPAWNING_KIT_PROCESS_RESPONSE_FROM_PRELOADER,
SPAWNING_KIT_HANDSHAKE_PERFORM,
SPAWNING_KIT_FINISH,
// Steps in preloader (when spawning a worker process)
PRELOADER_PREPARATION,
PRELOADER_FORK_SUBPROCESS,
PRELOADER_SEND_RESPONSE,
PRELOADER_FINISH,
// Steps in subprocess
SUBPROCESS_BEFORE_FIRST_EXEC,
SUBPROCESS_SPAWN_ENV_SETUPPER_BEFORE_SHELL,
SUBPROCESS_OS_SHELL,
SUBPROCESS_SPAWN_ENV_SETUPPER_AFTER_SHELL,
SUBPROCESS_EXEC_WRAPPER,
SUBPROCESS_WRAPPER_PREPARATION,
SUBPROCESS_APP_LOAD_OR_EXEC,
SUBPROCESS_PREPARE_AFTER_FORKING_FROM_PRELOADER,
SUBPROCESS_LISTEN,
SUBPROCESS_FINISH,
// Other
UNKNOWN_JOURNEY_STEP
};
enum JourneyStepState {
/**
* This step has not started yet. Will be visualized with an empty
* placeholder.
*/
STEP_NOT_STARTED,
/**
* This step is currently in progress. Will be visualized with a spinner.
*/
STEP_IN_PROGRESS,
/**
* This step has already been performed successfully. Will be
* visualized with a green tick.
*/
STEP_PERFORMED,
/**
* This step has failed. Will be visualized with a red mark.
*/
STEP_ERRORED,
UNKNOWN_JOURNEY_STEP_STATE
};
inline OXT_PURE StaticString journeyTypeToString(JourneyType type);
inline OXT_PURE StaticString journeyStepToString(JourneyStep step);
inline OXT_PURE string journeyStepToStringLowerCase(JourneyStep step);
inline OXT_PURE StaticString journeyStepStateToString(JourneyStepState state);
inline OXT_PURE JourneyStepState stringToJourneyStepState(const StaticString &value);
inline OXT_PURE JourneyStep getFirstCoreJourneyStep() { return SPAWNING_KIT_PREPARATION; }
inline OXT_PURE JourneyStep getLastCoreJourneyStep() { return SPAWNING_KIT_FINISH; }
inline OXT_PURE JourneyStep getFirstPreloaderJourneyStep() { return PRELOADER_PREPARATION; }
inline OXT_PURE JourneyStep getLastPreloaderJourneyStep() { return PRELOADER_FINISH; }
inline OXT_PURE JourneyStep getFirstSubprocessJourneyStep() { return SUBPROCESS_BEFORE_FIRST_EXEC; }
inline OXT_PURE JourneyStep getLastSubprocessJourneyStep() { return SUBPROCESS_FINISH; }
class JourneyStepInfo {
private:
MonotonicTimeUsec getEndTime(const JourneyStepInfo *nextStepInfo) const {
if (nextStepInfo != NULL && nextStepInfo->beginTime != 0) {
return nextStepInfo->beginTime;
} else {
return endTime;
}
}
public:
JourneyStep step, nextStep;
JourneyStepState state;
MonotonicTimeUsec beginTime;
MonotonicTimeUsec endTime;
JourneyStepInfo(JourneyStep _step, JourneyStepState _state = STEP_NOT_STARTED)
: step(_step),
nextStep(UNKNOWN_JOURNEY_STEP),
state(_state),
beginTime(0),
endTime(0)
{ }
unsigned long long usecDuration(const JourneyStepInfo *nextStepInfo) const {
if (getEndTime(nextStepInfo) >= beginTime) {
return getEndTime(nextStepInfo) - beginTime;
} else {
return 0;
}
}
Json::Value inspectAsJson(const JourneyStepInfo *nextStepInfo, MonotonicTimeUsec monoNow,
unsigned long long now) const
{
Json::Value doc;
doc["state"] = journeyStepStateToString(state).toString();
if (beginTime != 0) {
doc["begin_time"] = monoTimeToJson(beginTime, monoNow, now);
}
if (endTime != 0) {
doc["end_time"] = monoTimeToJson(endTime, monoNow, now);
doc["duration"] = usecDuration(nextStepInfo) / 1000000.0;
}
return doc;
}
};
/**
* For an introduction see README.md, sections:
*
* - "The Journey class"
* - "Subprocess journey logging"
*/
class Journey {
public:
typedef map<JourneyStep, JourneyStepInfo> Map;
private:
JourneyType type;
bool usingWrapper;
Map steps;
void insertStep(JourneyStep step, bool first = false) {
steps.insert(make_pair(step, JourneyStepInfo(step)));
if (!first) {
Map::iterator prev = steps.end();
prev--;
prev--;
prev->second.nextStep = step;
}
}
void fillInStepsForSpawnDirectlyJourney() {
insertStep(SPAWNING_KIT_PREPARATION, true);
insertStep(SPAWNING_KIT_FORK_SUBPROCESS);
insertStep(SPAWNING_KIT_HANDSHAKE_PERFORM);
insertStep(SPAWNING_KIT_FINISH);
insertStep(SUBPROCESS_BEFORE_FIRST_EXEC, true);
insertStep(SUBPROCESS_SPAWN_ENV_SETUPPER_BEFORE_SHELL);
insertStep(SUBPROCESS_OS_SHELL);
insertStep(SUBPROCESS_SPAWN_ENV_SETUPPER_AFTER_SHELL);
if (usingWrapper) {
insertStep(SUBPROCESS_EXEC_WRAPPER);
insertStep(SUBPROCESS_WRAPPER_PREPARATION);
}
insertStep(SUBPROCESS_APP_LOAD_OR_EXEC);
insertStep(SUBPROCESS_LISTEN);
insertStep(SUBPROCESS_FINISH);
}
void fillInStepsForPreloaderStartJourney() {
insertStep(SPAWNING_KIT_PREPARATION, true);
insertStep(SPAWNING_KIT_FORK_SUBPROCESS);
insertStep(SPAWNING_KIT_HANDSHAKE_PERFORM);
insertStep(SPAWNING_KIT_FINISH);
insertStep(SUBPROCESS_BEFORE_FIRST_EXEC, true);
insertStep(SUBPROCESS_SPAWN_ENV_SETUPPER_BEFORE_SHELL);
insertStep(SUBPROCESS_OS_SHELL);
insertStep(SUBPROCESS_SPAWN_ENV_SETUPPER_AFTER_SHELL);
if (usingWrapper) {
insertStep(SUBPROCESS_EXEC_WRAPPER);
insertStep(SUBPROCESS_WRAPPER_PREPARATION);
}
insertStep(SUBPROCESS_APP_LOAD_OR_EXEC);
insertStep(SUBPROCESS_LISTEN);
insertStep(SUBPROCESS_FINISH);
}
void fillInStepsForSpawnThroughPreloaderJourney() {
insertStep(SPAWNING_KIT_PREPARATION, true);
insertStep(SPAWNING_KIT_CONNECT_TO_PRELOADER);
insertStep(SPAWNING_KIT_SEND_COMMAND_TO_PRELOADER);
insertStep(SPAWNING_KIT_READ_RESPONSE_FROM_PRELOADER);
insertStep(SPAWNING_KIT_PARSE_RESPONSE_FROM_PRELOADER);
insertStep(SPAWNING_KIT_PROCESS_RESPONSE_FROM_PRELOADER);
insertStep(SPAWNING_KIT_HANDSHAKE_PERFORM);
insertStep(SPAWNING_KIT_FINISH);
insertStep(PRELOADER_PREPARATION, true);
insertStep(PRELOADER_FORK_SUBPROCESS);
insertStep(PRELOADER_SEND_RESPONSE);
insertStep(PRELOADER_FINISH);
insertStep(SUBPROCESS_PREPARE_AFTER_FORKING_FROM_PRELOADER, true);
insertStep(SUBPROCESS_LISTEN);
insertStep(SUBPROCESS_FINISH);
}
JourneyStepInfo &getStepInfoMutable(JourneyStep step) {
Map::iterator it = steps.find(step);
if (it == steps.end()) {
throw RuntimeException("Invalid step " + journeyStepToString(step));
}
return it->second;
}
public:
Journey(JourneyType _type, bool _usingWrapper)
: type(_type),
usingWrapper(_usingWrapper)
{
switch (_type) {
case SPAWN_DIRECTLY:
fillInStepsForSpawnDirectlyJourney();
break;
case START_PRELOADER:
fillInStepsForPreloaderStartJourney();
break;
case SPAWN_THROUGH_PRELOADER:
fillInStepsForSpawnThroughPreloaderJourney();
break;
default:
P_BUG("Unknown journey type " << toString((int) _type));
break;
}
}
JourneyType getType() const {
return type;
}
bool isUsingWrapper() const {
return usingWrapper;
}
bool hasStep(JourneyStep step) const {
Map::const_iterator it = steps.find(step);
return it != steps.end();
}
const JourneyStepInfo &getStepInfo(JourneyStep step) const {
Map::const_iterator it = steps.find(step);
if (it == steps.end()) {
throw RuntimeException("Invalid step " + journeyStepToString(step));
}
return it->second;
}
JourneyStep getFirstFailedStep() const {
Map::const_iterator it, end = steps.end();
for (it = steps.begin(); it != end; it++) {
if (it->second.state == STEP_ERRORED) {
return it->first;
}
}
return UNKNOWN_JOURNEY_STEP;
}
void setStepNotStarted(JourneyStep step, bool force = false) {
JourneyStepInfo &info = getStepInfoMutable(step);
if (info.state == STEP_NOT_STARTED || info.state == STEP_IN_PROGRESS || force) {
info.state = STEP_NOT_STARTED;
info.beginTime = 0;
info.endTime = 0;
} else {
throw RuntimeException("Unable to change state for journey step "
+ journeyStepToString(step) + " because it wasn't already in progress");
}
}
void setStepInProgress(JourneyStep step, bool force = false) {
JourneyStepInfo &info = getStepInfoMutable(step);
if (info.state == STEP_IN_PROGRESS) {
return;
} else if (info.state == STEP_NOT_STARTED || force) {
info.state = STEP_IN_PROGRESS;
// When `force` is true, we don't want to overwrite the previous endTime.
if (info.endTime == 0) {
info.beginTime =
SystemTime::getMonotonicUsecWithGranularity<SystemTime::GRAN_10MSEC>();
}
} else {
throw RuntimeException("Unable to change state for journey step "
+ journeyStepToString(step)
+ " because it was already in progress or completed");
}
}
void setStepPerformed(JourneyStep step, bool force = false) {
JourneyStepInfo &info = getStepInfoMutable(step);
if (info.state == STEP_PERFORMED) {
return;
} else if (info.state == STEP_IN_PROGRESS || true) {
info.state = STEP_PERFORMED;
// When `force` is true, we don't want to overwrite the previous endTime.
if (info.endTime == 0) {
info.endTime =
SystemTime::getMonotonicUsecWithGranularity<SystemTime::GRAN_10MSEC>();
if (info.beginTime == 0) {
info.beginTime = info.endTime;
}
}
} else {
throw RuntimeException("Unable to change state for journey step "
+ journeyStepToString(step) + " because it wasn't already in progress");
}
}
void setStepErrored(JourneyStep step, bool force = false) {
JourneyStepInfo &info = getStepInfoMutable(step);
if (info.state == STEP_ERRORED) {
return;
} else if (info.state == STEP_IN_PROGRESS || force) {
info.state = STEP_ERRORED;
// When `force` is true, we don't want to overwrite the previous endTime.
if (info.endTime == 0) {
info.endTime =
SystemTime::getMonotonicUsecWithGranularity<SystemTime::GRAN_10MSEC>();
if (info.beginTime == 0) {
info.beginTime = info.endTime;
}
}
} else {
throw RuntimeException("Unable to change state for journey step "
+ journeyStepToString(step) + " because it wasn't already in progress");
}
}
void setStepBeginTime(JourneyStep step, MonotonicTimeUsec timestamp) {
JourneyStepInfo &info = getStepInfoMutable(step);
info.beginTime = timestamp;
}
void setStepEndTime(JourneyStep step, MonotonicTimeUsec timestamp) {
JourneyStepInfo &info = getStepInfoMutable(step);
info.endTime = timestamp;
}
void reset() {
Map::iterator it, end = steps.end();
for (it = steps.begin(); it != end; it++) {
it->second.state = STEP_NOT_STARTED;
it->second.beginTime = 0;
it->second.endTime = 0;
}
}
Json::Value inspectAsJson() const {
Json::Value doc, steps;
MonotonicTimeUsec monoNow = SystemTime::getMonotonicUsec();
unsigned long long now = SystemTime::getUsec();
doc["type"] = journeyTypeToString(type).toString();
Map::const_iterator it, end = this->steps.end();
for (it = this->steps.begin(); it != end; it++) {
const JourneyStep step = it->first;
const JourneyStepInfo &info = it->second;
const JourneyStepInfo *nextStepInfo = NULL;
if (info.nextStep != UNKNOWN_JOURNEY_STEP) {
nextStepInfo = &this->steps.find(info.nextStep)->second;
}
steps[journeyStepToString(step).toString()] =
info.inspectAsJson(nextStepInfo, monoNow, now);
}
doc["steps"] = steps;
return doc;
}
};
inline OXT_PURE StaticString
journeyTypeToString(JourneyType type) {
switch (type) {
case SPAWN_DIRECTLY:
return P_STATIC_STRING("SPAWN_DIRECTLY");
case START_PRELOADER:
return P_STATIC_STRING("START_PRELOADER");
case SPAWN_THROUGH_PRELOADER:
return P_STATIC_STRING("SPAWN_THROUGH_PRELOADER");
default:
return P_STATIC_STRING("UNKNOWN_JOURNEY_TYPE");
}
}
inline OXT_PURE StaticString
journeyStepToString(JourneyStep step) {
switch (step) {
case SPAWNING_KIT_PREPARATION:
return P_STATIC_STRING("SPAWNING_KIT_PREPARATION");
case SPAWNING_KIT_FORK_SUBPROCESS:
return P_STATIC_STRING("SPAWNING_KIT_FORK_SUBPROCESS");
case SPAWNING_KIT_CONNECT_TO_PRELOADER:
return P_STATIC_STRING("SPAWNING_KIT_CONNECT_TO_PRELOADER");
case SPAWNING_KIT_SEND_COMMAND_TO_PRELOADER:
return P_STATIC_STRING("SPAWNING_KIT_SEND_COMMAND_TO_PRELOADER");
case SPAWNING_KIT_READ_RESPONSE_FROM_PRELOADER:
return P_STATIC_STRING("SPAWNING_KIT_READ_RESPONSE_FROM_PRELOADER");
case SPAWNING_KIT_PARSE_RESPONSE_FROM_PRELOADER:
return P_STATIC_STRING("SPAWNING_KIT_PARSE_RESPONSE_FROM_PRELOADER");
case SPAWNING_KIT_PROCESS_RESPONSE_FROM_PRELOADER:
return P_STATIC_STRING("SPAWNING_KIT_PROCESS_RESPONSE_FROM_PRELOADER");
case SPAWNING_KIT_HANDSHAKE_PERFORM:
return P_STATIC_STRING("SPAWNING_KIT_HANDSHAKE_PERFORM");
case SPAWNING_KIT_FINISH:
return P_STATIC_STRING("SPAWNING_KIT_FINISH");
case PRELOADER_PREPARATION:
return P_STATIC_STRING("PRELOADER_PREPARATION");
case PRELOADER_FORK_SUBPROCESS:
return P_STATIC_STRING("PRELOADER_FORK_SUBPROCESS");
case PRELOADER_SEND_RESPONSE:
return P_STATIC_STRING("PRELOADER_SEND_RESPONSE");
case PRELOADER_FINISH:
return P_STATIC_STRING("PRELOADER_FINISH");
case SUBPROCESS_BEFORE_FIRST_EXEC:
return P_STATIC_STRING("SUBPROCESS_BEFORE_FIRST_EXEC");
case SUBPROCESS_SPAWN_ENV_SETUPPER_BEFORE_SHELL:
return P_STATIC_STRING("SUBPROCESS_SPAWN_ENV_SETUPPER_BEFORE_SHELL");
case SUBPROCESS_OS_SHELL:
return P_STATIC_STRING("SUBPROCESS_OS_SHELL");
case SUBPROCESS_SPAWN_ENV_SETUPPER_AFTER_SHELL:
return P_STATIC_STRING("SUBPROCESS_SPAWN_ENV_SETUPPER_AFTER_SHELL");
case SUBPROCESS_EXEC_WRAPPER:
return P_STATIC_STRING("SUBPROCESS_EXEC_WRAPPER");
case SUBPROCESS_WRAPPER_PREPARATION:
return P_STATIC_STRING("SUBPROCESS_WRAPPER_PREPARATION");
case SUBPROCESS_APP_LOAD_OR_EXEC:
return P_STATIC_STRING("SUBPROCESS_APP_LOAD_OR_EXEC");
case SUBPROCESS_PREPARE_AFTER_FORKING_FROM_PRELOADER:
return P_STATIC_STRING("SUBPROCESS_PREPARE_AFTER_FORKING_FROM_PRELOADER");
case SUBPROCESS_LISTEN:
return P_STATIC_STRING("SUBPROCESS_LISTEN");
case SUBPROCESS_FINISH:
return P_STATIC_STRING("SUBPROCESS_FINISH");
default:
return P_STATIC_STRING("UNKNOWN_JOURNEY_STEP");
}
}
inline OXT_PURE string
journeyStepToStringLowerCase(JourneyStep step) {
StaticString stepString = journeyStepToString(step);
DynamicBuffer stepStringLcBuffer(stepString.size());
convertLowerCase((const unsigned char *) stepString.data(),
(unsigned char *) stepStringLcBuffer.data, stepString.size());
return string(stepStringLcBuffer.data, stepString.size());
}
inline OXT_PURE StaticString
journeyStepStateToString(JourneyStepState state) {
switch (state) {
case STEP_NOT_STARTED:
return P_STATIC_STRING("STEP_NOT_STARTED");
case STEP_IN_PROGRESS:
return P_STATIC_STRING("STEP_IN_PROGRESS");
case STEP_PERFORMED:
return P_STATIC_STRING("STEP_PERFORMED");
case STEP_ERRORED:
return P_STATIC_STRING("STEP_ERRORED");
default:
return P_STATIC_STRING("UNKNOWN_JOURNEY_STEP_STATE");
}
}
inline OXT_PURE JourneyStepState
stringToJourneyStepState(const StaticString &value) {
if (value == P_STATIC_STRING("STEP_NOT_STARTED")) {
return STEP_NOT_STARTED;
} else if (value == P_STATIC_STRING("STEP_IN_PROGRESS")) {
return STEP_IN_PROGRESS;
} else if (value == P_STATIC_STRING("STEP_PERFORMED")) {
return STEP_PERFORMED;
} else if (value == P_STATIC_STRING("STEP_ERRORED")) {
return STEP_ERRORED;
} else {
return UNKNOWN_JOURNEY_STEP_STATE;
}
}
} // namespace SpawningKit
} // namespace Passenger
#endif /* _PASSENGER_SPAWNING_KIT_HANDSHAKE_JOURNEY_H_ */