<commit_before>//===-LTOBackend.cpp - LLVM Link Time Optimizer Backend -------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the "backend" phase of LTO, i.e. it performs
// optimization and code generation on a loaded module. It is generally used
// internally by the LTO class but can also be used independently, for example
// to implement a standalone ThinLTO backend.
//
//===----------------------------------------------------------------------===//

#include "llvm/LTO/LTOBackend.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/LTO/LTO.h"
#include "llvm/MC/SubtargetFeature.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils/FunctionImportUtils.h"
#include "llvm/Transforms/Utils/SplitModule.h"

using namespace llvm;
using namespace lto;

Error Config::addSaveTemps(std::string OutputFileName,
                           bool UseInputModulePath) {
  ShouldDiscardValueNames = false;

  std::error_code EC;
  ResolutionFile = llvm::make_unique<raw_fd_ostream>(
      OutputFileName + ".resolution.txt", EC, sys::fs::OpenFlags::F_Text);
  if (EC)
    return errorCodeToError(EC);

  auto setHook = [&](std::string PathSuffix, ModuleHookFn &Hook) {
    // Keep track of the hook provided by the linker, which also needs to run.
    ModuleHookFn LinkerHook = Hook;
    Hook = [=](unsigned Task, const Module &M) {
      // If the linker's hook returned false, we need to pass that result
      // through.
      if (LinkerHook && !LinkerHook(Task, M))
        return false;

      std::string PathPrefix;
      // If this is the combined module (not a ThinLTO backend compile) or the
      // user hasn't requested using the input module's path, emit to a file
      // named from the provided OutputFileName with the Task ID appended.
      if (M.getModuleIdentifier() == "ld-temp.o" || !UseInputModulePath) {
        PathPrefix = OutputFileName;
        if (Task != 0)
          PathPrefix += "." + utostr(Task);
      } else
        PathPrefix = M.getModuleIdentifier();
      std::string Path = PathPrefix + "." + PathSuffix + ".bc";
      std::error_code EC;
      raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::F_None);
      if (EC) {
        // Because -save-temps is a debugging feature, we report the error
        // directly and exit.
        llvm::errs() << "failed to open " << Path << ": " << EC.message()
                     << '\n';
        exit(1);
      }
      WriteBitcodeToFile(&M, OS, /*ShouldPreserveUseListOrder=*/false);
      return true;
    };
  };

  setHook("0.preopt", PreOptModuleHook);
  setHook("1.promote", PostPromoteModuleHook);
  setHook("2.internalize", PostInternalizeModuleHook);
  setHook("3.import", PostImportModuleHook);
  setHook("4.opt", PostOptModuleHook);
  setHook("5.precodegen", PreCodeGenModuleHook);

  CombinedIndexHook = [=](const ModuleSummaryIndex &Index) {
    std::string Path = OutputFileName + ".index.bc";
    std::error_code EC;
    raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::F_None);
    if (EC) {
      // Because -save-temps is a debugging feature, we report the error
      // directly and exit.
      llvm::errs() << "failed to open " << Path << ": " << EC.message() << '\n';
      exit(1);
    }
    WriteIndexToFile(Index, OS);
    return true;
  };

  return Error();
}

namespace {

std::unique_ptr<TargetMachine>
createTargetMachine(Config &C, StringRef TheTriple, const Target *TheTarget) {
  SubtargetFeatures Features;
  Features.getDefaultSubtargetFeatures(Triple(TheTriple));
  for (const std::string &A : C.MAttrs)
    Features.AddFeature(A);

  return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
      TheTriple, C.CPU, Features.getString(), C.Options, C.RelocModel,
      C.CodeModel, C.CGOptLevel));
}

bool opt(Config &C, TargetMachine *TM, unsigned Task, Module &M,
         bool IsThinLto) {
  M.setDataLayout(TM->createDataLayout());

  legacy::PassManager passes;
  passes.add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis()));

  PassManagerBuilder PMB;
  PMB.LibraryInfo = new TargetLibraryInfoImpl(Triple(TM->getTargetTriple()));
  PMB.Inliner = createFunctionInliningPass();
  // Unconditionally verify input since it is not verified before this
  // point and has unknown origin.
  PMB.VerifyInput = true;
  PMB.VerifyOutput = !C.DisableVerify;
  PMB.LoopVectorize = true;
  PMB.SLPVectorize = true;
  PMB.OptLevel = C.OptLevel;
  if (IsThinLto)
    PMB.populateThinLTOPassManager(passes);
  else
    PMB.populateLTOPassManager(passes);
  passes.run(M);

  if (C.PostOptModuleHook && !C.PostOptModuleHook(Task, M))
    return false;

  return true;
}

/// Monolithic LTO does not support caching (yet), this is a convenient wrapper
/// around AddOutput to workaround this.
static AddOutputFn getUncachedOutputWrapper(AddOutputFn &AddOutput,
                                            unsigned Task) {
  return [Task, &AddOutput](unsigned TaskId) {
    auto Output = AddOutput(Task);
    if (Output->isCachingEnabled() && Output->tryLoadFromCache(""))
      report_fatal_error("Cache hit without a valid key?");
    errs() << Task << " == " << TaskId << "\n";
    assert(Task == TaskId && "Unexpexted TaskId mismatch");
    return Output;
  };
}

void codegen(Config &C, TargetMachine *TM, AddOutputFn AddOutput, unsigned Task,
             Module &M) {
  if (C.PreCodeGenModuleHook && !C.PreCodeGenModuleHook(Task, M))
    return;

  auto Output = AddOutput(Task);
  std::unique_ptr<raw_pwrite_stream> OS = Output->getStream();
  legacy::PassManager CodeGenPasses;
  if (TM->addPassesToEmitFile(CodeGenPasses, *OS,
                              TargetMachine::CGFT_ObjectFile))
    report_fatal_error("Failed to setup codegen");
  CodeGenPasses.run(M);
}

void splitCodeGen(Config &C, TargetMachine *TM, AddOutputFn AddOutput,
                  unsigned ParallelCodeGenParallelismLevel,
                  std::unique_ptr<Module> M) {
  ThreadPool CodegenThreadPool(ParallelCodeGenParallelismLevel);
  unsigned ThreadCount = 0;
  const Target *T = &TM->getTarget();

  SplitModule(
      std::move(M), ParallelCodeGenParallelismLevel,
      [&](std::unique_ptr<Module> MPart) {
        // We want to clone the module in a new context to multi-thread the
        // codegen. We do it by serializing partition modules to bitcode
        // (while still on the main thread, in order to avoid data races) and
        // spinning up new threads which deserialize the partitions into
        // separate contexts.
        // FIXME: Provide a more direct way to do this in LLVM.
        SmallString<0> BC;
        raw_svector_ostream BCOS(BC);
        WriteBitcodeToFile(MPart.get(), BCOS);

        // Enqueue the task
        CodegenThreadPool.async(
            [&](const SmallString<0> &BC, unsigned ThreadId) {
              LTOLLVMContext Ctx(C);
              ErrorOr<std::unique_ptr<Module>> MOrErr = parseBitcodeFile(
                  MemoryBufferRef(StringRef(BC.data(), BC.size()), "ld-temp.o"),
                  Ctx);
              if (!MOrErr)
                report_fatal_error("Failed to read bitcode");
              std::unique_ptr<Module> MPartInCtx = std::move(MOrErr.get());

              std::unique_ptr<TargetMachine> TM =
                  createTargetMachine(C, MPartInCtx->getTargetTriple(), T);

              codegen(C, TM.get(),
                      getUncachedOutputWrapper(AddOutput, ThreadId), ThreadId,
                      *MPartInCtx);
            },
            // Pass BC using std::move to ensure that it get moved rather than
            // copied into the thread's context.
            std::move(BC), ThreadCount++);
      },
      false);
}

Expected<const Target *> initAndLookupTarget(Config &C, Module &M) {
  if (!C.OverrideTriple.empty())
    M.setTargetTriple(C.OverrideTriple);
  else if (M.getTargetTriple().empty())
    M.setTargetTriple(C.DefaultTriple);

  std::string Msg;
  const Target *T = TargetRegistry::lookupTarget(M.getTargetTriple(), Msg);
  if (!T)
    return make_error<StringError>(Msg, inconvertibleErrorCode());
  return T;
}

}

Error lto::backend(Config &C, AddOutputFn AddOutput,
                   unsigned ParallelCodeGenParallelismLevel,
                   std::unique_ptr<Module> M) {
  Expected<const Target *> TOrErr = initAndLookupTarget(C, *M);
  if (!TOrErr)
    return TOrErr.takeError();

  std::unique_ptr<TargetMachine> TM =
      createTargetMachine(C, M->getTargetTriple(), *TOrErr);

  if (!C.CodeGenOnly)
    if (!opt(C, TM.get(), 0, *M, /*IsThinLto=*/false))
      return Error();

  if (ParallelCodeGenParallelismLevel == 1) {
    codegen(C, TM.get(), getUncachedOutputWrapper(AddOutput, 0), 0, *M);
  } else {
    splitCodeGen(C, TM.get(), AddOutput, ParallelCodeGenParallelismLevel,
                 std::move(M));
  }
  return Error();
}

Error lto::thinBackend(Config &Conf, unsigned Task, AddOutputFn AddOutput,
                       Module &Mod, ModuleSummaryIndex &CombinedIndex,
                       const FunctionImporter::ImportMapTy &ImportList,
                       const GVSummaryMapTy &DefinedGlobals,
                       MapVector<StringRef, MemoryBufferRef> &ModuleMap) {
  Expected<const Target *> TOrErr = initAndLookupTarget(Conf, Mod);
  if (!TOrErr)
    return TOrErr.takeError();

  std::unique_ptr<TargetMachine> TM =
      createTargetMachine(Conf, Mod.getTargetTriple(), *TOrErr);

  if (Conf.CodeGenOnly) {
    codegen(Conf, TM.get(), AddOutput, Task, Mod);
    return Error();
  }

  if (Conf.PreOptModuleHook && !Conf.PreOptModuleHook(Task, Mod))
    return Error();

  renameModuleForThinLTO(Mod, CombinedIndex);

  thinLTOResolveWeakForLinkerModule(Mod, DefinedGlobals);

  if (Conf.PostPromoteModuleHook && !Conf.PostPromoteModuleHook(Task, Mod))
    return Error();

  if (!DefinedGlobals.empty())
    thinLTOInternalizeModule(Mod, DefinedGlobals);

  if (Conf.PostInternalizeModuleHook &&
      !Conf.PostInternalizeModuleHook(Task, Mod))
    return Error();

  auto ModuleLoader = [&](StringRef Identifier) {
    assert(Mod.getContext().isODRUniquingDebugTypes() &&
           "ODR Type uniquing shoudl be enabled on the context");
    return std::move(getLazyBitcodeModule(MemoryBuffer::getMemBuffer(
                                              ModuleMap[Identifier], false),
                                          Mod.getContext(),
                                          /*ShouldLazyLoadMetadata=*/true)
                         .get());
  };

  FunctionImporter Importer(CombinedIndex, ModuleLoader);
  Importer.importFunctions(Mod, ImportList);

  if (Conf.PostImportModuleHook && !Conf.PostImportModuleHook(Task, Mod))
    return Error();

  if (!opt(Conf, TM.get(), Task, Mod, /*IsThinLto=*/true))
    return Error();

  codegen(Conf, TM.get(), AddOutput, Task, Mod);
  return Error();
}
<commit_msg>[LTO] Remove extraneous output<commit_after>//===-LTOBackend.cpp - LLVM Link Time Optimizer Backend -------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the "backend" phase of LTO, i.e. it performs
// optimization and code generation on a loaded module. It is generally used
// internally by the LTO class but can also be used independently, for example
// to implement a standalone ThinLTO backend.
//
//===----------------------------------------------------------------------===//

#include "llvm/LTO/LTOBackend.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/LTO/LTO.h"
#include "llvm/MC/SubtargetFeature.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils/FunctionImportUtils.h"
#include "llvm/Transforms/Utils/SplitModule.h"

using namespace llvm;
using namespace lto;

Error Config::addSaveTemps(std::string OutputFileName,
                           bool UseInputModulePath) {
  ShouldDiscardValueNames = false;

  std::error_code EC;
  ResolutionFile = llvm::make_unique<raw_fd_ostream>(
      OutputFileName + ".resolution.txt", EC, sys::fs::OpenFlags::F_Text);
  if (EC)
    return errorCodeToError(EC);

  auto setHook = [&](std::string PathSuffix, ModuleHookFn &Hook) {
    // Keep track of the hook provided by the linker, which also needs to run.
    ModuleHookFn LinkerHook = Hook;
    Hook = [=](unsigned Task, const Module &M) {
      // If the linker's hook returned false, we need to pass that result
      // through.
      if (LinkerHook && !LinkerHook(Task, M))
        return false;

      std::string PathPrefix;
      // If this is the combined module (not a ThinLTO backend compile) or the
      // user hasn't requested using the input module's path, emit to a file
      // named from the provided OutputFileName with the Task ID appended.
      if (M.getModuleIdentifier() == "ld-temp.o" || !UseInputModulePath) {
        PathPrefix = OutputFileName;
        if (Task != 0)
          PathPrefix += "." + utostr(Task);
      } else
        PathPrefix = M.getModuleIdentifier();
      std::string Path = PathPrefix + "." + PathSuffix + ".bc";
      std::error_code EC;
      raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::F_None);
      if (EC) {
        // Because -save-temps is a debugging feature, we report the error
        // directly and exit.
        llvm::errs() << "failed to open " << Path << ": " << EC.message()
                     << '\n';
        exit(1);
      }
      WriteBitcodeToFile(&M, OS, /*ShouldPreserveUseListOrder=*/false);
      return true;
    };
  };

  setHook("0.preopt", PreOptModuleHook);
  setHook("1.promote", PostPromoteModuleHook);
  setHook("2.internalize", PostInternalizeModuleHook);
  setHook("3.import", PostImportModuleHook);
  setHook("4.opt", PostOptModuleHook);
  setHook("5.precodegen", PreCodeGenModuleHook);

  CombinedIndexHook = [=](const ModuleSummaryIndex &Index) {
    std::string Path = OutputFileName + ".index.bc";
    std::error_code EC;
    raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::F_None);
    if (EC) {
      // Because -save-temps is a debugging feature, we report the error
      // directly and exit.
      llvm::errs() << "failed to open " << Path << ": " << EC.message() << '\n';
      exit(1);
    }
    WriteIndexToFile(Index, OS);
    return true;
  };

  return Error();
}

namespace {

std::unique_ptr<TargetMachine>
createTargetMachine(Config &C, StringRef TheTriple, const Target *TheTarget) {
  SubtargetFeatures Features;
  Features.getDefaultSubtargetFeatures(Triple(TheTriple));
  for (const std::string &A : C.MAttrs)
    Features.AddFeature(A);

  return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
      TheTriple, C.CPU, Features.getString(), C.Options, C.RelocModel,
      C.CodeModel, C.CGOptLevel));
}

bool opt(Config &C, TargetMachine *TM, unsigned Task, Module &M,
         bool IsThinLto) {
  M.setDataLayout(TM->createDataLayout());

  legacy::PassManager passes;
  passes.add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis()));

  PassManagerBuilder PMB;
  PMB.LibraryInfo = new TargetLibraryInfoImpl(Triple(TM->getTargetTriple()));
  PMB.Inliner = createFunctionInliningPass();
  // Unconditionally verify input since it is not verified before this
  // point and has unknown origin.
  PMB.VerifyInput = true;
  PMB.VerifyOutput = !C.DisableVerify;
  PMB.LoopVectorize = true;
  PMB.SLPVectorize = true;
  PMB.OptLevel = C.OptLevel;
  if (IsThinLto)
    PMB.populateThinLTOPassManager(passes);
  else
    PMB.populateLTOPassManager(passes);
  passes.run(M);

  if (C.PostOptModuleHook && !C.PostOptModuleHook(Task, M))
    return false;

  return true;
}

/// Monolithic LTO does not support caching (yet), this is a convenient wrapper
/// around AddOutput to workaround this.
static AddOutputFn getUncachedOutputWrapper(AddOutputFn &AddOutput,
                                            unsigned Task) {
  return [Task, &AddOutput](unsigned TaskId) {
    auto Output = AddOutput(Task);
    if (Output->isCachingEnabled() && Output->tryLoadFromCache(""))
      report_fatal_error("Cache hit without a valid key?");
    assert(Task == TaskId && "Unexpexted TaskId mismatch");
    return Output;
  };
}

void codegen(Config &C, TargetMachine *TM, AddOutputFn AddOutput, unsigned Task,
             Module &M) {
  if (C.PreCodeGenModuleHook && !C.PreCodeGenModuleHook(Task, M))
    return;

  auto Output = AddOutput(Task);
  std::unique_ptr<raw_pwrite_stream> OS = Output->getStream();
  legacy::PassManager CodeGenPasses;
  if (TM->addPassesToEmitFile(CodeGenPasses, *OS,
                              TargetMachine::CGFT_ObjectFile))
    report_fatal_error("Failed to setup codegen");
  CodeGenPasses.run(M);
}

void splitCodeGen(Config &C, TargetMachine *TM, AddOutputFn AddOutput,
                  unsigned ParallelCodeGenParallelismLevel,
                  std::unique_ptr<Module> M) {
  ThreadPool CodegenThreadPool(ParallelCodeGenParallelismLevel);
  unsigned ThreadCount = 0;
  const Target *T = &TM->getTarget();

  SplitModule(
      std::move(M), ParallelCodeGenParallelismLevel,
      [&](std::unique_ptr<Module> MPart) {
        // We want to clone the module in a new context to multi-thread the
        // codegen. We do it by serializing partition modules to bitcode
        // (while still on the main thread, in order to avoid data races) and
        // spinning up new threads which deserialize the partitions into
        // separate contexts.
        // FIXME: Provide a more direct way to do this in LLVM.
        SmallString<0> BC;
        raw_svector_ostream BCOS(BC);
        WriteBitcodeToFile(MPart.get(), BCOS);

        // Enqueue the task
        CodegenThreadPool.async(
            [&](const SmallString<0> &BC, unsigned ThreadId) {
              LTOLLVMContext Ctx(C);
              ErrorOr<std::unique_ptr<Module>> MOrErr = parseBitcodeFile(
                  MemoryBufferRef(StringRef(BC.data(), BC.size()), "ld-temp.o"),
                  Ctx);
              if (!MOrErr)
                report_fatal_error("Failed to read bitcode");
              std::unique_ptr<Module> MPartInCtx = std::move(MOrErr.get());

              std::unique_ptr<TargetMachine> TM =
                  createTargetMachine(C, MPartInCtx->getTargetTriple(), T);

              codegen(C, TM.get(),
                      getUncachedOutputWrapper(AddOutput, ThreadId), ThreadId,
                      *MPartInCtx);
            },
            // Pass BC using std::move to ensure that it get moved rather than
            // copied into the thread's context.
            std::move(BC), ThreadCount++);
      },
      false);
}

Expected<const Target *> initAndLookupTarget(Config &C, Module &M) {
  if (!C.OverrideTriple.empty())
    M.setTargetTriple(C.OverrideTriple);
  else if (M.getTargetTriple().empty())
    M.setTargetTriple(C.DefaultTriple);

  std::string Msg;
  const Target *T = TargetRegistry::lookupTarget(M.getTargetTriple(), Msg);
  if (!T)
    return make_error<StringError>(Msg, inconvertibleErrorCode());
  return T;
}

}

Error lto::backend(Config &C, AddOutputFn AddOutput,
                   unsigned ParallelCodeGenParallelismLevel,
                   std::unique_ptr<Module> M) {
  Expected<const Target *> TOrErr = initAndLookupTarget(C, *M);
  if (!TOrErr)
    return TOrErr.takeError();

  std::unique_ptr<TargetMachine> TM =
      createTargetMachine(C, M->getTargetTriple(), *TOrErr);

  if (!C.CodeGenOnly)
    if (!opt(C, TM.get(), 0, *M, /*IsThinLto=*/false))
      return Error();

  if (ParallelCodeGenParallelismLevel == 1) {
    codegen(C, TM.get(), getUncachedOutputWrapper(AddOutput, 0), 0, *M);
  } else {
    splitCodeGen(C, TM.get(), AddOutput, ParallelCodeGenParallelismLevel,
                 std::move(M));
  }
  return Error();
}

Error lto::thinBackend(Config &Conf, unsigned Task, AddOutputFn AddOutput,
                       Module &Mod, ModuleSummaryIndex &CombinedIndex,
                       const FunctionImporter::ImportMapTy &ImportList,
                       const GVSummaryMapTy &DefinedGlobals,
                       MapVector<StringRef, MemoryBufferRef> &ModuleMap) {
  Expected<const Target *> TOrErr = initAndLookupTarget(Conf, Mod);
  if (!TOrErr)
    return TOrErr.takeError();

  std::unique_ptr<TargetMachine> TM =
      createTargetMachine(Conf, Mod.getTargetTriple(), *TOrErr);

  if (Conf.CodeGenOnly) {
    codegen(Conf, TM.get(), AddOutput, Task, Mod);
    return Error();
  }

  if (Conf.PreOptModuleHook && !Conf.PreOptModuleHook(Task, Mod))
    return Error();

  renameModuleForThinLTO(Mod, CombinedIndex);

  thinLTOResolveWeakForLinkerModule(Mod, DefinedGlobals);

  if (Conf.PostPromoteModuleHook && !Conf.PostPromoteModuleHook(Task, Mod))
    return Error();

  if (!DefinedGlobals.empty())
    thinLTOInternalizeModule(Mod, DefinedGlobals);

  if (Conf.PostInternalizeModuleHook &&
      !Conf.PostInternalizeModuleHook(Task, Mod))
    return Error();

  auto ModuleLoader = [&](StringRef Identifier) {
    assert(Mod.getContext().isODRUniquingDebugTypes() &&
           "ODR Type uniquing shoudl be enabled on the context");
    return std::move(getLazyBitcodeModule(MemoryBuffer::getMemBuffer(
                                              ModuleMap[Identifier], false),
                                          Mod.getContext(),
                                          /*ShouldLazyLoadMetadata=*/true)
                         .get());
  };

  FunctionImporter Importer(CombinedIndex, ModuleLoader);
  Importer.importFunctions(Mod, ImportList);

  if (Conf.PostImportModuleHook && !Conf.PostImportModuleHook(Task, Mod))
    return Error();

  if (!opt(Conf, TM.get(), Task, Mod, /*IsThinLto=*/true))
    return Error();

  codegen(Conf, TM.get(), AddOutput, Task, Mod);
  return Error();
}
<|endoftext|>


<commit_before>// Copyright 2013 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 "init_webrtc.h"

#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/files/file_path.h"
#include "base/native_library.h"
#include "base/path_service.h"
#include "talk/base/basictypes.h"

const unsigned char* GetCategoryGroupEnabled(const char* category_group) {
  return TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_group);
}

void AddTraceEvent(char phase,
                   const unsigned char* category_group_enabled,
                   const char* name,
                   unsigned long long id,
                   int num_args,
                   const char** arg_names,
                   const unsigned char* arg_types,
                   const unsigned long long* arg_values,
                   unsigned char flags) {
  TRACE_EVENT_API_ADD_TRACE_EVENT(phase, category_group_enabled, name, id,
                                  num_args, arg_names, arg_types, arg_values,
                                  NULL, flags);
}

#if defined(LIBPEERCONNECTION_LIB)

// libpeerconnection is being compiled as a static lib.  In this case
// we don't need to do any initializing but to keep things simple we
// provide an empty intialization routine so that this #ifdef doesn't
// have to be in other places.
bool InitializeWebRtcModule() {
  webrtc::SetupEventTracer(&GetCategoryGroupEnabled, &AddTraceEvent);
  return true;
}

#else  // !LIBPEERCONNECTION_LIB

// When being compiled as a shared library, we need to bridge the gap between
// the current module and the libpeerconnection module, so things get a tad
// more complicated.

// Global function pointers to the factory functions in the shared library.
CreateWebRtcMediaEngineFunction g_create_webrtc_media_engine = NULL;
DestroyWebRtcMediaEngineFunction g_destroy_webrtc_media_engine = NULL;

// Returns the full or relative path to the libpeerconnection module depending
// on what platform we're on.
static base::FilePath GetLibPeerConnectionPath() {
#if defined(OS_WIN)
  base::FilePath path(FILE_PATH_LITERAL("libpeerconnection.dll"));
#elif defined(OS_MACOSX)
  // Simulate '@loader_path/Libraries'.
  base::FilePath path;
  CHECK(PathService::Get(base::DIR_MODULE, &path));
  path = path.Append(FILE_PATH_LITERAL("Libraries"))
             .Append(FILE_PATH_LITERAL("libpeerconnection.so"));
#elif defined(OS_ANDROID)
  base::FilePath path;
  CHECK(PathService::Get(base::DIR_MODULE, &path));
  path = path.Append(FILE_PATH_LITERAL("libpeerconnection.so"));
#else
  base::FilePath path;
  CHECK(PathService::Get(base::DIR_MODULE, &path));
  path = path.Append(FILE_PATH_LITERAL("lib"))
             .Append(FILE_PATH_LITERAL("libpeerconnection.so"));
#endif
  return path;
}

bool InitializeWebRtcModule() {
  if (g_create_webrtc_media_engine)
    return true;  // InitializeWebRtcModule has already been called.

  base::FilePath path(GetLibPeerConnectionPath());
  DVLOG(1) << "Loading WebRTC module: " << path.value();

  std::string error;
  static base::NativeLibrary lib =
      base::LoadNativeLibrary(path, &error);
  CHECK(lib) << error;

  InitializeModuleFunction initialize_module =
      reinterpret_cast<InitializeModuleFunction>(
          base::GetFunctionPointerFromNativeLibrary(
              lib, "InitializeModule"));

  // Initialize the proxy by supplying it with a pointer to our
  // allocator/deallocator routines.
  // On mac we use malloc zones, which are global, so we provide NULLs for
  // the alloc/dealloc functions.
  // PS: This function is actually implemented in allocator_proxy.cc with the
  // new/delete overrides.
  return initialize_module(*CommandLine::ForCurrentProcess(),
#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
      &Allocate, &Dellocate,
#endif
      logging::GetLogMessageHandler(),
      &GetCategoryGroupEnabled, &AddTraceEvent,
      &g_create_webrtc_media_engine, &g_destroy_webrtc_media_engine);
}

cricket::MediaEngineInterface* CreateWebRtcMediaEngine(
    webrtc::AudioDeviceModule* adm,
    webrtc::AudioDeviceModule* adm_sc,
    cricket::WebRtcVideoEncoderFactory* encoder_factory,
    cricket::WebRtcVideoDecoderFactory* decoder_factory) {
  // For convenience of tests etc, we call InitializeWebRtcModule here.
  // For Chrome however, InitializeWebRtcModule must be called
  // explicitly before the sandbox is initialized.  In that case, this call is
  // effectively a noop.
  InitializeWebRtcModule();
  return g_create_webrtc_media_engine(adm, adm_sc, encoder_factory,
      decoder_factory);
}

void DestroyWebRtcMediaEngine(cricket::MediaEngineInterface* media_engine) {
  g_destroy_webrtc_media_engine(media_engine);
}

#endif  // LIBPEERCONNECTION_LIB
<commit_msg>Candidate fix for failure on Windows to load libpeerconnection.dll. All platforms now pass the full path to the library.<commit_after>// Copyright 2013 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 "init_webrtc.h"

#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/files/file_path.h"
#include "base/native_library.h"
#include "base/path_service.h"
#include "talk/base/basictypes.h"

const unsigned char* GetCategoryGroupEnabled(const char* category_group) {
  return TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_group);
}

void AddTraceEvent(char phase,
                   const unsigned char* category_group_enabled,
                   const char* name,
                   unsigned long long id,
                   int num_args,
                   const char** arg_names,
                   const unsigned char* arg_types,
                   const unsigned long long* arg_values,
                   unsigned char flags) {
  TRACE_EVENT_API_ADD_TRACE_EVENT(phase, category_group_enabled, name, id,
                                  num_args, arg_names, arg_types, arg_values,
                                  NULL, flags);
}

#if defined(LIBPEERCONNECTION_LIB)

// libpeerconnection is being compiled as a static lib.  In this case
// we don't need to do any initializing but to keep things simple we
// provide an empty intialization routine so that this #ifdef doesn't
// have to be in other places.
bool InitializeWebRtcModule() {
  webrtc::SetupEventTracer(&GetCategoryGroupEnabled, &AddTraceEvent);
  return true;
}

#else  // !LIBPEERCONNECTION_LIB

// When being compiled as a shared library, we need to bridge the gap between
// the current module and the libpeerconnection module, so things get a tad
// more complicated.

// Global function pointers to the factory functions in the shared library.
CreateWebRtcMediaEngineFunction g_create_webrtc_media_engine = NULL;
DestroyWebRtcMediaEngineFunction g_destroy_webrtc_media_engine = NULL;

// Returns the full or relative path to the libpeerconnection module depending
// on what platform we're on.
static base::FilePath GetLibPeerConnectionPath() {
  base::FilePath path;
  CHECK(PathService::Get(base::DIR_MODULE, &path));
#if defined(OS_WIN)
  path = path.Append(FILE_PATH_LITERAL("libpeerconnection.dll"));
#elif defined(OS_MACOSX)
  // Simulate '@loader_path/Libraries'.
  path = path.Append(FILE_PATH_LITERAL("Libraries"))
             .Append(FILE_PATH_LITERAL("libpeerconnection.so"));
#elif defined(OS_ANDROID)
  path = path.Append(FILE_PATH_LITERAL("libpeerconnection.so"));
#else
  path = path.Append(FILE_PATH_LITERAL("lib"))
             .Append(FILE_PATH_LITERAL("libpeerconnection.so"));
#endif
  return path;
}

bool InitializeWebRtcModule() {
  if (g_create_webrtc_media_engine)
    return true;  // InitializeWebRtcModule has already been called.

  base::FilePath path(GetLibPeerConnectionPath());
  DVLOG(1) << "Loading WebRTC module: " << path.value();

  std::string error;
  static base::NativeLibrary lib =
      base::LoadNativeLibrary(path, &error);
  CHECK(lib) << error;

  InitializeModuleFunction initialize_module =
      reinterpret_cast<InitializeModuleFunction>(
          base::GetFunctionPointerFromNativeLibrary(
              lib, "InitializeModule"));

  // Initialize the proxy by supplying it with a pointer to our
  // allocator/deallocator routines.
  // On mac we use malloc zones, which are global, so we provide NULLs for
  // the alloc/dealloc functions.
  // PS: This function is actually implemented in allocator_proxy.cc with the
  // new/delete overrides.
  return initialize_module(*CommandLine::ForCurrentProcess(),
#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
      &Allocate, &Dellocate,
#endif
      logging::GetLogMessageHandler(),
      &GetCategoryGroupEnabled, &AddTraceEvent,
      &g_create_webrtc_media_engine, &g_destroy_webrtc_media_engine);
}

cricket::MediaEngineInterface* CreateWebRtcMediaEngine(
    webrtc::AudioDeviceModule* adm,
    webrtc::AudioDeviceModule* adm_sc,
    cricket::WebRtcVideoEncoderFactory* encoder_factory,
    cricket::WebRtcVideoDecoderFactory* decoder_factory) {
  // For convenience of tests etc, we call InitializeWebRtcModule here.
  // For Chrome however, InitializeWebRtcModule must be called
  // explicitly before the sandbox is initialized.  In that case, this call is
  // effectively a noop.
  InitializeWebRtcModule();
  return g_create_webrtc_media_engine(adm, adm_sc, encoder_factory,
      decoder_factory);
}

void DestroyWebRtcMediaEngine(cricket::MediaEngineInterface* media_engine) {
  g_destroy_webrtc_media_engine(media_engine);
}

#endif  // LIBPEERCONNECTION_LIB
<|endoftext|>


<commit_before>// Copyright (c) 2011, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// ---
// Author: Rebecca Shapiro <bxx@google.com>
//
// This file contains functions that implement doubly linked and
// singly linked lists.  The singly linked lists are null terminated,
// use raw pointers to link neighboring elements, and these pointers
// are stored at the start of each element, independently of the
// elements's size.  Because pointers are stored within each element,
// each element must be large enough to store two raw pointers if
// doubly linked lists are employed, or one raw pointer if singly
// linked lists are employed.  On machines with 64 bit pointers, this
// means elements must be at least 16 bytes in size for doubly linked
// list support, and 8 bytes for singly linked list support.  No
// attempts are made to preserve the data in elements stored in the
// list.
//
// Given a machine with pointers of size N (on a 64bit machine N=8, on
// a 32bit machine, N=4), the list pointers are stored in the
// following manner:
// -In doubly linked lists, the |next| pointer is stored in the first N
// bytes of the node and the |previous| pointer is writtend into the
// second N bytes.
// -In singly linked lists, the |next| pointer is stored in the first N
// bytes of the node.
//
// For both types of lists: when a pop operation is performed on a non
// empty list, the new list head becomes that which is pointed to by
// the former head's |next| pointer.  If the list is doubly linked, the
// new head |previous| pointer gets changed from pointing to the former
// head to NULL.

#include <limits>
#include <stddef.h>
#include "free_list.h"
#include "system-alloc.h"

#if defined(TCMALLOC_USE_DOUBLYLINKED_FREELIST)

using tcmalloc::kCrash;

// TODO(jar): We should use C++ rather than a macro here.
#define MEMORY_CHECK(v1, v2) \
  if (v1 != v2) Log(kCrash, __FILE__, __LINE__, "Memory corruption detected.")

namespace {
void EnsureNonLoop(void* node, void* next) {
  // We only have time to do minimal checking.  We don't traverse the list, but
  // only look for an immediate loop (cycle back to ourself).
  if (node != next) return;
  Log(kCrash, __FILE__, __LINE__, "Circular loop in list detected: ", next);
}

inline void* MaskPtr(void* p) {
  // Maximize ASLR entropy and guarantee the result is an invalid address.
  const uintptr_t mask = ~(reinterpret_cast<uintptr_t>(TCMalloc_SystemAlloc)
                           >> 13) | 1;
  // Do not mask NULL pointers, otherwise we could leak address state.
  if (p)
    return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(p) ^ mask);
  return p;
}

inline void* UnmaskPtr(void* p) {
  return MaskPtr(p);
}

// Returns value of the |previous| pointer w/out running a sanity
// check.
inline void *FL_Previous_No_Check(void *t) {
  return UnmaskPtr(reinterpret_cast<void**>(t)[1]);
}

// Returns value of the |next| pointer w/out running a sanity check.
inline void *FL_Next_No_Check(void *t) {
  return UnmaskPtr(reinterpret_cast<void**>(t)[0]);
}

void *FL_Previous(void *t) {
  void *previous = FL_Previous_No_Check(t);
  if (previous) {
    MEMORY_CHECK(FL_Next_No_Check(previous), t);
  }
  return previous;
}

inline void FL_SetPrevious(void *t, void *n) {
  EnsureNonLoop(t, n);
  reinterpret_cast<void**>(t)[1] = MaskPtr(n);
}

inline void FL_SetNext(void *t, void *n) {
  EnsureNonLoop(t, n);
  reinterpret_cast<void**>(t)[0] = MaskPtr(n);
}

} // namespace

namespace tcmalloc {

void *FL_Next(void *t) {
  void *next = FL_Next_No_Check(t);
  if (next) {
    MEMORY_CHECK(FL_Previous_No_Check(next), t);
  }
  return next;
}

// Makes the element at |t| a singleton doubly linked list.
void FL_Init(void *t) {
  FL_SetPrevious(t, NULL);
  FL_SetNext(t, NULL);
}

// Pushes element to a linked list whose first element is at
// |*list|. When this call returns, |list| will point to the new head
// of the linked list.
void FL_Push(void **list, void *element) {
  void *old = *list;
  if (old == NULL) { // Builds singleton list.
    FL_Init(element);
  } else {
    ASSERT(FL_Previous_No_Check(old) == NULL);
    FL_SetNext(element, old);
    FL_SetPrevious(old, element);
    FL_SetPrevious(element, NULL);
  }
  *list = element;
}

// Pops the top element off the linked list whose first element is at
// |*list|, and updates |*list| to point to the next element in the
// list.  Returns the address of the element that was removed from the
// linked list.  |list| must not be NULL.
void *FL_Pop(void **list) {
  void *result = *list;
  ASSERT(FL_Previous_No_Check(result) == NULL);
  *list = FL_Next(result);
  if (*list != NULL) {
    FL_SetPrevious(*list, NULL);
  }
  return result;
}

// Remove |n| elements from linked list at whose first element is at
// |*head|.  |head| will be modified to point to the new head.
// |start| will point to the first node of the range, |end| will point
// to the last node in the range. |n| must be <= FL_Size(|*head|)
// If |n| > 0, |head| must not be NULL.
void FL_PopRange(void **head, int n, void **start, void **end) {
  if (n == 0) {
    *start = NULL;
    *end = NULL;
    return;
  }

  *start = *head; // Remember the first node in the range.
  void *tmp = *head;
  for (int i = 1; i < n; ++i) { // Find end of range.
    tmp = FL_Next(tmp);
  }
  *end = tmp; // |end| now set to point to last node in range.
  *head = FL_Next(*end);
  FL_SetNext(*end, NULL); // Unlink range from list.

  if (*head ) { // Fixup popped list.
    FL_SetPrevious(*head, NULL);
  }
}

// Pushes the nodes in the list begginning at |start| whose last node
// is |end| into the linked list at |*head|. |*head| is updated to
// point be the new head of the list.  |head| must not be NULL.
void FL_PushRange(void **head, void *start, void *end) {
  if (!start) return;

  // Sanity checking of ends of list to push is done by calling
  // FL_Next and FL_Previous.
  FL_Next(start);
  FL_Previous(end);
  ASSERT(FL_Previous_No_Check(start) == NULL);
  ASSERT(FL_Next_No_Check(end) == NULL);

  if (*head) {
    MEMORY_CHECK(FL_Previous_No_Check(*head), NULL);
    FL_SetNext(end, *head);
    FL_SetPrevious(*head, end);
  }
  *head = start;
}

// Calculates the size of the list that begins at |head|.
size_t FL_Size(void *head){
  int count = 0;
  if (head) {
    MEMORY_CHECK(FL_Previous_No_Check(head), NULL);
  }
  while (head) {
    count++;
    head = FL_Next(head);
  }
  return count;
}

} // namespace tcmalloc

#else
#include "linked_list.h" // for SLL_SetNext

namespace {

inline void FL_SetNext(void *t, void *n) {
  tcmalloc::SLL_SetNext(t,n);
}

}

#endif // TCMALLOC_USE_DOUBLYLINKED_FREELIST
<commit_msg>Tweak TCMalloc freelist pointer masking<commit_after>// Copyright (c) 2011, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// ---
// Author: Rebecca Shapiro <bxx@google.com>
//
// This file contains functions that implement doubly linked and
// singly linked lists.  The singly linked lists are null terminated,
// use raw pointers to link neighboring elements, and these pointers
// are stored at the start of each element, independently of the
// elements's size.  Because pointers are stored within each element,
// each element must be large enough to store two raw pointers if
// doubly linked lists are employed, or one raw pointer if singly
// linked lists are employed.  On machines with 64 bit pointers, this
// means elements must be at least 16 bytes in size for doubly linked
// list support, and 8 bytes for singly linked list support.  No
// attempts are made to preserve the data in elements stored in the
// list.
//
// Given a machine with pointers of size N (on a 64bit machine N=8, on
// a 32bit machine, N=4), the list pointers are stored in the
// following manner:
// -In doubly linked lists, the |next| pointer is stored in the first N
// bytes of the node and the |previous| pointer is writtend into the
// second N bytes.
// -In singly linked lists, the |next| pointer is stored in the first N
// bytes of the node.
//
// For both types of lists: when a pop operation is performed on a non
// empty list, the new list head becomes that which is pointed to by
// the former head's |next| pointer.  If the list is doubly linked, the
// new head |previous| pointer gets changed from pointing to the former
// head to NULL.

#include <limits>
#include <stddef.h>
#include "free_list.h"
#include "system-alloc.h"

#if defined(TCMALLOC_USE_DOUBLYLINKED_FREELIST)

using tcmalloc::kCrash;

// TODO(jar): We should use C++ rather than a macro here.
#define MEMORY_CHECK(v1, v2) \
  if (v1 != v2) Log(kCrash, __FILE__, __LINE__, "Memory corruption detected.")

namespace {
void EnsureNonLoop(void* node, void* next) {
  // We only have time to do minimal checking.  We don't traverse the list, but
  // only look for an immediate loop (cycle back to ourself).
  if (node != next) return;
  Log(kCrash, __FILE__, __LINE__, "Circular loop in list detected: ", next);
}

inline void* MaskPtr(void* p) {
  // Maximize ASLR entropy and guarantee the result is an invalid address.
  const uintptr_t mask = ~(reinterpret_cast<uintptr_t>(TCMalloc_SystemAlloc)
                           >> 13);
  return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(p) ^ mask);
}

inline void* UnmaskPtr(void* p) {
  return MaskPtr(p);
}

// Returns value of the |previous| pointer w/out running a sanity
// check.
inline void *FL_Previous_No_Check(void *t) {
  return UnmaskPtr(reinterpret_cast<void**>(t)[1]);
}

// Returns value of the |next| pointer w/out running a sanity check.
inline void *FL_Next_No_Check(void *t) {
  return UnmaskPtr(reinterpret_cast<void**>(t)[0]);
}

void *FL_Previous(void *t) {
  void *previous = FL_Previous_No_Check(t);
  if (previous) {
    MEMORY_CHECK(FL_Next_No_Check(previous), t);
  }
  return previous;
}

inline void FL_SetPrevious(void *t, void *n) {
  EnsureNonLoop(t, n);
  reinterpret_cast<void**>(t)[1] = MaskPtr(n);
}

inline void FL_SetNext(void *t, void *n) {
  EnsureNonLoop(t, n);
  reinterpret_cast<void**>(t)[0] = MaskPtr(n);
}

} // namespace

namespace tcmalloc {

void *FL_Next(void *t) {
  void *next = FL_Next_No_Check(t);
  if (next) {
    MEMORY_CHECK(FL_Previous_No_Check(next), t);
  }
  return next;
}

// Makes the element at |t| a singleton doubly linked list.
void FL_Init(void *t) {
  FL_SetPrevious(t, NULL);
  FL_SetNext(t, NULL);
}

// Pushes element to a linked list whose first element is at
// |*list|. When this call returns, |list| will point to the new head
// of the linked list.
void FL_Push(void **list, void *element) {
  void *old = *list;
  if (old == NULL) { // Builds singleton list.
    FL_Init(element);
  } else {
    ASSERT(FL_Previous_No_Check(old) == NULL);
    FL_SetNext(element, old);
    FL_SetPrevious(old, element);
    FL_SetPrevious(element, NULL);
  }
  *list = element;
}

// Pops the top element off the linked list whose first element is at
// |*list|, and updates |*list| to point to the next element in the
// list.  Returns the address of the element that was removed from the
// linked list.  |list| must not be NULL.
void *FL_Pop(void **list) {
  void *result = *list;
  ASSERT(FL_Previous_No_Check(result) == NULL);
  *list = FL_Next(result);
  if (*list != NULL) {
    FL_SetPrevious(*list, NULL);
  }
  return result;
}

// Remove |n| elements from linked list at whose first element is at
// |*head|.  |head| will be modified to point to the new head.
// |start| will point to the first node of the range, |end| will point
// to the last node in the range. |n| must be <= FL_Size(|*head|)
// If |n| > 0, |head| must not be NULL.
void FL_PopRange(void **head, int n, void **start, void **end) {
  if (n == 0) {
    *start = NULL;
    *end = NULL;
    return;
  }

  *start = *head; // Remember the first node in the range.
  void *tmp = *head;
  for (int i = 1; i < n; ++i) { // Find end of range.
    tmp = FL_Next(tmp);
  }
  *end = tmp; // |end| now set to point to last node in range.
  *head = FL_Next(*end);
  FL_SetNext(*end, NULL); // Unlink range from list.

  if (*head ) { // Fixup popped list.
    FL_SetPrevious(*head, NULL);
  }
}

// Pushes the nodes in the list begginning at |start| whose last node
// is |end| into the linked list at |*head|. |*head| is updated to
// point be the new head of the list.  |head| must not be NULL.
void FL_PushRange(void **head, void *start, void *end) {
  if (!start) return;

  // Sanity checking of ends of list to push is done by calling
  // FL_Next and FL_Previous.
  FL_Next(start);
  FL_Previous(end);
  ASSERT(FL_Previous_No_Check(start) == NULL);
  ASSERT(FL_Next_No_Check(end) == NULL);

  if (*head) {
    MEMORY_CHECK(FL_Previous_No_Check(*head), NULL);
    FL_SetNext(end, *head);
    FL_SetPrevious(*head, end);
  }
  *head = start;
}

// Calculates the size of the list that begins at |head|.
size_t FL_Size(void *head){
  int count = 0;
  if (head) {
    MEMORY_CHECK(FL_Previous_No_Check(head), NULL);
  }
  while (head) {
    count++;
    head = FL_Next(head);
  }
  return count;
}

} // namespace tcmalloc

#else
#include "linked_list.h" // for SLL_SetNext

namespace {

inline void FL_SetNext(void *t, void *n) {
  tcmalloc::SLL_SetNext(t,n);
}

}

#endif // TCMALLOC_USE_DOUBLYLINKED_FREELIST
<|endoftext|>


<commit_before>/* This source file is part of the Tomviz project, https://tomviz.org/.
   It is released under the 3-Clause BSD License, see "LICENSE". */

#include "vtkChartHistogramColorOpacityEditor.h"

#include <vtkAxis.h>
#include <vtkChart.h>
#include <vtkColorTransferControlPointsItem.h>
#include <vtkColorTransferFunction.h>
#include <vtkColorTransferFunctionItem.h>
#include <vtkContextItem.h>
#include <vtkContextScene.h>
#include <vtkObjectFactory.h>
#include <vtkPiecewiseFunction.h>
#include <vtkScalarsToColors.h>
#include <vtkSmartPointer.h>
#include <vtkTable.h>
#include <vtkVector.h>

#include "vtkChartHistogram.h"

class vtkChartHistogramColorOpacityEditor::PIMPL
{
public:
  PIMPL() : Geometry(0, 0), NeedsUpdate(true) {}
  ~PIMPL() {}

  void ForwardEvent(vtkObject* vtkNotUsed(object), unsigned long eventId,
                    void* vtkNotUsed(data))
  {
    this->Self->InvokeEvent(eventId);
  }

  // Cached geometry of the chart
  vtkVector2i Geometry;

  // Dirty bit
  bool NeedsUpdate;

  // Reference to owner of the PIMPL
  vtkChartHistogramColorOpacityEditor* Self;
};

vtkStandardNewMacro(vtkChartHistogramColorOpacityEditor)

vtkChartHistogramColorOpacityEditor::vtkChartHistogramColorOpacityEditor()
{
  this->Private = new PIMPL();
  this->Private->Self = this;

  this->Borders[vtkAxis::LEFT] = 8;
  this->Borders[vtkAxis::BOTTOM] = 8;
  this->Borders[vtkAxis::RIGHT] = 8;
  this->Borders[vtkAxis::TOP] = 20;

  this->HistogramChart->SetHiddenAxisBorder(10);
  this->HistogramChart->SetLayoutStrategy(vtkChart::AXES_TO_RECT);

  this->ColorTransferFunctionChart->SetBarWidthFraction(1.0);
  this->ColorTransferFunctionChart->SetHiddenAxisBorder(8);
  this->ColorTransferFunctionChart->SetRenderEmpty(true);
  this->ColorTransferFunctionChart->SetAutoAxes(false);
  this->ColorTransferFunctionChart->ZoomWithMouseWheelOff();
  this->ColorTransferFunctionChart->SetLayoutStrategy(vtkChart::AXES_TO_RECT);

  this->ColorTransferFunctionItem->SelectableOff();

  this->ColorTransferControlPointsItem->SetEndPointsXMovable(false);
  this->ColorTransferControlPointsItem->SetEndPointsYMovable(true);
  this->ColorTransferControlPointsItem->SetEndPointsRemovable(false);
  this->ColorTransferControlPointsItem->SelectableOff();

  this->ColorTransferFunctionChart->AddPlot(
    this->ColorTransferFunctionItem);
  this->ColorTransferFunctionChart->SetPlotCorner(
    this->ColorTransferFunctionItem, 1);
  this->ColorTransferFunctionChart->AddPlot(
    this->ColorTransferControlPointsItem);
  this->ColorTransferFunctionChart->SetPlotCorner(
    this->ColorTransferControlPointsItem, 1);

  vtkAxis* bottomAxis =
    this->ColorTransferFunctionChart->GetAxis(vtkAxis::BOTTOM);
  bottomAxis->SetTitle("");
  bottomAxis->SetBehavior(vtkAxis::FIXED);
  bottomAxis->SetVisible(false);
  bottomAxis->SetRange(0, 255);

  vtkAxis* leftAxis = this->ColorTransferFunctionChart->GetAxis(vtkAxis::LEFT);
  leftAxis->SetTitle("");
  leftAxis->SetBehavior(vtkAxis::FIXED);
  leftAxis->SetVisible(false);

  vtkAxis* topAxis = this->ColorTransferFunctionChart->GetAxis(vtkAxis::TOP);
  topAxis->SetVisible(false);

  this->AddItem(this->HistogramChart);
  this->AddItem(this->ColorTransferFunctionChart);

  // Forward events from internal charts to observers of this object
  this->HistogramChart->AddObserver(vtkCommand::CursorChangedEvent,
                                    this->Private, &PIMPL::ForwardEvent);
  this->ColorTransferControlPointsItem->AddObserver(
    vtkCommand::EndEvent, this->Private, &PIMPL::ForwardEvent);
  this->ColorTransferControlPointsItem->AddObserver(
    vtkControlPointsItem::CurrentPointEditEvent, this->Private,
    &PIMPL::ForwardEvent);
}

vtkChartHistogramColorOpacityEditor::~vtkChartHistogramColorOpacityEditor()
{
  delete this->Private;
}

void vtkChartHistogramColorOpacityEditor::SetHistogramInputData(
  vtkTable* table, const char* xAxisColumn, const char* yAxisColumn)
{
  this->HistogramChart->SetHistogramInputData(table, xAxisColumn, yAxisColumn);

  if (!table) {
    this->ColorTransferFunctionChart->SetVisible(false);
    return;
  }

  if (!this->ColorTransferFunctionChart->GetVisible()) {
    this->ColorTransferFunctionChart->SetVisible(true);
    this->ColorTransferFunctionChart->RecalculateBounds();
  }

  // The histogram chart bottom axis range was updated in the call above.
  // Set the same range for the color bar bottom axis here.
  auto histogramBottomAxis = this->HistogramChart->GetAxis(vtkAxis::BOTTOM);
  double axisRange[2];
  histogramBottomAxis->GetRange(axisRange);

  auto bottomAxis = this->ColorTransferFunctionChart->GetAxis(vtkAxis::BOTTOM);
  bottomAxis->SetRange(axisRange);

  // The data range may change and cause the labels to change. Hence, update
  // the geometry.
  this->Private->NeedsUpdate = true;
}

void vtkChartHistogramColorOpacityEditor::SetColorTransferFunction(
  vtkColorTransferFunction* ctf)
{
  this->HistogramChart->SetLookupTable(ctf);
  this->ColorTransferFunctionItem->SetColorTransferFunction(ctf);
  this->ColorTransferControlPointsItem->SetColorTransferFunction(ctf);
  this->ColorTransferFunctionChart->RecalculateBounds();
}

void vtkChartHistogramColorOpacityEditor::SetScalarVisibility(bool visible)
{
  this->HistogramChart->SetScalarVisibility(visible);
}

void vtkChartHistogramColorOpacityEditor::SelectColorArray(
  const char* arrayName)
{
  this->HistogramChart->SelectColorArray(arrayName);
}

void vtkChartHistogramColorOpacityEditor::SetOpacityFunction(
  vtkPiecewiseFunction* opacityFunction)
{
  this->HistogramChart->SetOpacityFunction(opacityFunction);
}

vtkAxis* vtkChartHistogramColorOpacityEditor::GetHistogramAxis(int axis)
{
  return this->HistogramChart->GetAxis(axis);
}

bool vtkChartHistogramColorOpacityEditor::GetCurrentControlPointColor(
  double rgb[3])
{
  auto ctf = this->ColorTransferControlPointsItem->GetColorTransferFunction();
  if (!ctf) {
    return false;
  }

  vtkIdType currentIdx =
    this->ColorTransferControlPointsItem->GetCurrentPoint();
  if (currentIdx < 0) {
    return false;
  }

  double xrgbms[6];
  ctf->GetNodeValue(currentIdx, xrgbms);
  rgb[0] = xrgbms[1];
  rgb[1] = xrgbms[2];
  rgb[2] = xrgbms[3];

  return true;
}

void vtkChartHistogramColorOpacityEditor::SetCurrentControlPointColor(
  const double rgb[3])
{
  auto ctf = this->ColorTransferControlPointsItem->GetColorTransferFunction();
  if (!ctf) {
    return;
  }

  auto currentIdx = this->ColorTransferControlPointsItem->GetCurrentPoint();
  if (currentIdx < 0) {
    return;
  }

  double xrgbms[6];
  ctf->GetNodeValue(currentIdx, xrgbms);
  xrgbms[1] = rgb[0];
  xrgbms[2] = rgb[1];
  xrgbms[3] = rgb[2];
  ctf->SetNodeValue(currentIdx, xrgbms);
}

double vtkChartHistogramColorOpacityEditor::GetContourValue()
{
  return this->HistogramChart->GetContourValue();
}

void vtkChartHistogramColorOpacityEditor::SetDPI(int dpi)
{
  if (this->HistogramChart) {
    this->HistogramChart->SetDPI(dpi);
  }
}

bool vtkChartHistogramColorOpacityEditor::Paint(vtkContext2D* painter)
{
  auto scene = this->GetScene();
  int sceneWidth = scene->GetSceneWidth();
  int sceneHeight = scene->GetSceneHeight();
  if (this->Private->NeedsUpdate ||
      sceneWidth != this->Private->Geometry.GetX() ||
      sceneHeight != this->Private->Geometry.GetY()) {
    this->Private->NeedsUpdate = false;

    // Update the geometry size cache
    this->Private->Geometry.Set(sceneWidth, sceneHeight);

    // Upper chart (histogram) expands, lower chart (color bar) is fixed height.
    float x = this->Borders[vtkAxis::LEFT];
    float y = this->Borders[vtkAxis::BOTTOM];

    // Add the width of the left axis to x to make room for y labels
    this->GetHistogramAxis(vtkAxis::LEFT)->Update();
    float leftAxisWidth = this->GetHistogramAxis(vtkAxis::LEFT)
                            ->GetBoundingRect(painter)
                            .GetWidth();
    x += leftAxisWidth;

    float colorBarThickness = 20;
    float plotWidth = sceneWidth - x - this->Borders[vtkAxis::RIGHT];

    vtkRectf colorTransferFunctionChartSize(x, y, plotWidth, colorBarThickness);
    this->ColorTransferFunctionChart->SetSize(colorTransferFunctionChartSize);
    this->ColorTransferFunctionChart->RecalculateBounds();

    float bottomAxisHeight = this->GetHistogramAxis(vtkAxis::BOTTOM)
                               ->GetBoundingRect(painter)
                               .GetHeight();
    float verticalMargin = bottomAxisHeight;
    y += colorBarThickness + verticalMargin - 5;
    vtkRectf histogramChart(x, y, plotWidth,
                            sceneHeight - y - this->Borders[vtkAxis::TOP]);
    this->HistogramChart->SetSize(histogramChart);
  }

  return this->Superclass::Paint(painter);
}
<commit_msg>Ensure the color map is rebuilt upon edits<commit_after>/* This source file is part of the Tomviz project, https://tomviz.org/.
   It is released under the 3-Clause BSD License, see "LICENSE". */

#include "vtkChartHistogramColorOpacityEditor.h"

#include <vtkAxis.h>
#include <vtkChart.h>
#include <vtkColorTransferControlPointsItem.h>
#include <vtkColorTransferFunction.h>
#include <vtkColorTransferFunctionItem.h>
#include <vtkContextItem.h>
#include <vtkContextScene.h>
#include <vtkObjectFactory.h>
#include <vtkPiecewiseFunction.h>
#include <vtkScalarsToColors.h>
#include <vtkSmartPointer.h>
#include <vtkTable.h>
#include <vtkVector.h>

#include "vtkChartHistogram.h"

class vtkChartHistogramColorOpacityEditor::PIMPL
{
public:
  PIMPL() : Geometry(0, 0), NeedsUpdate(true) {}
  ~PIMPL() {}

  void ForwardEvent(vtkObject* vtkNotUsed(object), unsigned long eventId,
                    void* vtkNotUsed(data))
  {
    this->Self->InvokeEvent(eventId);
  }

  // Cached geometry of the chart
  vtkVector2i Geometry;

  // Dirty bit
  bool NeedsUpdate;

  // Reference to owner of the PIMPL
  vtkChartHistogramColorOpacityEditor* Self;
};

vtkStandardNewMacro(vtkChartHistogramColorOpacityEditor)

vtkChartHistogramColorOpacityEditor::vtkChartHistogramColorOpacityEditor()
{
  this->Private = new PIMPL();
  this->Private->Self = this;

  this->Borders[vtkAxis::LEFT] = 8;
  this->Borders[vtkAxis::BOTTOM] = 8;
  this->Borders[vtkAxis::RIGHT] = 8;
  this->Borders[vtkAxis::TOP] = 20;

  this->HistogramChart->SetHiddenAxisBorder(10);
  this->HistogramChart->SetLayoutStrategy(vtkChart::AXES_TO_RECT);

  this->ColorTransferFunctionChart->SetBarWidthFraction(1.0);
  this->ColorTransferFunctionChart->SetHiddenAxisBorder(8);
  this->ColorTransferFunctionChart->SetRenderEmpty(true);
  this->ColorTransferFunctionChart->SetAutoAxes(false);
  this->ColorTransferFunctionChart->ZoomWithMouseWheelOff();
  this->ColorTransferFunctionChart->SetLayoutStrategy(vtkChart::AXES_TO_RECT);

  this->ColorTransferFunctionItem->SelectableOff();

  this->ColorTransferControlPointsItem->SetEndPointsXMovable(false);
  this->ColorTransferControlPointsItem->SetEndPointsYMovable(true);
  this->ColorTransferControlPointsItem->SetEndPointsRemovable(false);
  this->ColorTransferControlPointsItem->SelectableOff();

  this->ColorTransferFunctionChart->AddPlot(
    this->ColorTransferFunctionItem);
  this->ColorTransferFunctionChart->SetPlotCorner(
    this->ColorTransferFunctionItem, 1);
  this->ColorTransferFunctionChart->AddPlot(
    this->ColorTransferControlPointsItem);
  this->ColorTransferFunctionChart->SetPlotCorner(
    this->ColorTransferControlPointsItem, 1);

  vtkAxis* bottomAxis =
    this->ColorTransferFunctionChart->GetAxis(vtkAxis::BOTTOM);
  bottomAxis->SetTitle("");
  bottomAxis->SetBehavior(vtkAxis::FIXED);
  bottomAxis->SetVisible(false);
  bottomAxis->SetRange(0, 255);

  vtkAxis* leftAxis = this->ColorTransferFunctionChart->GetAxis(vtkAxis::LEFT);
  leftAxis->SetTitle("");
  leftAxis->SetBehavior(vtkAxis::FIXED);
  leftAxis->SetVisible(false);

  vtkAxis* topAxis = this->ColorTransferFunctionChart->GetAxis(vtkAxis::TOP);
  topAxis->SetVisible(false);

  this->AddItem(this->HistogramChart);
  this->AddItem(this->ColorTransferFunctionChart);

  // Forward events from internal charts to observers of this object
  this->HistogramChart->AddObserver(vtkCommand::CursorChangedEvent,
                                    this->Private, &PIMPL::ForwardEvent);
  this->ColorTransferControlPointsItem->AddObserver(
    vtkCommand::EndEvent, this->Private, &PIMPL::ForwardEvent);
  this->ColorTransferControlPointsItem->AddObserver(
    vtkControlPointsItem::CurrentPointEditEvent, this->Private,
    &PIMPL::ForwardEvent);
}

vtkChartHistogramColorOpacityEditor::~vtkChartHistogramColorOpacityEditor()
{
  delete this->Private;
}

void vtkChartHistogramColorOpacityEditor::SetHistogramInputData(
  vtkTable* table, const char* xAxisColumn, const char* yAxisColumn)
{
  this->HistogramChart->SetHistogramInputData(table, xAxisColumn, yAxisColumn);

  if (!table) {
    this->ColorTransferFunctionChart->SetVisible(false);
    return;
  }

  if (!this->ColorTransferFunctionChart->GetVisible()) {
    this->ColorTransferFunctionChart->SetVisible(true);
    this->ColorTransferFunctionChart->RecalculateBounds();
  }

  // The histogram chart bottom axis range was updated in the call above.
  // Set the same range for the color bar bottom axis here.
  auto histogramBottomAxis = this->HistogramChart->GetAxis(vtkAxis::BOTTOM);
  double axisRange[2];
  histogramBottomAxis->GetRange(axisRange);

  auto bottomAxis = this->ColorTransferFunctionChart->GetAxis(vtkAxis::BOTTOM);
  bottomAxis->SetRange(axisRange);

  // The data range may change and cause the labels to change. Hence, update
  // the geometry.
  this->Private->NeedsUpdate = true;
}

void vtkChartHistogramColorOpacityEditor::SetColorTransferFunction(
  vtkColorTransferFunction* ctf)
{
  this->HistogramChart->SetLookupTable(ctf);
  this->ColorTransferFunctionItem->SetColorTransferFunction(ctf);
  this->ColorTransferControlPointsItem->SetColorTransferFunction(ctf);
  this->ColorTransferFunctionChart->RecalculateBounds();
}

void vtkChartHistogramColorOpacityEditor::SetScalarVisibility(bool visible)
{
  this->HistogramChart->SetScalarVisibility(visible);
}

void vtkChartHistogramColorOpacityEditor::SelectColorArray(
  const char* arrayName)
{
  this->HistogramChart->SelectColorArray(arrayName);
}

void vtkChartHistogramColorOpacityEditor::SetOpacityFunction(
  vtkPiecewiseFunction* opacityFunction)
{
  this->HistogramChart->SetOpacityFunction(opacityFunction);
}

vtkAxis* vtkChartHistogramColorOpacityEditor::GetHistogramAxis(int axis)
{
  return this->HistogramChart->GetAxis(axis);
}

bool vtkChartHistogramColorOpacityEditor::GetCurrentControlPointColor(
  double rgb[3])
{
  auto ctf = this->ColorTransferControlPointsItem->GetColorTransferFunction();
  if (!ctf) {
    return false;
  }

  vtkIdType currentIdx =
    this->ColorTransferControlPointsItem->GetCurrentPoint();
  if (currentIdx < 0) {
    return false;
  }

  double xrgbms[6];
  ctf->GetNodeValue(currentIdx, xrgbms);
  rgb[0] = xrgbms[1];
  rgb[1] = xrgbms[2];
  rgb[2] = xrgbms[3];

  return true;
}

void vtkChartHistogramColorOpacityEditor::SetCurrentControlPointColor(
  const double rgb[3])
{
  auto ctf = this->ColorTransferControlPointsItem->GetColorTransferFunction();
  if (!ctf) {
    return;
  }

  auto currentIdx = this->ColorTransferControlPointsItem->GetCurrentPoint();
  if (currentIdx < 0) {
    return;
  }

  double xrgbms[6];
  ctf->GetNodeValue(currentIdx, xrgbms);
  xrgbms[1] = rgb[0];
  xrgbms[2] = rgb[1];
  xrgbms[3] = rgb[2];
  ctf->SetNodeValue(currentIdx, xrgbms);

  // Make sure we rebuild the color map so that it is used in later renders.
  ctf->Modified();
  ctf->Build();
}

double vtkChartHistogramColorOpacityEditor::GetContourValue()
{
  return this->HistogramChart->GetContourValue();
}

void vtkChartHistogramColorOpacityEditor::SetDPI(int dpi)
{
  if (this->HistogramChart) {
    this->HistogramChart->SetDPI(dpi);
  }
}

bool vtkChartHistogramColorOpacityEditor::Paint(vtkContext2D* painter)
{
  auto scene = this->GetScene();
  int sceneWidth = scene->GetSceneWidth();
  int sceneHeight = scene->GetSceneHeight();
  if (this->Private->NeedsUpdate ||
      sceneWidth != this->Private->Geometry.GetX() ||
      sceneHeight != this->Private->Geometry.GetY()) {
    this->Private->NeedsUpdate = false;

    // Update the geometry size cache
    this->Private->Geometry.Set(sceneWidth, sceneHeight);

    // Upper chart (histogram) expands, lower chart (color bar) is fixed height.
    float x = this->Borders[vtkAxis::LEFT];
    float y = this->Borders[vtkAxis::BOTTOM];

    // Add the width of the left axis to x to make room for y labels
    this->GetHistogramAxis(vtkAxis::LEFT)->Update();
    float leftAxisWidth = this->GetHistogramAxis(vtkAxis::LEFT)
                            ->GetBoundingRect(painter)
                            .GetWidth();
    x += leftAxisWidth;

    float colorBarThickness = 20;
    float plotWidth = sceneWidth - x - this->Borders[vtkAxis::RIGHT];

    vtkRectf colorTransferFunctionChartSize(x, y, plotWidth, colorBarThickness);
    this->ColorTransferFunctionChart->SetSize(colorTransferFunctionChartSize);
    this->ColorTransferFunctionChart->RecalculateBounds();

    float bottomAxisHeight = this->GetHistogramAxis(vtkAxis::BOTTOM)
                               ->GetBoundingRect(painter)
                               .GetHeight();
    float verticalMargin = bottomAxisHeight;
    y += colorBarThickness + verticalMargin - 5;
    vtkRectf histogramChart(x, y, plotWidth,
                            sceneHeight - y - this->Borders[vtkAxis::TOP]);
    this->HistogramChart->SetSize(histogramChart);
  }

  return this->Superclass::Paint(painter);
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "VisualStreamTimingModule.h"

#include "SkCanvas.h"

DEFINE_bool(reset, true, "Reset the GL context between samples.");

VisualStreamTimingModule::VisualStreamTimingModule(VisualBench* owner)
    : fInitState(kReset_InitState)
    , fOwner(owner) {
    fBenchmarkStream.reset(new VisualBenchmarkStream(owner->getSurfaceProps()));
}

inline void VisualStreamTimingModule::handleInitState(SkCanvas* canvas) {
    switch (fInitState) {
        case kNewBenchmark_InitState:
            fBenchmarkStream->current()->delayedSetup();
            // fallthrough
        case kReset_InitState:
            // This will flicker unfortunately, but as we are reseting the GLContext each bench,
            // we unfortunately don't have a choice
            fOwner->clear(canvas, SK_ColorWHITE, 2);
            fBenchmarkStream->current()->preTimingHooks(canvas);
            break;
        case kNone_InitState:
            break;
    }
    fInitState = kNone_InitState;
}

inline void VisualStreamTimingModule::handleTimingEvent(SkCanvas* canvas,
                                                        TimingStateMachine::ParentEvents event) {
    switch (event) {
        case TimingStateMachine::kTiming_ParentEvents:
            break;
        case TimingStateMachine::kTimingFinished_ParentEvents:
            if (this->timingFinished(fBenchmarkStream->current(), fTSM.loops(),
                                     fTSM.lastMeasurement())) {
                fBenchmarkStream->current()->postTimingHooks(canvas);
                fOwner->reset();
                fTSM.nextBenchmark();
                if (!fBenchmarkStream->next()) {
                    SkDebugf("Exiting VisualBench successfully\n");
                    fOwner->closeWindow();
                } else {
                    fInitState = kNewBenchmark_InitState;
                }
                break;
            }
            // fallthrough
        case TimingStateMachine::kReset_ParentEvents:
            if (FLAGS_reset) {
                fBenchmarkStream->current()->postTimingHooks(canvas);
                fOwner->reset();
                fInitState = kReset_InitState;
            }
            break;
    }
}

void VisualStreamTimingModule::draw(SkCanvas* canvas) {
    if (!fBenchmarkStream->current()) {
        // this should never happen but just to be safe
        // TODO research why this does happen on mac
        return;
    }

    this->handleInitState(canvas);
    this->renderFrame(canvas, fBenchmarkStream->current(), fTSM.loops());
    fOwner->present();
    TimingStateMachine::ParentEvents event = fTSM.nextFrame(FLAGS_reset);
    this->handleTimingEvent(canvas, event);
}
<commit_msg>Fix visualbench flicker on triple buffered platforms<commit_after>/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "VisualStreamTimingModule.h"

#include "SkCanvas.h"

DEFINE_bool(reset, true, "Reset the GL context between samples.");

VisualStreamTimingModule::VisualStreamTimingModule(VisualBench* owner)
    : fInitState(kReset_InitState)
    , fOwner(owner) {
    fBenchmarkStream.reset(new VisualBenchmarkStream(owner->getSurfaceProps()));
}

inline void VisualStreamTimingModule::handleInitState(SkCanvas* canvas) {
    switch (fInitState) {
        case kNewBenchmark_InitState:
            fBenchmarkStream->current()->delayedSetup();
            // fallthrough
        case kReset_InitState:
            // This will flicker unfortunately, but as we are reseting the GLContext each bench,
            // we unfortunately don't have a choice
            fOwner->clear(canvas, SK_ColorWHITE, 3);
            fBenchmarkStream->current()->preTimingHooks(canvas);
            break;
        case kNone_InitState:
            break;
    }
    fInitState = kNone_InitState;
}

inline void VisualStreamTimingModule::handleTimingEvent(SkCanvas* canvas,
                                                        TimingStateMachine::ParentEvents event) {
    switch (event) {
        case TimingStateMachine::kTiming_ParentEvents:
            break;
        case TimingStateMachine::kTimingFinished_ParentEvents:
            if (this->timingFinished(fBenchmarkStream->current(), fTSM.loops(),
                                     fTSM.lastMeasurement())) {
                fBenchmarkStream->current()->postTimingHooks(canvas);
                fOwner->reset();
                fTSM.nextBenchmark();
                if (!fBenchmarkStream->next()) {
                    SkDebugf("Exiting VisualBench successfully\n");
                    fOwner->closeWindow();
                } else {
                    fInitState = kNewBenchmark_InitState;
                }
                break;
            }
            // fallthrough
        case TimingStateMachine::kReset_ParentEvents:
            if (FLAGS_reset) {
                fBenchmarkStream->current()->postTimingHooks(canvas);
                fOwner->reset();
                fInitState = kReset_InitState;
            }
            break;
    }
}

void VisualStreamTimingModule::draw(SkCanvas* canvas) {
    if (!fBenchmarkStream->current()) {
        // this should never happen but just to be safe
        // TODO research why this does happen on mac
        return;
    }

    this->handleInitState(canvas);
    this->renderFrame(canvas, fBenchmarkStream->current(), fTSM.loops());
    fOwner->present();
    TimingStateMachine::ParentEvents event = fTSM.nextFrame(FLAGS_reset);
    this->handleTimingEvent(canvas, event);
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017, Ford Motor Company
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided with the
 * distribution.
 *
 * Neither the name of the Ford Motor Company nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "hmi_message_handler/hmi_message_handler_impl.h"
#include "application_manager/message.h"
#include "gtest/gtest.h"
#include "hmi_message_handler/messagebroker_adapter.h"
#include "hmi_message_handler/mock_hmi_message_adapter_impl.h"
#include "hmi_message_handler/mock_hmi_message_handler_settings.h"
#include "hmi_message_handler/mock_hmi_message_observer.h"
#include "utils/jsoncpp_reader_wrapper.h"
#include "utils/test_async_waiter.h"

namespace test {
namespace components {
namespace hmi_message_handler_test {

using ::testing::_;
using ::testing::Return;

class HMIMessageHandlerImplTest : public ::testing::Test {
 public:
  HMIMessageHandlerImplTest()
      : mb_adapter_(NULL)
      , hmi_handler_(NULL)
      , mock_hmi_message_observer_(NULL) {}

 protected:
  hmi_message_handler::MessageBrokerAdapter* mb_adapter_;
  hmi_message_handler::HMIMessageHandlerImpl* hmi_handler_;
  MockHMIMessageObserver* mock_hmi_message_observer_;

  testing::NiceMock<MockHMIMessageHandlerSettings>
      mock_hmi_message_handler_settings;
  const uint64_t stack_size = 1000u;

  virtual void SetUp() OVERRIDE {
    ON_CALL(mock_hmi_message_handler_settings, thread_min_stack_size())
        .WillByDefault(Return(stack_size));
    hmi_handler_ = new hmi_message_handler::HMIMessageHandlerImpl(
        mock_hmi_message_handler_settings);
    ASSERT_TRUE(NULL != hmi_handler_);
    mb_adapter_ = new hmi_message_handler::MessageBrokerAdapter(
        hmi_handler_, "127.0.0.1", 8087);
    ASSERT_TRUE(NULL != mb_adapter_);
    mock_hmi_message_observer_ = new MockHMIMessageObserver();
    ASSERT_TRUE(NULL != mock_hmi_message_observer_);
    hmi_handler_->set_message_observer(mock_hmi_message_observer_);
    EXPECT_TRUE(NULL != hmi_handler_->observer());
  }

  void TearDown() OVERRIDE {
    hmi_handler_->set_message_observer(NULL);
    delete mock_hmi_message_observer_;
    delete hmi_handler_;
    delete mb_adapter_;
  }

  hmi_message_handler::MessageSharedPointer CreateMessage() {
    // The ServiceType doesn't really matter
    return std::make_shared<application_manager::Message>(
        protocol_handler::MessagePriority::FromServiceType(
            protocol_handler::ServiceType::kInvalidServiceType));
  }
};

TEST_F(HMIMessageHandlerImplTest,
       OnErrorSending_EmptyMessage_OnErrorSendingProceeded) {
  // Arrange
  hmi_message_handler::MessageSharedPointer empty_message;
  EXPECT_CALL(*mock_hmi_message_observer_, OnErrorSending(empty_message));
  // Act
  hmi_handler_->OnErrorSending(empty_message);
}

TEST_F(HMIMessageHandlerImplTest,
       OnErrorSending_NotEmptyMessage_ExpectOnErrorSendingProceeded) {
  // Arrange
  std::shared_ptr<application_manager::Message> message = CreateMessage();

  EXPECT_CALL(*mock_hmi_message_observer_, OnErrorSending(message));
  // Act
  hmi_handler_->OnErrorSending(message);
}

TEST_F(HMIMessageHandlerImplTest, OnErrorSending_InvalidObserver_Cancelled) {
  // Arrange
  std::shared_ptr<application_manager::Message> message = CreateMessage();

  hmi_handler_->set_message_observer(NULL);
  EXPECT_CALL(*mock_hmi_message_observer_, OnErrorSending(_)).Times(0);
  // Act
  hmi_handler_->OnErrorSending(message);
}

TEST_F(HMIMessageHandlerImplTest,
       AddHMIMessageAdapter_AddExistedAdapter_ExpectAdded) {
  // Check before action
  EXPECT_TRUE(hmi_handler_->message_adapters().empty());
  // Act
  hmi_handler_->AddHMIMessageAdapter(mb_adapter_);
  // Check after action
  EXPECT_EQ(1u, hmi_handler_->message_adapters().size());
}

TEST_F(HMIMessageHandlerImplTest,
       AddHMIMessageAdapter_AddUnexistedAdapter_ExpectNotAdded) {
  // Check before action
  EXPECT_TRUE(hmi_handler_->message_adapters().empty());
  // Act
  mb_adapter_ = NULL;
  hmi_handler_->AddHMIMessageAdapter(mb_adapter_);
  // Check adapter not added
  EXPECT_TRUE(hmi_handler_->message_adapters().empty());
}

TEST_F(HMIMessageHandlerImplTest, RemoveHMIMessageAdapter_ExpectRemoved) {
  // Arrange
  hmi_handler_->AddHMIMessageAdapter(mb_adapter_);
  // Act
  hmi_handler_->RemoveHMIMessageAdapter(mb_adapter_);
  // Check after action
  EXPECT_TRUE(hmi_handler_->message_adapters().empty());
}

// TODO(atimchenko) SDLOPEN-44 Wrong message to observer
TEST_F(HMIMessageHandlerImplTest,
       DISABLED_OnMessageReceived_ValidObserver_Success) {
  hmi_message_handler::MessageSharedPointer message = CreateMessage();
  EXPECT_CALL(*mock_hmi_message_observer_, OnMessageReceived(message));

  hmi_handler_->OnMessageReceived(message);
  // Wait for the message to be processed
  hmi_handler_->messages_from_hmi()->WaitDumpQueue();
}

TEST_F(HMIMessageHandlerImplTest, OnMessageReceived_InvalidObserver_Cancelled) {
  hmi_message_handler::MessageSharedPointer message = CreateMessage();
  EXPECT_CALL(*mock_hmi_message_observer_, OnMessageReceived(_)).Times(0);
  // Make the observer invalid
  hmi_handler_->set_message_observer(NULL);
  hmi_handler_->OnMessageReceived(message);
  hmi_handler_->messages_from_hmi()->WaitDumpQueue();
}

TEST_F(HMIMessageHandlerImplTest, SendMessageToHMI_Success) {
  hmi_message_handler::MessageSharedPointer message = CreateMessage();

  TestAsyncWaiter waiter;

  MockHMIMessageAdapterImpl message_adapter(hmi_handler_);
  EXPECT_CALL(message_adapter, SendMessageToHMI(message))
      .WillOnce(NotifyTestAsyncWaiter(&waiter));

  hmi_handler_->AddHMIMessageAdapter(&message_adapter);
  hmi_handler_->SendMessageToHMI(message);

  // Wait for the message to be processed
  hmi_handler_->messages_to_hmi()->WaitDumpQueue();

  EXPECT_TRUE(waiter.WaitFor(1, 100));
}

TEST(WebsocketSessionTest, SendMessage_UnpreparedConnection_WithoutFall) {
  ::testing::FLAGS_gtest_death_test_style = "threadsafe";

  auto send_message = []() {
    auto message =
        "{\"id\" : 1,\"jsonrpc\" : \"2.0\",\"method\" : "
        "\"BasicCommunication.GetSystemInfo\"}";

    utils::JsonReader reader;
    Json::Value json_value;

    ASSERT_TRUE(reader.parse(message, &json_value));

    // Make unprepared connection
    boost::asio::io_context ioc{1};
    boost::asio::ip::tcp::acceptor acceptor{
        ioc, {boost::asio::ip::make_address("127.0.0.1"), 8087}};
    boost::asio::ip::tcp::socket socket{ioc};

    std::unique_ptr<hmi_message_handler::WebsocketSession> session(
        new hmi_message_handler::WebsocketSession(std::move(socket), nullptr));

    // Send message to unprepared connection
    session->sendJsonMessage(json_value);

    // Wait for the message to be processed
    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    // Stopping connection thread
    session->Shutdown();
    session = nullptr;

    exit(0);
  };

  // Expected exit code 0, if test terminate by other signal(SIGABRT or
  // SIGSEGV), we will get failed test
  EXPECT_EXIT(send_message(), ::testing::ExitedWithCode(0), "");
}

}  // namespace hmi_message_handler_test
}  // namespace components
}  // namespace test
<commit_msg>Add workaround to handle problem, caused by gcc compiler (#3418)<commit_after>/*
 * Copyright (c) 2017, Ford Motor Company
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided with the
 * distribution.
 *
 * Neither the name of the Ford Motor Company nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "hmi_message_handler/hmi_message_handler_impl.h"
#include "application_manager/message.h"
#include "gtest/gtest.h"
#include "hmi_message_handler/messagebroker_adapter.h"
#include "hmi_message_handler/mock_hmi_message_adapter_impl.h"
#include "hmi_message_handler/mock_hmi_message_handler_settings.h"
#include "hmi_message_handler/mock_hmi_message_observer.h"
#include "utils/jsoncpp_reader_wrapper.h"
#include "utils/test_async_waiter.h"

namespace test {
namespace components {
namespace hmi_message_handler_test {

using ::testing::_;
using ::testing::Return;

class HMIMessageHandlerImplTest : public ::testing::Test {
 public:
  HMIMessageHandlerImplTest()
      : mb_adapter_(NULL)
      , hmi_handler_(NULL)
      , mock_hmi_message_observer_(NULL) {}

 protected:
  hmi_message_handler::MessageBrokerAdapter* mb_adapter_;
  hmi_message_handler::HMIMessageHandlerImpl* hmi_handler_;
  MockHMIMessageObserver* mock_hmi_message_observer_;

  testing::NiceMock<MockHMIMessageHandlerSettings>
      mock_hmi_message_handler_settings;
  const uint64_t stack_size = 1000u;

  virtual void SetUp() OVERRIDE {
    ON_CALL(mock_hmi_message_handler_settings, thread_min_stack_size())
        .WillByDefault(Return(stack_size));
    hmi_handler_ = new hmi_message_handler::HMIMessageHandlerImpl(
        mock_hmi_message_handler_settings);
    ASSERT_TRUE(NULL != hmi_handler_);
    mb_adapter_ = new hmi_message_handler::MessageBrokerAdapter(
        hmi_handler_, "127.0.0.1", 8087);
    ASSERT_TRUE(NULL != mb_adapter_);
    mock_hmi_message_observer_ = new MockHMIMessageObserver();
    ASSERT_TRUE(NULL != mock_hmi_message_observer_);
    hmi_handler_->set_message_observer(mock_hmi_message_observer_);
    EXPECT_TRUE(NULL != hmi_handler_->observer());
  }

  void TearDown() OVERRIDE {
    hmi_handler_->set_message_observer(NULL);
    delete mock_hmi_message_observer_;
    delete hmi_handler_;
    delete mb_adapter_;
  }

  hmi_message_handler::MessageSharedPointer CreateMessage() {
    // The ServiceType doesn't really matter
    return std::make_shared<application_manager::Message>(
        protocol_handler::MessagePriority::FromServiceType(
            protocol_handler::ServiceType::kInvalidServiceType));
  }
};

TEST_F(HMIMessageHandlerImplTest,
       OnErrorSending_EmptyMessage_OnErrorSendingProceeded) {
  // Arrange
  hmi_message_handler::MessageSharedPointer empty_message;
  EXPECT_CALL(*mock_hmi_message_observer_, OnErrorSending(empty_message));
  // Act
  hmi_handler_->OnErrorSending(empty_message);
}

TEST_F(HMIMessageHandlerImplTest,
       OnErrorSending_NotEmptyMessage_ExpectOnErrorSendingProceeded) {
  // Arrange
  std::shared_ptr<application_manager::Message> message = CreateMessage();

  EXPECT_CALL(*mock_hmi_message_observer_, OnErrorSending(message));
  // Act
  hmi_handler_->OnErrorSending(message);
}

TEST_F(HMIMessageHandlerImplTest, OnErrorSending_InvalidObserver_Cancelled) {
  // Arrange
  std::shared_ptr<application_manager::Message> message = CreateMessage();

  hmi_handler_->set_message_observer(NULL);
  EXPECT_CALL(*mock_hmi_message_observer_, OnErrorSending(_)).Times(0);
  // Act
  hmi_handler_->OnErrorSending(message);
}

TEST_F(HMIMessageHandlerImplTest,
       AddHMIMessageAdapter_AddExistedAdapter_ExpectAdded) {
  // Check before action
  EXPECT_TRUE(hmi_handler_->message_adapters().empty());
  // Act
  hmi_handler_->AddHMIMessageAdapter(mb_adapter_);
  // Check after action
  EXPECT_EQ(1u, hmi_handler_->message_adapters().size());
}

TEST_F(HMIMessageHandlerImplTest,
       AddHMIMessageAdapter_AddUnexistedAdapter_ExpectNotAdded) {
  // Check before action
  EXPECT_TRUE(hmi_handler_->message_adapters().empty());
  // Act
  mb_adapter_ = NULL;
  hmi_handler_->AddHMIMessageAdapter(mb_adapter_);
  // Check adapter not added
  EXPECT_TRUE(hmi_handler_->message_adapters().empty());
}

TEST_F(HMIMessageHandlerImplTest, RemoveHMIMessageAdapter_ExpectRemoved) {
  // Arrange
  hmi_handler_->AddHMIMessageAdapter(mb_adapter_);
  // Act
  hmi_handler_->RemoveHMIMessageAdapter(mb_adapter_);
  // Check after action
  EXPECT_TRUE(hmi_handler_->message_adapters().empty());
}

// TODO(atimchenko) SDLOPEN-44 Wrong message to observer
TEST_F(HMIMessageHandlerImplTest,
       DISABLED_OnMessageReceived_ValidObserver_Success) {
  hmi_message_handler::MessageSharedPointer message = CreateMessage();
  EXPECT_CALL(*mock_hmi_message_observer_, OnMessageReceived(message));

  hmi_handler_->OnMessageReceived(message);
  // Wait for the message to be processed
  hmi_handler_->messages_from_hmi()->WaitDumpQueue();
}

TEST_F(HMIMessageHandlerImplTest, OnMessageReceived_InvalidObserver_Cancelled) {
  hmi_message_handler::MessageSharedPointer message = CreateMessage();
  EXPECT_CALL(*mock_hmi_message_observer_, OnMessageReceived(_)).Times(0);
  // Make the observer invalid
  hmi_handler_->set_message_observer(NULL);
  hmi_handler_->OnMessageReceived(message);
  hmi_handler_->messages_from_hmi()->WaitDumpQueue();
}

TEST_F(HMIMessageHandlerImplTest, SendMessageToHMI_Success) {
  hmi_message_handler::MessageSharedPointer message = CreateMessage();

  TestAsyncWaiter waiter;

  MockHMIMessageAdapterImpl message_adapter(hmi_handler_);
  EXPECT_CALL(message_adapter, SendMessageToHMI(message))
      .WillOnce(NotifyTestAsyncWaiter(&waiter));

  hmi_handler_->AddHMIMessageAdapter(&message_adapter);
  hmi_handler_->SendMessageToHMI(message);

  // Wait for the message to be processed
  hmi_handler_->messages_to_hmi()->WaitDumpQueue();

  EXPECT_TRUE(waiter.WaitFor(1, 100));
}

TEST(WebsocketSessionTest, SendMessage_UnpreparedConnection_WithoutFall) {
  // Value "threadsafe" is preferable for this case, but as workaround for
  // possible bug in gcc compiler value "fast" was set. Because of this warning
  // message is appeared.

  // ToDo: set value "threadsafe", if current version of gcc can handle
  // this value correctly without causing of core dump.

  ::testing::FLAGS_gtest_death_test_style = "fast";

  auto send_message = []() {
    auto message =
        "{\"id\" : 1,\"jsonrpc\" : \"2.0\",\"method\" : "
        "\"BasicCommunication.GetSystemInfo\"}";

    utils::JsonReader reader;
    Json::Value json_value;

    ASSERT_TRUE(reader.parse(message, &json_value));

    // Make unprepared connection
    boost::asio::io_context ioc{1};
    boost::asio::ip::tcp::acceptor acceptor{
        ioc, {boost::asio::ip::make_address("127.0.0.1"), 8087}};
    boost::asio::ip::tcp::socket socket{ioc};

    std::unique_ptr<hmi_message_handler::WebsocketSession> session(
        new hmi_message_handler::WebsocketSession(std::move(socket), nullptr));

    // Send message to unprepared connection
    session->sendJsonMessage(json_value);

    // Wait for the message to be processed
    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    // Stopping connection thread
    session->Shutdown();
    session = nullptr;

    exit(0);
  };

  // Expected exit code 0, if test terminate by other signal(SIGABRT or
  // SIGSEGV), we will get failed test
  EXPECT_EXIT(send_message(), ::testing::ExitedWithCode(0), "");
}

}  // namespace hmi_message_handler_test
}  // namespace components
}  // namespace test
<|endoftext|>


<commit_before>#include "StudentTRand.h"
#include "NormalRand.h"
#include "CauchyRand.h"

StudentTRand::StudentTRand(double degree, double location, double scale)
{
    SetDegree(degree);
    SetLocation(location);
    SetScale(scale);
}

std::string StudentTRand::Name() const
{
    if (mu == 0.0 && sigma == 1.0)
        return "Student's t(" + toStringWithPrecision(GetDegree()) + ")";
    return "Student's t(" + toStringWithPrecision(GetDegree()) + ", "
                          + toStringWithPrecision(GetLocation()) + ", "
                          + toStringWithPrecision(GetScale()) + ")";
}

void StudentTRand::SetDegree(double degree)
{
    nu = degree > 0 ? degree : 1;
    Y.SetParameters(0.5 * nu, 0.5);

    nup1Half = 0.5 * (nu + 1);
    pdfCoef = std::lgamma(nup1Half);
    pdfCoef -= 0.5 * M_LNPI;
    pdfCoef -= Y.GetLogGammaFunction();
    betaInv = std::exp(pdfCoef);
    pdfCoef -= 0.5 * std::log(nu);
}

void StudentTRand::SetLocation(double location)
{
    mu = location;
}

void StudentTRand::SetScale(double scale)
{
    sigma = scale > 0 ? scale : 1.0;
}

double StudentTRand::f(double x) const
{
    // TODO: consider special cases
    /// adjustment
    x -= mu;
    x /= sigma;

    double y = 1 + x * x / nu;
    y = -nup1Half * std::log(y);
    return std::exp(pdfCoef + y) / sigma;
}

double StudentTRand::F(double x) const
{
    x -= mu;
    x /= sigma;
    if (x == 0.0)
        return 0.5;

    // TODO: consider nu == 3 and nu == 4
    if (nu == 1)
        return 0.5 + std::atan(x) / M_PI;
    if (nu == 2)
        return 0.5 * (1.0 + x / std::sqrt(2 + x * x));

    double t = nu / (x * x + nu);
    double y = 0.5 * RandMath::incompleteBetaFun(t, 0.5 * nu, 0.5) * betaInv;
    return (x > 0.0) ? (1 - y) : y;
}

double StudentTRand::Variate() const
{
    if (nu == 1)
        return CauchyRand::Variate(mu, sigma);
    // TODO: do we need to divide on nu and make sqrt? can we use nakagami distribution?
    return mu + sigma * NormalRand::StandardVariate() / std::sqrt(Y.Variate() / nu);
}

void StudentTRand::Sample(std::vector<double> &outputData) const
{
    if (nu == 1) {
        for (double &var : outputData)
            var = CauchyRand::Variate(mu, sigma);
    }
    else {
        Y.Sample(outputData);
        for (double &var : outputData)
            var = mu + sigma * NormalRand::StandardVariate() / std::sqrt(var / nu);
    }
}

double StudentTRand::Mean() const
{
    return (nu > 1) ? mu : NAN;
}

double StudentTRand::Variance() const
{
    if (nu > 2)
        return sigma * sigma * nu / (nu - 2);
    return (nu > 1) ? INFINITY : NAN;
}

std::complex<double> StudentTRand::CFImpl(double t) const
{
    double x = std::sqrt(nu) * std::fabs(t * sigma); // value of sqrt(nu) can be hashed
    double vHalf = 0.5 * nu;
    double y = vHalf * std::log(x);
    y -= Y.GetLogGammaFunction();
    y -= (vHalf - 1) * M_LN2;
    y += RandMath::logModifiedBesselSecondKind(x, vHalf);
    double costmu = std::cos(t * mu), sintmu = std::sin(t * mu);
    std::complex<double> cf(costmu, sintmu);
    return std::exp(y) * cf;
}

double StudentTRand::quantileImpl(double p) const
{
    // TODO: consider nu == 3
    double temp = p - 0.5;
    if (nu == 1)
        return std::tan(M_PI * temp) * sigma + mu;
    double pq = p * (1.0 - p);
    if (nu == 2)
        return sigma * 2.0 * temp * std::sqrt(0.5 / pq) + mu;
    if (nu == 4)
    {
        double alpha = 2 * std::sqrt(pq);
        double beta = std::cos(std::acos(alpha) / 3.0) / alpha - 1;
        return mu + sigma * 2 * RandMath::sign(temp) * std::sqrt(beta);
    }
    return ContinuousDistribution::quantileImpl(p);
}

double StudentTRand::quantileImpl1m(double p) const
{
    // TODO: consider nu == 3
    double temp = 0.5 - p;
    if (nu == 1)
        return std::tan(M_PI * temp) * sigma + mu;
    double pq = p * (1.0 - p);
    if (nu == 2)
        return sigma * 2 * temp * std::sqrt(0.5 / pq) + mu;
    if (nu == 4)
    {
        double alpha = 2 * std::sqrt(pq);
        double beta = std::cos(std::acos(alpha) / 3.0) / alpha - 1;
        return mu + sigma * 2 * RandMath::sign(temp) * std::sqrt(beta);
    }
    return ContinuousDistribution::quantileImpl1m(p);
}

double StudentTRand::Median() const
{
    return mu;
}

double StudentTRand::Mode() const
{
    return mu;
}

double StudentTRand::Skewness() const
{
    return (nu > 3) ? 0.0 : NAN;
}

double StudentTRand::ExcessKurtosis() const
{
    if (nu > 4)
        return 6 / (nu - 4);
    return (nu > 2) ? INFINITY : NAN;
}
<commit_msg>Updated Student's pdf<commit_after>#include "StudentTRand.h"
#include "NormalRand.h"
#include "CauchyRand.h"

StudentTRand::StudentTRand(double degree, double location, double scale)
{
    SetDegree(degree);
    SetLocation(location);
    SetScale(scale);
}

std::string StudentTRand::Name() const
{
    if (mu == 0.0 && sigma == 1.0)
        return "Student's t(" + toStringWithPrecision(GetDegree()) + ")";
    return "Student's t(" + toStringWithPrecision(GetDegree()) + ", "
                          + toStringWithPrecision(GetLocation()) + ", "
                          + toStringWithPrecision(GetScale()) + ")";
}

void StudentTRand::SetDegree(double degree)
{
    nu = degree > 0 ? degree : 1;
    Y.SetParameters(0.5 * nu, 0.5);

    nup1Half = 0.5 * (nu + 1);
    pdfCoef = std::lgamma(nup1Half);
    pdfCoef -= 0.5 * M_LNPI;
    pdfCoef -= Y.GetLogGammaFunction();
    betaInv = std::exp(pdfCoef);
    pdfCoef -= 0.5 * std::log(nu);
}

void StudentTRand::SetLocation(double location)
{
    mu = location;
}

void StudentTRand::SetScale(double scale)
{
    sigma = scale > 0 ? scale : 1.0;
}

double StudentTRand::f(double x) const
{
    // TODO: consider special cases
    /// adjustment
    x -= mu;
    x /= sigma;

    double y = x * x / nu;
    y = -nup1Half * std::log1p(y);
    return std::exp(pdfCoef + y) / sigma;
}

double StudentTRand::F(double x) const
{
    x -= mu;
    x /= sigma;
    if (x == 0.0)
        return 0.5;

    // TODO: consider nu == 3 and nu == 4
    if (nu == 1)
        return 0.5 + std::atan(x) / M_PI;
    if (nu == 2)
        return 0.5 * (1.0 + x / std::sqrt(2 + x * x));

    double t = nu / (x * x + nu);
    double y = 0.5 * RandMath::incompleteBetaFun(t, 0.5 * nu, 0.5) * betaInv;
    return (x > 0.0) ? (1 - y) : y;
}

double StudentTRand::Variate() const
{
    if (nu == 1)
        return CauchyRand::Variate(mu, sigma);
    // TODO: do we need to divide on nu and make sqrt? can we use nakagami distribution?
    return mu + sigma * NormalRand::StandardVariate() / std::sqrt(Y.Variate() / nu);
}

void StudentTRand::Sample(std::vector<double> &outputData) const
{
    if (nu == 1) {
        for (double &var : outputData)
            var = CauchyRand::Variate(mu, sigma);
    }
    else {
        Y.Sample(outputData);
        for (double &var : outputData)
            var = mu + sigma * NormalRand::StandardVariate() / std::sqrt(var / nu);
    }
}

double StudentTRand::Mean() const
{
    return (nu > 1) ? mu : NAN;
}

double StudentTRand::Variance() const
{
    if (nu > 2)
        return sigma * sigma * nu / (nu - 2);
    return (nu > 1) ? INFINITY : NAN;
}

std::complex<double> StudentTRand::CFImpl(double t) const
{
    double x = std::sqrt(nu) * std::fabs(t * sigma); // value of sqrt(nu) can be hashed
    double vHalf = 0.5 * nu;
    double y = vHalf * std::log(x);
    y -= Y.GetLogGammaFunction();
    y -= (vHalf - 1) * M_LN2;
    y += RandMath::logModifiedBesselSecondKind(x, vHalf);
    double costmu = std::cos(t * mu), sintmu = std::sin(t * mu);
    std::complex<double> cf(costmu, sintmu);
    return std::exp(y) * cf;
}

double StudentTRand::quantileImpl(double p) const
{
    // TODO: consider nu == 3
    double temp = p - 0.5;
    if (nu == 1)
        return std::tan(M_PI * temp) * sigma + mu;
    double pq = p * (1.0 - p);
    if (nu == 2)
        return sigma * 2.0 * temp * std::sqrt(0.5 / pq) + mu;
    if (nu == 4)
    {
        double alpha = 2 * std::sqrt(pq);
        double beta = std::cos(std::acos(alpha) / 3.0) / alpha - 1;
        return mu + sigma * 2 * RandMath::sign(temp) * std::sqrt(beta);
    }
    return ContinuousDistribution::quantileImpl(p);
}

double StudentTRand::quantileImpl1m(double p) const
{
    // TODO: consider nu == 3
    double temp = 0.5 - p;
    if (nu == 1)
        return std::tan(M_PI * temp) * sigma + mu;
    double pq = p * (1.0 - p);
    if (nu == 2)
        return sigma * 2 * temp * std::sqrt(0.5 / pq) + mu;
    if (nu == 4)
    {
        double alpha = 2 * std::sqrt(pq);
        double beta = std::cos(std::acos(alpha) / 3.0) / alpha - 1;
        return mu + sigma * 2 * RandMath::sign(temp) * std::sqrt(beta);
    }
    return ContinuousDistribution::quantileImpl1m(p);
}

double StudentTRand::Median() const
{
    return mu;
}

double StudentTRand::Mode() const
{
    return mu;
}

double StudentTRand::Skewness() const
{
    return (nu > 3) ? 0.0 : NAN;
}

double StudentTRand::ExcessKurtosis() const
{
    if (nu > 4)
        return 6 / (nu - 4);
    return (nu > 2) ? INFINITY : NAN;
}
<|endoftext|>


<commit_before><commit_msg>fix post cast removal build error<commit_after><|endoftext|>