<commit_before>#include <babylon/materials/node/blocks/fragment/perturb_normal_block.h>

#include <babylon/core/json_util.h>
#include <babylon/engines/scene.h>
#include <babylon/materials/effect.h>
#include <babylon/materials/node/blocks/input/input_block.h>
#include <babylon/materials/node/node_material.h>
#include <babylon/materials/node/node_material_build_state.h>
#include <babylon/materials/node/node_material_build_state_shared_data.h>
#include <babylon/materials/node/node_material_connection_point.h>
#include <babylon/materials/node/node_material_defines.h>
#include <babylon/misc/string_tools.h>

namespace BABYLON {

PerturbNormalBlock::PerturbNormalBlock(const std::string& iName)
    : NodeMaterialBlock{iName, NodeMaterialBlockTargets::Fragment}
    , invertX{false}
    , invertY{false}
    , worldPosition{this, &PerturbNormalBlock::get_worldPosition}
    , worldNormal{this, &PerturbNormalBlock::get_worldNormal}
    , worldTangent{this, &PerturbNormalBlock::get_worldTangent}
    , uv{this, &PerturbNormalBlock::get_uv}
    , normalMapColor{this, &PerturbNormalBlock::get_normalMapColor}
    , strength{this, &PerturbNormalBlock::get_strength}
    , output{this, &PerturbNormalBlock::get_output}
{
  _isUnique = true;

  // Vertex
  registerInput("worldPosition", NodeMaterialBlockConnectionPointTypes::Vector4, false);
  registerInput("worldNormal", NodeMaterialBlockConnectionPointTypes::Vector4, false);
  registerInput("worldTangent", NodeMaterialBlockConnectionPointTypes::Vector4, true);
  registerInput("uv", NodeMaterialBlockConnectionPointTypes::Vector2, false);
  registerInput("normalMapColor", NodeMaterialBlockConnectionPointTypes::Color3, false);
  registerInput("strength", NodeMaterialBlockConnectionPointTypes::Float, false);

  // Fragment
  registerOutput("output", NodeMaterialBlockConnectionPointTypes::Vector4);
}

PerturbNormalBlock::~PerturbNormalBlock() = default;

std::string PerturbNormalBlock::getClassName() const
{
  return "PerturbNormalBlock";
}

NodeMaterialConnectionPointPtr& PerturbNormalBlock::get_worldPosition()
{
  return _inputs[0];
}

NodeMaterialConnectionPointPtr& PerturbNormalBlock::get_worldNormal()
{
  return _inputs[1];
}

NodeMaterialConnectionPointPtr& PerturbNormalBlock::get_worldTangent()
{
  return _inputs[2];
}

NodeMaterialConnectionPointPtr& PerturbNormalBlock::get_uv()
{
  return _inputs[3];
}

NodeMaterialConnectionPointPtr& PerturbNormalBlock::get_normalMapColor()
{
  return _inputs[4];
}

NodeMaterialConnectionPointPtr& PerturbNormalBlock::get_strength()
{
  return _inputs[5];
}

NodeMaterialConnectionPointPtr& PerturbNormalBlock::get_output()
{
  return _outputs[0];
}

void PerturbNormalBlock::prepareDefines(AbstractMesh* /*mesh*/,
                                        const NodeMaterialPtr& /*nodeMaterial*/,
                                        NodeMaterialDefines& defines, bool /*useInstances*/,
                                        SubMesh* /*subMesh*/)
{
  defines.setValue("BUMP", true);
}

void PerturbNormalBlock::bind(Effect* effect, const NodeMaterialPtr& nodeMaterial, Mesh* /*mesh*/,
                              SubMesh* /*subMesh*/)
{
  if (nodeMaterial->getScene()->_mirroredCameraPosition) {
    effect->setFloat2(_tangentSpaceParameterName, invertX ? 1.f : -1.f, invertY ? 1.f : -1.f);
  }
  else {
    effect->setFloat2(_tangentSpaceParameterName, invertX ? -1.f : 1.f, invertY ? -1.f : 1.f);
  }
}

void PerturbNormalBlock::autoConfigure(const NodeMaterialPtr& material)
{
  if (!uv()->isConnected()) {
    auto uvInput = material->getInputBlockByPredicate(
      [](const InputBlockPtr& b) -> bool { return b->isAttribute() && b->name() == "uv"; });

    if (!uvInput) {
      uvInput = InputBlock::New("uv");
      uvInput->setAsAttribute();
    }
    uvInput->output()->connectTo(uv);
  }

  if (!strength()->isConnected()) {
    auto strengthInput   = InputBlock::New("strength");
    strengthInput->value = std::make_shared<AnimationValue>(1.f);
    strengthInput->output()->connectTo(strength);
  }
}

PerturbNormalBlock& PerturbNormalBlock::_buildBlock(NodeMaterialBuildState& state)
{
  NodeMaterialBlock::_buildBlock(state);

  const auto iComments       = StringTools::printf("//%s", name().c_str());
  const auto& _uv            = uv();
  const auto& _worldPosition = worldPosition();
  const auto& _worldNormal   = worldNormal();
  const auto& _worldTangent  = worldTangent();

  state.sharedData->blocksWithDefines.emplace_back(shared_from_this());
  state.sharedData->bindableBlocks.emplace_back(shared_from_this());

  _tangentSpaceParameterName = state._getFreeDefineName("tangentSpaceParameter");

  state._emitUniformFromString(_tangentSpaceParameterName, "vec2");

  const auto replaceForBumpInfos
    = strength()->isConnectedToInputBlock() && strength()->connectInputBlock()->isConstant ?
        StringTools::printf(
          "\r\n#if !defined(NORMALXYSCALE)\r\n1.0/\r\n#endif\r\n%s",
          state._emitFloat(strength()->connectInputBlock()->value()->get<float>())) :
        StringTools::printf("\r\n#if !defined(NORMALXYSCALE)\r\n1.0/\r\n#endif\r\n%s",
                            strength()->associatedVariableName().c_str());

  state._emitExtension("derivatives", "#extension GL_OES_standard_derivatives : enable");

  StringsReplacement tangentReplaceString{
    "defined\\(TANGENT\\)",                                               // search
    _worldTangent->isConnected() ? "defined(TANGENT)" : "defined(IGNORE)" // replace
  };

  if (_worldTangent->isConnected()) {
    state.compilationString += StringTools::printf("vec3 tbnNormal = normalize(%s.xyz);\r\n",
                                                   _worldNormal->associatedVariableName().c_str());
    state.compilationString += StringTools::printf("vec3 tbnTangent = normalize(%s.xyz);\r\n",
                                                   _worldTangent->associatedVariableName().c_str());
    state.compilationString += "vec3 tbnBitangent = cross(tbnNormal, tbnTangent);\r\n";
    state.compilationString += "mat3 vTBN = mat3(tbnTangent, tbnBitangent, tbnNormal);\r\n";
  }

  {
    EmitFunctionFromIncludeOptions emitFunctionFromIncludeOptions;
    emitFunctionFromIncludeOptions.replaceStrings = {tangentReplaceString};
    state._emitFunctionFromInclude("bumpFragmentMainFunctions", iComments,
                                   emitFunctionFromIncludeOptions);
  }

  {
    EmitFunctionFromIncludeOptions emitFunctionFromIncludeOptions;
    emitFunctionFromIncludeOptions.replaceStrings
      = {{"vBumpInfos.y", replaceForBumpInfos},
         {"vTangentSpaceParams", _tangentSpaceParameterName},
         {"vPositionW", _worldPosition->associatedVariableName() + ".xyz"},
         {"varying vec2 vBumpUV;", ""},
         {R"(uniform sampler2D bumpSampler;[\s\S]*?\})", ""}};
    state._emitFunctionFromInclude("bumpFragmentFunctions", iComments,
                                   emitFunctionFromIncludeOptions);
  }

  state.compilationString += _declareOutput(output, state) + " = vec4(0.);\r\n";
  EmitCodeFromIncludeOptions emitCodeFromIncludeOptions;
  emitCodeFromIncludeOptions.replaceStrings
    = {{"perturbNormal\\(TBN,vBumpUV\\+uvOffset\\)",
        StringTools::printf("perturbNormal(TBN, %s)",
                            normalMapColor()->associatedVariableName().c_str())},
       {"vBumpInfos.y", replaceForBumpInfos},
       {"vBumpUV", _uv->associatedVariableName()},
       {"vPositionW", _worldPosition->associatedVariableName() + ".xyz"},
       {"normalW=", output()->associatedVariableName() + ".xyz = "},
       {R"(mat3\(normalMatrix\)\*normalW)",
        "mat3(normalMatrix) * " + output()->associatedVariableName() + ".xyz"},
       {"normalW", _worldNormal->associatedVariableName() + ".xyz"},
       tangentReplaceString};
  state.compilationString
    += state._emitCodeFromInclude("bumpFragment", iComments, emitCodeFromIncludeOptions);

  return *this;
}

std::string PerturbNormalBlock::_dumpPropertiesCode()
{
  auto codeString = StringTools::printf("%s.invertX = %s;\r\n", _codeVariableName.c_str(),
                                        invertX ? "true" : "false");

  codeString += StringTools::printf("%s.invertY = %s;\r\n", _codeVariableName.c_str(),
                                    invertY ? "true" : "false");

  return codeString;
}

json PerturbNormalBlock::serialize() const
{
  return nullptr;
}

void PerturbNormalBlock::_deserialize(const json& /*serializationObject*/, Scene* /*scene*/,
                                      const std::string& /*rootUrl*/)
{
}

} // end of namespace BABYLON
<commit_msg>Added missing c_str() function call<commit_after>#include <babylon/materials/node/blocks/fragment/perturb_normal_block.h>

#include <babylon/core/json_util.h>
#include <babylon/engines/scene.h>
#include <babylon/materials/effect.h>
#include <babylon/materials/node/blocks/input/input_block.h>
#include <babylon/materials/node/node_material.h>
#include <babylon/materials/node/node_material_build_state.h>
#include <babylon/materials/node/node_material_build_state_shared_data.h>
#include <babylon/materials/node/node_material_connection_point.h>
#include <babylon/materials/node/node_material_defines.h>
#include <babylon/misc/string_tools.h>

namespace BABYLON {

PerturbNormalBlock::PerturbNormalBlock(const std::string& iName)
    : NodeMaterialBlock{iName, NodeMaterialBlockTargets::Fragment}
    , invertX{false}
    , invertY{false}
    , worldPosition{this, &PerturbNormalBlock::get_worldPosition}
    , worldNormal{this, &PerturbNormalBlock::get_worldNormal}
    , worldTangent{this, &PerturbNormalBlock::get_worldTangent}
    , uv{this, &PerturbNormalBlock::get_uv}
    , normalMapColor{this, &PerturbNormalBlock::get_normalMapColor}
    , strength{this, &PerturbNormalBlock::get_strength}
    , output{this, &PerturbNormalBlock::get_output}
{
  _isUnique = true;

  // Vertex
  registerInput("worldPosition", NodeMaterialBlockConnectionPointTypes::Vector4, false);
  registerInput("worldNormal", NodeMaterialBlockConnectionPointTypes::Vector4, false);
  registerInput("worldTangent", NodeMaterialBlockConnectionPointTypes::Vector4, true);
  registerInput("uv", NodeMaterialBlockConnectionPointTypes::Vector2, false);
  registerInput("normalMapColor", NodeMaterialBlockConnectionPointTypes::Color3, false);
  registerInput("strength", NodeMaterialBlockConnectionPointTypes::Float, false);

  // Fragment
  registerOutput("output", NodeMaterialBlockConnectionPointTypes::Vector4);
}

PerturbNormalBlock::~PerturbNormalBlock() = default;

std::string PerturbNormalBlock::getClassName() const
{
  return "PerturbNormalBlock";
}

NodeMaterialConnectionPointPtr& PerturbNormalBlock::get_worldPosition()
{
  return _inputs[0];
}

NodeMaterialConnectionPointPtr& PerturbNormalBlock::get_worldNormal()
{
  return _inputs[1];
}

NodeMaterialConnectionPointPtr& PerturbNormalBlock::get_worldTangent()
{
  return _inputs[2];
}

NodeMaterialConnectionPointPtr& PerturbNormalBlock::get_uv()
{
  return _inputs[3];
}

NodeMaterialConnectionPointPtr& PerturbNormalBlock::get_normalMapColor()
{
  return _inputs[4];
}

NodeMaterialConnectionPointPtr& PerturbNormalBlock::get_strength()
{
  return _inputs[5];
}

NodeMaterialConnectionPointPtr& PerturbNormalBlock::get_output()
{
  return _outputs[0];
}

void PerturbNormalBlock::prepareDefines(AbstractMesh* /*mesh*/,
                                        const NodeMaterialPtr& /*nodeMaterial*/,
                                        NodeMaterialDefines& defines, bool /*useInstances*/,
                                        SubMesh* /*subMesh*/)
{
  defines.setValue("BUMP", true);
}

void PerturbNormalBlock::bind(Effect* effect, const NodeMaterialPtr& nodeMaterial, Mesh* /*mesh*/,
                              SubMesh* /*subMesh*/)
{
  if (nodeMaterial->getScene()->_mirroredCameraPosition) {
    effect->setFloat2(_tangentSpaceParameterName, invertX ? 1.f : -1.f, invertY ? 1.f : -1.f);
  }
  else {
    effect->setFloat2(_tangentSpaceParameterName, invertX ? -1.f : 1.f, invertY ? -1.f : 1.f);
  }
}

void PerturbNormalBlock::autoConfigure(const NodeMaterialPtr& material)
{
  if (!uv()->isConnected()) {
    auto uvInput = material->getInputBlockByPredicate(
      [](const InputBlockPtr& b) -> bool { return b->isAttribute() && b->name() == "uv"; });

    if (!uvInput) {
      uvInput = InputBlock::New("uv");
      uvInput->setAsAttribute();
    }
    uvInput->output()->connectTo(uv);
  }

  if (!strength()->isConnected()) {
    auto strengthInput   = InputBlock::New("strength");
    strengthInput->value = std::make_shared<AnimationValue>(1.f);
    strengthInput->output()->connectTo(strength);
  }
}

PerturbNormalBlock& PerturbNormalBlock::_buildBlock(NodeMaterialBuildState& state)
{
  NodeMaterialBlock::_buildBlock(state);

  const auto iComments       = StringTools::printf("//%s", name().c_str());
  const auto& _uv            = uv();
  const auto& _worldPosition = worldPosition();
  const auto& _worldNormal   = worldNormal();
  const auto& _worldTangent  = worldTangent();

  state.sharedData->blocksWithDefines.emplace_back(shared_from_this());
  state.sharedData->bindableBlocks.emplace_back(shared_from_this());

  _tangentSpaceParameterName = state._getFreeDefineName("tangentSpaceParameter");

  state._emitUniformFromString(_tangentSpaceParameterName, "vec2");

  const auto replaceForBumpInfos
    = strength()->isConnectedToInputBlock() && strength()->connectInputBlock()->isConstant ?
        StringTools::printf(
          "\r\n#if !defined(NORMALXYSCALE)\r\n1.0/\r\n#endif\r\n%s",
          state._emitFloat(strength()->connectInputBlock()->value()->get<float>()).c_str()) :
        StringTools::printf("\r\n#if !defined(NORMALXYSCALE)\r\n1.0/\r\n#endif\r\n%s",
                            strength()->associatedVariableName().c_str());

  state._emitExtension("derivatives", "#extension GL_OES_standard_derivatives : enable");

  StringsReplacement tangentReplaceString{
    "defined\\(TANGENT\\)",                                               // search
    _worldTangent->isConnected() ? "defined(TANGENT)" : "defined(IGNORE)" // replace
  };

  if (_worldTangent->isConnected()) {
    state.compilationString += StringTools::printf("vec3 tbnNormal = normalize(%s.xyz);\r\n",
                                                   _worldNormal->associatedVariableName().c_str());
    state.compilationString += StringTools::printf("vec3 tbnTangent = normalize(%s.xyz);\r\n",
                                                   _worldTangent->associatedVariableName().c_str());
    state.compilationString += "vec3 tbnBitangent = cross(tbnNormal, tbnTangent);\r\n";
    state.compilationString += "mat3 vTBN = mat3(tbnTangent, tbnBitangent, tbnNormal);\r\n";
  }

  {
    EmitFunctionFromIncludeOptions emitFunctionFromIncludeOptions;
    emitFunctionFromIncludeOptions.replaceStrings = {tangentReplaceString};
    state._emitFunctionFromInclude("bumpFragmentMainFunctions", iComments,
                                   emitFunctionFromIncludeOptions);
  }

  {
    EmitFunctionFromIncludeOptions emitFunctionFromIncludeOptions;
    emitFunctionFromIncludeOptions.replaceStrings
      = {{"vBumpInfos.y", replaceForBumpInfos},
         {"vTangentSpaceParams", _tangentSpaceParameterName},
         {"vPositionW", _worldPosition->associatedVariableName() + ".xyz"},
         {"varying vec2 vBumpUV;", ""},
         {R"(uniform sampler2D bumpSampler;[\s\S]*?\})", ""}};
    state._emitFunctionFromInclude("bumpFragmentFunctions", iComments,
                                   emitFunctionFromIncludeOptions);
  }

  state.compilationString += _declareOutput(output, state) + " = vec4(0.);\r\n";
  EmitCodeFromIncludeOptions emitCodeFromIncludeOptions;
  emitCodeFromIncludeOptions.replaceStrings
    = {{"perturbNormal\\(TBN,vBumpUV\\+uvOffset\\)",
        StringTools::printf("perturbNormal(TBN, %s)",
                            normalMapColor()->associatedVariableName().c_str())},
       {"vBumpInfos.y", replaceForBumpInfos},
       {"vBumpUV", _uv->associatedVariableName()},
       {"vPositionW", _worldPosition->associatedVariableName() + ".xyz"},
       {"normalW=", output()->associatedVariableName() + ".xyz = "},
       {R"(mat3\(normalMatrix\)\*normalW)",
        "mat3(normalMatrix) * " + output()->associatedVariableName() + ".xyz"},
       {"normalW", _worldNormal->associatedVariableName() + ".xyz"},
       tangentReplaceString};
  state.compilationString
    += state._emitCodeFromInclude("bumpFragment", iComments, emitCodeFromIncludeOptions);

  return *this;
}

std::string PerturbNormalBlock::_dumpPropertiesCode()
{
  auto codeString = StringTools::printf("%s.invertX = %s;\r\n", _codeVariableName.c_str(),
                                        invertX ? "true" : "false");

  codeString += StringTools::printf("%s.invertY = %s;\r\n", _codeVariableName.c_str(),
                                    invertY ? "true" : "false");

  return codeString;
}

json PerturbNormalBlock::serialize() const
{
  return nullptr;
}

void PerturbNormalBlock::_deserialize(const json& /*serializationObject*/, Scene* /*scene*/,
                                      const std::string& /*rootUrl*/)
{
}

} // end of namespace BABYLON
<|endoftext|>


<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/boot_times_loader.h"

#include <vector>

#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/process_util.h"
#include "base/singleton.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/thread.h"
#include "base/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/chromeos/login/authentication_notification_details.h"
#include "chrome/browser/chromeos/network_state_notifier.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/notification_service.h"

namespace chromeos {

// File uptime logs are located in.
static const char kLogPath[] = "/tmp";
// Prefix for the time measurement files.
static const char kUptimePrefix[] = "uptime-";
// Prefix for the disk usage files.
static const char kDiskPrefix[] = "disk-";
// Name of the time that Chrome's main() is called.
static const char kChromeMain[] = "chrome-main";
// Delay in milliseconds between file read attempts.
static const int64 kReadAttemptDelayMs = 250;
// Delay in milliseconds before writing the login times to disk.
static const int64 kLoginTimeWriteDelayMs = 3000;

// Names of login stats files.
static const char kLoginSuccess[] = "login-success";
static const char kChromeFirstRender[] = "chrome-first-render";

// Names of login UMA values.
static const char kUmaAuthenticate[] = "BootTime.Authenticate";
static const char kUmaLogin[] = "BootTime.Login";

// Name of file collecting login times.
static const char kLoginTimes[] = "login-times-sent";

BootTimesLoader::BootTimesLoader()
    : backend_(new Backend()),
      have_registered_(false) {
  login_time_markers_.reserve(30);
}

// static
BootTimesLoader* BootTimesLoader::Get() {
  return Singleton<BootTimesLoader>::get();
}

BootTimesLoader::Handle BootTimesLoader::GetBootTimes(
    CancelableRequestConsumerBase* consumer,
    BootTimesLoader::GetBootTimesCallback* callback) {
  if (!g_browser_process->file_thread()) {
    // This should only happen if Chrome is shutting down, so we don't do
    // anything.
    return 0;
  }

  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
  if (command_line.HasSwitch(switches::kTestType)) {
    // TODO(davemoore) This avoids boottimes for tests. This needs to be
    // replaced with a mock of BootTimesLoader.
    return 0;
  }

  scoped_refptr<CancelableRequest<GetBootTimesCallback> > request(
      new CancelableRequest<GetBootTimesCallback>(callback));
  AddRequest(request, consumer);

  BrowserThread::PostTask(
      BrowserThread::FILE,
      FROM_HERE,
      NewRunnableMethod(backend_.get(), &Backend::GetBootTimes, request));
  return request->handle();
}

// Extracts the uptime value from files located in /tmp, returning the
// value as a double in value.
static bool GetTime(const std::string& log, double* value) {
  FilePath log_dir(kLogPath);
  FilePath log_file = log_dir.Append(log);
  std::string contents;
  *value = 0.0;
  if (file_util::ReadFileToString(log_file, &contents)) {
    size_t space_index = contents.find(' ');
    size_t chars_left =
        space_index != std::string::npos ? space_index : std::string::npos;
    std::string value_string = contents.substr(0, chars_left);
    return base::StringToDouble(value_string, value);
  }
  return false;
}

// Converts double seconds to a TimeDelta object.
static base::TimeDelta SecondsToTimeDelta(double seconds) {
  double ms = seconds * base::Time::kMillisecondsPerSecond;
  return base::TimeDelta::FromMilliseconds(static_cast<int64>(ms));
}

// Reports the collected boot times to UMA if they haven't been
// reported yet and if metrics collection is enabled.
static void SendBootTimesToUMA(const BootTimesLoader::BootTimes& boot_times) {
  // Checks if the times for the most recent boot event have been
  // reported already to avoid sending boot time histogram samples
  // every time the user logs out.
  static const char kBootTimesSent[] = "/tmp/boot-times-sent";
  FilePath sent(kBootTimesSent);
  if (file_util::PathExists(sent))
    return;

  UMA_HISTOGRAM_TIMES("BootTime.Total",
                      SecondsToTimeDelta(boot_times.total));
  UMA_HISTOGRAM_TIMES("BootTime.Firmware",
                      SecondsToTimeDelta(boot_times.firmware));
  UMA_HISTOGRAM_TIMES("BootTime.Kernel",
                      SecondsToTimeDelta(boot_times.pre_startup));
  UMA_HISTOGRAM_TIMES("BootTime.System",
                      SecondsToTimeDelta(boot_times.system));
  if (boot_times.chrome > 0) {
    UMA_HISTOGRAM_TIMES("BootTime.Chrome",
                        SecondsToTimeDelta(boot_times.chrome));
  }

  // Stores the boot times to a file in /tmp to indicate that the
  // times for the most recent boot event have been reported
  // already. The file will be deleted at system shutdown/reboot.
  std::string boot_times_text = base::StringPrintf("total: %.2f\n"
                                                   "firmware: %.2f\n"
                                                   "kernel: %.2f\n"
                                                   "system: %.2f\n"
                                                   "chrome: %.2f\n",
                                                   boot_times.total,
                                                   boot_times.firmware,
                                                   boot_times.pre_startup,
                                                   boot_times.system,
                                                   boot_times.chrome);
  file_util::WriteFile(sent, boot_times_text.data(), boot_times_text.size());
  DCHECK(file_util::PathExists(sent));
}

void BootTimesLoader::Backend::GetBootTimes(
    scoped_refptr<GetBootTimesRequest> request) {
  const char* kFirmwareBootTime = "firmware-boot-time";
  const char* kPreStartup = "pre-startup";
  const char* kChromeExec = "chrome-exec";
  const char* kChromeMain = "chrome-main";
  const char* kXStarted = "x-started";
  const char* kLoginPromptReady = "login-prompt-ready";
  std::string uptime_prefix = kUptimePrefix;

  if (request->canceled())
    return;

  // Wait until login_prompt_ready is output by reposting.
  FilePath log_dir(kLogPath);
  FilePath log_file = log_dir.Append(uptime_prefix + kLoginPromptReady);
  if (!file_util::PathExists(log_file)) {
    BrowserThread::PostDelayedTask(
        BrowserThread::FILE,
        FROM_HERE,
        NewRunnableMethod(this, &Backend::GetBootTimes, request),
        kReadAttemptDelayMs);
    return;
  }

  BootTimes boot_times;

  GetTime(kFirmwareBootTime, &boot_times.firmware);
  GetTime(uptime_prefix + kPreStartup, &boot_times.pre_startup);
  GetTime(uptime_prefix + kXStarted, &boot_times.x_started);
  GetTime(uptime_prefix + kChromeExec, &boot_times.chrome_exec);
  GetTime(uptime_prefix + kChromeMain, &boot_times.chrome_main);
  GetTime(uptime_prefix + kLoginPromptReady, &boot_times.login_prompt_ready);

  boot_times.total = boot_times.firmware + boot_times.login_prompt_ready;
  if (boot_times.chrome_exec > 0) {
    boot_times.system = boot_times.chrome_exec - boot_times.pre_startup;
    boot_times.chrome = boot_times.login_prompt_ready - boot_times.chrome_exec;
  } else {
    boot_times.system = boot_times.login_prompt_ready - boot_times.pre_startup;
  }

  SendBootTimesToUMA(boot_times);

  request->ForwardResult(
      GetBootTimesCallback::TupleType(request->handle(), boot_times));
}

static void RecordStatsDelayed(
    const std::string& name,
    const std::string& uptime,
    const std::string& disk) {
  const FilePath log_path(kLogPath);
  std::string disk_prefix = kDiskPrefix;
  const FilePath uptime_output =
      log_path.Append(FilePath(kUptimePrefix + name));
  const FilePath disk_output = log_path.Append(FilePath(kDiskPrefix + name));

  // Write out the files, ensuring that they don't exist already.
  if (!file_util::PathExists(uptime_output))
    file_util::WriteFile(uptime_output, uptime.data(), uptime.size());
  if (!file_util::PathExists(disk_output))
    file_util::WriteFile(disk_output, disk.data(), disk.size());
}

// static
void BootTimesLoader::WriteLoginTimes(
    const std::vector<TimeMarker> login_times) {
  const int kMinTimeMillis = 1;
  const int kMaxTimeMillis = 30;
  const int kNumBuckets = 100;
  const char kUmaPrefix[] = "BootTime.";
  const FilePath log_path(kLogPath);

  base::Time first = login_times.front().time();
  base::Time last = login_times.back().time();
  base::TimeDelta total = last - first;
  scoped_refptr<base::Histogram>total_hist = base::Histogram::FactoryTimeGet(
      kUmaLogin,
      base::TimeDelta::FromMilliseconds(kMinTimeMillis),
      base::TimeDelta::FromMilliseconds(kMaxTimeMillis),
      kNumBuckets,
      base::Histogram::kUmaTargetedHistogramFlag);
  total_hist->AddTime(total);
  std::string output =
      base::StringPrintf("%s: %.2f", kUmaLogin, total.InSecondsF());
  base::Time prev = first;
  for (unsigned int i = 0; i < login_times.size(); ++i) {
    TimeMarker tm = login_times[i];
    base::TimeDelta since_first = tm.time() - first;
    base::TimeDelta since_prev = tm.time() - prev;
    std::string name;

    if (tm.send_to_uma()) {
      name = kUmaPrefix + tm.name();
      scoped_refptr<base::Histogram>prev_hist = base::Histogram::FactoryTimeGet(
          name,
          base::TimeDelta::FromMilliseconds(kMinTimeMillis),
          base::TimeDelta::FromMilliseconds(kMaxTimeMillis),
          kNumBuckets,
          base::Histogram::kUmaTargetedHistogramFlag);
      prev_hist->AddTime(since_prev);
    } else {
      name = tm.name();
    }
    output +=
        StringPrintf(
            "\n%.2f +%.2f %s",
            since_first.InSecondsF(),
            since_prev.InSecondsF(),
            name.data());
    prev = tm.time();
  }
  file_util::WriteFile(
      log_path.Append(kLoginTimes), output.data(), output.size());
}

void BootTimesLoader::RecordStats(const std::string& name, const Stats& stats) {
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      NewRunnableFunction(
          RecordStatsDelayed, name, stats.uptime, stats.disk));
}

BootTimesLoader::Stats BootTimesLoader::GetCurrentStats() {
  const FilePath kProcUptime("/proc/uptime");
  const FilePath kDiskStat("/sys/block/sda/stat");
  Stats stats;

  file_util::ReadFileToString(kProcUptime, &stats.uptime);
  file_util::ReadFileToString(kDiskStat, &stats.disk);
  return stats;
}

void BootTimesLoader::RecordCurrentStats(const std::string& name) {
  RecordStats(name, GetCurrentStats());
}

void BootTimesLoader::SaveChromeMainStats() {
  chrome_main_stats_ = GetCurrentStats();
}

void BootTimesLoader::RecordChromeMainStats() {
  RecordStats(kChromeMain, chrome_main_stats_);
}

void BootTimesLoader::RecordLoginAttempted() {
  login_time_markers_.clear();
  AddLoginTimeMarker("LoginStarted", false);
  if (!have_registered_) {
    have_registered_ = true;
    registrar_.Add(this, NotificationType::LOAD_START,
                   NotificationService::AllSources());
    registrar_.Add(this, NotificationType::LOGIN_AUTHENTICATION,
                   NotificationService::AllSources());
  }
}

void BootTimesLoader::AddLoginTimeMarker(
    const std::string& marker_name, bool send_to_uma) {
  login_time_markers_.push_back(TimeMarker(marker_name, send_to_uma));
}

void BootTimesLoader::Observe(
    NotificationType type,
    const NotificationSource& source,
    const NotificationDetails& details) {
  if (type == NotificationType::LOGIN_AUTHENTICATION) {
    Details<AuthenticationNotificationDetails> auth_details(details);
    if (auth_details->success()) {
      AddLoginTimeMarker("Authenticate", true);
      RecordCurrentStats(kLoginSuccess);
      registrar_.Remove(this, NotificationType::LOGIN_AUTHENTICATION,
                        NotificationService::AllSources());
    }
  } else if (type == NotificationType::LOAD_START) {
    // Only log for first tab to render.  Make sure this is only done once.
    // If the network isn't connected we'll get a second LOAD_START once it is
    // and the page is reloaded.
    if (NetworkStateNotifier::Get()->is_connected()) {
      // Post difference between first tab and login success time.
      AddLoginTimeMarker("LoginDone", true);
      RecordCurrentStats(kChromeFirstRender);
      // Post chrome first render stat.
      registrar_.Remove(this, NotificationType::LOAD_START,
                        NotificationService::AllSources());
      // Don't swamp the FILE thread right away.
      BrowserThread::PostDelayedTask(
          BrowserThread::FILE, FROM_HERE,
          NewRunnableFunction(WriteLoginTimes, login_time_markers_),
          kLoginTimeWriteDelayMs);
      have_registered_ = false;
    } else {
      AddLoginTimeMarker("LoginRenderNoNetwork", false);
    }
  }
}

}  // namespace chromeos
<commit_msg>Corrected constant value from seconds to milliseconds. Fixed DCHECK this way.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/boot_times_loader.h"

#include <vector>

#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/process_util.h"
#include "base/singleton.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/thread.h"
#include "base/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/chromeos/login/authentication_notification_details.h"
#include "chrome/browser/chromeos/network_state_notifier.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/notification_service.h"

namespace chromeos {

// File uptime logs are located in.
static const char kLogPath[] = "/tmp";
// Prefix for the time measurement files.
static const char kUptimePrefix[] = "uptime-";
// Prefix for the disk usage files.
static const char kDiskPrefix[] = "disk-";
// Name of the time that Chrome's main() is called.
static const char kChromeMain[] = "chrome-main";
// Delay in milliseconds between file read attempts.
static const int64 kReadAttemptDelayMs = 250;
// Delay in milliseconds before writing the login times to disk.
static const int64 kLoginTimeWriteDelayMs = 3000;

// Names of login stats files.
static const char kLoginSuccess[] = "login-success";
static const char kChromeFirstRender[] = "chrome-first-render";

// Names of login UMA values.
static const char kUmaAuthenticate[] = "BootTime.Authenticate";
static const char kUmaLogin[] = "BootTime.Login";

// Name of file collecting login times.
static const char kLoginTimes[] = "login-times-sent";

BootTimesLoader::BootTimesLoader()
    : backend_(new Backend()),
      have_registered_(false) {
  login_time_markers_.reserve(30);
}

// static
BootTimesLoader* BootTimesLoader::Get() {
  return Singleton<BootTimesLoader>::get();
}

BootTimesLoader::Handle BootTimesLoader::GetBootTimes(
    CancelableRequestConsumerBase* consumer,
    BootTimesLoader::GetBootTimesCallback* callback) {
  if (!g_browser_process->file_thread()) {
    // This should only happen if Chrome is shutting down, so we don't do
    // anything.
    return 0;
  }

  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
  if (command_line.HasSwitch(switches::kTestType)) {
    // TODO(davemoore) This avoids boottimes for tests. This needs to be
    // replaced with a mock of BootTimesLoader.
    return 0;
  }

  scoped_refptr<CancelableRequest<GetBootTimesCallback> > request(
      new CancelableRequest<GetBootTimesCallback>(callback));
  AddRequest(request, consumer);

  BrowserThread::PostTask(
      BrowserThread::FILE,
      FROM_HERE,
      NewRunnableMethod(backend_.get(), &Backend::GetBootTimes, request));
  return request->handle();
}

// Extracts the uptime value from files located in /tmp, returning the
// value as a double in value.
static bool GetTime(const std::string& log, double* value) {
  FilePath log_dir(kLogPath);
  FilePath log_file = log_dir.Append(log);
  std::string contents;
  *value = 0.0;
  if (file_util::ReadFileToString(log_file, &contents)) {
    size_t space_index = contents.find(' ');
    size_t chars_left =
        space_index != std::string::npos ? space_index : std::string::npos;
    std::string value_string = contents.substr(0, chars_left);
    return base::StringToDouble(value_string, value);
  }
  return false;
}

// Converts double seconds to a TimeDelta object.
static base::TimeDelta SecondsToTimeDelta(double seconds) {
  double ms = seconds * base::Time::kMillisecondsPerSecond;
  return base::TimeDelta::FromMilliseconds(static_cast<int64>(ms));
}

// Reports the collected boot times to UMA if they haven't been
// reported yet and if metrics collection is enabled.
static void SendBootTimesToUMA(const BootTimesLoader::BootTimes& boot_times) {
  // Checks if the times for the most recent boot event have been
  // reported already to avoid sending boot time histogram samples
  // every time the user logs out.
  static const char kBootTimesSent[] = "/tmp/boot-times-sent";
  FilePath sent(kBootTimesSent);
  if (file_util::PathExists(sent))
    return;

  UMA_HISTOGRAM_TIMES("BootTime.Total",
                      SecondsToTimeDelta(boot_times.total));
  UMA_HISTOGRAM_TIMES("BootTime.Firmware",
                      SecondsToTimeDelta(boot_times.firmware));
  UMA_HISTOGRAM_TIMES("BootTime.Kernel",
                      SecondsToTimeDelta(boot_times.pre_startup));
  UMA_HISTOGRAM_TIMES("BootTime.System",
                      SecondsToTimeDelta(boot_times.system));
  if (boot_times.chrome > 0) {
    UMA_HISTOGRAM_TIMES("BootTime.Chrome",
                        SecondsToTimeDelta(boot_times.chrome));
  }

  // Stores the boot times to a file in /tmp to indicate that the
  // times for the most recent boot event have been reported
  // already. The file will be deleted at system shutdown/reboot.
  std::string boot_times_text = base::StringPrintf("total: %.2f\n"
                                                   "firmware: %.2f\n"
                                                   "kernel: %.2f\n"
                                                   "system: %.2f\n"
                                                   "chrome: %.2f\n",
                                                   boot_times.total,
                                                   boot_times.firmware,
                                                   boot_times.pre_startup,
                                                   boot_times.system,
                                                   boot_times.chrome);
  file_util::WriteFile(sent, boot_times_text.data(), boot_times_text.size());
  DCHECK(file_util::PathExists(sent));
}

void BootTimesLoader::Backend::GetBootTimes(
    scoped_refptr<GetBootTimesRequest> request) {
  const char* kFirmwareBootTime = "firmware-boot-time";
  const char* kPreStartup = "pre-startup";
  const char* kChromeExec = "chrome-exec";
  const char* kChromeMain = "chrome-main";
  const char* kXStarted = "x-started";
  const char* kLoginPromptReady = "login-prompt-ready";
  std::string uptime_prefix = kUptimePrefix;

  if (request->canceled())
    return;

  // Wait until login_prompt_ready is output by reposting.
  FilePath log_dir(kLogPath);
  FilePath log_file = log_dir.Append(uptime_prefix + kLoginPromptReady);
  if (!file_util::PathExists(log_file)) {
    BrowserThread::PostDelayedTask(
        BrowserThread::FILE,
        FROM_HERE,
        NewRunnableMethod(this, &Backend::GetBootTimes, request),
        kReadAttemptDelayMs);
    return;
  }

  BootTimes boot_times;

  GetTime(kFirmwareBootTime, &boot_times.firmware);
  GetTime(uptime_prefix + kPreStartup, &boot_times.pre_startup);
  GetTime(uptime_prefix + kXStarted, &boot_times.x_started);
  GetTime(uptime_prefix + kChromeExec, &boot_times.chrome_exec);
  GetTime(uptime_prefix + kChromeMain, &boot_times.chrome_main);
  GetTime(uptime_prefix + kLoginPromptReady, &boot_times.login_prompt_ready);

  boot_times.total = boot_times.firmware + boot_times.login_prompt_ready;
  if (boot_times.chrome_exec > 0) {
    boot_times.system = boot_times.chrome_exec - boot_times.pre_startup;
    boot_times.chrome = boot_times.login_prompt_ready - boot_times.chrome_exec;
  } else {
    boot_times.system = boot_times.login_prompt_ready - boot_times.pre_startup;
  }

  SendBootTimesToUMA(boot_times);

  request->ForwardResult(
      GetBootTimesCallback::TupleType(request->handle(), boot_times));
}

static void RecordStatsDelayed(
    const std::string& name,
    const std::string& uptime,
    const std::string& disk) {
  const FilePath log_path(kLogPath);
  std::string disk_prefix = kDiskPrefix;
  const FilePath uptime_output =
      log_path.Append(FilePath(kUptimePrefix + name));
  const FilePath disk_output = log_path.Append(FilePath(kDiskPrefix + name));

  // Write out the files, ensuring that they don't exist already.
  if (!file_util::PathExists(uptime_output))
    file_util::WriteFile(uptime_output, uptime.data(), uptime.size());
  if (!file_util::PathExists(disk_output))
    file_util::WriteFile(disk_output, disk.data(), disk.size());
}

// static
void BootTimesLoader::WriteLoginTimes(
    const std::vector<TimeMarker> login_times) {
  const int kMinTimeMillis = 1;
  const int kMaxTimeMillis = 30000;
  const int kNumBuckets = 100;
  const char kUmaPrefix[] = "BootTime.";
  const FilePath log_path(kLogPath);

  base::Time first = login_times.front().time();
  base::Time last = login_times.back().time();
  base::TimeDelta total = last - first;
  scoped_refptr<base::Histogram>total_hist = base::Histogram::FactoryTimeGet(
      kUmaLogin,
      base::TimeDelta::FromMilliseconds(kMinTimeMillis),
      base::TimeDelta::FromMilliseconds(kMaxTimeMillis),
      kNumBuckets,
      base::Histogram::kUmaTargetedHistogramFlag);
  total_hist->AddTime(total);
  std::string output =
      base::StringPrintf("%s: %.2f", kUmaLogin, total.InSecondsF());
  base::Time prev = first;
  for (unsigned int i = 0; i < login_times.size(); ++i) {
    TimeMarker tm = login_times[i];
    base::TimeDelta since_first = tm.time() - first;
    base::TimeDelta since_prev = tm.time() - prev;
    std::string name;

    if (tm.send_to_uma()) {
      name = kUmaPrefix + tm.name();
      scoped_refptr<base::Histogram>prev_hist = base::Histogram::FactoryTimeGet(
          name,
          base::TimeDelta::FromMilliseconds(kMinTimeMillis),
          base::TimeDelta::FromMilliseconds(kMaxTimeMillis),
          kNumBuckets,
          base::Histogram::kUmaTargetedHistogramFlag);
      prev_hist->AddTime(since_prev);
    } else {
      name = tm.name();
    }
    output +=
        StringPrintf(
            "\n%.2f +%.2f %s",
            since_first.InSecondsF(),
            since_prev.InSecondsF(),
            name.data());
    prev = tm.time();
  }
  file_util::WriteFile(
      log_path.Append(kLoginTimes), output.data(), output.size());
}

void BootTimesLoader::RecordStats(const std::string& name, const Stats& stats) {
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      NewRunnableFunction(
          RecordStatsDelayed, name, stats.uptime, stats.disk));
}

BootTimesLoader::Stats BootTimesLoader::GetCurrentStats() {
  const FilePath kProcUptime("/proc/uptime");
  const FilePath kDiskStat("/sys/block/sda/stat");
  Stats stats;

  file_util::ReadFileToString(kProcUptime, &stats.uptime);
  file_util::ReadFileToString(kDiskStat, &stats.disk);
  return stats;
}

void BootTimesLoader::RecordCurrentStats(const std::string& name) {
  RecordStats(name, GetCurrentStats());
}

void BootTimesLoader::SaveChromeMainStats() {
  chrome_main_stats_ = GetCurrentStats();
}

void BootTimesLoader::RecordChromeMainStats() {
  RecordStats(kChromeMain, chrome_main_stats_);
}

void BootTimesLoader::RecordLoginAttempted() {
  login_time_markers_.clear();
  AddLoginTimeMarker("LoginStarted", false);
  if (!have_registered_) {
    have_registered_ = true;
    registrar_.Add(this, NotificationType::LOAD_START,
                   NotificationService::AllSources());
    registrar_.Add(this, NotificationType::LOGIN_AUTHENTICATION,
                   NotificationService::AllSources());
  }
}

void BootTimesLoader::AddLoginTimeMarker(
    const std::string& marker_name, bool send_to_uma) {
  login_time_markers_.push_back(TimeMarker(marker_name, send_to_uma));
}

void BootTimesLoader::Observe(
    NotificationType type,
    const NotificationSource& source,
    const NotificationDetails& details) {
  if (type == NotificationType::LOGIN_AUTHENTICATION) {
    Details<AuthenticationNotificationDetails> auth_details(details);
    if (auth_details->success()) {
      AddLoginTimeMarker("Authenticate", true);
      RecordCurrentStats(kLoginSuccess);
      registrar_.Remove(this, NotificationType::LOGIN_AUTHENTICATION,
                        NotificationService::AllSources());
    }
  } else if (type == NotificationType::LOAD_START) {
    // Only log for first tab to render.  Make sure this is only done once.
    // If the network isn't connected we'll get a second LOAD_START once it is
    // and the page is reloaded.
    if (NetworkStateNotifier::Get()->is_connected()) {
      // Post difference between first tab and login success time.
      AddLoginTimeMarker("LoginDone", true);
      RecordCurrentStats(kChromeFirstRender);
      // Post chrome first render stat.
      registrar_.Remove(this, NotificationType::LOAD_START,
                        NotificationService::AllSources());
      // Don't swamp the FILE thread right away.
      BrowserThread::PostDelayedTask(
          BrowserThread::FILE, FROM_HERE,
          NewRunnableFunction(WriteLoginTimes, login_time_markers_),
          kLoginTimeWriteDelayMs);
      have_registered_ = false;
    } else {
      AddLoginTimeMarker("LoginRenderNoNetwork", false);
    }
  }
}

}  // namespace chromeos
<|endoftext|>


<commit_before><commit_msg>INTEGRATION: CWS ooo19126 (1.9.146); FILE MERGED 2005/09/05 15:03:07 rt 1.9.146.1: #i54170# Change license header: remove SISSL<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/tab_contents/site_instance.h"

#include "chrome/browser/renderer_host/browser_render_process_host.h"
#include "chrome/common/url_constants.h"
#include "chrome/common/notification_service.h"
#include "net/base/registry_controlled_domain.h"

SiteInstance::SiteInstance(BrowsingInstance* browsing_instance)
    : browsing_instance_(browsing_instance),
      render_process_host_factory_(NULL),
      process_(NULL),
      max_page_id_(-1),
      has_site_(false) {
  DCHECK(browsing_instance);

  NotificationService::current()->AddObserver(this,
      NotificationType::RENDERER_PROCESS_TERMINATED,
      NotificationService::AllSources());
}

SiteInstance::~SiteInstance() {
  // Now that no one is referencing us, we can safely remove ourselves from
  // the BrowsingInstance.  Any future visits to a page from this site
  // (within the same BrowsingInstance) can safely create a new SiteInstance.
  if (has_site_)
    browsing_instance_->UnregisterSiteInstance(this);

  NotificationService::current()->RemoveObserver(this,
      NotificationType::RENDERER_PROCESS_TERMINATED,
      NotificationService::AllSources());
}

RenderProcessHost* SiteInstance::GetProcess() {
  // Create a new process if ours went away or was reused.
  if (!process_) {
    // See if we should reuse an old process
    if (RenderProcessHost::ShouldTryToUseExistingProcessHost())
      process_ = RenderProcessHost::GetExistingProcessHost(
          browsing_instance_->profile());

    // Otherwise (or if that fails), create a new one.
    if (!process_) {
      if (render_process_host_factory_) {
        process_ = render_process_host_factory_->CreateRenderProcessHost(
            browsing_instance_->profile());
      } else {
        process_ = new BrowserRenderProcessHost(browsing_instance_->profile());
      }
    }

    // Make sure the process starts at the right max_page_id
    process_->UpdateMaxPageID(max_page_id_);
  }
  DCHECK(process_);

  return process_;
}

void SiteInstance::SetSite(const GURL& url) {
  // A SiteInstance's site should not change.
  // TODO(creis): When following links or script navigations, we can currently
  // render pages from other sites in this SiteInstance.  This will eventually
  // be fixed, but until then, we should still not set the site of a
  // SiteInstance more than once.
  DCHECK(!has_site_);

  // Remember that this SiteInstance has been used to load a URL, even if the
  // URL is invalid.
  has_site_ = true;
  site_ = GetSiteForURL(url);

  // Now that we have a site, register it with the BrowsingInstance.  This
  // ensures that we won't create another SiteInstance for this site within
  // the same BrowsingInstance, because all same-site pages within a
  // BrowsingInstance can script each other.
  browsing_instance_->RegisterSiteInstance(this);
}

bool SiteInstance::HasRelatedSiteInstance(const GURL& url) {
  return browsing_instance_->HasSiteInstance(url);
}

SiteInstance* SiteInstance::GetRelatedSiteInstance(const GURL& url) {
  return browsing_instance_->GetSiteInstanceForURL(url);
}

/*static*/
SiteInstance* SiteInstance::CreateSiteInstance(Profile* profile) {
  return new SiteInstance(new BrowsingInstance(profile));
}

/*static*/
SiteInstance* SiteInstance::CreateSiteInstanceForURL(Profile* profile,
                                                     const GURL& url) {
  return (new BrowsingInstance(profile))->GetSiteInstanceForURL(url);
}

/*static*/
GURL SiteInstance::GetSiteForURL(const GURL& url) {
  // URLs with no host should have an empty site.
  GURL site;

  // TODO(creis): For many protocols, we should just treat the scheme as the
  // site, since there is no host.  e.g., file:, about:, chrome:

  // If the url has a host, then determine the site.
  if (url.has_host()) {
    // Only keep the scheme and registered domain as given by GetOrigin.  This
    // may also include a port, which we need to drop.
    site = url.GetOrigin();

    // Remove port, if any.
    if (site.has_port()) {
      GURL::Replacements rep;
      rep.ClearPort();
      site = site.ReplaceComponents(rep);
    }

    // If this URL has a registered domain, we only want to remember that part.
    std::string domain =
        net::RegistryControlledDomainService::GetDomainAndRegistry(url);
    if (!domain.empty()) {
      GURL::Replacements rep;
      rep.SetHostStr(domain);
      site = site.ReplaceComponents(rep);
    }
  }
  return site;
}

/*static*/
bool SiteInstance::IsSameWebSite(const GURL& url1, const GURL& url2) {
  // We infer web site boundaries based on the registered domain name of the
  // top-level page and the scheme.  We do not pay attention to the port if
  // one is present, because pages served from different ports can still
  // access each other if they change their document.domain variable.

  // We must treat javascript: URLs as part of the same site, regardless of
  // the site.
  if (url1.SchemeIs(chrome::kJavaScriptScheme) ||
      url2.SchemeIs(chrome::kJavaScriptScheme))
    return true;

  // We treat about:crash, about:hang, and about:shorthang as the same site as
  // any URL, since they are used as demos for crashing/hanging a process.
  GURL about_crash = GURL("about:crash");
  GURL about_hang = GURL("about:hang");
  GURL about_shorthang = GURL("about:shorthang");
  if (url1 == about_crash || url2 == about_crash ||
    url1 == about_hang || url2 == about_hang ||
    url1 == about_shorthang || url2 == about_shorthang)
    return true;

  // If either URL is invalid, they aren't part of the same site.
  if (!url1.is_valid() || !url2.is_valid()) {
    return false;
  }

  // If the schemes differ, they aren't part of the same site.
  if (url1.scheme() != url2.scheme()) {
    return false;
  }

  return net::RegistryControlledDomainService::SameDomainOrHost(url1, url2);
}

void SiteInstance::Observe(NotificationType type,
                           const NotificationSource& source,
                           const NotificationDetails& details) {
  DCHECK(type == NotificationType::RENDERER_PROCESS_TERMINATED);
  RenderProcessHost* rph = Source<RenderProcessHost>(source).ptr();
  if (rph == process_)
    process_ = NULL;
}
<commit_msg>Fix memory leak in SiteInstance::CreateSiteInstanceForURL.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/tab_contents/site_instance.h"

#include "chrome/browser/renderer_host/browser_render_process_host.h"
#include "chrome/common/url_constants.h"
#include "chrome/common/notification_service.h"
#include "net/base/registry_controlled_domain.h"

SiteInstance::SiteInstance(BrowsingInstance* browsing_instance)
    : browsing_instance_(browsing_instance),
      render_process_host_factory_(NULL),
      process_(NULL),
      max_page_id_(-1),
      has_site_(false) {
  DCHECK(browsing_instance);

  NotificationService::current()->AddObserver(this,
      NotificationType::RENDERER_PROCESS_TERMINATED,
      NotificationService::AllSources());
}

SiteInstance::~SiteInstance() {
  // Now that no one is referencing us, we can safely remove ourselves from
  // the BrowsingInstance.  Any future visits to a page from this site
  // (within the same BrowsingInstance) can safely create a new SiteInstance.
  if (has_site_)
    browsing_instance_->UnregisterSiteInstance(this);

  NotificationService::current()->RemoveObserver(this,
      NotificationType::RENDERER_PROCESS_TERMINATED,
      NotificationService::AllSources());
}

RenderProcessHost* SiteInstance::GetProcess() {
  // Create a new process if ours went away or was reused.
  if (!process_) {
    // See if we should reuse an old process
    if (RenderProcessHost::ShouldTryToUseExistingProcessHost())
      process_ = RenderProcessHost::GetExistingProcessHost(
          browsing_instance_->profile());

    // Otherwise (or if that fails), create a new one.
    if (!process_) {
      if (render_process_host_factory_) {
        process_ = render_process_host_factory_->CreateRenderProcessHost(
            browsing_instance_->profile());
      } else {
        process_ = new BrowserRenderProcessHost(browsing_instance_->profile());
      }
    }

    // Make sure the process starts at the right max_page_id
    process_->UpdateMaxPageID(max_page_id_);
  }
  DCHECK(process_);

  return process_;
}

void SiteInstance::SetSite(const GURL& url) {
  // A SiteInstance's site should not change.
  // TODO(creis): When following links or script navigations, we can currently
  // render pages from other sites in this SiteInstance.  This will eventually
  // be fixed, but until then, we should still not set the site of a
  // SiteInstance more than once.
  DCHECK(!has_site_);

  // Remember that this SiteInstance has been used to load a URL, even if the
  // URL is invalid.
  has_site_ = true;
  site_ = GetSiteForURL(url);

  // Now that we have a site, register it with the BrowsingInstance.  This
  // ensures that we won't create another SiteInstance for this site within
  // the same BrowsingInstance, because all same-site pages within a
  // BrowsingInstance can script each other.
  browsing_instance_->RegisterSiteInstance(this);
}

bool SiteInstance::HasRelatedSiteInstance(const GURL& url) {
  return browsing_instance_->HasSiteInstance(url);
}

SiteInstance* SiteInstance::GetRelatedSiteInstance(const GURL& url) {
  return browsing_instance_->GetSiteInstanceForURL(url);
}

/*static*/
SiteInstance* SiteInstance::CreateSiteInstance(Profile* profile) {
  return new SiteInstance(new BrowsingInstance(profile));
}

/*static*/
SiteInstance* SiteInstance::CreateSiteInstanceForURL(Profile* profile,
                                                     const GURL& url) {
  // This BrowsingInstance may be deleted if it returns an existing
  // SiteInstance.
  scoped_refptr<BrowsingInstance> instance(new BrowsingInstance(profile));
  return instance->GetSiteInstanceForURL(url);
}

/*static*/
GURL SiteInstance::GetSiteForURL(const GURL& url) {
  // URLs with no host should have an empty site.
  GURL site;

  // TODO(creis): For many protocols, we should just treat the scheme as the
  // site, since there is no host.  e.g., file:, about:, chrome:

  // If the url has a host, then determine the site.
  if (url.has_host()) {
    // Only keep the scheme and registered domain as given by GetOrigin.  This
    // may also include a port, which we need to drop.
    site = url.GetOrigin();

    // Remove port, if any.
    if (site.has_port()) {
      GURL::Replacements rep;
      rep.ClearPort();
      site = site.ReplaceComponents(rep);
    }

    // If this URL has a registered domain, we only want to remember that part.
    std::string domain =
        net::RegistryControlledDomainService::GetDomainAndRegistry(url);
    if (!domain.empty()) {
      GURL::Replacements rep;
      rep.SetHostStr(domain);
      site = site.ReplaceComponents(rep);
    }
  }
  return site;
}

/*static*/
bool SiteInstance::IsSameWebSite(const GURL& url1, const GURL& url2) {
  // We infer web site boundaries based on the registered domain name of the
  // top-level page and the scheme.  We do not pay attention to the port if
  // one is present, because pages served from different ports can still
  // access each other if they change their document.domain variable.

  // We must treat javascript: URLs as part of the same site, regardless of
  // the site.
  if (url1.SchemeIs(chrome::kJavaScriptScheme) ||
      url2.SchemeIs(chrome::kJavaScriptScheme))
    return true;

  // We treat about:crash, about:hang, and about:shorthang as the same site as
  // any URL, since they are used as demos for crashing/hanging a process.
  GURL about_crash = GURL("about:crash");
  GURL about_hang = GURL("about:hang");
  GURL about_shorthang = GURL("about:shorthang");
  if (url1 == about_crash || url2 == about_crash ||
    url1 == about_hang || url2 == about_hang ||
    url1 == about_shorthang || url2 == about_shorthang)
    return true;

  // If either URL is invalid, they aren't part of the same site.
  if (!url1.is_valid() || !url2.is_valid()) {
    return false;
  }

  // If the schemes differ, they aren't part of the same site.
  if (url1.scheme() != url2.scheme()) {
    return false;
  }

  return net::RegistryControlledDomainService::SameDomainOrHost(url1, url2);
}

void SiteInstance::Observe(NotificationType type,
                           const NotificationSource& source,
                           const NotificationDetails& details) {
  DCHECK(type == NotificationType::RENDERER_PROCESS_TERMINATED);
  RenderProcessHost* rph = Source<RenderProcessHost>(source).ptr();
  if (rph == process_)
    process_ = NULL;
}
<|endoftext|>


<commit_before><commit_msg>INTEGRATION: CWS qpro03 (1.1.2); FILE ADDED 2005/10/17 18:25:26 er 1.1.2.5: #i41688# identifier mismatch in initialization; remove superfluous array of String init 2005/10/14 15:11:09 mmeeks 1.1.2.4: Issue i#41688#<commit_after><|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2016 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Jan Steemann
////////////////////////////////////////////////////////////////////////////////

#include "ApplicationServer.h"

#include "ApplicationFeatures/ApplicationFeature.h"
#include "Basics/StringUtils.h"
#include "ProgramOptions/ArgumentParser.h"
#include "Logger/Logger.h"

using namespace arangodb::application_features;
using namespace arangodb::basics;
using namespace arangodb::options;

ApplicationServer* ApplicationServer::server = nullptr;

ApplicationServer::ApplicationServer(std::shared_ptr<ProgramOptions> options)
    : _options(options),
      _stopping(false),
      _privilegesDropped(false),
      _dumpDependencies(false) {
  if (ApplicationServer::server != nullptr) {
    LOG(ERR) << "ApplicationServer initialized twice";
  }

  ApplicationServer::server = this;
}

ApplicationServer::~ApplicationServer() {
  for (auto& it : _features) {
    delete it.second;
  }

  ApplicationServer::server = nullptr;
}

ApplicationFeature* ApplicationServer::lookupFeature(std::string const& name) {
  if (ApplicationServer::server == nullptr) {
    return nullptr;
  }

  try {
    return ApplicationServer::server->feature(name);
  } catch (...) {
  }

  return nullptr;
}

void ApplicationServer::disableFeatures(std::vector<std::string> const& names) {
  for (auto name : names) {
    auto feature = ApplicationServer::lookupFeature(name);

    if (feature != nullptr) {
      feature->disable();
    }
  }
}

// adds a feature to the application server. the application server
// will take ownership of the feature object and destroy it in its
// destructor
void ApplicationServer::addFeature(ApplicationFeature* feature) {
  _features.emplace(feature->name(), feature);
}

// checks for the existence of a named feature. will not throw when used for
// a non-existing feature
bool ApplicationServer::exists(std::string const& name) const {
  return (_features.find(name) != _features.end());
}

// returns a pointer to a named feature. will throw when used for
// a non-existing feature
ApplicationFeature* ApplicationServer::feature(std::string const& name) const {
  auto it = _features.find(name);

  if (it == _features.end()) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "unknown feature '" + name + "'");
  }
  return (*it).second;
}

// return whether or not a feature is enabled
// will throw when called for a non-existing feature
bool ApplicationServer::isEnabled(std::string const& name) const {
  return feature(name)->isEnabled();
}

// return whether or not a feature is optional
// will throw when called for a non-existing feature
bool ApplicationServer::isOptional(std::string const& name) const {
  return feature(name)->isOptional();
}

// return whether or not a feature is required
// will throw when called for a non-existing feature
bool ApplicationServer::isRequired(std::string const& name) const {
  return feature(name)->isRequired();
}

// this method will initialize and validate options
// of all feature, start them and wait for a shutdown
// 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
  collectOptions();

  // setup dependency, but ignore any failure for now
  setupDependencies(false);

  // parse the command line parameters and load any configuration
  // file(s)
  parseOptions(argc, argv);

  // seal the options
  _options->seal();

  // validate options of all features
  validateOptions();

  // enable automatic features
  enableAutomaticFeatures();

  // setup and validate all feature dependencies
  setupDependencies(true);

  // allows process control
  daemonize();

  // now the features will actually do some preparation work
  // in the preparation phase, the features must not start any threads
  // furthermore, they must not write any files under elevated privileges
  // if they want other features to access them, or if they want to access
  // these files with dropped privileges
  prepare();

  // permanently drop the privileges
  dropPrivilegesPermanently();

  // start features. now features are allowed to start threads, write files etc.
  start();

  // wait until we get signaled the shutdown request
  wait();

  // stop all features
  stop();
}

// signal the server to shut down
void ApplicationServer::beginShutdown() {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "";
  LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::beginShutdown";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "------------------------------------------------";

  // fowards the begin shutdown signal to all features
  for (auto it = _orderedFeatures.rbegin(); it != _orderedFeatures.rend();
       ++it) {
    if ((*it)->isEnabled()) {
      (*it)->beginShutdown();
    }
  }

  _stopping = true;
  // TODO: use condition variable for signaling shutdown
  // to run method
}

VPackBuilder ApplicationServer::options(
    std::unordered_set<std::string> const& excludes) const {
  return _options->toVPack(false, excludes);
}

// fail and abort with the specified message
void ApplicationServer::fail(std::string const& message) {
  LOG(FATAL) << "error. cannot proceed. reason: " << message;
  FATAL_ERROR_EXIT();
}

// walks over all features and runs a callback function for them
// the order in which features are visited is unspecified
void ApplicationServer::apply(std::function<void(ApplicationFeature*)> callback,
                              bool enabledOnly) {
  for (auto& it : _features) {
    if (!enabledOnly || it.second->isEnabled()) {
      callback(it.second);
    }
  }
}

void ApplicationServer::collectOptions() {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::collectOptions";

  _options->addSection(
      Section("", "Global configuration", "global options", false, false));

  _options->addHiddenOption("--dump-dependencies", "dump dependency graph",
                            new BooleanParameter(&_dumpDependencies, false));

  apply([this](ApplicationFeature* feature) {
    feature->collectOptions(_options);
  }, true);
}

void ApplicationServer::parseOptions(int argc, char* argv[]) {
  ArgumentParser parser(_options.get());

  std::string helpSection = parser.helpSection(argc, argv);

  if (!helpSection.empty()) {
    // user asked for "--help"
    _options->printHelp(helpSection);
    exit(EXIT_SUCCESS);
  }

  if (!parser.parse(argc, argv)) {
    // command-line option parsing failed. an error was already printed
    // by now, so we can exit
    exit(EXIT_FAILURE);
  }

  if (_dumpDependencies) {
    std::cout << "digraph dependencies\n"
              << "{\n"
              << "  overlap = false;\n";
    for (auto feature : _features) {
      for (auto before : feature.second->startsAfter()) {
        std::cout << "  " << feature.first << " -> " << before << ";\n";
      }
    }
    std::cout << "}\n";
    exit(EXIT_SUCCESS);
  }

  for (auto it = _orderedFeatures.begin(); it != _orderedFeatures.end(); ++it) {
    if ((*it)->isEnabled()) {
      (*it)->loadOptions(_options);
    }
  }
}

void ApplicationServer::validateOptions() {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "";
  LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::validateOptions";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "------------------------------------------------";

  for (auto it = _orderedFeatures.begin(); it != _orderedFeatures.end(); ++it) {
    if ((*it)->isEnabled()) {
      (*it)->validateOptions(_options);
    }
  }
}

void ApplicationServer::enableAutomaticFeatures() {
  bool changed;
  do {
    changed = false;
    for (auto& it : _features) {
      auto other = it.second->enableWith();
      if (other.empty()) {
        continue;
      }
      if (!this->exists(other)) {
        fail("feature '" + it.second->name() +
             "' depends on unknown feature '" + other + "'");
      }
      bool otherIsEnabled = this->feature(other)->isEnabled();
      if (otherIsEnabled != it.second->isEnabled()) {
        it.second->setEnabled(otherIsEnabled);
        changed = true;
      }
    }
  } while (changed);
}

// setup and validate all feature dependencies, determine feature order
void ApplicationServer::setupDependencies(bool failOnMissing) {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "ApplicationServer::validateDependencies";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "------------------------------------------------";

  // first check if a feature references an unknown other feature
  if (failOnMissing) {
    apply([this](ApplicationFeature* feature) {
      for (auto& other : feature->requires()) {
        if (!this->exists(other)) {
          fail("feature '" + feature->name() +
               "' depends on unknown feature '" + other + "'");
        }
        if (!this->feature(other)->isEnabled()) {
          fail("enabled feature '" + feature->name() +
               "' depends on other feature '" + other + "', which is disabled");
        }
      }
    }, true);
  }

  // first insert all features, even the inactive ones
  std::vector<ApplicationFeature*> features;
  for (auto& it : _features) {
    auto insertPosition = features.end();

    if (!features.empty()) {
      for (size_t i = features.size(); i > 0; --i) {
        if (it.second->doesStartBefore(features[i - 1]->name())) {
          insertPosition = features.begin() + (i - 1);
        }
      }
    }
    features.insert(insertPosition, it.second);
  }

  LOG_TOPIC(TRACE, Logger::STARTUP) << "ordered features:";

  for (auto feature : features) {
    LOG_TOPIC(TRACE, Logger::STARTUP)
        << "  " << feature->name()
        << (feature->isEnabled() ? "" : "(disabled)");

    auto startsAfter = feature->startsAfter();

    if (!startsAfter.empty()) {
      LOG_TOPIC(TRACE, Logger::STARTUP)
          << "    " << StringUtils::join(feature->startsAfter(), ", ");
    }
  }

  // remove all inactive features
  for (auto it = features.begin(); it != features.end(); /* no hoisting */) {
    if ((*it)->isEnabled()) {
      // keep feature
      ++it;
    } else {
      // remove feature
      it = features.erase(it);
    }
  }

  _orderedFeatures = features;
}

void ApplicationServer::daemonize() {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "";
  LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::daemonize";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "------------------------------------------------";

  for (auto it = _orderedFeatures.begin(); it != _orderedFeatures.end(); ++it) {
    if ((*it)->isEnabled()) {
      (*it)->daemonize();
    }
  }
}

void ApplicationServer::prepare() {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "";
  LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::prepare";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "------------------------------------------------";

  // 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();

      if (requiresElevated != privilegesElevated) {
        // must change privileges for the feature
        if (requiresElevated) {
          raisePrivilegesTemporarily();
          privilegesElevated = true;
        } else {
          dropPrivilegesTemporarily();
          privilegesElevated = false;
        }
      }

      try {
        (*it)->prepare();
      } catch (...) {
        // restore original privileges
        if (!privilegesElevated) {
          raisePrivilegesTemporarily();
        }
        throw;
      }
    }
  }
}

void ApplicationServer::start() {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "";
  LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::start";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "------------------------------------------------";

  for (auto it = _orderedFeatures.begin(); it != _orderedFeatures.end(); ++it) {
    (*it)->start();
  }
}

void ApplicationServer::stop() {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "";
  LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::stop";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "------------------------------------------------";

  for (auto it = _orderedFeatures.rbegin(); it != _orderedFeatures.rend();
       ++it) {
    (*it)->stop();
  }
}

void ApplicationServer::wait() {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "";
  LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::wait";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "------------------------------------------------";

  while (!_stopping) {
    // TODO: use condition variable for waiting for shutdown
    ::usleep(100000);
  }
}

// temporarily raise privileges
void ApplicationServer::raisePrivilegesTemporarily() {
  if (_privilegesDropped) {
    THROW_ARANGO_EXCEPTION_MESSAGE(
        TRI_ERROR_INTERNAL, "must not raise privileges after dropping them");
  }

  // TODO
}

// temporarily drop privileges
void ApplicationServer::dropPrivilegesTemporarily() {
  if (_privilegesDropped) {
    THROW_ARANGO_EXCEPTION_MESSAGE(
        TRI_ERROR_INTERNAL,
        "must not try to drop privileges after dropping them");
  }

  // TODO
}

// permanently dropped privileges
void ApplicationServer::dropPrivilegesPermanently() {
  if (_privilegesDropped) {
    THROW_ARANGO_EXCEPTION_MESSAGE(
        TRI_ERROR_INTERNAL,
        "must not try to drop privileges after dropping them");
  }
  _privilegesDropped = true;

  // TODO
}
<commit_msg>re-added "--help-all"<commit_after>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2016 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Jan Steemann
////////////////////////////////////////////////////////////////////////////////

#include "ApplicationServer.h"

#include "ApplicationFeatures/ApplicationFeature.h"
#include "Basics/StringUtils.h"
#include "ProgramOptions/ArgumentParser.h"
#include "Logger/Logger.h"

using namespace arangodb::application_features;
using namespace arangodb::basics;
using namespace arangodb::options;

ApplicationServer* ApplicationServer::server = nullptr;

ApplicationServer::ApplicationServer(std::shared_ptr<ProgramOptions> options)
    : _options(options),
      _stopping(false),
      _privilegesDropped(false),
      _dumpDependencies(false) {
  if (ApplicationServer::server != nullptr) {
    LOG(ERR) << "ApplicationServer initialized twice";
  }

  ApplicationServer::server = this;
}

ApplicationServer::~ApplicationServer() {
  for (auto& it : _features) {
    delete it.second;
  }

  ApplicationServer::server = nullptr;
}

ApplicationFeature* ApplicationServer::lookupFeature(std::string const& name) {
  if (ApplicationServer::server == nullptr) {
    return nullptr;
  }

  try {
    return ApplicationServer::server->feature(name);
  } catch (...) {
  }

  return nullptr;
}

void ApplicationServer::disableFeatures(std::vector<std::string> const& names) {
  for (auto name : names) {
    auto feature = ApplicationServer::lookupFeature(name);

    if (feature != nullptr) {
      feature->disable();
    }
  }
}

// adds a feature to the application server. the application server
// will take ownership of the feature object and destroy it in its
// destructor
void ApplicationServer::addFeature(ApplicationFeature* feature) {
  _features.emplace(feature->name(), feature);
}

// checks for the existence of a named feature. will not throw when used for
// a non-existing feature
bool ApplicationServer::exists(std::string const& name) const {
  return (_features.find(name) != _features.end());
}

// returns a pointer to a named feature. will throw when used for
// a non-existing feature
ApplicationFeature* ApplicationServer::feature(std::string const& name) const {
  auto it = _features.find(name);

  if (it == _features.end()) {
    THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
                                   "unknown feature '" + name + "'");
  }
  return (*it).second;
}

// return whether or not a feature is enabled
// will throw when called for a non-existing feature
bool ApplicationServer::isEnabled(std::string const& name) const {
  return feature(name)->isEnabled();
}

// return whether or not a feature is optional
// will throw when called for a non-existing feature
bool ApplicationServer::isOptional(std::string const& name) const {
  return feature(name)->isOptional();
}

// return whether or not a feature is required
// will throw when called for a non-existing feature
bool ApplicationServer::isRequired(std::string const& name) const {
  return feature(name)->isRequired();
}

// this method will initialize and validate options
// of all feature, start them and wait for a shutdown
// 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
  collectOptions();

  // setup dependency, but ignore any failure for now
  setupDependencies(false);

  // parse the command line parameters and load any configuration
  // file(s)
  parseOptions(argc, argv);

  // seal the options
  _options->seal();

  // validate options of all features
  validateOptions();

  // enable automatic features
  enableAutomaticFeatures();

  // setup and validate all feature dependencies
  setupDependencies(true);

  // allows process control
  daemonize();

  // now the features will actually do some preparation work
  // in the preparation phase, the features must not start any threads
  // furthermore, they must not write any files under elevated privileges
  // if they want other features to access them, or if they want to access
  // these files with dropped privileges
  prepare();

  // permanently drop the privileges
  dropPrivilegesPermanently();

  // start features. now features are allowed to start threads, write files etc.
  start();

  // wait until we get signaled the shutdown request
  wait();

  // stop all features
  stop();
}

// signal the server to shut down
void ApplicationServer::beginShutdown() {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "";
  LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::beginShutdown";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "------------------------------------------------";

  // fowards the begin shutdown signal to all features
  for (auto it = _orderedFeatures.rbegin(); it != _orderedFeatures.rend();
       ++it) {
    if ((*it)->isEnabled()) {
      (*it)->beginShutdown();
    }
  }

  _stopping = true;
  // TODO: use condition variable for signaling shutdown
  // to run method
}

VPackBuilder ApplicationServer::options(
    std::unordered_set<std::string> const& excludes) const {
  return _options->toVPack(false, excludes);
}

// fail and abort with the specified message
void ApplicationServer::fail(std::string const& message) {
  LOG(FATAL) << "error. cannot proceed. reason: " << message;
  FATAL_ERROR_EXIT();
}

// walks over all features and runs a callback function for them
// the order in which features are visited is unspecified
void ApplicationServer::apply(std::function<void(ApplicationFeature*)> callback,
                              bool enabledOnly) {
  for (auto& it : _features) {
    if (!enabledOnly || it.second->isEnabled()) {
      callback(it.second);
    }
  }
}

void ApplicationServer::collectOptions() {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::collectOptions";

  _options->addSection(
      Section("", "Global configuration", "global options", false, false));

  _options->addHiddenOption("--dump-dependencies", "dump dependency graph",
                            new BooleanParameter(&_dumpDependencies, false));

  apply([this](ApplicationFeature* feature) {
    feature->collectOptions(_options);
  }, true);
}

void ApplicationServer::parseOptions(int argc, char* argv[]) {
  ArgumentParser parser(_options.get());

  std::string helpSection = parser.helpSection(argc, argv);

  if (!helpSection.empty()) {
    // user asked for "--help"

    // translate "all" to "*"
    if (helpSection == "all") {
      helpSection = "*";
    }
    _options->printHelp(helpSection);
    exit(EXIT_SUCCESS);
  }

  if (!parser.parse(argc, argv)) {
    // command-line option parsing failed. an error was already printed
    // by now, so we can exit
    exit(EXIT_FAILURE);
  }

  if (_dumpDependencies) {
    std::cout << "digraph dependencies\n"
              << "{\n"
              << "  overlap = false;\n";
    for (auto feature : _features) {
      for (auto before : feature.second->startsAfter()) {
        std::cout << "  " << feature.first << " -> " << before << ";\n";
      }
    }
    std::cout << "}\n";
    exit(EXIT_SUCCESS);
  }

  for (auto it = _orderedFeatures.begin(); it != _orderedFeatures.end(); ++it) {
    if ((*it)->isEnabled()) {
      (*it)->loadOptions(_options);
    }
  }
}

void ApplicationServer::validateOptions() {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "";
  LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::validateOptions";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "------------------------------------------------";

  for (auto it = _orderedFeatures.begin(); it != _orderedFeatures.end(); ++it) {
    if ((*it)->isEnabled()) {
      (*it)->validateOptions(_options);
    }
  }
}

void ApplicationServer::enableAutomaticFeatures() {
  bool changed;
  do {
    changed = false;
    for (auto& it : _features) {
      auto other = it.second->enableWith();
      if (other.empty()) {
        continue;
      }
      if (!this->exists(other)) {
        fail("feature '" + it.second->name() +
             "' depends on unknown feature '" + other + "'");
      }
      bool otherIsEnabled = this->feature(other)->isEnabled();
      if (otherIsEnabled != it.second->isEnabled()) {
        it.second->setEnabled(otherIsEnabled);
        changed = true;
      }
    }
  } while (changed);
}

// setup and validate all feature dependencies, determine feature order
void ApplicationServer::setupDependencies(bool failOnMissing) {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "ApplicationServer::validateDependencies";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "------------------------------------------------";

  // first check if a feature references an unknown other feature
  if (failOnMissing) {
    apply([this](ApplicationFeature* feature) {
      for (auto& other : feature->requires()) {
        if (!this->exists(other)) {
          fail("feature '" + feature->name() +
               "' depends on unknown feature '" + other + "'");
        }
        if (!this->feature(other)->isEnabled()) {
          fail("enabled feature '" + feature->name() +
               "' depends on other feature '" + other + "', which is disabled");
        }
      }
    }, true);
  }

  // first insert all features, even the inactive ones
  std::vector<ApplicationFeature*> features;
  for (auto& it : _features) {
    auto insertPosition = features.end();

    if (!features.empty()) {
      for (size_t i = features.size(); i > 0; --i) {
        if (it.second->doesStartBefore(features[i - 1]->name())) {
          insertPosition = features.begin() + (i - 1);
        }
      }
    }
    features.insert(insertPosition, it.second);
  }

  LOG_TOPIC(TRACE, Logger::STARTUP) << "ordered features:";

  for (auto feature : features) {
    LOG_TOPIC(TRACE, Logger::STARTUP)
        << "  " << feature->name()
        << (feature->isEnabled() ? "" : "(disabled)");

    auto startsAfter = feature->startsAfter();

    if (!startsAfter.empty()) {
      LOG_TOPIC(TRACE, Logger::STARTUP)
          << "    " << StringUtils::join(feature->startsAfter(), ", ");
    }
  }

  // remove all inactive features
  for (auto it = features.begin(); it != features.end(); /* no hoisting */) {
    if ((*it)->isEnabled()) {
      // keep feature
      ++it;
    } else {
      // remove feature
      it = features.erase(it);
    }
  }

  _orderedFeatures = features;
}

void ApplicationServer::daemonize() {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "";
  LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::daemonize";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "------------------------------------------------";

  for (auto it = _orderedFeatures.begin(); it != _orderedFeatures.end(); ++it) {
    if ((*it)->isEnabled()) {
      (*it)->daemonize();
    }
  }
}

void ApplicationServer::prepare() {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "";
  LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::prepare";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "------------------------------------------------";

  // 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();

      if (requiresElevated != privilegesElevated) {
        // must change privileges for the feature
        if (requiresElevated) {
          raisePrivilegesTemporarily();
          privilegesElevated = true;
        } else {
          dropPrivilegesTemporarily();
          privilegesElevated = false;
        }
      }

      try {
        (*it)->prepare();
      } catch (...) {
        // restore original privileges
        if (!privilegesElevated) {
          raisePrivilegesTemporarily();
        }
        throw;
      }
    }
  }
}

void ApplicationServer::start() {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "";
  LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::start";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "------------------------------------------------";

  for (auto it = _orderedFeatures.begin(); it != _orderedFeatures.end(); ++it) {
    (*it)->start();
  }
}

void ApplicationServer::stop() {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "";
  LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::stop";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "------------------------------------------------";

  for (auto it = _orderedFeatures.rbegin(); it != _orderedFeatures.rend();
       ++it) {
    (*it)->stop();
  }
}

void ApplicationServer::wait() {
  LOG_TOPIC(TRACE, Logger::STARTUP) << "";
  LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::wait";
  LOG_TOPIC(TRACE, Logger::STARTUP)
      << "------------------------------------------------";

  while (!_stopping) {
    // TODO: use condition variable for waiting for shutdown
    ::usleep(100000);
  }
}

// temporarily raise privileges
void ApplicationServer::raisePrivilegesTemporarily() {
  if (_privilegesDropped) {
    THROW_ARANGO_EXCEPTION_MESSAGE(
        TRI_ERROR_INTERNAL, "must not raise privileges after dropping them");
  }

  // TODO
}

// temporarily drop privileges
void ApplicationServer::dropPrivilegesTemporarily() {
  if (_privilegesDropped) {
    THROW_ARANGO_EXCEPTION_MESSAGE(
        TRI_ERROR_INTERNAL,
        "must not try to drop privileges after dropping them");
  }

  // TODO
}

// permanently dropped privileges
void ApplicationServer::dropPrivilegesPermanently() {
  if (_privilegesDropped) {
    THROW_ARANGO_EXCEPTION_MESSAGE(
        TRI_ERROR_INTERNAL,
        "must not try to drop privileges after dropping them");
  }
  _privilegesDropped = true;

  // TODO
}
<|endoftext|>