packages feed

zeolite-lang-0.7.0.0: base/logging.cpp

/* -----------------------------------------------------------------------------
Copyright 2019-2020 Kevin P. Barry

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

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

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

// Author: Kevin P. Barry [ta0kira@gmail.com]

#include "logging.hpp"

#include <cassert>
#include <csignal>
#include <iostream>


LogThenCrash::LogThenCrash(bool fail, const std::string& condition)
    : fail_(fail), signal_(SIGABRT), condition_(condition) {}

LogThenCrash::LogThenCrash(bool fail, int signal)
    : fail_(fail), signal_(signal) {}

LogThenCrash::~LogThenCrash() {
  if (fail_) {
    std::signal(signal_, SIG_DFL);
    if (Argv::ArgCount() > 0) {
      std::cerr << Argv::GetArgAt(0) << ": ";
    }
    std::cerr << "Failed condition";
    if (!condition_.empty()) {
      std::cerr << " '" << condition_ << "'";
    }
    std::cerr << ": " << output_.str() << std::endl;
    PrintTrace(TraceContext::GetTrace());
    const TraceList creation_trace = TraceCreation::GetTrace();
    if (!creation_trace.empty()) {
      std::cerr << TraceCreation::GetType() << " value originally created at:" << std::endl;
      PrintTrace(creation_trace);
    }
    std::raise(signal_);
  }
}

// static
void LogThenCrash::PrintTrace(const TraceList &call_trace) {
  for (const auto& trace : call_trace) {
    const std::string message = trace();
    if (!message.empty()) {
      std::cerr << "  " << message << std::endl;
    }
  }
}

// TODO: Should only be available used if POSIX is defined.
void TraceOnSignal(int signal) {
  LogThenCrash(true,signal) << "Recieved signal " << signal;
}

// TODO: Should only be available used if POSIX is defined.
void SetSignalHandler() {
#ifdef SIGINT
  std::signal(SIGINT, &TraceOnSignal);
#endif
#ifdef SIGILL
  std::signal(SIGILL, &TraceOnSignal);
#endif
#ifdef SIGABRT
  std::signal(SIGABRT, &TraceOnSignal);
#endif
#ifdef SIGFPE
  std::signal(SIGFPE, &TraceOnSignal);
#endif
#ifdef SIGQUIT
  std::signal(SIGQUIT, &TraceOnSignal);
#endif
#ifdef SIGSEGV
  std::signal(SIGSEGV, &TraceOnSignal);
#endif
#ifdef SIGPIPE
  std::signal(SIGPIPE, &TraceOnSignal);
#endif
#ifdef SIGALRM
  std::signal(SIGALRM, &TraceOnSignal);
#endif
#ifdef SIGTERM
  std::signal(SIGTERM, &TraceOnSignal);
#endif
#ifdef SIGUSR1
  std::signal(SIGUSR1, &TraceOnSignal);
#endif
#ifdef SIGUSR2
  std::signal(SIGUSR2, &TraceOnSignal);
#endif
}


TraceList TraceContext::GetTrace() {
  TraceList trace;
  const TraceContext* current = GetCurrent();
  while (current) {
    current->AppendTrace(trace);
    current = current->GetNext();
  }
  return trace;
}


void SourceContext::SetLocal(const char* at) {
  at_ = at;
}

void SourceContext::AppendTrace(TraceList& trace) const {
  const char* const name = name_;
  const char* const at   = at_;
  trace.push_back([name,at]() {
      std::ostringstream output;
      if (at == nullptr || at[0] == 0x00) {
        output << "From " << name;
      } else {
        output << "From " << name << " at " << at;
      }
      return output.str();
    });
}

const TraceContext* SourceContext::GetNext() const {
  return capture_to_.Previous();
}


void CleanupContext::SetLocal(const char* at) {
  at_ = at;
}

void CleanupContext::AppendTrace(TraceList& trace) const {
  const char* const at = at_;
  trace.push_back([at]() {
      std::ostringstream output;
      if (at == nullptr || at[0] == 0x00) {
        output << "In cleanup block";
      } else {
        output << "In cleanup block at " << at;
      }
      return output.str();
    });
}

const TraceContext* CleanupContext::GetNext() const {
  return capture_to_.Previous();
}

int Argv::ArgCount() {
  if (GetCurrent()) {
    return GetCurrent()->GetArgs().size();
  } else {
    return 0;
  }
}

const std::string& Argv::GetArgAt(int pos) {
  if (pos < 0 || pos >= ArgCount()) {
    FAIL() << "Argv index " << pos << " is out of bounds";
    __builtin_unreachable();
  } else {
    return GetCurrent()->GetArgs()[pos];
  }
}

const std::vector<std::string>& ProgramArgv::GetArgs() const {
  return argv_;
}