<commit_before>//===-- ARMSubtarget.cpp - ARM Subtarget Information ----------------------===//
//
//                     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 ARM specific subclass of TargetSubtargetInfo.
//
//===----------------------------------------------------------------------===//

#include "ARMSubtarget.h"
#include "ARMFrameLowering.h"
#include "ARMISelLowering.h"
#include "ARMInstrInfo.h"
#include "ARMMachineFunctionInfo.h"
#include "ARMSelectionDAGInfo.h"
#include "ARMSubtarget.h"
#include "ARMTargetMachine.h"
#include "Thumb1FrameLowering.h"
#include "Thumb1InstrInfo.h"
#include "Thumb2InstrInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/Target/TargetRegisterInfo.h"
#include "llvm/Support/TargetParser.h"

using namespace llvm;

#define DEBUG_TYPE "arm-subtarget"

#define GET_SUBTARGETINFO_TARGET_DESC
#define GET_SUBTARGETINFO_CTOR
#include "ARMGenSubtargetInfo.inc"

static cl::opt<bool>
UseFusedMulOps("arm-use-mulops",
               cl::init(true), cl::Hidden);

enum ITMode {
  DefaultIT,
  RestrictedIT,
  NoRestrictedIT
};

static cl::opt<ITMode>
IT(cl::desc("IT block support"), cl::Hidden, cl::init(DefaultIT),
   cl::ZeroOrMore,
   cl::values(clEnumValN(DefaultIT, "arm-default-it",
                         "Generate IT block based on arch"),
              clEnumValN(RestrictedIT, "arm-restrict-it",
                         "Disallow deprecated IT based on ARMv8"),
              clEnumValN(NoRestrictedIT, "arm-no-restrict-it",
                         "Allow IT blocks based on ARMv7")));

/// ForceFastISel - Use the fast-isel, even for subtargets where it is not
/// currently supported (for testing only).
static cl::opt<bool>
ForceFastISel("arm-force-fast-isel",
               cl::init(false), cl::Hidden);

/// initializeSubtargetDependencies - Initializes using a CPU and feature string
/// so that we can use initializer lists for subtarget initialization.
ARMSubtarget &ARMSubtarget::initializeSubtargetDependencies(StringRef CPU,
                                                            StringRef FS) {
  initializeEnvironment();
  initSubtargetFeatures(CPU, FS);
  return *this;
}

/// EnableExecuteOnly - Enables the generation of execute-only code on supported
/// targets
static cl::opt<bool>
EnableExecuteOnly("arm-execute-only");

ARMFrameLowering *ARMSubtarget::initializeFrameLowering(StringRef CPU,
                                                        StringRef FS) {
  ARMSubtarget &STI = initializeSubtargetDependencies(CPU, FS);
  if (STI.isThumb1Only())
    return (ARMFrameLowering *)new Thumb1FrameLowering(STI);

  return new ARMFrameLowering(STI);
}

ARMSubtarget::ARMSubtarget(const Triple &TT, const std::string &CPU,
                           const std::string &FS,
                           const ARMBaseTargetMachine &TM, bool IsLittle)
    : ARMGenSubtargetInfo(TT, CPU, FS), UseMulOps(UseFusedMulOps),
      CPUString(CPU), IsLittle(IsLittle), TargetTriple(TT), Options(TM.Options),
      TM(TM), GenExecuteOnly(EnableExecuteOnly),
      FrameLowering(initializeFrameLowering(CPU, FS)),
      // At this point initializeSubtargetDependencies has been called so
      // we can query directly.
      InstrInfo(isThumb1Only()
                    ? (ARMBaseInstrInfo *)new Thumb1InstrInfo(*this)
                    : !isThumb()
                          ? (ARMBaseInstrInfo *)new ARMInstrInfo(*this)
                          : (ARMBaseInstrInfo *)new Thumb2InstrInfo(*this)),
      TLInfo(TM, *this), GISel() {}

const CallLowering *ARMSubtarget::getCallLowering() const {
  assert(GISel && "Access to GlobalISel APIs not set");
  return GISel->getCallLowering();
}

const InstructionSelector *ARMSubtarget::getInstructionSelector() const {
  assert(GISel && "Access to GlobalISel APIs not set");
  return GISel->getInstructionSelector();
}

const LegalizerInfo *ARMSubtarget::getLegalizerInfo() const {
  assert(GISel && "Access to GlobalISel APIs not set");
  return GISel->getLegalizerInfo();
}

const RegisterBankInfo *ARMSubtarget::getRegBankInfo() const {
  assert(GISel && "Access to GlobalISel APIs not set");
  return GISel->getRegBankInfo();
}

bool ARMSubtarget::isXRaySupported() const {
  // We don't currently suppport Thumb, but Windows requires Thumb.
  return hasV6Ops() && hasARMOps() && !isTargetWindows();
}

void ARMSubtarget::initializeEnvironment() {
  // MCAsmInfo isn't always present (e.g. in opt) so we can't initialize this
  // directly from it, but we can try to make sure they're consistent when both
  // available.
  UseSjLjEH = isTargetDarwin() && !isTargetWatchABI();
  assert((!TM.getMCAsmInfo() ||
          (TM.getMCAsmInfo()->getExceptionHandlingType() ==
           ExceptionHandling::SjLj) == UseSjLjEH) &&
         "inconsistent sjlj choice between CodeGen and MC");
}

void ARMSubtarget::initSubtargetFeatures(StringRef CPU, StringRef FS) {
  if (CPUString.empty()) {
    CPUString = "generic";

    if (isTargetDarwin()) {
      StringRef ArchName = TargetTriple.getArchName();
      unsigned ArchKind = llvm::ARM::parseArch(ArchName);
      if (ArchKind == llvm::ARM::AK_ARMV7S)
        // Default to the Swift CPU when targeting armv7s/thumbv7s.
        CPUString = "swift";
      else if (ArchKind == llvm::ARM::AK_ARMV7K)
        // Default to the Cortex-a7 CPU when targeting armv7k/thumbv7k.
        // ARMv7k does not use SjLj exception handling.
        CPUString = "cortex-a7";
    }
  }

  // Insert the architecture feature derived from the target triple into the
  // feature string. This is important for setting features that are implied
  // based on the architecture version.
  std::string ArchFS = ARM_MC::ParseARMTriple(TargetTriple, CPUString);
  if (!FS.empty()) {
    if (!ArchFS.empty())
      ArchFS = (Twine(ArchFS) + "," + FS).str();
    else
      ArchFS = FS;
  }
  ParseSubtargetFeatures(CPUString, ArchFS);

  // FIXME: This used enable V6T2 support implicitly for Thumb2 mode.
  // Assert this for now to make the change obvious.
  assert(hasV6T2Ops() || !hasThumb2());

  // Execute only support requires movt support
  if (genExecuteOnly())
    assert(hasV8MBaselineOps() && !NoMovt && "Cannot generate execute-only code for this target");

  // Keep a pointer to static instruction cost data for the specified CPU.
  SchedModel = getSchedModelForCPU(CPUString);

  // Initialize scheduling itinerary for the specified CPU.
  InstrItins = getInstrItineraryForCPU(CPUString);

  // FIXME: this is invalid for WindowsCE
  if (isTargetWindows())
    NoARM = true;

  if (isAAPCS_ABI())
    stackAlignment = 8;
  if (isTargetNaCl() || isAAPCS16_ABI())
    stackAlignment = 16;

  // FIXME: Completely disable sibcall for Thumb1 since ThumbRegisterInfo::
  // emitEpilogue is not ready for them. Thumb tail calls also use t2B, as
  // the Thumb1 16-bit unconditional branch doesn't have sufficient relocation
  // support in the assembler and linker to be used. This would need to be
  // fixed to fully support tail calls in Thumb1.
  //
  // Doing this is tricky, since the LDM/POP instruction on Thumb doesn't take
  // LR.  This means if we need to reload LR, it takes an extra instructions,
  // which outweighs the value of the tail call; but here we don't know yet
  // whether LR is going to be used.  Probably the right approach is to
  // generate the tail call here and turn it back into CALL/RET in
  // emitEpilogue if LR is used.

  // Thumb1 PIC calls to external symbols use BX, so they can be tail calls,
  // but we need to make sure there are enough registers; the only valid
  // registers are the 4 used for parameters.  We don't currently do this
  // case.

  SupportsTailCall = !isThumb() || hasV8MBaselineOps();

  if (isTargetMachO() && isTargetIOS() && getTargetTriple().isOSVersionLT(5, 0))
    SupportsTailCall = false;

  switch (IT) {
  case DefaultIT:
    RestrictIT = hasV8Ops();
    break;
  case RestrictedIT:
    RestrictIT = true;
    break;
  case NoRestrictedIT:
    RestrictIT = false;
    break;
  }

  // NEON f32 ops are non-IEEE 754 compliant. Darwin is ok with it by default.
  const FeatureBitset &Bits = getFeatureBits();
  if ((Bits[ARM::ProcA5] || Bits[ARM::ProcA8]) && // Where this matters
      (Options.UnsafeFPMath || isTargetDarwin()))
    UseNEONForSinglePrecisionFP = true;

  if (isRWPI())
    ReserveR9 = true;

  // FIXME: Teach TableGen to deal with these instead of doing it manually here.
  switch (ARMProcFamily) {
  case Others:
  case CortexA5:
    break;
  case CortexA7:
    LdStMultipleTiming = DoubleIssue;
    break;
  case CortexA8:
    LdStMultipleTiming = DoubleIssue;
    break;
  case CortexA9:
    LdStMultipleTiming = DoubleIssueCheckUnalignedAccess;
    PreISelOperandLatencyAdjustment = 1;
    break;
  case CortexA12:
    break;
  case CortexA15:
    MaxInterleaveFactor = 2;
    PreISelOperandLatencyAdjustment = 1;
    PartialUpdateClearance = 12;
    break;
  case CortexA17:
  case CortexA32:
  case CortexA35:
  case CortexA53:
  case CortexA57:
  case CortexA72:
  case CortexA73:
  case CortexR4:
  case CortexR4F:
  case CortexR5:
  case CortexR7:
  case CortexM3:
  case ExynosM1:
  case CortexR52:
    break;
  case Krait:
    PreISelOperandLatencyAdjustment = 1;
    break;
  case Swift:
    MaxInterleaveFactor = 2;
    LdStMultipleTiming = SingleIssuePlusExtras;
    PreISelOperandLatencyAdjustment = 1;
    PartialUpdateClearance = 12;
    break;
  }
}

bool ARMSubtarget::isAPCS_ABI() const {
  assert(TM.TargetABI != ARMBaseTargetMachine::ARM_ABI_UNKNOWN);
  return TM.TargetABI == ARMBaseTargetMachine::ARM_ABI_APCS;
}
bool ARMSubtarget::isAAPCS_ABI() const {
  assert(TM.TargetABI != ARMBaseTargetMachine::ARM_ABI_UNKNOWN);
  return TM.TargetABI == ARMBaseTargetMachine::ARM_ABI_AAPCS ||
         TM.TargetABI == ARMBaseTargetMachine::ARM_ABI_AAPCS16;
}
bool ARMSubtarget::isAAPCS16_ABI() const {
  assert(TM.TargetABI != ARMBaseTargetMachine::ARM_ABI_UNKNOWN);
  return TM.TargetABI == ARMBaseTargetMachine::ARM_ABI_AAPCS16;
}

bool ARMSubtarget::isROPI() const {
  return TM.getRelocationModel() == Reloc::ROPI ||
         TM.getRelocationModel() == Reloc::ROPI_RWPI;
}
bool ARMSubtarget::isRWPI() const {
  return TM.getRelocationModel() == Reloc::RWPI ||
         TM.getRelocationModel() == Reloc::ROPI_RWPI;
}

bool ARMSubtarget::isGVIndirectSymbol(const GlobalValue *GV) const {
  if (!TM.shouldAssumeDSOLocal(*GV->getParent(), GV))
    return true;

  // 32 bit macho has no relocation for a-b if a is undefined, even if b is in
  // the section that is being relocated. This means we have to use o load even
  // for GVs that are known to be local to the dso.
  if (isTargetMachO() && TM.isPositionIndependent() &&
      (GV->isDeclarationForLinker() || GV->hasCommonLinkage()))
    return true;

  return false;
}

unsigned ARMSubtarget::getMispredictionPenalty() const {
  return SchedModel.MispredictPenalty;
}

bool ARMSubtarget::hasSinCos() const {
  return isTargetWatchOS() ||
    (isTargetIOS() && !getTargetTriple().isOSVersionLT(7, 0));
}

bool ARMSubtarget::enableMachineScheduler() const {
  // Enable the MachineScheduler before register allocation for out-of-order
  // architectures where we do not use the PostRA scheduler anymore (for now
  // restricted to swift).
  return getSchedModel().isOutOfOrder() && isSwift();
}

// This overrides the PostRAScheduler bit in the SchedModel for any CPU.
bool ARMSubtarget::enablePostRAScheduler() const {
  // No need for PostRA scheduling on out of order CPUs (for now restricted to
  // swift).
  if (getSchedModel().isOutOfOrder() && isSwift())
    return false;
  return (!isThumb() || hasThumb2());
}

bool ARMSubtarget::enableAtomicExpand() const { return hasAnyDataBarrier(); }

bool ARMSubtarget::useStride4VFPs(const MachineFunction &MF) const {
  // For general targets, the prologue can grow when VFPs are allocated with
  // stride 4 (more vpush instructions). But WatchOS uses a compact unwind
  // format which it's more important to get right.
  return isTargetWatchABI() || (isSwift() && !MF.getFunction()->optForMinSize());
}

bool ARMSubtarget::useMovt(const MachineFunction &MF) const {
  // NOTE Windows on ARM needs to use mov.w/mov.t pairs to materialise 32-bit
  // immediates as it is inherently position independent, and may be out of
  // range otherwise.
  return !NoMovt && hasV8MBaselineOps() &&
         (isTargetWindows() || !MF.getFunction()->optForMinSize() || genExecuteOnly());
}

bool ARMSubtarget::useFastISel() const {
  // Enable fast-isel for any target, for testing only.
  if (ForceFastISel)
    return true;

  // Limit fast-isel to the targets that are or have been tested.
  if (!hasV6Ops())
    return false;

  // Thumb2 support on iOS; ARM support on iOS, Linux and NaCl.
  return TM.Options.EnableFastISel &&
         ((isTargetMachO() && !isThumb1Only()) ||
          (isTargetLinux() && !isThumb()) || (isTargetNaCl() && !isThumb()));
}
<commit_msg>Fix for build warning in execute-only support<commit_after>//===-- ARMSubtarget.cpp - ARM Subtarget Information ----------------------===//
//
//                     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 ARM specific subclass of TargetSubtargetInfo.
//
//===----------------------------------------------------------------------===//

#include "ARMSubtarget.h"
#include "ARMFrameLowering.h"
#include "ARMISelLowering.h"
#include "ARMInstrInfo.h"
#include "ARMMachineFunctionInfo.h"
#include "ARMSelectionDAGInfo.h"
#include "ARMSubtarget.h"
#include "ARMTargetMachine.h"
#include "Thumb1FrameLowering.h"
#include "Thumb1InstrInfo.h"
#include "Thumb2InstrInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/Target/TargetRegisterInfo.h"
#include "llvm/Support/TargetParser.h"

using namespace llvm;

#define DEBUG_TYPE "arm-subtarget"

#define GET_SUBTARGETINFO_TARGET_DESC
#define GET_SUBTARGETINFO_CTOR
#include "ARMGenSubtargetInfo.inc"

static cl::opt<bool>
UseFusedMulOps("arm-use-mulops",
               cl::init(true), cl::Hidden);

enum ITMode {
  DefaultIT,
  RestrictedIT,
  NoRestrictedIT
};

static cl::opt<ITMode>
IT(cl::desc("IT block support"), cl::Hidden, cl::init(DefaultIT),
   cl::ZeroOrMore,
   cl::values(clEnumValN(DefaultIT, "arm-default-it",
                         "Generate IT block based on arch"),
              clEnumValN(RestrictedIT, "arm-restrict-it",
                         "Disallow deprecated IT based on ARMv8"),
              clEnumValN(NoRestrictedIT, "arm-no-restrict-it",
                         "Allow IT blocks based on ARMv7")));

/// ForceFastISel - Use the fast-isel, even for subtargets where it is not
/// currently supported (for testing only).
static cl::opt<bool>
ForceFastISel("arm-force-fast-isel",
               cl::init(false), cl::Hidden);

/// initializeSubtargetDependencies - Initializes using a CPU and feature string
/// so that we can use initializer lists for subtarget initialization.
ARMSubtarget &ARMSubtarget::initializeSubtargetDependencies(StringRef CPU,
                                                            StringRef FS) {
  initializeEnvironment();
  initSubtargetFeatures(CPU, FS);
  return *this;
}

/// EnableExecuteOnly - Enables the generation of execute-only code on supported
/// targets
static cl::opt<bool>
EnableExecuteOnly("arm-execute-only");

ARMFrameLowering *ARMSubtarget::initializeFrameLowering(StringRef CPU,
                                                        StringRef FS) {
  ARMSubtarget &STI = initializeSubtargetDependencies(CPU, FS);
  if (STI.isThumb1Only())
    return (ARMFrameLowering *)new Thumb1FrameLowering(STI);

  return new ARMFrameLowering(STI);
}

ARMSubtarget::ARMSubtarget(const Triple &TT, const std::string &CPU,
                           const std::string &FS,
                           const ARMBaseTargetMachine &TM, bool IsLittle)
    : ARMGenSubtargetInfo(TT, CPU, FS), UseMulOps(UseFusedMulOps),
      GenExecuteOnly(EnableExecuteOnly), CPUString(CPU), IsLittle(IsLittle),
      TargetTriple(TT), Options(TM.Options), TM(TM),
      FrameLowering(initializeFrameLowering(CPU, FS)),
      // At this point initializeSubtargetDependencies has been called so
      // we can query directly.
      InstrInfo(isThumb1Only()
                    ? (ARMBaseInstrInfo *)new Thumb1InstrInfo(*this)
                    : !isThumb()
                          ? (ARMBaseInstrInfo *)new ARMInstrInfo(*this)
                          : (ARMBaseInstrInfo *)new Thumb2InstrInfo(*this)),
      TLInfo(TM, *this), GISel() {}

const CallLowering *ARMSubtarget::getCallLowering() const {
  assert(GISel && "Access to GlobalISel APIs not set");
  return GISel->getCallLowering();
}

const InstructionSelector *ARMSubtarget::getInstructionSelector() const {
  assert(GISel && "Access to GlobalISel APIs not set");
  return GISel->getInstructionSelector();
}

const LegalizerInfo *ARMSubtarget::getLegalizerInfo() const {
  assert(GISel && "Access to GlobalISel APIs not set");
  return GISel->getLegalizerInfo();
}

const RegisterBankInfo *ARMSubtarget::getRegBankInfo() const {
  assert(GISel && "Access to GlobalISel APIs not set");
  return GISel->getRegBankInfo();
}

bool ARMSubtarget::isXRaySupported() const {
  // We don't currently suppport Thumb, but Windows requires Thumb.
  return hasV6Ops() && hasARMOps() && !isTargetWindows();
}

void ARMSubtarget::initializeEnvironment() {
  // MCAsmInfo isn't always present (e.g. in opt) so we can't initialize this
  // directly from it, but we can try to make sure they're consistent when both
  // available.
  UseSjLjEH = isTargetDarwin() && !isTargetWatchABI();
  assert((!TM.getMCAsmInfo() ||
          (TM.getMCAsmInfo()->getExceptionHandlingType() ==
           ExceptionHandling::SjLj) == UseSjLjEH) &&
         "inconsistent sjlj choice between CodeGen and MC");
}

void ARMSubtarget::initSubtargetFeatures(StringRef CPU, StringRef FS) {
  if (CPUString.empty()) {
    CPUString = "generic";

    if (isTargetDarwin()) {
      StringRef ArchName = TargetTriple.getArchName();
      unsigned ArchKind = llvm::ARM::parseArch(ArchName);
      if (ArchKind == llvm::ARM::AK_ARMV7S)
        // Default to the Swift CPU when targeting armv7s/thumbv7s.
        CPUString = "swift";
      else if (ArchKind == llvm::ARM::AK_ARMV7K)
        // Default to the Cortex-a7 CPU when targeting armv7k/thumbv7k.
        // ARMv7k does not use SjLj exception handling.
        CPUString = "cortex-a7";
    }
  }

  // Insert the architecture feature derived from the target triple into the
  // feature string. This is important for setting features that are implied
  // based on the architecture version.
  std::string ArchFS = ARM_MC::ParseARMTriple(TargetTriple, CPUString);
  if (!FS.empty()) {
    if (!ArchFS.empty())
      ArchFS = (Twine(ArchFS) + "," + FS).str();
    else
      ArchFS = FS;
  }
  ParseSubtargetFeatures(CPUString, ArchFS);

  // FIXME: This used enable V6T2 support implicitly for Thumb2 mode.
  // Assert this for now to make the change obvious.
  assert(hasV6T2Ops() || !hasThumb2());

  // Execute only support requires movt support
  if (genExecuteOnly())
    assert(hasV8MBaselineOps() && !NoMovt && "Cannot generate execute-only code for this target");

  // Keep a pointer to static instruction cost data for the specified CPU.
  SchedModel = getSchedModelForCPU(CPUString);

  // Initialize scheduling itinerary for the specified CPU.
  InstrItins = getInstrItineraryForCPU(CPUString);

  // FIXME: this is invalid for WindowsCE
  if (isTargetWindows())
    NoARM = true;

  if (isAAPCS_ABI())
    stackAlignment = 8;
  if (isTargetNaCl() || isAAPCS16_ABI())
    stackAlignment = 16;

  // FIXME: Completely disable sibcall for Thumb1 since ThumbRegisterInfo::
  // emitEpilogue is not ready for them. Thumb tail calls also use t2B, as
  // the Thumb1 16-bit unconditional branch doesn't have sufficient relocation
  // support in the assembler and linker to be used. This would need to be
  // fixed to fully support tail calls in Thumb1.
  //
  // Doing this is tricky, since the LDM/POP instruction on Thumb doesn't take
  // LR.  This means if we need to reload LR, it takes an extra instructions,
  // which outweighs the value of the tail call; but here we don't know yet
  // whether LR is going to be used.  Probably the right approach is to
  // generate the tail call here and turn it back into CALL/RET in
  // emitEpilogue if LR is used.

  // Thumb1 PIC calls to external symbols use BX, so they can be tail calls,
  // but we need to make sure there are enough registers; the only valid
  // registers are the 4 used for parameters.  We don't currently do this
  // case.

  SupportsTailCall = !isThumb() || hasV8MBaselineOps();

  if (isTargetMachO() && isTargetIOS() && getTargetTriple().isOSVersionLT(5, 0))
    SupportsTailCall = false;

  switch (IT) {
  case DefaultIT:
    RestrictIT = hasV8Ops();
    break;
  case RestrictedIT:
    RestrictIT = true;
    break;
  case NoRestrictedIT:
    RestrictIT = false;
    break;
  }

  // NEON f32 ops are non-IEEE 754 compliant. Darwin is ok with it by default.
  const FeatureBitset &Bits = getFeatureBits();
  if ((Bits[ARM::ProcA5] || Bits[ARM::ProcA8]) && // Where this matters
      (Options.UnsafeFPMath || isTargetDarwin()))
    UseNEONForSinglePrecisionFP = true;

  if (isRWPI())
    ReserveR9 = true;

  // FIXME: Teach TableGen to deal with these instead of doing it manually here.
  switch (ARMProcFamily) {
  case Others:
  case CortexA5:
    break;
  case CortexA7:
    LdStMultipleTiming = DoubleIssue;
    break;
  case CortexA8:
    LdStMultipleTiming = DoubleIssue;
    break;
  case CortexA9:
    LdStMultipleTiming = DoubleIssueCheckUnalignedAccess;
    PreISelOperandLatencyAdjustment = 1;
    break;
  case CortexA12:
    break;
  case CortexA15:
    MaxInterleaveFactor = 2;
    PreISelOperandLatencyAdjustment = 1;
    PartialUpdateClearance = 12;
    break;
  case CortexA17:
  case CortexA32:
  case CortexA35:
  case CortexA53:
  case CortexA57:
  case CortexA72:
  case CortexA73:
  case CortexR4:
  case CortexR4F:
  case CortexR5:
  case CortexR7:
  case CortexM3:
  case ExynosM1:
  case CortexR52:
    break;
  case Krait:
    PreISelOperandLatencyAdjustment = 1;
    break;
  case Swift:
    MaxInterleaveFactor = 2;
    LdStMultipleTiming = SingleIssuePlusExtras;
    PreISelOperandLatencyAdjustment = 1;
    PartialUpdateClearance = 12;
    break;
  }
}

bool ARMSubtarget::isAPCS_ABI() const {
  assert(TM.TargetABI != ARMBaseTargetMachine::ARM_ABI_UNKNOWN);
  return TM.TargetABI == ARMBaseTargetMachine::ARM_ABI_APCS;
}
bool ARMSubtarget::isAAPCS_ABI() const {
  assert(TM.TargetABI != ARMBaseTargetMachine::ARM_ABI_UNKNOWN);
  return TM.TargetABI == ARMBaseTargetMachine::ARM_ABI_AAPCS ||
         TM.TargetABI == ARMBaseTargetMachine::ARM_ABI_AAPCS16;
}
bool ARMSubtarget::isAAPCS16_ABI() const {
  assert(TM.TargetABI != ARMBaseTargetMachine::ARM_ABI_UNKNOWN);
  return TM.TargetABI == ARMBaseTargetMachine::ARM_ABI_AAPCS16;
}

bool ARMSubtarget::isROPI() const {
  return TM.getRelocationModel() == Reloc::ROPI ||
         TM.getRelocationModel() == Reloc::ROPI_RWPI;
}
bool ARMSubtarget::isRWPI() const {
  return TM.getRelocationModel() == Reloc::RWPI ||
         TM.getRelocationModel() == Reloc::ROPI_RWPI;
}

bool ARMSubtarget::isGVIndirectSymbol(const GlobalValue *GV) const {
  if (!TM.shouldAssumeDSOLocal(*GV->getParent(), GV))
    return true;

  // 32 bit macho has no relocation for a-b if a is undefined, even if b is in
  // the section that is being relocated. This means we have to use o load even
  // for GVs that are known to be local to the dso.
  if (isTargetMachO() && TM.isPositionIndependent() &&
      (GV->isDeclarationForLinker() || GV->hasCommonLinkage()))
    return true;

  return false;
}

unsigned ARMSubtarget::getMispredictionPenalty() const {
  return SchedModel.MispredictPenalty;
}

bool ARMSubtarget::hasSinCos() const {
  return isTargetWatchOS() ||
    (isTargetIOS() && !getTargetTriple().isOSVersionLT(7, 0));
}

bool ARMSubtarget::enableMachineScheduler() const {
  // Enable the MachineScheduler before register allocation for out-of-order
  // architectures where we do not use the PostRA scheduler anymore (for now
  // restricted to swift).
  return getSchedModel().isOutOfOrder() && isSwift();
}

// This overrides the PostRAScheduler bit in the SchedModel for any CPU.
bool ARMSubtarget::enablePostRAScheduler() const {
  // No need for PostRA scheduling on out of order CPUs (for now restricted to
  // swift).
  if (getSchedModel().isOutOfOrder() && isSwift())
    return false;
  return (!isThumb() || hasThumb2());
}

bool ARMSubtarget::enableAtomicExpand() const { return hasAnyDataBarrier(); }

bool ARMSubtarget::useStride4VFPs(const MachineFunction &MF) const {
  // For general targets, the prologue can grow when VFPs are allocated with
  // stride 4 (more vpush instructions). But WatchOS uses a compact unwind
  // format which it's more important to get right.
  return isTargetWatchABI() || (isSwift() && !MF.getFunction()->optForMinSize());
}

bool ARMSubtarget::useMovt(const MachineFunction &MF) const {
  // NOTE Windows on ARM needs to use mov.w/mov.t pairs to materialise 32-bit
  // immediates as it is inherently position independent, and may be out of
  // range otherwise.
  return !NoMovt && hasV8MBaselineOps() &&
         (isTargetWindows() || !MF.getFunction()->optForMinSize() || genExecuteOnly());
}

bool ARMSubtarget::useFastISel() const {
  // Enable fast-isel for any target, for testing only.
  if (ForceFastISel)
    return true;

  // Limit fast-isel to the targets that are or have been tested.
  if (!hasV6Ops())
    return false;

  // Thumb2 support on iOS; ARM support on iOS, Linux and NaCl.
  return TM.Options.EnableFastISel &&
         ((isTargetMachO() && !isThumb1Only()) ||
          (isTargetLinux() && !isThumb()) || (isTargetNaCl() && !isThumb()));
}
<|endoftext|>


<commit_before>/** This library is under the 3-Clause BSD License

Copyright (c) 2017, Johannes Heinecke

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
  1. Redistributions of source code must retain the above copyright notice,
     this list of conditions and the following disclaimer.
  2. 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.
  3. Neither the name of the copyright holder 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.

 Author: Johannes Heinecke
 Version:  1.0 as of 24th September 2017
*/

#include <iostream>
#include <map>
#include <vector>

#include <cstring>
#include <cstdlib>

#include <gtk/gtk.h>

#include "cpuinfo.h"
#include "utils.h"
#include "widgets.h"

#define APPNAME "cgpuinfo"

using namespace std;

/** structure to give information to callback */
//struct cb_data {
//    CpuInfo *ci;
//   vector<FieldsGroup *>fgs;
    //vector<const char *>infos;
    //map<const char *, vector<GtkWidget *> > labels;
//};

/* TODO:
   process cpu - core correctly (which cpu on which core)
 */

/** used to update labels */
gboolean time_handler(CallBackDataCPU *cbdata) {
    unsigned int u[MAX_CPU], s[MAX_CPU], n[MAX_CPU], i[MAX_CPU], t[MAX_CPU];
    cbdata->ci->getCPUtime(u, s, n, i);
    cbdata->ci->getCoreTemp(t);

    unsigned int x = 0;
    for (vector<FieldsGroup *>::iterator it = cbdata->cpu_fgs.begin(); it != cbdata->cpu_fgs.end(); ++it, ++x) {
	// pour tous les FieldsGroup des CPU
	(*it)->setValue('u', u[x]/10.0);
	(*it)->setValue('n', n[x]/10.0);
	(*it)->setValue('s', s[x]/10.0);
    }

    x = 1;
    for (vector<FieldsGroup *>::iterator it = cbdata->core_fgs.begin(); it != cbdata->core_fgs.end(); ++it, ++x) {

	(*it)->setValue('t', t[x]/1000.0);
    }

    return TRUE;
}

int main(int argc, char *argv[]) {
    if (argc <= 1) {
	cerr << argv[0] << " [options]" << endl
	     << "options:" << endl
	     << "  -d  update delay (default 500ms)" << endl
	     << "  -T  textual output" << endl
	     << "\n without -T" <<endl
	     << "  --nn do not show niced" << endl
	     << "  -v vertical layout" << endl
	     << "  -c compact view (no labels)" << endl
	     << " -p progressbar view" << endl
	     << "\n with -T" <<endl
	     << "  -t  add time stamp" << endl
	     << "  -l  log mode" << endl
	     << endl;
	;
    }

    unsigned int delay = 1000;
    bool textonly = false;
    bool logmode = false;
    bool timest = false;
    bool showniced = true;
    bool vertical = false;
    bool progressbar = false;
    bool compact = false;

    for (int i = 1; i<argc; ++i) {
	if (strcmp(argv[i], "-d") == 0 && i < argc-1) {
	    unsigned int d = atoi(argv[i+1]);
	    if (d < 1) {
		cerr << "invalid option: " << argv[i] << " " << argv[i+1] << endl;
	    } else {
		++i;
		delay = d;
	    }

	} else if (strcmp(argv[i], "-T") == 0) {
	    textonly = true;
	} else if (strcmp(argv[i], "-c") == 0) {
	    compact = true;
	} else if (strcmp(argv[i], "-p") == 0) {
	    progressbar = true;
	} else if (strcmp(argv[i], "-v") == 0) {
	    vertical = true;
	} else if (strcmp(argv[i], "--nn") == 0) {
	    showniced = false;
	} else if (strcmp(argv[i], "-l") == 0) {
	    logmode = true;
	} else if (strcmp(argv[i], "-t") == 0) {
	    timest = true;
	} else {
	    cerr << "invalid option: " << argv[i] << endl;
	    break;
	}
    }

    CpuInfo ci;
    unsigned int cpus = ci.getCpus(); // cpu number + 1 (total)
    unsigned int cores = ci.getCores(); // core number + 1

    unsigned int cpus_par_core = (cpus-1)/(cores-1);

    if (!textonly) {
	// initialise cbdata
	CallBackDataCPU cbdata;
	cbdata.ci = &ci;

	gtk_init(&argc, &argv);

	//printf("GTK+ version: %d.%d.%d\n", gtk_major_version, gtk_minor_version, gtk_micro_version);
	//printf("Glib version: %d.%d.%d\n", glib_major_version, glib_minor_version, glib_micro_version);

	//PangoFontDescription *pfd = pango_font_description_from_string("Serif 12");

	// main window
	GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	//gtk_window_set_default_size(GTK_WINDOW(window), 300, 100);
	gtk_window_set_title(GTK_WINDOW(window), APPNAME);
	//gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
	gtk_container_set_border_width(GTK_CONTAINER(window), 1);

	// create main table (a column per core)
	GtkWidget *maintable;
	if (vertical)
	    maintable = gtk_table_new(cpus, 2, FALSE); // rows, columns, homogenuous
	else {
	    // first column for headers
	    maintable = gtk_table_new(2, cpus+1, FALSE); // rows, columns, homogenuous
	}
	gtk_table_set_row_spacings(GTK_TABLE(maintable), 2);
	gtk_table_set_col_spacings(GTK_TABLE(maintable), 2);
	gtk_container_add(GTK_CONTAINER(window), maintable);

	char tmp[70];

// 	// create headers
//   	if (!compact) {
//   	    GtkWidget *label = gtk_label_new("temperature");
// 	    gtk_label_set_angle(GTK_LABEL(label), 90);
//   	    // left, right, top, bottom
//   	    gtk_table_attach_defaults(GTK_TABLE(maintable), label,
//   				      0, 1,
//   				      0, 1);
//   	}

	// create core infos
	int offset = 2; // headers, total column
	for (unsigned int i = 0; i < cores-1; ++i) {
	    sprintf(tmp, "core %d:", i);
	    FieldsGroup *fg = new FieldsGroup(tmp, vertical);
	    cbdata.core_fgs.push_back(fg);
	    const char *name = 0;
	    if (!compact && i == 0) name = "temperature";

	    fg->add('t', new InfoField(name, "%.fC", vertical, progressbar));
	    fg->setValue('t', 40);

	    if (vertical) {
		// left, right, top, bottom
		gtk_table_attach_defaults(GTK_TABLE(maintable), fg->getWidget(),
					  0, 1,
					  offset + i*cpus_par_core, offset + (i+1)*cpus_par_core);
	    } else {
		// left, right, top, bottom

		gtk_table_attach_defaults(GTK_TABLE(maintable), fg->getWidget(),
					  offset + i*cpus_par_core, offset + (i+1)*cpus_par_core,
					  0, 1);
	    }

	}

	//cerr << "cpc " << cpus_par_core << endl;
	// create cpu infos
	for (unsigned int i = 0; i < cpus; ++i) {
	    if (i == 0) sprintf(tmp, "total:");
	    else {
		sprintf(tmp, "cpu %d:", i-1);
		//cerr << i << " core: " << ci.getCore(i-1) << endl;
	    }
	    int currentcore = ci.getCore(i-1);
	    int offset = 1;
	    int column = offset; // with headers
	    if (i != 0) column = offset + 1 + currentcore*cpus_par_core +  (i-1)/(cores-1);

	    //cerr << i << " " << (i-1) % 2<< " xxx: " << " " << currentcore << " " << column << endl;
	    FieldsGroup *fg = new FieldsGroup(tmp, vertical);
	    cbdata.cpu_fgs.push_back(fg);
	    if (vertical) {
		// left, right, top, bottom
		gtk_table_attach_defaults(GTK_TABLE(maintable), fg->getWidget(), 1, 2, column, column+1);
	    } else {
		// left, right, top, bottom
		gtk_table_attach_defaults(GTK_TABLE(maintable), fg->getWidget(), column, column+1, 1, 2);
	    }
	    const char *name = 0;
	    if (!compact && i == 0) name = "user";
	    fg->add('u', new InfoField(name, "%.1f%%", vertical, progressbar));
	    //fg->setValue('u', 20.0);

	    if (showniced) {
		if (!compact && i == 0) name = "niced";
		fg->add('n', new InfoField(name, "%.1f%%", vertical, progressbar));
		fg->setValue('n', 40);
	    }
	    if (!compact && i == 0) name = "system";
	    fg->add('s', new InfoField(name, "%.1f%%", vertical, progressbar));
	    fg->setValue('s', 30);
	}

	//pango_font_description_free(pfd);

	// destroy applicatation when window is closed
	g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), G_OBJECT(window));
	//g_signal_connect(window, "expose-event",  G_CALLBACK(on_expose_event), NULL);

	// set first values
	time_handler(&cbdata);

	// update values every <delay> milliseconds
	g_timeout_add(delay, (GSourceFunc) time_handler, &cbdata);

	// draw window
	gtk_widget_show_all(window);

	// loop
	gtk_main();

    }

    else {
	while(true) {
	    if (timest) {
		timestamp(cerr);
	    }

	    if (!logmode) {
		cerr << '\r';
		for (unsigned int i = 0; i < cpus; ++i) {
		    cerr << "          \t";
		}
		cerr << '\r';
	    }
	    ci.out(cerr);
	    if (logmode)
		cerr <<endl;
	    usleep(delay*1000);
	}

    }
}
<commit_msg>deleting unneeded file<commit_after><|endoftext|>


<commit_before><commit_msg>洛谷 4886<commit_after><|endoftext|>


<commit_before>/**
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */

/**
 * @file TransactionPlugin.cc
 */

#include <memory>
#include <cstddef>
#include <cassert>
#include <ts/ts.h>
#include "atscppapi/TransactionPlugin.h"
#include "atscppapi/Mutex.h"
#include "utils_internal.h"
#include "atscppapi/noncopyable.h"
#include "logging_internal.h"

using namespace atscppapi;
using atscppapi::TransactionPlugin;

/**
 * @private
 */
struct atscppapi::TransactionPluginState : noncopyable {
  TSCont cont_;
  TSHttpTxn ats_txn_handle_;
  std::shared_ptr<Mutex> mutex_;
  TransactionPluginState(TSHttpTxn ats_txn_handle) : ats_txn_handle_(ats_txn_handle), mutex_(new Mutex(Mutex::TYPE_RECURSIVE)) {}
};

namespace
{
static int
handleTransactionPluginEvents(TSCont cont, TSEvent event, void *edata)
{
  TSHttpTxn txn             = static_cast<TSHttpTxn>(edata);
  TransactionPlugin *plugin = static_cast<TransactionPlugin *>(TSContDataGet(cont));
  LOG_DEBUG("cont=%p, event=%d, tshttptxn=%p, plugin=%p", cont, event, edata, plugin);
  atscppapi::utils::internal::invokePluginForEvent(plugin, txn, event);
  return 0;
}

} /* anonymous namespace */

TransactionPlugin::TransactionPlugin(Transaction &transaction)
{
  state_        = new TransactionPluginState(static_cast<TSHttpTxn>(transaction.getAtsHandle()));
  TSMutex mutex = nullptr;
  state_->cont_ = TSContCreate(handleTransactionPluginEvents, mutex);
  TSContDataSet(state_->cont_, static_cast<void *>(this));
  LOG_DEBUG("Creating new TransactionPlugin=%p tshttptxn=%p, cont=%p", this, state_->ats_txn_handle_, state_->cont_);
}

std::shared_ptr<Mutex>
TransactionPlugin::getMutex()
{
  return state_->mutex_;
}

bool
TransactionPlugin::isWebsocket() const
{
  return TSHttpTxnIsWebsocket(state_->ats_txn_handle_);
}

TransactionPlugin::~TransactionPlugin()
{
  LOG_DEBUG("Destroying TransactionPlugin=%p", this);
  TSContDestroy(state_->cont_);
  delete state_;
}

void
TransactionPlugin::registerHook(Plugin::HookType hook_type)
{
  LOG_DEBUG("TransactionPlugin=%p tshttptxn=%p registering hook_type=%d [%s]", this, state_->ats_txn_handle_, hook_type,
            HOOK_TYPE_STRINGS[hook_type].c_str());
  TSHttpHookID hook_id = atscppapi::utils::internal::convertInternalHookToTsHook(hook_type);
  TSHttpTxnHookAdd(state_->ats_txn_handle_, hook_id, state_->cont_);
}
<commit_msg>coverity 1200036: Uninitialized pointer field<commit_after>/**
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */

/**
 * @file TransactionPlugin.cc
 */

#include <memory>
#include <cstddef>
#include <cassert>
#include <ts/ts.h>
#include "atscppapi/TransactionPlugin.h"
#include "atscppapi/Mutex.h"
#include "utils_internal.h"
#include "atscppapi/noncopyable.h"
#include "logging_internal.h"

using namespace atscppapi;
using atscppapi::TransactionPlugin;

/**
 * @private
 */
struct atscppapi::TransactionPluginState : noncopyable {
  TSCont cont_ = nullptr;
  TSHttpTxn ats_txn_handle_;
  std::shared_ptr<Mutex> mutex_;
  TransactionPluginState(TSHttpTxn ats_txn_handle) : ats_txn_handle_(ats_txn_handle), mutex_(new Mutex(Mutex::TYPE_RECURSIVE)) {}
};

namespace
{
static int
handleTransactionPluginEvents(TSCont cont, TSEvent event, void *edata)
{
  TSHttpTxn txn             = static_cast<TSHttpTxn>(edata);
  TransactionPlugin *plugin = static_cast<TransactionPlugin *>(TSContDataGet(cont));
  LOG_DEBUG("cont=%p, event=%d, tshttptxn=%p, plugin=%p", cont, event, edata, plugin);
  atscppapi::utils::internal::invokePluginForEvent(plugin, txn, event);
  return 0;
}

} /* anonymous namespace */

TransactionPlugin::TransactionPlugin(Transaction &transaction)
{
  state_        = new TransactionPluginState(static_cast<TSHttpTxn>(transaction.getAtsHandle()));
  TSMutex mutex = nullptr;
  state_->cont_ = TSContCreate(handleTransactionPluginEvents, mutex);
  TSContDataSet(state_->cont_, static_cast<void *>(this));
  LOG_DEBUG("Creating new TransactionPlugin=%p tshttptxn=%p, cont=%p", this, state_->ats_txn_handle_, state_->cont_);
}

std::shared_ptr<Mutex>
TransactionPlugin::getMutex()
{
  return state_->mutex_;
}

bool
TransactionPlugin::isWebsocket() const
{
  return TSHttpTxnIsWebsocket(state_->ats_txn_handle_);
}

TransactionPlugin::~TransactionPlugin()
{
  LOG_DEBUG("Destroying TransactionPlugin=%p", this);
  TSContDestroy(state_->cont_);
  delete state_;
}

void
TransactionPlugin::registerHook(Plugin::HookType hook_type)
{
  LOG_DEBUG("TransactionPlugin=%p tshttptxn=%p registering hook_type=%d [%s]", this, state_->ats_txn_handle_, hook_type,
            HOOK_TYPE_STRINGS[hook_type].c_str());
  TSHttpHookID hook_id = atscppapi::utils::internal::convertInternalHookToTsHook(hook_type);
  TSHttpTxnHookAdd(state_->ats_txn_handle_, hook_id, state_->cont_);
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- vim:set ts=4 sw=4 sts=4 noet: */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "gui/httpd.h"

#include <cstdio>
#include <cstring>
#include <thread>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-qual"
#include <wx/wx.h>
#include <wx/filename.h>
#pragma GCC diagnostic pop

// winsock2.h has to come after wx/wx.h
#ifdef _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#include <sys/socket.h>
#endif

#include <microhttpd.h>

#include "flint/utf8path.h"
#include "gui/filename.h"
#include "gui/main-frame.h"
#include "run.h"

namespace flint {
namespace gui {

namespace {

int Respond(MHD_Connection *connection, int status)
{
	MHD_Response *response = MHD_create_response_from_buffer(0, nullptr, MHD_RESPMEM_PERSISTENT);
	int r = MHD_queue_response(connection, status, response);
	MHD_destroy_response(response);
	return r;
}

void Run(std::unique_ptr<cli::RunOption> &&option)
{
	auto o = std::move(option);

	wxFileName fileName;
	fileName.AssignHomeDir();
	fileName.AppendDir(".flint");
	fileName.AppendDir("2");
	AppendCurrentTimestampDir(fileName);
	fileName.Mkdir(wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL); // make sure that it exists
	run::Run(*o, GetPathFromWxFileName(fileName));
}

#if MHD_VERSION >= 0x00097002
enum MHD_Result
#else
int
#endif
AccessHandler(void *cls,
				  MHD_Connection *connection,
				  const char *url,
				  const char *method,
				  const char */*version*/,
				  const char *upload_data,
				  size_t *upload_data_size,
				  void **con_cls)
{
	const size_t kMaxDataSize = 8192;
	thread_local int status = MHD_HTTP_BAD_REQUEST;

	if (std::strcmp(method, "POST") != 0)
		return MHD_NO;
	if (*con_cls == nullptr) {
		*con_cls = &status;
		return MHD_YES;
	}
	if (*upload_data_size == 0) {
		*con_cls = nullptr;
		return Respond(connection, status);
	}
	if (*upload_data_size > kMaxDataSize) {
		status = 413; // MHD_HTTP_PAYLOAD_TOO_LARGE; not yet defined as of libmicrohttpd 0.9.33
		*upload_data_size = 0;
		return MHD_YES;
	}
	if (std::strcmp(url, "/open-model") == 0) {
		std::unique_ptr<char[]> buf(new char[*upload_data_size+1]);
		std::strcpy(buf.get(), upload_data);
		auto path = GetPathFromUtf8(buf.get());
		auto *frame = static_cast<MainFrame *>(cls);
		auto lmbd = [frame, path]{frame->OpenFile(path.string());};
		frame->GetEventHandler()->CallAfter(lmbd);
		status = MHD_HTTP_OK;
		*upload_data_size = 0;
		return MHD_YES;
	}
	if (std::strcmp(url, "/run") == 0) {
		std::unique_ptr<cli::RunOption> option(new cli::RunOption);
		if (option->ParseFromArray(upload_data, *upload_data_size)) {
			std::thread th(Run, std::move(option));
			th.detach();
			status = MHD_HTTP_OK;
		} else {
			status = MHD_HTTP_BAD_REQUEST;
		}
		*upload_data_size = 0;
		return MHD_YES;
	}
	status = MHD_HTTP_NOT_FOUND;
	*upload_data_size = 0;
	return MHD_YES;
}

}

Httpd::Httpd()
	: daemon_(nullptr)
{}

Httpd::~Httpd()
{
	MHD_stop_daemon(static_cast<MHD_Daemon *>(daemon_));
}

bool Httpd::Start(MainFrame *frame)
{
	const int kPort = 20465;

	// Allow access only from loopback address:
	// https://stackoverflow.com/questions/46023399/make-local-http-server-inaccessable-from-outside
	static struct sockaddr_in loopback_addr;
	std::memset(&loopback_addr, 0, sizeof(loopback_addr));
	loopback_addr.sin_family = AF_INET;
	loopback_addr.sin_port = htons(kPort);
	loopback_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

	daemon_ = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION,
							   kPort, // ignored as MHD_OPTION_SOCK_ADDR is used below
							   nullptr,
							   nullptr,
							   &AccessHandler,
							   frame,
							   MHD_OPTION_SOCK_ADDR, reinterpret_cast<struct sockaddr *>(&loopback_addr),
							   MHD_OPTION_END);
	return daemon_ != nullptr;
}

}
}
<commit_msg>One more<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- vim:set ts=4 sw=4 sts=4 noet: */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "gui/httpd.h"

#include <cstdio>
#include <cstring>
#include <thread>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-qual"
#include <wx/wx.h>
#include <wx/filename.h>
#pragma GCC diagnostic pop

// winsock2.h has to come after wx/wx.h
#ifdef _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#include <sys/socket.h>
#endif

#include <microhttpd.h>

#include "flint/utf8path.h"
#include "gui/filename.h"
#include "gui/main-frame.h"
#include "run.h"

namespace flint {
namespace gui {

namespace {

#if MHD_VERSION >= 0x00097002
enum MHD_Result
#else
int
#endif
Respond(MHD_Connection *connection, int status)
{
	MHD_Response *response = MHD_create_response_from_buffer(0, nullptr, MHD_RESPMEM_PERSISTENT);
	auto r = MHD_queue_response(connection, status, response);
	MHD_destroy_response(response);
	return r;
}

void Run(std::unique_ptr<cli::RunOption> &&option)
{
	auto o = std::move(option);

	wxFileName fileName;
	fileName.AssignHomeDir();
	fileName.AppendDir(".flint");
	fileName.AppendDir("2");
	AppendCurrentTimestampDir(fileName);
	fileName.Mkdir(wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL); // make sure that it exists
	run::Run(*o, GetPathFromWxFileName(fileName));
}

#if MHD_VERSION >= 0x00097002
enum MHD_Result
#else
int
#endif
AccessHandler(void *cls,
				  MHD_Connection *connection,
				  const char *url,
				  const char *method,
				  const char */*version*/,
				  const char *upload_data,
				  size_t *upload_data_size,
				  void **con_cls)
{
	const size_t kMaxDataSize = 8192;
	thread_local int status = MHD_HTTP_BAD_REQUEST;

	if (std::strcmp(method, "POST") != 0)
		return MHD_NO;
	if (*con_cls == nullptr) {
		*con_cls = &status;
		return MHD_YES;
	}
	if (*upload_data_size == 0) {
		*con_cls = nullptr;
		return Respond(connection, status);
	}
	if (*upload_data_size > kMaxDataSize) {
		status = 413; // MHD_HTTP_PAYLOAD_TOO_LARGE; not yet defined as of libmicrohttpd 0.9.33
		*upload_data_size = 0;
		return MHD_YES;
	}
	if (std::strcmp(url, "/open-model") == 0) {
		std::unique_ptr<char[]> buf(new char[*upload_data_size+1]);
		std::strcpy(buf.get(), upload_data);
		auto path = GetPathFromUtf8(buf.get());
		auto *frame = static_cast<MainFrame *>(cls);
		auto lmbd = [frame, path]{frame->OpenFile(path.string());};
		frame->GetEventHandler()->CallAfter(lmbd);
		status = MHD_HTTP_OK;
		*upload_data_size = 0;
		return MHD_YES;
	}
	if (std::strcmp(url, "/run") == 0) {
		std::unique_ptr<cli::RunOption> option(new cli::RunOption);
		if (option->ParseFromArray(upload_data, *upload_data_size)) {
			std::thread th(Run, std::move(option));
			th.detach();
			status = MHD_HTTP_OK;
		} else {
			status = MHD_HTTP_BAD_REQUEST;
		}
		*upload_data_size = 0;
		return MHD_YES;
	}
	status = MHD_HTTP_NOT_FOUND;
	*upload_data_size = 0;
	return MHD_YES;
}

}

Httpd::Httpd()
	: daemon_(nullptr)
{}

Httpd::~Httpd()
{
	MHD_stop_daemon(static_cast<MHD_Daemon *>(daemon_));
}

bool Httpd::Start(MainFrame *frame)
{
	const int kPort = 20465;

	// Allow access only from loopback address:
	// https://stackoverflow.com/questions/46023399/make-local-http-server-inaccessable-from-outside
	static struct sockaddr_in loopback_addr;
	std::memset(&loopback_addr, 0, sizeof(loopback_addr));
	loopback_addr.sin_family = AF_INET;
	loopback_addr.sin_port = htons(kPort);
	loopback_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

	daemon_ = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION,
							   kPort, // ignored as MHD_OPTION_SOCK_ADDR is used below
							   nullptr,
							   nullptr,
							   &AccessHandler,
							   frame,
							   MHD_OPTION_SOCK_ADDR, reinterpret_cast<struct sockaddr *>(&loopback_addr),
							   MHD_OPTION_END);
	return daemon_ != nullptr;
}

}
}
<|endoftext|>


<commit_before>#if !defined(_MSC_VER) && !defined(__MINGW32__)
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#else
#include <io.h>
#endif

#include <assert.h>

#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
#include <string>
#include <cstdio>

#include "pocl.h"
#include "pocl_file_util.h"

#include <llvm/Support/LockFileManager.h>

#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_os_ostream.h"

#include "llvm/Bitcode/ReaderWriter.h"

/* namespace of OpenFlags enum (F_Binary, F_Excl etc) */
#if defined(LLVM_3_2) || defined(LLVM_3_3)
#define OPEN_FLAGS_ENUM raw_fd_ostream
#else
#define OPEN_FLAGS_ENUM sys::fs
#endif

/* Older llvms:
 * llvm::error_code instead of std::error_code
 * "file existed" is an output argument, instead of "ignore if file existed"
 * different OpenFlags (no F_Binary)
 * File locking disabled: llvm::LockManager class doesn't exist in LLVM < 3.5
 */
#if LLVM_OLDER_THAN_3_5

#define DISABLE_LOCKMANAGER

#include <llvm/Support/system_error.h>
#define STD_ERROR_CODE llvm::error_code
static bool existed;
#define EXIST_ARG existed
#define DEFAULT_OPEN_FLAGS OPEN_FLAGS_ENUM::F_Binary

#else

#include <llvm/Support/Errc.h>
#define STD_ERROR_CODE std::error_code
#define EXIST_ARG true
#define DEFAULT_OPEN_FLAGS OPEN_FLAGS_ENUM::F_RW

#endif

#define RETURN_IF_EC if (ec) return ec.default_error_condition().value()

/* no openFile* functions in sys::fs before llvm 3.4, so fallback to open */
#if defined(LLVM_3_2) || defined(LLVM_3_3)
    #define OPEN_FOR_READ fd = open(path, O_RDONLY)
    #define OPEN_CREATE fd = open(path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)
    #define OPEN_FOR_APPEND fd = open(path, O_WRONLY | O_APPEND)
    #define RETURN_IF_ERRNO if (fd < 0) return errno;
#else
    #define OPEN_FOR_READ ec = sys::fs::openFileForRead(p, fd)
    #define OPEN_CREATE ec = sys::fs::openFileForWrite(p, fd, DEFAULT_OPEN_FLAGS | sys::fs::F_Excl)
    #define OPEN_FOR_APPEND ec = sys::fs::openFileForWrite(p, fd, DEFAULT_OPEN_FLAGS | sys::fs::F_Append)
    #define RETURN_IF_ERRNO RETURN_IF_EC
#endif

using namespace llvm;

/*****************************************************************************/

int
pocl_rm_rf(const char* path) {
    STD_ERROR_CODE ec;
    SmallString<128> DirNative;

    sys::path::native(Twine(path), DirNative);

    std::vector<std::string> FileSet, DirSet;

    for (sys::fs::recursive_directory_iterator Dir(DirNative.str(), ec), DirEnd;
         Dir != DirEnd && !ec; Dir.increment(ec)) {
        Twine p = Dir->path();
        std::string s = p.str();
#if defined(LLVM_3_2) || defined(LLVM_3_3)
        sys::fs::file_status result;
        sys::fs::status(p, result);
        if (sys::fs::is_directory(result)) {
#else
        if (sys::fs::is_directory(p)) {
#endif
            DirSet.push_back(s);
        } else
            FileSet.push_back(s);
    }
    RETURN_IF_EC;

    std::vector<std::string>::iterator it;
    for (it = FileSet.begin(); it != FileSet.end(); ++it) {
        ec = sys::fs::remove(*it, EXIST_ARG);
        RETURN_IF_EC;
    }

    std::sort(DirSet.begin(), DirSet.end());
    std::vector<std::string>::reverse_iterator it2;
    for (it2 = DirSet.rbegin(); it2 != DirSet.rend(); ++it2) {
        ec = sys::fs::remove(*it2, EXIST_ARG);
        RETURN_IF_EC;
    }

    return 0;
}

int
pocl_mkdir_p(const char* path) {
    Twine p(path);
    STD_ERROR_CODE ec = sys::fs::create_directories(p, EXIST_ARG);
    return ec.default_error_condition().value();
}

int
pocl_remove(const char* path) {
    Twine p(path);
    STD_ERROR_CODE ec = sys::fs::remove(p, EXIST_ARG);
    return ec.default_error_condition().value();
}

int
pocl_exists(const char* path) {
    Twine p(path);
    if (sys::fs::exists(p))
        return 1;
    else
        return 0;
}

int
pocl_filesize(const char* path, uint64_t* res) {
    Twine p(path);
    STD_ERROR_CODE ec = sys::fs::file_size(p, *res);
    return ec.default_error_condition().value();
}

int pocl_touch_file(const char* path) {
    Twine p(path);
    STD_ERROR_CODE ec = sys::fs::remove(p, EXIST_ARG);
    RETURN_IF_EC;

    int fd;
    OPEN_CREATE;
    RETURN_IF_ERRNO;

    return (close(fd) ? (-errno) : 0);

}
/****************************************************************************/

int
pocl_read_file(const char* path, char** content, uint64_t *filesize) {
    assert(content);
    assert(path);
    assert(filesize);

    *content = NULL;

    int errcode = pocl_filesize(path, filesize);
    ssize_t fsize = (ssize_t)(*filesize);
    if (!errcode) {
        int fd;
        STD_ERROR_CODE ec;
        Twine p(path);

        OPEN_FOR_READ;
        RETURN_IF_ERRNO;

        // +1 so we can later simply turn it into a C string, if needed
        *content = (char*)malloc(fsize+1);

        size_t rsize = read(fd, *content, fsize);
        (*content)[rsize] = '\0';
        if (rsize < fsize) {
            errcode = errno ? -errno : -1;
            close(fd);
        } else {
            if (close(fd))
                errcode = errno ? -errno : -1;
        }
    }
    return errcode;
}

int pocl_write_file(const char *path, const char* content,
                                    uint64_t    count,
                                    int         append,
                                    int         dont_rewrite) {
    int fd;
    STD_ERROR_CODE ec;
    Twine p(path);

    assert(path);
    assert(content);

    if (pocl_exists(path)) {
        if (dont_rewrite) {
            if (!append)
                return 0;
        } else {
            int res = pocl_remove(path);
            if (res)
                return res;
        }
    }

    if (append)
        OPEN_FOR_APPEND;
    else
        OPEN_CREATE;

    RETURN_IF_ERRNO;

    if (write(fd, content, (ssize_t)count) < (ssize_t)count)
        return (-errno || -1);

    return (close(fd) ? (-errno) : 0);
}

int pocl_write_module(void *module, const char* path, int dont_rewrite) {

    assert(module);
    assert(path);

    Twine p(path);
#ifdef LLVM_OLDER_THAN_3_6
    std::string ec;
#else
    STD_ERROR_CODE ec;
#endif

    if (pocl_exists(path)) {
        if (dont_rewrite)
            return 0;
        else {
            int res = pocl_remove(path);
            if (res)
                return res;
        }
    }

    raw_fd_ostream os(path, ec, DEFAULT_OPEN_FLAGS | OPEN_FLAGS_ENUM::F_Excl);
#ifdef LLVM_OLDER_THAN_3_6
    if (!ec.empty())
        return 2;
#else
    RETURN_IF_EC;
#endif

    WriteBitcodeToFile((llvm::Module*)module, os);
    os.close();
    return (os.has_error() ? 1 : 0);

}

/****************************************************************************/

static void* acquire_lock_internal(const char* path, int shared) {
#ifdef DISABLE_LOCKMANAGER
    /* Can't return value that compares equal to NULL */
    return (void*)4096;
#else
    assert(path);
    LockFileManager *lfm = new LockFileManager(path);

    switch (lfm->getState()) {
    case LockFileManager::LockFileState::LFS_Owned:
        return (void*)lfm;

    case LockFileManager::LockFileState::LFS_Shared:
        if (shared)
            return (void*)lfm;
        lfm->waitForUnlock();
        delete lfm;
        lfm = new LockFileManager(path);
        if (lfm->getState() == LockFileManager::LockFileState::LFS_Owned)
            return (void*)lfm;
        else
          {
            delete lfm;
            return NULL;
          }

    case LockFileManager::LockFileState::LFS_Error:
        return NULL;
    }
    return NULL;
#endif
}

void* acquire_lock(const char *path, int shared) {
    return acquire_lock_internal(path, shared);
}

void release_lock(void* lock) {
#ifdef DISABLE_LOCKMANAGER
    return;
#else
    if (!lock)
        return;
    LockFileManager *l = (LockFileManager*)lock;
    delete l;
#endif
}
<commit_msg>Warning fix.<commit_after>#if !defined(_MSC_VER) && !defined(__MINGW32__)
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#else
#include <io.h>
#endif

#include <assert.h>

#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
#include <string>
#include <cstdio>

#include "pocl.h"
#include "pocl_file_util.h"

#include <llvm/Support/LockFileManager.h>

#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_os_ostream.h"

#include "llvm/Bitcode/ReaderWriter.h"

/* namespace of OpenFlags enum (F_Binary, F_Excl etc) */
#if defined(LLVM_3_2) || defined(LLVM_3_3)
#define OPEN_FLAGS_ENUM raw_fd_ostream
#else
#define OPEN_FLAGS_ENUM sys::fs
#endif

/* Older llvms:
 * llvm::error_code instead of std::error_code
 * "file existed" is an output argument, instead of "ignore if file existed"
 * different OpenFlags (no F_Binary)
 * File locking disabled: llvm::LockManager class doesn't exist in LLVM < 3.5
 */
#if LLVM_OLDER_THAN_3_5

#define DISABLE_LOCKMANAGER

#include <llvm/Support/system_error.h>
#define STD_ERROR_CODE llvm::error_code
static bool existed;
#define EXIST_ARG existed
#define DEFAULT_OPEN_FLAGS OPEN_FLAGS_ENUM::F_Binary

#else

#include <llvm/Support/Errc.h>
#define STD_ERROR_CODE std::error_code
#define EXIST_ARG true
#define DEFAULT_OPEN_FLAGS OPEN_FLAGS_ENUM::F_RW

#endif

#define RETURN_IF_EC if (ec) return ec.default_error_condition().value()

/* no openFile* functions in sys::fs before llvm 3.4, so fallback to open */
#if defined(LLVM_3_2) || defined(LLVM_3_3)
    #define OPEN_FOR_READ fd = open(path, O_RDONLY)
    #define OPEN_CREATE fd = open(path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)
    #define OPEN_FOR_APPEND fd = open(path, O_WRONLY | O_APPEND)
    #define RETURN_IF_ERRNO if (fd < 0) return errno;
#else
    #define OPEN_FOR_READ ec = sys::fs::openFileForRead(p, fd)
    #define OPEN_CREATE ec = sys::fs::openFileForWrite(p, fd, DEFAULT_OPEN_FLAGS | sys::fs::F_Excl)
    #define OPEN_FOR_APPEND ec = sys::fs::openFileForWrite(p, fd, DEFAULT_OPEN_FLAGS | sys::fs::F_Append)
    #define RETURN_IF_ERRNO RETURN_IF_EC
#endif

using namespace llvm;

/*****************************************************************************/

int
pocl_rm_rf(const char* path) {
    STD_ERROR_CODE ec;
    SmallString<128> DirNative;

    sys::path::native(Twine(path), DirNative);

    std::vector<std::string> FileSet, DirSet;

    for (sys::fs::recursive_directory_iterator Dir(DirNative.str(), ec), DirEnd;
         Dir != DirEnd && !ec; Dir.increment(ec)) {
        Twine p = Dir->path();
        std::string s = p.str();
#if defined(LLVM_3_2) || defined(LLVM_3_3)
        sys::fs::file_status result;
        sys::fs::status(p, result);
        if (sys::fs::is_directory(result)) {
#else
        if (sys::fs::is_directory(p)) {
#endif
            DirSet.push_back(s);
        } else
            FileSet.push_back(s);
    }
    RETURN_IF_EC;

    std::vector<std::string>::iterator it;
    for (it = FileSet.begin(); it != FileSet.end(); ++it) {
        ec = sys::fs::remove(*it, EXIST_ARG);
        RETURN_IF_EC;
    }

    std::sort(DirSet.begin(), DirSet.end());
    std::vector<std::string>::reverse_iterator it2;
    for (it2 = DirSet.rbegin(); it2 != DirSet.rend(); ++it2) {
        ec = sys::fs::remove(*it2, EXIST_ARG);
        RETURN_IF_EC;
    }

    return 0;
}

int
pocl_mkdir_p(const char* path) {
    Twine p(path);
    STD_ERROR_CODE ec = sys::fs::create_directories(p, EXIST_ARG);
    return ec.default_error_condition().value();
}

int
pocl_remove(const char* path) {
    Twine p(path);
    STD_ERROR_CODE ec = sys::fs::remove(p, EXIST_ARG);
    return ec.default_error_condition().value();
}

int
pocl_exists(const char* path) {
    Twine p(path);
    if (sys::fs::exists(p))
        return 1;
    else
        return 0;
}

int
pocl_filesize(const char* path, uint64_t* res) {
    Twine p(path);
    STD_ERROR_CODE ec = sys::fs::file_size(p, *res);
    return ec.default_error_condition().value();
}

int pocl_touch_file(const char* path) {
    Twine p(path);
    STD_ERROR_CODE ec = sys::fs::remove(p, EXIST_ARG);
    RETURN_IF_EC;

    int fd;
    OPEN_CREATE;
    RETURN_IF_ERRNO;

    return (close(fd) ? (-errno) : 0);

}
/****************************************************************************/

int
pocl_read_file(const char* path, char** content, uint64_t *filesize) {
    assert(content);
    assert(path);
    assert(filesize);

    *content = NULL;

    int errcode = pocl_filesize(path, filesize);
    ssize_t fsize = (ssize_t)(*filesize);
    if (!errcode) {
        int fd;
        STD_ERROR_CODE ec;
        Twine p(path);

        OPEN_FOR_READ;
        RETURN_IF_ERRNO;

        // +1 so we can later simply turn it into a C string, if needed
        *content = (char*)malloc(fsize+1);

        size_t rsize = read(fd, *content, fsize);
        (*content)[rsize] = '\0';
        if (rsize < (size_t)fsize) {
            errcode = errno ? -errno : -1;
            close(fd);
        } else {
            if (close(fd))
                errcode = errno ? -errno : -1;
        }
    }
    return errcode;
}

int pocl_write_file(const char *path, const char* content,
                                    uint64_t    count,
                                    int         append,
                                    int         dont_rewrite) {
    int fd;
    STD_ERROR_CODE ec;
    Twine p(path);

    assert(path);
    assert(content);

    if (pocl_exists(path)) {
        if (dont_rewrite) {
            if (!append)
                return 0;
        } else {
            int res = pocl_remove(path);
            if (res)
                return res;
        }
    }

    if (append)
        OPEN_FOR_APPEND;
    else
        OPEN_CREATE;

    RETURN_IF_ERRNO;

    if (write(fd, content, (ssize_t)count) < (ssize_t)count)
        return (-errno || -1);

    return (close(fd) ? (-errno) : 0);
}

int pocl_write_module(void *module, const char* path, int dont_rewrite) {

    assert(module);
    assert(path);

    Twine p(path);
#ifdef LLVM_OLDER_THAN_3_6
    std::string ec;
#else
    STD_ERROR_CODE ec;
#endif

    if (pocl_exists(path)) {
        if (dont_rewrite)
            return 0;
        else {
            int res = pocl_remove(path);
            if (res)
                return res;
        }
    }

    raw_fd_ostream os(path, ec, DEFAULT_OPEN_FLAGS | OPEN_FLAGS_ENUM::F_Excl);
#ifdef LLVM_OLDER_THAN_3_6
    if (!ec.empty())
        return 2;
#else
    RETURN_IF_EC;
#endif

    WriteBitcodeToFile((llvm::Module*)module, os);
    os.close();
    return (os.has_error() ? 1 : 0);

}

/****************************************************************************/

static void* acquire_lock_internal(const char* path, int shared) {
#ifdef DISABLE_LOCKMANAGER
    /* Can't return value that compares equal to NULL */
    return (void*)4096;
#else
    assert(path);
    LockFileManager *lfm = new LockFileManager(path);

    switch (lfm->getState()) {
    case LockFileManager::LockFileState::LFS_Owned:
        return (void*)lfm;

    case LockFileManager::LockFileState::LFS_Shared:
        if (shared)
            return (void*)lfm;
        lfm->waitForUnlock();
        delete lfm;
        lfm = new LockFileManager(path);
        if (lfm->getState() == LockFileManager::LockFileState::LFS_Owned)
            return (void*)lfm;
        else
          {
            delete lfm;
            return NULL;
          }

    case LockFileManager::LockFileState::LFS_Error:
        return NULL;
    }
    return NULL;
#endif
}

void* acquire_lock(const char *path, int shared) {
    return acquire_lock_internal(path, shared);
}

void release_lock(void* lock) {
#ifdef DISABLE_LOCKMANAGER
    return;
#else
    if (!lock)
        return;
    LockFileManager *l = (LockFileManager*)lock;
    delete l;
#endif
}
<|endoftext|>


<commit_before><commit_msg>fix x86 build<commit_after><|endoftext|>


<commit_before>// Copyright (C) 2015 The Regents of the University of California (Regents).
// 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 Regents or University of California 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 HOLDERS 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.
//
// Please contact the author of this library if you have any questions.
// Author: Chris Sweeney (cmsweeney@cs.ucsb.edu)

#include <glog/logging.h>
#include <gflags/gflags.h>
#include <theia/theia.h>

#include <fstream>  // NOLINT

DEFINE_string(reconstruction, "", "Theia Reconstruction file.");
DEFINE_string(images, "",
              "A filepath wildcard specifying all images that were used in the "
              "reconstruction.");
DEFINE_string(pmvs_working_directory, "",
              "A directory to store the necessary pmvs files.");
DEFINE_int32(num_threads, 1, "Number of threads used in PMVS.");

void CreateDirectoryIfDoesNotExist(const std::string& directory) {
  if (!theia::DirectoryExists(directory)) {
    CHECK(theia::CreateDirectory(directory))
        << "Could not create the directory: " << directory;
  }
}

int WriteCamerasToPMVS(const theia::Reconstruction& reconstruction) {
  const std::string txt_dir = FLAGS_pmvs_working_directory + "/txt";
  CreateDirectoryIfDoesNotExist(txt_dir);
  const std::string visualize_dir = FLAGS_pmvs_working_directory + "/visualize";

  std::vector<std::string> image_files;
  CHECK(theia::GetFilepathsFromWildcard(FLAGS_images, &image_files))
      << "Could not find images that matched the filepath: " << FLAGS_images
      << ". NOTE that the ~ filepath is not supported.";
  CHECK_GT(image_files.size(), 0) << "No images found in: " << FLAGS_images;

  // Format for printing eigen matrices.
  const Eigen::IOFormat unaligned(Eigen::StreamPrecision, Eigen::DontAlignCols);

  int current_image_index = 0;
  for (int i = 0; i < image_files.size(); i++) {
    std::string image_name;
    CHECK(theia::GetFilenameFromFilepath(image_files[i], true, &image_name));
    const theia::ViewId view_id = reconstruction.ViewIdFromName(image_name);
    if (view_id == theia::kInvalidViewId) {
      continue;
    }

    LOG(INFO) << "Exporting parameters for image: " << image_name;

    // Copy the image into a jpeg format with the filename in the form of
    // %08d.jpg.
    const std::string new_image_file = theia::StringPrintf(
        "%s/%08d.jpg", visualize_dir.c_str(), current_image_index);
    theia::Image<float> old_image(image_files[i]);
    old_image.Write(new_image_file);

    // Write the camera projection matrix.
    const std::string txt_file = theia::StringPrintf(
        "%s/%08d.txt", txt_dir.c_str(), current_image_index);
    const theia::Camera camera = reconstruction.View(view_id)->Camera();

    theia::Matrix3x4d projection_matrix;
    camera.GetProjectionMatrix(&projection_matrix);
    std::ofstream ofs(txt_file);
    ofs << "CONTOUR" << std::endl;
    ofs << projection_matrix.format(unaligned);
    ofs.close();

    ++current_image_index;
  }

  return current_image_index;
}

void WritePMVSOptions(const std::string& working_dir,
                      const int num_images) {
  std::ofstream ofs(working_dir + "/pmvs_options.txt");
  ofs << "level 1" << std::endl;
  ofs << "csize 2" << std::endl;
  ofs << "threshold 0.7" << std::endl;
  ofs << "wsize 7" << std::endl;
  ofs << "minImageNum 3" << std::endl;
  ofs << "CPU " << FLAGS_num_threads << std::endl;
  ofs << "setEdge 0" << std::endl;
  ofs << "useBound 0" << std::endl;
  ofs << "useVisData 1" << std::endl;
  ofs << "sequence -1" << std::endl;
  ofs << "timages -1 0 " << num_images << std::endl;
  ofs << "oimages 0" << std::endl;
}

int main(int argc, char* argv[]) {
  google::InitGoogleLogging(argv[0]);
  THEIA_GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);

  // Set up output directories.
  CreateDirectoryIfDoesNotExist(FLAGS_pmvs_working_directory);
  const std::string visualize_dir = FLAGS_pmvs_working_directory + "/visualize";
  CreateDirectoryIfDoesNotExist(visualize_dir);
  const std::string txt_dir = FLAGS_pmvs_working_directory + "/txt";
  CreateDirectoryIfDoesNotExist(txt_dir);
  const std::string models_dir = FLAGS_pmvs_working_directory + "/models";
  CreateDirectoryIfDoesNotExist(models_dir);

  theia::Reconstruction reconstruction;
  CHECK(theia::ReadReconstruction(FLAGS_reconstruction, &reconstruction))
      << "Could not read Reconstruction files.";

  const int num_cameras = WriteCamerasToPMVS(reconstruction);
  WritePMVSOptions(FLAGS_pmvs_working_directory, num_cameras);

  const std::string lists_file = FLAGS_pmvs_working_directory + "/list.txt";
  const std::string bundle_file =
      FLAGS_pmvs_working_directory + "/bundle.rd.out";
  CHECK(theia::WriteBundlerFiles(reconstruction, lists_file, bundle_file));

  return 0;
}
<commit_msg>bug fix for issue #57<commit_after>// Copyright (C) 2015 The Regents of the University of California (Regents).
// 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 Regents or University of California 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 HOLDERS 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.
//
// Please contact the author of this library if you have any questions.
// Author: Chris Sweeney (cmsweeney@cs.ucsb.edu)

#include <glog/logging.h>
#include <gflags/gflags.h>
#include <theia/theia.h>

#include <fstream>  // NOLINT

DEFINE_string(reconstruction, "", "Theia Reconstruction file.");
DEFINE_string(images, "",
              "A filepath wildcard specifying all images that were used in the "
              "reconstruction.");
DEFINE_string(pmvs_working_directory, "",
              "A directory to store the necessary pmvs files.");
DEFINE_int32(num_threads, 1, "Number of threads used in PMVS.");

void CreateDirectoryIfDoesNotExist(const std::string& directory) {
  if (!theia::DirectoryExists(directory)) {
    CHECK(theia::CreateDirectory(directory))
        << "Could not create the directory: " << directory;
  }
}

int WriteCamerasToPMVS(const theia::Reconstruction& reconstruction) {
  const std::string txt_dir = FLAGS_pmvs_working_directory + "/txt";
  CreateDirectoryIfDoesNotExist(txt_dir);
  const std::string visualize_dir = FLAGS_pmvs_working_directory + "/visualize";

  std::vector<std::string> image_files;
  CHECK(theia::GetFilepathsFromWildcard(FLAGS_images, &image_files))
      << "Could not find images that matched the filepath: " << FLAGS_images
      << ". NOTE that the ~ filepath is not supported.";
  CHECK_GT(image_files.size(), 0) << "No images found in: " << FLAGS_images;

  // Format for printing eigen matrices.
  const Eigen::IOFormat unaligned(Eigen::StreamPrecision, Eigen::DontAlignCols);

  int current_image_index = 0;
  for (int i = 0; i < image_files.size(); i++) {
    std::string image_name;
    CHECK(theia::GetFilenameFromFilepath(image_files[i], true, &image_name));
    const theia::ViewId view_id = reconstruction.ViewIdFromName(image_name);
    if (view_id == theia::kInvalidViewId) {
      continue;
    }

    LOG(INFO) << "Exporting parameters for image: " << image_name;

    // Copy the image into a jpeg format with the filename in the form of
    // %08d.jpg.
    const std::string new_image_file = theia::StringPrintf(
        "%s/%08d.jpg", visualize_dir.c_str(), current_image_index);
    theia::Image<float> old_image(image_files[i]);
    old_image.Write(new_image_file);

    // Write the camera projection matrix.
    const std::string txt_file = theia::StringPrintf(
        "%s/%08d.txt", txt_dir.c_str(), current_image_index);
    const theia::Camera camera = reconstruction.View(view_id)->Camera();

    theia::Matrix3x4d projection_matrix;
    camera.GetProjectionMatrix(&projection_matrix);
    std::ofstream ofs(txt_file);
    ofs << "CONTOUR" << std::endl;
    ofs << projection_matrix.format(unaligned);
    ofs.close();

    ++current_image_index;
  }

  return current_image_index;
}

void WritePMVSOptions(const std::string& working_dir,
                      const int num_images) {
  std::ofstream ofs(working_dir + "/pmvs_options.txt");
  ofs << "level 1" << std::endl;
  ofs << "csize 2" << std::endl;
  ofs << "threshold 0.7" << std::endl;
  ofs << "wsize 7" << std::endl;
  ofs << "minImageNum 3" << std::endl;
  ofs << "CPU " << FLAGS_num_threads << std::endl;
  ofs << "setEdge 0" << std::endl;
  ofs << "useBound 0" << std::endl;
  ofs << "useVisData 0" << std::endl;
  ofs << "sequence -1" << std::endl;
  ofs << "timages -1 0 " << num_images << std::endl;
  ofs << "oimages 0" << std::endl;
}

int main(int argc, char* argv[]) {
  google::InitGoogleLogging(argv[0]);
  THEIA_GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);

  // Set up output directories.
  CreateDirectoryIfDoesNotExist(FLAGS_pmvs_working_directory);
  const std::string visualize_dir = FLAGS_pmvs_working_directory + "/visualize";
  CreateDirectoryIfDoesNotExist(visualize_dir);
  const std::string txt_dir = FLAGS_pmvs_working_directory + "/txt";
  CreateDirectoryIfDoesNotExist(txt_dir);
  const std::string models_dir = FLAGS_pmvs_working_directory + "/models";
  CreateDirectoryIfDoesNotExist(models_dir);

  theia::Reconstruction reconstruction;
  CHECK(theia::ReadReconstruction(FLAGS_reconstruction, &reconstruction))
      << "Could not read Reconstruction files.";

  const int num_cameras = WriteCamerasToPMVS(reconstruction);
  WritePMVSOptions(FLAGS_pmvs_working_directory, num_cameras);

  const std::string lists_file = FLAGS_pmvs_working_directory + "/list.txt";
  const std::string bundle_file =
      FLAGS_pmvs_working_directory + "/bundle.rd.out";
  CHECK(theia::WriteBundlerFiles(reconstruction, lists_file, bundle_file));

  return 0;
}
<|endoftext|>


<commit_before>#include <SofaPython/PythonScriptEvent.h>

#include "Python_test.h"

#include <sofa/helper/system/PluginManager.h>

#include <sofa/simulation/Simulation.h>
#include <SofaSimulationGraph/DAGSimulation.h>

#include <sofa/helper/logging/Messaging.h>
#include <sofa/helper/system/FileSystem.h>

#include <SofaPython/PythonMacros.h>
#include <SofaPython/PythonFactory.h>

namespace sofa {

Python_test::Python_test()
{
    static const std::string plugin = "SofaPython";
    sofa::helper::system::PluginManager::getInstance().loadPlugin(plugin);
}

void Python_test::run( const Python_test_data& data ) {

    msg_info("Python_test") << "running " << data.filepath;

    {
        // Check the file exists
        std::ifstream file(data.filepath.c_str());
        bool scriptFound = file.good();
        ASSERT_TRUE(scriptFound) << " ("<<data.filepath<<")";
    }

    ASSERT_TRUE( loader.loadTestWithArguments(data.filepath.c_str(),data.arguments) ) << " ("<<data.filepath<<")";

}

static bool ends_with(const std::string& suffix, const std::string& full){
    const std::size_t lf = full.length();
    const std::size_t ls = suffix.length();

    if(lf < ls) return false;

    return (0 == full.compare(lf - ls, ls, suffix));
}

static bool starts_with(const std::string& prefix, const std::string& full){
    const std::size_t lf = full.length();
    const std::size_t lp = prefix.length();

    if(lf < lp) return false;

    return (0 == full.compare(0, lp, prefix));
}

void Python_test_list::addTestDir(const std::string& dir, const std::string& prefix) {

    std::vector<std::string> files;
    helper::system::FileSystem::listDirectory(dir, files);

    for(const std::string& file : files) {
        if( starts_with(prefix, file) && ends_with(".py", file) ) {
            addTest(file, dir);
        }
    }

}

////////////////////////////////////////////////////
////////////////////////////////////////////////////
////////////////////////////////////////////////////

struct Listener : core::objectmodel::BaseObject {

    Listener() {
        f_listening = true;
    }

    virtual void handleEvent(core::objectmodel::Event * event) {
        if (core::objectmodel::PythonScriptEvent::checkEventType(event)
              || core::objectmodel::ScriptEvent::checkEventType(event) )
       {
            core::objectmodel::ScriptEvent* e = static_cast<core::objectmodel::ScriptEvent*>(event);
            std::string name = e->getEventName();
            if( name == "success" ) {
                throw Python_scene_test::result(true);
            } else if (name == "failure") {
                throw Python_scene_test::result(false);
            }
        }
    }

};

struct fail {
    const char* message;
    fail(const char* message)
        : message(message) { }
};

static PyObject* operator||(PyObject* obj, const fail& error) {
    if(obj) return obj;
    throw std::runtime_error(error.message);
    return nullptr;
}

static void operator||(int code, const fail& error) {
    if(code >= 0) return;
    throw std::runtime_error(error.message);
}

enum flag : char {
    stop = 1 << 1,
    test_failure = 1 << 2,
    python_error = 1 << 3
};

PyObject* default_excepthook {nullptr} ;

// TODO FIXME: there's probably a MEMLEAK hiding in there, figure it out
static PyObject* except_hook(PyObject* self, PyObject* args) {
    // raise the stop flag
    char* flags = reinterpret_cast<char*>(PyCapsule_GetPointer(self, NULL));
    assert(flags && "cannot get flags pointer (wtf?)");
    if(!flags) std::exit(1);

    *flags |= flag::stop;

    // switch on exception type
    PyObject* type;
    PyObject* value;
    PyObject* traceback;

    if( !PyArg_ParseTuple(args, "OOO", &type, &value, &traceback) ) {
        assert(false && "cannot parse excepthook args (wtf?)");
        std::exit(1);
    }

    if( type == PyExc_AssertionError ) {
        // test failure
        *flags |= flag::test_failure;
    } else {
        // other python error
        *flags |= flag::python_error;
    }

    // call default excepthook to get traceback etc
    return PyObject_CallObject(default_excepthook, args);
}

static PyMethodDef except_hook_def = {
    "sofa_excepthook",
    except_hook,
    METH_VARARGS,
    NULL
};

static void install_sys_excepthook(char* flags) {
    if(!default_excepthook){
        default_excepthook = PySys_GetObject((char*)"excepthook") || fail("cannot get default excepthook");

        PyObject* self = PyCapsule_New(flags, NULL, NULL) || fail("cant wrap flags pointer");

        PyObject* excepthook = PyCFunction_NewEx(&except_hook_def, self, NULL)
                || fail("cannot create excepthook closure");

        PySys_SetObject((char*)"excepthook", excepthook) || fail("cannot set sys.excepthook");
    }
}

Python_scene_test::Python_scene_test()
    : max_steps(1000) {

}

void Python_scene_test::run( const Python_test_data& data ) {

    msg_info("Python_scene_test") << "running "<< data.filepath;

    {
        // Check the file exists
        std::ifstream file(data.filepath.c_str());
        bool scriptFound = file.good();
        ASSERT_TRUE(scriptFound) << " ("<<data.filepath<<")";
    }

    if( !simulation::getSimulation() ) {
        simulation::setSimulation( new sofa::simulation::graph::DAGSimulation() );
    }

    char flags = 0;
    try {
        install_sys_excepthook(&flags);
    } catch( std::runtime_error& e) {
        ASSERT_TRUE(false) << "error setting up python excepthook, aborting test" << " ("<<data.filepath<<")";
    }

    simulation::Node::SPtr root;

    try {

        loader.loadSceneWithArguments(data.filepath.c_str(),
                                      data.arguments,
                                      &root);
        ASSERT_TRUE(bool(root)) << "scene creation failed!" << " ("<<data.filepath<<")";

        root->addObject( new Listener );
        simulation::getSimulation()->init(root.get());

        // TODO eventually tests should only stop by throwing SystemExit
        unsigned i;
        for(i = 0; (i < max_steps) && !(flags & flag::stop) && root->isActive(); ++i) {
            simulation::getSimulation()->animate(root.get(), root->getDt());
        }

        ASSERT_TRUE(i != max_steps) << "maximum allowed steps reached: " << max_steps << " ("<<data.filepath<<")";

        if( flags & flag::test_failure ) {
            FAIL() << "test failure";
        }

        if( flags & flag::python_error) {
            FAIL() << "python error";
        }

    } catch( simulation::PythonEnvironment::system_exit& e) {
        SUCCEED() << "test terminated normally";
    } catch( const result& test_result ) {
        ASSERT_TRUE(test_result.value) << " ("<<data.filepath<<")";

        // TODO raii for unloading
        simulation::getSimulation()->unload( root.get() );
    }
}

} // namespace sofa

<commit_msg>[SofaPython] forward python error to gtest<commit_after>#include <SofaPython/PythonScriptEvent.h>

#include "Python_test.h"

#include <sofa/helper/system/PluginManager.h>

#include <sofa/simulation/Simulation.h>
#include <SofaSimulationGraph/DAGSimulation.h>

#include <sofa/helper/logging/Messaging.h>
#include <sofa/helper/system/FileSystem.h>

#include <SofaPython/PythonMacros.h>
#include <SofaPython/PythonFactory.h>

#include <SofaPython/helper.hpp>

namespace sofa {

Python_test::Python_test()
{
    static const std::string plugin = "SofaPython";
    sofa::helper::system::PluginManager::getInstance().loadPlugin(plugin);
}

void Python_test::run( const Python_test_data& data ) {

    msg_info("Python_test") << "running " << data.filepath;

    {
        // Check the file exists
        std::ifstream file(data.filepath.c_str());
        bool scriptFound = file.good();
        ASSERT_TRUE(scriptFound) << " ("<<data.filepath<<")";
    }

    ASSERT_TRUE( loader.loadTestWithArguments(data.filepath.c_str(),data.arguments) ) << " ("<<data.filepath<<")";

}

static bool ends_with(const std::string& suffix, const std::string& full){
    const std::size_t lf = full.length();
    const std::size_t ls = suffix.length();

    if(lf < ls) return false;

    return (0 == full.compare(lf - ls, ls, suffix));
}

static bool starts_with(const std::string& prefix, const std::string& full){
    const std::size_t lf = full.length();
    const std::size_t lp = prefix.length();

    if(lf < lp) return false;

    return (0 == full.compare(0, lp, prefix));
}

void Python_test_list::addTestDir(const std::string& dir, const std::string& prefix) {

    std::vector<std::string> files;
    helper::system::FileSystem::listDirectory(dir, files);

    for(const std::string& file : files) {
        if( starts_with(prefix, file) && ends_with(".py", file) ) {
            addTest(file, dir);
        }
    }

}

////////////////////////////////////////////////////
////////////////////////////////////////////////////
////////////////////////////////////////////////////

struct Listener : core::objectmodel::BaseObject {

    Listener() {
        f_listening = true;
    }

    virtual void handleEvent(core::objectmodel::Event * event) {
        if (core::objectmodel::PythonScriptEvent::checkEventType(event)
              || core::objectmodel::ScriptEvent::checkEventType(event) )
       {
            core::objectmodel::ScriptEvent* e = static_cast<core::objectmodel::ScriptEvent*>(event);
            std::string name = e->getEventName();
            if( name == "success" ) {
                throw Python_scene_test::result(true);
            } else if (name == "failure") {
                throw Python_scene_test::result(false);
            }
        }
    }

};

struct fail {
    const char* message;
    fail(const char* message)
        : message(message) { }
};

static PyObject* operator||(PyObject* obj, const fail& error) {
    if(obj) return obj;
    throw std::runtime_error(error.message);
    return nullptr;
}

static void operator||(int code, const fail& error) {
    if(code >= 0) return;
    throw std::runtime_error(error.message);
}

enum flag : char {
    stop = 1 << 1,
    test_failure = 1 << 2,
    python_error = 1 << 3
};

// shared data between except_hook and simulation loop
struct shared_type {
    char flags = 0;
    std::string message;
    PyObject* default_excepthook = nullptr;
};

// TODO FIXME: there's probably a MEMLEAK hiding in there, figure it out
static PyObject* except_hook(PyObject* self, PyObject* args) {

    // raise the stop flag
    shared_type* shared = reinterpret_cast<shared_type*>(PyCapsule_GetPointer(self, NULL));

    assert(shared && "cannot get shared_type pointer (wtf?)");
    if(!shared) std::exit(1);

    shared->flags |= flag::stop;

    // switch on exception type
    PyObject* type;
    PyObject* value;
    PyObject* traceback;

    if( !PyArg_ParseTuple(args, "OOO", &type, &value, &traceback) ) {
        assert(false && "cannot parse excepthook args (wtf?)");
        std::exit(1);
    }

    if( type == PyExc_AssertionError ) {
        // test failure
        shared->flags |= flag::test_failure;
    } else {
        // other python error
        shared->flags |= flag::python_error;
    }

    {
        // TODO find a way to push traceback as well
        const py::object value_string = py::ptr(value).str();
        assert(value_string.c_str());
        shared->message += value_string.c_str();
    }

    // call default excepthook to get traceback etc
    return PyObject_CallObject(shared->default_excepthook, args);
}

static PyMethodDef except_hook_def = {
    "sofa_excepthook",
    except_hook,
    METH_VARARGS,
    NULL
};

// save/restore excepthook
class excepthook_installer {
    PyObject* default_excepthook = nullptr;
public:
    excepthook_installer(shared_type* shared) {
        default_excepthook = PySys_GetObject((char*)"excepthook") || fail("cannot get default excepthook");

        shared->default_excepthook = default_excepthook;

        PyObject* self = PyCapsule_New(shared, NULL, NULL) || fail("cant wrap shared_type pointer");

        // build closure
        PyObject* excepthook = PyCFunction_NewEx(&except_hook_def, self, NULL)
                || fail("cannot create excepthook closure");

        PySys_SetObject((char*)"excepthook", excepthook) || fail("cannot set sys.excepthook");
    }

    ~excepthook_installer() {
        PySys_SetObject((char*)"excepthook", default_excepthook) || fail("cannot restore default excepthook");
    }
};

Python_scene_test::Python_scene_test()
    : max_steps(1000) {

}

void Python_scene_test::run( const Python_test_data& data ) {

    msg_info("Python_scene_test") << "running "<< data.filepath;

    {
        // Check the file exists
        std::ifstream file(data.filepath.c_str());
        bool scriptFound = file.good();
        ASSERT_TRUE(scriptFound) << " ("<<data.filepath<<")";
    }

    if( !simulation::getSimulation() ) {
        simulation::setSimulation( new sofa::simulation::graph::DAGSimulation() );
    }

    simulation::Node::SPtr root;

    try {
        shared_type shared;
        const excepthook_installer lock(&shared);

        loader.loadSceneWithArguments(data.filepath.c_str(),
                                      data.arguments,
                                      &root);
        ASSERT_TRUE(bool(root)) << "scene creation failed!" << " ("<<data.filepath<<")";

        root->addObject( new Listener );
        simulation::getSimulation()->init(root.get());

        // TODO eventually tests should only stop by throwing SystemExit
        unsigned i;
        for(i = 0; (i < max_steps) && !(shared.flags & flag::stop) && root->isActive(); ++i) {
            simulation::getSimulation()->animate(root.get(), root->getDt());
        }

        ASSERT_TRUE(i != max_steps) << "maximum allowed steps reached: " << max_steps << " ("<<data.filepath<<")";

        if( shared.flags & flag::test_failure ) {
            FAIL() << "test failure:\n" << shared.message;
        }

        if( shared.flags & flag::python_error) {
            FAIL() << "python error:\n" << shared.message;
        }

    } catch( simulation::PythonEnvironment::system_exit& e) {
        SUCCEED() << "test terminated normally";
    } catch( const result& test_result ) {
        ASSERT_TRUE(test_result.value) << " ("<<data.filepath<<")";

        // TODO raii for unloading
        simulation::getSimulation()->unload( root.get() );

    } catch( std::runtime_error& e) {
        ASSERT_TRUE(false) << "error setting up python excepthook, aborting test" << " ("<<data.filepath<<")";
    }
}

} // namespace sofa

<|endoftext|>