<commit_before>/* Copyright 2016 Stanford University, NVIDIA Corporation
 *
 * 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.
 */

// Realm logging infrastructure

#include "logging.h"

#ifdef SHARED_LOWLEVEL
#define gasnet_mynode() 0
#define gasnet_nodes() 1
#else
#include "activemsg.h"
#endif

#include "cmdline.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>

#include <set>
#include <map>

namespace Realm {

  // abstract class for an output stream
  class LoggerOutputStream {
  public:
    LoggerOutputStream(void) {}
    virtual ~LoggerOutputStream(void) {}

    virtual void write(const char *buffer, size_t len) = 0;
    virtual void flush(void) = 0;
  };

  class LoggerFileStream : public LoggerOutputStream {
  public:
    LoggerFileStream(FILE *_f, bool _close_file)
      : f(_f), close_file(_close_file)
    {}

    virtual ~LoggerFileStream(void)
    {
      if(close_file)
	fclose(f);
    }

    virtual void write(const char *buffer, size_t len)
    {
#ifndef NDEBUG
      size_t amt =
#endif
	fwrite(buffer, 1, len, f);
      assert(amt == len);
    }

    virtual void flush(void)
    {
      fflush(f);
    }

  protected:
    FILE *f;
    bool close_file;
  };

  // use a pthread mutex to prevent simultaneous writes to a stream
  template <typename T>
  class LoggerStreamSerialized : public LoggerOutputStream {
  public:
    LoggerStreamSerialized(T *_stream, bool _delete_inner)
      : stream(_stream), delete_inner(_delete_inner)
    {
#ifndef NDEBUG
      int ret =
#endif
	pthread_mutex_init(&mutex, 0);
      assert(ret == 0);
    }

    virtual ~LoggerStreamSerialized(void)
    {
#ifndef NDEBUG
      int ret =
#endif
	pthread_mutex_destroy(&mutex);
      assert(ret == 0);
      if(delete_inner)
	delete stream;
    }

    virtual void write(const char *buffer, size_t len)
    {
#ifndef NDEBUG
      int ret;
      ret =
#endif
	pthread_mutex_lock(&mutex);
      assert(ret == 0);
      stream->write(buffer, len);
#ifndef NDEBUG
      ret =
#endif
	pthread_mutex_unlock(&mutex);
      assert(ret == 0);
    }

    virtual void flush(void)
    {
#ifndef NDEBUG
      int ret;
      ret =
#endif
	pthread_mutex_lock(&mutex);
      assert(ret == 0);
      stream->flush();
#ifndef NDEBUG
      ret =
#endif
	pthread_mutex_unlock(&mutex);
      assert(ret == 0);
    }

  protected:
    LoggerOutputStream *stream;
    bool delete_inner;
    pthread_mutex_t mutex;
  };

  class LoggerConfig {
  protected:
    LoggerConfig(void);
    ~LoggerConfig(void);

  public:
    static LoggerConfig *get_config(void);

    static void flush_all_streams(void);

    void read_command_line(std::vector<std::string>& cmdline);

    // either configures a logger right away or remembers it to config once
    //   we know the desired settings
    void configure(Logger *logger);

  protected:
    bool parse_level_argument(const std::string& s);

    bool cmdline_read;
    Logger::LoggingLevel default_level;
    std::map<std::string, Logger::LoggingLevel> category_levels;
    std::string cats_enabled;
    std::set<Logger *> pending_configs;
    LoggerOutputStream *stream;
  };

  LoggerConfig::LoggerConfig(void)
    : cmdline_read(false), default_level(Logger::LEVEL_PRINT), stream(0)
  {}

  LoggerConfig::~LoggerConfig(void)
  {
    delete stream;
  }

  /*static*/ LoggerConfig *LoggerConfig::get_config(void)
  {
    static LoggerConfig cfg;
    return &cfg;
  }

  /*static*/ void LoggerConfig::flush_all_streams(void)
  {
    LoggerConfig *cfg = get_config();
    if(cfg->stream)
      cfg->stream->flush();
  }

  bool LoggerConfig::parse_level_argument(const std::string& s)
  {
    const char *p1 = s.c_str();

    while(true) {
      // skip commas
      while(*p1 == ',') p1++;
      if(!*p1) break;

      // numbers may be preceeded by name= to specify a per-category level
      std::string catname;
      if(!isdigit(*p1)) {
	const char *p2 = p1;
	while(*p2 != '=') {
	  if(!*p2) {
	    fprintf(stderr, "ERROR: category name in -level must be followed by =\n");
	    return false;
	  }
	  p2++;
	}
	catname.assign(p1, p2 - p1);
	p1 = p2 + 1;
      }

      // levels are small integers
      if(isdigit(*p1)) {
	char *p2;
	errno = 0; // no leftover errors from elsewhere
	long v = strtol(p1, &p2, 10);

	if((errno == 0) && ((*p2 == 0) || (*p2) == ',') &&
	   (v >= 0) && (v <= Logger::LEVEL_NONE)) {
	  if(catname.empty())
	    default_level = (Logger::LoggingLevel)v;
	  else
	    category_levels[catname] = (Logger::LoggingLevel)v;

	  p1 = p2;
	  continue;
	}
      }

      fprintf(stderr, "ERROR: logger level malformed or out of range: '%s'\n", p1);
      return false;
    }

    return true;
  }

  void LoggerConfig::read_command_line(std::vector<std::string>& cmdline)
  {
    std::string logname;

    bool ok = CommandLineParser()
      .add_option_string("-cat", cats_enabled)
      .add_option_string("-logfile", logname)
      .add_option_method("-level", this, &LoggerConfig::parse_level_argument)
      .parse_command_line(cmdline);

    if(!ok) {
      fprintf(stderr, "couldn't parse logger config options\n");
      exit(1);
    }

    // lots of choices for log output
    if(logname.empty() || (logname == "stdout")) {
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(stdout, false),
							    true);
    } else if(logname == "stderr") {
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(stderr, false),
							    true);
    } else {
      // we're going to open a file, but key off a + for appending and
      //  look for a % for node number insertion
      bool append = false;
      size_t start = 0;

      if(logname[0] == '+') {
	append = true;
	start++;
      }

      FILE *f = 0;
      size_t pct = logname.find_first_of('%', start);
      if(pct == std::string::npos) {
	// no node number - everybody uses the same file
	if(gasnet_nodes() > 1) {
	  if(!append) {
	    if(gasnet_mynode() == 1)
	      fprintf(stderr, "WARNING: all ranks are logging to the same output file - appending is forced and output may be jumbled\n");
	    append = true;
	  }
	}
	const char *fn = logname.c_str() + start;
	f = fopen(fn, append ? "a" : "w");
	if(!f) {
	  fprintf(stderr, "could not open log file '%s': %s\n", fn, strerror(errno));
	  exit(1);
	}
      } else {
	// replace % with node number
	char filename[256];
	sprintf(filename, "%.*s%d%s",
		(int)(pct - start), logname.c_str() + start, gasnet_mynode(), logname.c_str() + pct + 1);

	f = fopen(filename, append ? "a" : "w");
	if(!f) {
	  fprintf(stderr, "could not open log file '%s': %s\n", filename, strerror(errno));
	  exit(1);
	}
      }
      // TODO: consider buffering in some cases?
      setbuf(f, 0); // disable output buffering
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(f, true),
							    true);
    }

    atexit(LoggerConfig::flush_all_streams);

    cmdline_read = true;
    if(!pending_configs.empty()) {
      for(std::set<Logger *>::iterator it = pending_configs.begin();
	  it != pending_configs.end();
	  it++)
	configure(*it);
      pending_configs.clear();
    }
  }

  void LoggerConfig::configure(Logger *logger)
  {
    // if we haven't read the command line yet, remember this for later
    if(!cmdline_read) {
      pending_configs.insert(logger);
      return;
    }

    // see if this logger is one of the categories we want
    if(!cats_enabled.empty()) {
      bool found = false;
      const char *p = cats_enabled.c_str();
      int l = logger->get_name().length();
      const char *n = logger->get_name().c_str();
      while(*p) {
	if(((p[l] == '\0') || (p[l] == ',')) && !strncmp(p, n, l)) {
	  found = true;
	  break;
	}
	// skip to after next comma
	while(*p && (*p != ',')) p++;
	while(*p && (*p == ',')) p++;
      }
      if(!found) {
	//printf("'%s' not in '%s'\n", n, cats_enabled);
	return;
      }
    }

    // see if the level for this category has been customized
    Logger::LoggingLevel level = default_level;
    std::map<std::string, Logger::LoggingLevel>::const_iterator it = category_levels.find(logger->get_name());
    if(it != category_levels.end())
      level = it->second;

    // give this logger a copy of the global stream
    logger->add_stream(stream, level,
		       false,  /* don't delete */
		       false); /* don't flush each write */
  }

  ////////////////////////////////////////////////////////////////////////
  //
  // class Logger

  Logger::Logger(const std::string& _name)
    : name(_name), log_level(LEVEL_NONE)
  {
    LoggerConfig::get_config()->configure(this);
  }

  Logger::~Logger(void)
  {
    // go through our streams and delete any we're supposed to
    for(std::vector<LogStream>::iterator it = streams.begin();
	it != streams.end();
	it++)
      if(it->delete_when_done)
	delete it->s;

    streams.clear();
  }

  /*static*/ void Logger::configure_from_cmdline(std::vector<std::string>& cmdline)
  {
    LoggerConfig::get_config()->read_command_line(cmdline);
  }

  void Logger::log_msg(LoggingLevel level, const std::string& msg)
  {
    // no logging of empty messages
    if(msg.length() == 0)
      return;

    // build message string, including prefix
    static const int MAXLEN = 4096;
    char buffer[MAXLEN];
    int len = snprintf(buffer, MAXLEN - 2, "[%d - %lx] {%d}{%s}: ",
		       gasnet_mynode(), (unsigned long)pthread_self(),
		       level, name.c_str());
    int amt = msg.length();
    // Unusual case, but handle it
    if((len + amt) >= MAXLEN)
    {
      // If this is an error or a warning, print out the
      // whole message no matter what
      if ((level == LEVEL_FATAL) ||
          (level == LEVEL_ERROR) || (level == LEVEL_WARNING))
      {
        const size_t full_len = len + amt + 2;
        char *full_buffer = (char*)malloc(full_len);
        memcpy(full_buffer, buffer, len);
        memcpy(full_buffer + len, msg.data(), amt);
        full_buffer[len+amt] = '\n';
        full_buffer[len+amt+1] = 0;
        // go through all the streams
        for(std::vector<LogStream>::iterator it = streams.begin();
            it != streams.end();
            it++) {
          if(level < it->min_level)
            continue;

          it->s->write(full_buffer, full_len);

          if(it->flush_each_write)
            it->s->flush();
        }
        free(full_buffer);
        return;
      }
      amt = MAXLEN - 2 - len;
    }
    memcpy(buffer + len, msg.data(), amt);
    len += amt;
    buffer[len++] = '\n';
    buffer[len] = 0;

    // go through all the streams
    for(std::vector<LogStream>::iterator it = streams.begin();
	it != streams.end();
	it++) {
      if(level < it->min_level)
	continue;

      it->s->write(buffer, len);

      if(it->flush_each_write)
	it->s->flush();
    }
  }

  void Logger::add_stream(LoggerOutputStream *s, LoggingLevel min_level,
			  bool delete_when_done, bool flush_each_write)
  {
    LogStream ls;
    ls.s = s;
    ls.min_level = min_level;
    ls.delete_when_done = delete_when_done;
    ls.flush_each_write = flush_each_write;
    streams.push_back(ls);

    // update our logging level if needed
    if(log_level > min_level)
      log_level = min_level;
  }

  ////////////////////////////////////////////////////////////////////////
  //
  // class LoggerMessage

  LoggerMessage& LoggerMessage::vprintf(const char *fmt, va_list args)
  {
    if(active) {
      static const int MAXLEN = 4096;
      char msg[MAXLEN];
      int full = vsnprintf(msg, MAXLEN, fmt, args);
      // If this is an error or a warning, print out the full string
      // no matter what
      if((full >= MAXLEN) && ((level == Logger::LEVEL_FATAL) ||
          (level == Logger::LEVEL_ERROR) || (level == Logger::LEVEL_WARNING))) {
        char *full_msg = (char*)malloc(full+1);
        vsnprintf(full_msg, full+1, fmt, args);
        (*oss) << full_msg;
        free(full_msg);
      } else {
        (*oss) << msg;
      }
    }
    return *this;
  }

}; // namespace Realm
<commit_msg>realm: support logging level names in command line, critical errors to stderr<commit_after>/* Copyright 2016 Stanford University, NVIDIA Corporation
 *
 * 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.
 */

// Realm logging infrastructure

#include "logging.h"

#ifdef SHARED_LOWLEVEL
#define gasnet_mynode() 0
#define gasnet_nodes() 1
#else
#include "activemsg.h"
#endif

#include "cmdline.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>

#include <set>
#include <map>

namespace Realm {

  // abstract class for an output stream
  class LoggerOutputStream {
  public:
    LoggerOutputStream(void) {}
    virtual ~LoggerOutputStream(void) {}

    virtual void write(const char *buffer, size_t len) = 0;
    virtual void flush(void) = 0;
  };

  class LoggerFileStream : public LoggerOutputStream {
  public:
    LoggerFileStream(FILE *_f, bool _close_file)
      : f(_f), close_file(_close_file)
    {}

    virtual ~LoggerFileStream(void)
    {
      if(close_file)
	fclose(f);
    }

    virtual void write(const char *buffer, size_t len)
    {
#ifndef NDEBUG
      size_t amt =
#endif
	fwrite(buffer, 1, len, f);
      assert(amt == len);
    }

    virtual void flush(void)
    {
      fflush(f);
    }

  protected:
    FILE *f;
    bool close_file;
  };

  // use a pthread mutex to prevent simultaneous writes to a stream
  template <typename T>
  class LoggerStreamSerialized : public LoggerOutputStream {
  public:
    LoggerStreamSerialized(T *_stream, bool _delete_inner)
      : stream(_stream), delete_inner(_delete_inner)
    {
#ifndef NDEBUG
      int ret =
#endif
	pthread_mutex_init(&mutex, 0);
      assert(ret == 0);
    }

    virtual ~LoggerStreamSerialized(void)
    {
#ifndef NDEBUG
      int ret =
#endif
	pthread_mutex_destroy(&mutex);
      assert(ret == 0);
      if(delete_inner)
	delete stream;
    }

    virtual void write(const char *buffer, size_t len)
    {
#ifndef NDEBUG
      int ret;
      ret =
#endif
	pthread_mutex_lock(&mutex);
      assert(ret == 0);
      stream->write(buffer, len);
#ifndef NDEBUG
      ret =
#endif
	pthread_mutex_unlock(&mutex);
      assert(ret == 0);
    }

    virtual void flush(void)
    {
#ifndef NDEBUG
      int ret;
      ret =
#endif
	pthread_mutex_lock(&mutex);
      assert(ret == 0);
      stream->flush();
#ifndef NDEBUG
      ret =
#endif
	pthread_mutex_unlock(&mutex);
      assert(ret == 0);
    }

  protected:
    LoggerOutputStream *stream;
    bool delete_inner;
    pthread_mutex_t mutex;
  };

  class LoggerConfig {
  protected:
    LoggerConfig(void);
    ~LoggerConfig(void);

  public:
    static LoggerConfig *get_config(void);

    static void flush_all_streams(void);

    void read_command_line(std::vector<std::string>& cmdline);

    // either configures a logger right away or remembers it to config once
    //   we know the desired settings
    void configure(Logger *logger);

  protected:
    bool parse_level_argument(const std::string& s);

    bool cmdline_read;
    Logger::LoggingLevel default_level, stderr_level;
    std::map<std::string, Logger::LoggingLevel> category_levels;
    std::string cats_enabled;
    std::set<Logger *> pending_configs;
    LoggerOutputStream *stream, *stderr_stream;
  };

  LoggerConfig::LoggerConfig(void)
    : cmdline_read(false)
    , default_level(Logger::LEVEL_PRINT)
    , stderr_level(Logger::LEVEL_ERROR)
    , stream(0)
    , stderr_stream(0)
  {}

  LoggerConfig::~LoggerConfig(void)
  {
    delete stream;
  }

  /*static*/ LoggerConfig *LoggerConfig::get_config(void)
  {
    static LoggerConfig cfg;
    return &cfg;
  }

  /*static*/ void LoggerConfig::flush_all_streams(void)
  {
    LoggerConfig *cfg = get_config();
    if(cfg->stream)
      cfg->stream->flush();
  }

  template <>
  bool convert_integer_cmdline_argument<Logger::LoggingLevel>(const std::string& s, Logger::LoggingLevel& target)
  {
    // match strings first
    if(s == "spew") {
      target = Logger::LEVEL_SPEW;
      return true;
    }
    if(s == "debug") {
      target = Logger::LEVEL_DEBUG;
      return true;
    }
    if(s == "info") {
      target = Logger::LEVEL_INFO;
      return true;
    }
    if(s == "print") {
      target = Logger::LEVEL_PRINT;
      return true;
    }
    if((s == "warning") || (s == "warn")) {
      target = Logger::LEVEL_WARNING;
      return true;
    }
    if(s == "error") {
      target = Logger::LEVEL_ERROR;
      return true;
    }
    if(s == "fatal") {
      target = Logger::LEVEL_FATAL;
      return true;
    }
    if(s == "none") {
      target = Logger::LEVEL_NONE;
      return true;
    }

    // try to decode an integer between LEVEL_SPEW and LEVEL_NONE
    errno = 0;  // no errors from before
    char *pos;
    long v = strtol(s.c_str(), &pos, 10);
    if((errno == 0) && (*pos == 0) &&
       (v >= Logger::LEVEL_SPEW) && (v <= Logger::LEVEL_NONE)) {
      target = static_cast<Logger::LoggingLevel>(v);
      return true;
    } else
      return false;
  }

  bool LoggerConfig::parse_level_argument(const std::string& s)
  {
    const char *p1 = s.c_str();

    while(true) {
      // skip commas
      while(*p1 == ',') p1++;
      if(!*p1) break;

      // numbers may be preceeded by name= to specify a per-category level
      std::string catname;
      if(!isdigit(*p1)) {
	const char *p2 = p1;
	while(*p2 != '=') {
	  if(!*p2) {
	    fprintf(stderr, "ERROR: category name in -level must be followed by =\n");
	    return false;
	  }
	  p2++;
	}
	catname.assign(p1, p2 - p1);
	p1 = p2 + 1;
      }

      // levels are small integers or words - scan forward to the first thing
      //  that's not a digit or a number - should be, or \0
      Logger::LoggingLevel lvl = Logger::LEVEL_SPEW;
      const char *p2 = p1;
      while(*p2 && isalnum(*p2)) p2++;
      if((!*p2 || (*p2 == ',')) &&
	 convert_integer_cmdline_argument(std::string(p1, p2-p1), lvl)) {
	if(catname.empty())
	  default_level = lvl;
	else
	  category_levels[catname] = lvl;

	p1 = p2;
	continue;
      }

      fprintf(stderr, "ERROR: logger level malformed or out of range: '%s'\n", p1);
      return false;
    }

    return true;
  }

  void LoggerConfig::read_command_line(std::vector<std::string>& cmdline)
  {
    std::string logname;

    bool ok = CommandLineParser()
      .add_option_string("-cat", cats_enabled)
      .add_option_string("-logfile", logname)
      .add_option_method("-level", this, &LoggerConfig::parse_level_argument)
      .add_option_int("-errlevel", stderr_level)
      .parse_command_line(cmdline);

    if(!ok) {
      fprintf(stderr, "couldn't parse logger config options\n");
      exit(1);
    }

    // lots of choices for log output
    if(logname.empty() || (logname == "stdout")) {
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(stdout, false),
							    true);
    } else if(logname == "stderr") {
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(stderr, false),
							    true);
    } else {
      // we're going to open a file, but key off a + for appending and
      //  look for a % for node number insertion
      bool append = false;
      size_t start = 0;

      if(logname[0] == '+') {
	append = true;
	start++;
      }

      FILE *f = 0;
      size_t pct = logname.find_first_of('%', start);
      if(pct == std::string::npos) {
	// no node number - everybody uses the same file
	if(gasnet_nodes() > 1) {
	  if(!append) {
	    if(gasnet_mynode() == 1)
	      fprintf(stderr, "WARNING: all ranks are logging to the same output file - appending is forced and output may be jumbled\n");
	    append = true;
	  }
	}
	const char *fn = logname.c_str() + start;
	f = fopen(fn, append ? "a" : "w");
	if(!f) {
	  fprintf(stderr, "could not open log file '%s': %s\n", fn, strerror(errno));
	  exit(1);
	}
      } else {
	// replace % with node number
	char filename[256];
	sprintf(filename, "%.*s%d%s",
		(int)(pct - start), logname.c_str() + start, gasnet_mynode(), logname.c_str() + pct + 1);

	f = fopen(filename, append ? "a" : "w");
	if(!f) {
	  fprintf(stderr, "could not open log file '%s': %s\n", filename, strerror(errno));
	  exit(1);
	}
      }
      // TODO: consider buffering in some cases?
      setbuf(f, 0); // disable output buffering
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(f, true),
							    true);

      // when logging to a file, also sent critical-enough messages to stderr
      if(stderr_level < Logger::LEVEL_NONE)
	stderr_stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(stderr, false),
								     true);
    }

    atexit(LoggerConfig::flush_all_streams);

    cmdline_read = true;
    if(!pending_configs.empty()) {
      for(std::set<Logger *>::iterator it = pending_configs.begin();
	  it != pending_configs.end();
	  it++)
	configure(*it);
      pending_configs.clear();
    }
  }

  void LoggerConfig::configure(Logger *logger)
  {
    // if we haven't read the command line yet, remember this for later
    if(!cmdline_read) {
      pending_configs.insert(logger);
      return;
    }

    // see if this logger is one of the categories we want
    if(!cats_enabled.empty()) {
      bool found = false;
      const char *p = cats_enabled.c_str();
      int l = logger->get_name().length();
      const char *n = logger->get_name().c_str();
      while(*p) {
	if(((p[l] == '\0') || (p[l] == ',')) && !strncmp(p, n, l)) {
	  found = true;
	  break;
	}
	// skip to after next comma
	while(*p && (*p != ',')) p++;
	while(*p && (*p == ',')) p++;
      }
      if(!found) {
	//printf("'%s' not in '%s'\n", n, cats_enabled);
	return;
      }
    }

    // see if the level for this category has been customized
    Logger::LoggingLevel level = default_level;
    std::map<std::string, Logger::LoggingLevel>::const_iterator it = category_levels.find(logger->get_name());
    if(it != category_levels.end())
      level = it->second;

    // give this logger a copy of the global stream
    logger->add_stream(stream, level,
		       false,  /* don't delete */
		       false); /* don't flush each write */

    // also use the stderr_stream, if present
    // make sure not to log at a level noisier than requested for this category
    if(stderr_stream)
      logger->add_stream(stderr_stream,
			 ((level > stderr_level) ? level : stderr_level),
			 false, /* don't delete */
			 true); /* flush each write */
  }

  ////////////////////////////////////////////////////////////////////////
  //
  // class Logger

  Logger::Logger(const std::string& _name)
    : name(_name), log_level(LEVEL_NONE)
  {
    LoggerConfig::get_config()->configure(this);
  }

  Logger::~Logger(void)
  {
    // go through our streams and delete any we're supposed to
    for(std::vector<LogStream>::iterator it = streams.begin();
	it != streams.end();
	it++)
      if(it->delete_when_done)
	delete it->s;

    streams.clear();
  }

  /*static*/ void Logger::configure_from_cmdline(std::vector<std::string>& cmdline)
  {
    LoggerConfig::get_config()->read_command_line(cmdline);
  }

  void Logger::log_msg(LoggingLevel level, const std::string& msg)
  {
    // no logging of empty messages
    if(msg.length() == 0)
      return;

    // build message string, including prefix
    static const int MAXLEN = 4096;
    char buffer[MAXLEN];
    int len = snprintf(buffer, MAXLEN - 2, "[%d - %lx] {%d}{%s}: ",
		       gasnet_mynode(), (unsigned long)pthread_self(),
		       level, name.c_str());
    int amt = msg.length();
    // Unusual case, but handle it
    if((len + amt) >= MAXLEN)
    {
      // If this is an error or a warning, print out the
      // whole message no matter what
      if ((level == LEVEL_FATAL) ||
          (level == LEVEL_ERROR) || (level == LEVEL_WARNING))
      {
        const size_t full_len = len + amt + 2;
        char *full_buffer = (char*)malloc(full_len);
        memcpy(full_buffer, buffer, len);
        memcpy(full_buffer + len, msg.data(), amt);
        full_buffer[len+amt] = '\n';
        full_buffer[len+amt+1] = 0;
        // go through all the streams
        for(std::vector<LogStream>::iterator it = streams.begin();
            it != streams.end();
            it++) {
          if(level < it->min_level)
            continue;

          it->s->write(full_buffer, full_len);

          if(it->flush_each_write)
            it->s->flush();
        }
        free(full_buffer);
        return;
      }
      amt = MAXLEN - 2 - len;
    }
    memcpy(buffer + len, msg.data(), amt);
    len += amt;
    buffer[len++] = '\n';
    buffer[len] = 0;

    // go through all the streams
    for(std::vector<LogStream>::iterator it = streams.begin();
	it != streams.end();
	it++) {
      if(level < it->min_level)
	continue;

      it->s->write(buffer, len);

      if(it->flush_each_write)
	it->s->flush();
    }
  }

  void Logger::add_stream(LoggerOutputStream *s, LoggingLevel min_level,
			  bool delete_when_done, bool flush_each_write)
  {
    LogStream ls;
    ls.s = s;
    ls.min_level = min_level;
    ls.delete_when_done = delete_when_done;
    ls.flush_each_write = flush_each_write;
    streams.push_back(ls);

    // update our logging level if needed
    if(log_level > min_level)
      log_level = min_level;
  }

  ////////////////////////////////////////////////////////////////////////
  //
  // class LoggerMessage

  LoggerMessage& LoggerMessage::vprintf(const char *fmt, va_list args)
  {
    if(active) {
      static const int MAXLEN = 4096;
      char msg[MAXLEN];
      int full = vsnprintf(msg, MAXLEN, fmt, args);
      // If this is an error or a warning, print out the full string
      // no matter what
      if((full >= MAXLEN) && ((level == Logger::LEVEL_FATAL) ||
          (level == Logger::LEVEL_ERROR) || (level == Logger::LEVEL_WARNING))) {
        char *full_msg = (char*)malloc(full+1);
        vsnprintf(full_msg, full+1, fmt, args);
        (*oss) << full_msg;
        free(full_msg);
      } else {
        (*oss) << msg;
      }
    }
    return *this;
  }

}; // namespace Realm
<|endoftext|>


<commit_before>/*

  Qt4xHb - Bindings libraries for Harbour/xHarbour and Qt Framework 4

  Copyright (C) 2020 Marcos Antonio Gambeta <marcosgambeta AT outlook DOT com>

*/

#include "hbapi.h"

HB_FUNC( QT4XHB_VERSION )
{
  hb_retc( (const char *) "2020.05" );
}
<commit_msg>source/version.cpp: updated<commit_after>/*

  Qt4xHb - Bindings libraries for Harbour/xHarbour and Qt Framework 4

  Copyright (C) 2020 Marcos Antonio Gambeta <marcosgambeta AT outlook DOT com>

*/

#include "hbapi.h"

HB_FUNC( QT4XHB_VERSION )
{
  hb_retc( (const char *) "2020.06" );
}
<|endoftext|>


<commit_before>/******************************************************************************
**
** Copyright (C) 2009-2011 Kyle Lutz <kyle.r.lutz@gmail.com>
**
** This file is part of chemkit. For more information see
** <http://www.chemkit.org>.
**
** chemkit is free software: you can redistribute it and/or modify
** it under the terms of the GNU Lesser General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** chemkit is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public License
** along with chemkit. If not, see <http://www.gnu.org/licenses/>.
**
******************************************************************************/

#include "ring.h"

#include <set>
#include <cassert>
#include <algorithm>

#include "atom.h"
#include "bond.h"
#include "foreach.h"
#include "molecule.h"

namespace chemkit {

// === Ring ================================================================ //
/// \class Ring ring.h chemkit/ring.h
/// \ingroup chemkit
/// \brief The Ring class represents a ring of bonded atoms in a
///        molecule.
///
/// Ring objects are returned from the various ring perception
/// methods such as Molecule::rings() and Atom::smallestRing().

// --- Construction and Destruction ---------------------------------------- //
/// Creates a new ring that contains the atoms is \p path.
Ring::Ring(std::vector<Atom *> path)
    : m_atoms(path)
{
    assert(isValid());
}

/// Destroys the ring object.
Ring::~Ring()
{
}

// --- Structure ----------------------------------------------------------- //
/// Returns the number of atoms in the ring with \p element.
int Ring::atomCount(const Element &element) const
{
    int count = 0;

    foreach(const Atom *atom, m_atoms){
        if(atom->is(element)){
            count++;
        }
    }

    return count;
}

/// Returns the bond at \p index in the ring.
Bond* Ring::bond(int index) const
{
    return bonds()[index];
}

/// Returns the bonds in the ring.
std::vector<Bond *> Ring::bonds() const
{
    std::vector<Bond *> bonds;

    for(int i = 0; i < size()-1; i++){
        bonds.push_back(m_atoms[i]->bondTo(m_atoms[i+1]));
    }

    bonds.push_back(m_atoms.front()->bondTo(m_atoms.back()));

    return bonds;
}

/// Returns the number of bonds in the ring.
int Ring::bondCount() const
{
    // number of bonds equals the number of atoms
    return m_atoms.size();
}

/// Returns a list of all bonds from atoms inside the ring
/// to atoms outside the ring.
std::vector<Bond *> Ring::exocyclicBonds() const
{
    std::set<Bond *> bondSet;

    foreach(Atom *atom, m_atoms){
        foreach(Bond *bond, atom->bonds()){
            if(!contains(bond)){
                bondSet.insert(bond);
            }
        }
    }

    std::vector<Bond *> bonds;
    foreach(Bond *bond, bondSet){
        bonds.push_back(bond);
    }

    return bonds;
}

/// Returns the number of exocyclic bonds.
int Ring::exocyclicBondCount() const
{
    return exocyclicBonds().size();
}

/// Returns the number of heteroatoms (non-carbon atoms) in the ring.
int Ring::heteroatomCount() const
{
    int count = 0;

    foreach(const Atom *atom, m_atoms){
        if(!atom->is(Atom::Carbon)){
            count++;
        }
    }

    return count;
}

/// Returns \c true if the ring contains any heteroatoms (non-carbon atoms).
bool Ring::isHeterocycle() const
{
    return heteroatomCount() > 0;
}

/// Returns the root atom of the ring. This is determined by finding
/// the non-carbon atom with the highest atomic number. In the case of
/// a tie the atom with the largest valence is returned.
Atom* Ring::root() const
{
    int highestAtomicNumber = 0;
    std::vector<Atom *> candidates;

    foreach(Atom *atom, m_atoms){
        if(atom->is(Atom::Carbon))
            continue;

        if(atom->atomicNumber() > highestAtomicNumber){
            candidates.clear();
            candidates.push_back(atom);
            highestAtomicNumber = atom->atomicNumber();
        }
        else if(atom->atomicNumber() == highestAtomicNumber){
            candidates.push_back(atom);
        }
    }

    if(candidates.empty()){
        candidates = m_atoms;
    }

    Atom *root = 0;
    int highestNeighborCount = 0;

    foreach(Atom *atom, candidates){
        if(atom->neighborCount() > highestNeighborCount){
            root = atom;
            highestNeighborCount = atom->neighborCount();
        }
    }

    return root;
}

/// Returns the relative postion (distance around the ring) of atom to
/// root. If root is 0, the atom returned from root() is used.
int Ring::position(const Atom *atom, const Atom *root) const
{
    if(!root || !contains(root)){
        root = this->root();
    }

    int index = std::distance(m_atoms.begin(), std::find(m_atoms.begin(), m_atoms.end(), atom));

    if(index == m_atoms.size() || atom == root){
        return 0;
    }

    int size = m_atoms.size();

    for(int i = 1; i <= (size / 2); i++){
        int a1 = (index + i) % size;
        int a2 = (index - i) % size;

        if(a1 < 0) a1 += size;
        if(a2 < 0) a2 += size;

        if(m_atoms[a1] == root || m_atoms[a2] == root){
            return i;
        }
    }

    // should not get here
    return 0;
}

/// Returns \c true if the ring is fused to \p ring.
bool Ring::isFusedTo(const Ring *ring) const
{
    if(ring == this)
        return false;

    Q_FOREACH(const Bond *bond, ring->bonds()){
        if(contains(bond)){
            return true;
        }
    }

    return false;
}

// --- Aromaticity --------------------------------------------------------- //
/// Returns \c true if the ring is aromatic.
bool Ring::isAromatic() const
{
    // check for planarity of all ring atoms
    if(!isPlanar()){
        return false;
    }

    // count number of pi electrons
    int piCount = 0;

    for(int i = 0; i < size(); i++){
        const Atom *atom = m_atoms[i];
        const Bond *nextBond = this->nextBond(atom);

        // double bond
        if(nextBond->order() == Bond::Double)
            piCount += 2;

        // sp2 oxygen group p-orbital lone pair
        if((atom->is(Atom::Oxygen) || atom->is(Atom::Sulfur)) &&
           atom->neighborCount() == 2 && atom->valence() == 2){
            piCount += 2;
        }

        // sp2 nitrogen group p-orbital lone pair
        else if((atom->is(Atom::Nitrogen) || atom->is(Atom::Phosphorus) || atom->is(Atom::Arsenic)) &&
                ((atom->neighborCount() == 3 && atom->valence() == 3) ||
                 (atom->neighborCount() == 2 && atom->valence() == 2))){
            piCount += 2;
        }
    }

    // check for aromaticity using huckel's rule
    if((piCount - 2) % 4 == 0){
        return true;
    }

    // add pi electrons from exocyclic double bonds
    Q_FOREACH(const Bond *bond, exocyclicBonds()){
        if(bond->order() == Bond::Double){
            piCount += 1;
        }
    }

    // round pi electron count up to next even number if it is odd
    if(piCount & 1){
        piCount++;
    }

    // check again for aromaticity using huckel's rule
    if((piCount - 2) % 4 == 0){
        return true;
    }

    return false;
}

// --- Internal Methods ---------------------------------------------------- //
bool Ring::isValid() const
{
    if(size() < 3)
        return false;

    for(int i = 0; i < size(); i++){
        if(!m_atoms[i]->isBondedTo(m_atoms[(i+1) % size()])){
            return false;
        }
    }

    return true;
}

const Atom* Ring::nextAtom(const Atom *atom) const
{
    int index = std::distance(m_atoms.begin(), std::find(m_atoms.begin(), m_atoms.end(), atom));

    return m_atoms[(index+1) % size()];
}

const Atom* Ring::previousAtom(const Atom *atom) const
{
    int index = std::distance(m_atoms.begin(), std::find(m_atoms.begin(), m_atoms.end(), atom));

    if(index == 0)
        index = size()-1;
    else
        index = (index-1) % size();

    return m_atoms[index];
}

const Bond* Ring::nextBond(const Atom *atom) const
{
    return atom->bondTo(nextAtom(atom));
}

const Bond* Ring::previousBond(const Atom *atom) const
{
    return atom->bondTo(previousAtom(atom));
}

bool Ring::isPlanar() const
{
    foreach(const Atom *atom, m_atoms){
        if(atom->is(Atom::Carbon) && atom->neighborCount() != 3){
            return false;
        }
        else if(atom->is(Atom::Oxygen) && atom->neighborCount() != 2){
            return false;
        }
        else if(atom->is(Atom::Sulfur) && atom->neighborCount() != 2){
            return false;
        }
    }

    return true;
}

int Ring::piElectronCount() const
{
    int count = 0;

    for(int i = 0; i < size(); i++){
        const Atom *atom = m_atoms[i];
        const Bond *nextBond = this->nextBond(atom);
        const Bond *previousBond = this->previousBond(atom);

        // double bond
        if(nextBond->order() == Bond::Double)
            count += 2;

        // sp2 oxygen group p-orbital lone pair
        if((atom->is(Atom::Oxygen) || atom->is(Atom::Sulfur)) &&
           atom->neighborCount() == 2 && atom->valence() == 2){
            count += 2;
        }

        // sp2 nitrogen group p-orbital lone pair
        else if((atom->is(Atom::Nitrogen) || atom->is(Atom::Phosphorus) || atom->is(Atom::Arsenic)) &&
                ((atom->neighborCount() == 3 && atom->valence() == 3) ||
                 (atom->neighborCount() == 2 && atom->valence() == 2))){
            count += 2;
        }

        else{
            // exocyclic double bonds
            Q_FOREACH(const Bond *bond, atom->bonds()){
                if(bond == nextBond || bond == previousBond){
                    // skip ring bonds
                    continue;
                }

                if(bond->order() == Bond::Double){
                    count += 1;
                }
            }
        }
    }

    return count;
}

} // end chemkit namespace
<commit_msg>Fix unsigned comparison warning in Ring class<commit_after>/******************************************************************************
**
** Copyright (C) 2009-2011 Kyle Lutz <kyle.r.lutz@gmail.com>
**
** This file is part of chemkit. For more information see
** <http://www.chemkit.org>.
**
** chemkit is free software: you can redistribute it and/or modify
** it under the terms of the GNU Lesser General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** chemkit is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public License
** along with chemkit. If not, see <http://www.gnu.org/licenses/>.
**
******************************************************************************/

#include "ring.h"

#include <set>
#include <cassert>
#include <algorithm>

#include "atom.h"
#include "bond.h"
#include "foreach.h"
#include "molecule.h"

namespace chemkit {

// === Ring ================================================================ //
/// \class Ring ring.h chemkit/ring.h
/// \ingroup chemkit
/// \brief The Ring class represents a ring of bonded atoms in a
///        molecule.
///
/// Ring objects are returned from the various ring perception
/// methods such as Molecule::rings() and Atom::smallestRing().

// --- Construction and Destruction ---------------------------------------- //
/// Creates a new ring that contains the atoms is \p path.
Ring::Ring(std::vector<Atom *> path)
    : m_atoms(path)
{
    assert(isValid());
}

/// Destroys the ring object.
Ring::~Ring()
{
}

// --- Structure ----------------------------------------------------------- //
/// Returns the number of atoms in the ring with \p element.
int Ring::atomCount(const Element &element) const
{
    int count = 0;

    foreach(const Atom *atom, m_atoms){
        if(atom->is(element)){
            count++;
        }
    }

    return count;
}

/// Returns the bond at \p index in the ring.
Bond* Ring::bond(int index) const
{
    return bonds()[index];
}

/// Returns the bonds in the ring.
std::vector<Bond *> Ring::bonds() const
{
    std::vector<Bond *> bonds;

    for(int i = 0; i < size()-1; i++){
        bonds.push_back(m_atoms[i]->bondTo(m_atoms[i+1]));
    }

    bonds.push_back(m_atoms.front()->bondTo(m_atoms.back()));

    return bonds;
}

/// Returns the number of bonds in the ring.
int Ring::bondCount() const
{
    // number of bonds equals the number of atoms
    return m_atoms.size();
}

/// Returns a list of all bonds from atoms inside the ring
/// to atoms outside the ring.
std::vector<Bond *> Ring::exocyclicBonds() const
{
    std::set<Bond *> bondSet;

    foreach(Atom *atom, m_atoms){
        foreach(Bond *bond, atom->bonds()){
            if(!contains(bond)){
                bondSet.insert(bond);
            }
        }
    }

    std::vector<Bond *> bonds;
    foreach(Bond *bond, bondSet){
        bonds.push_back(bond);
    }

    return bonds;
}

/// Returns the number of exocyclic bonds.
int Ring::exocyclicBondCount() const
{
    return exocyclicBonds().size();
}

/// Returns the number of heteroatoms (non-carbon atoms) in the ring.
int Ring::heteroatomCount() const
{
    int count = 0;

    foreach(const Atom *atom, m_atoms){
        if(!atom->is(Atom::Carbon)){
            count++;
        }
    }

    return count;
}

/// Returns \c true if the ring contains any heteroatoms (non-carbon atoms).
bool Ring::isHeterocycle() const
{
    return heteroatomCount() > 0;
}

/// Returns the root atom of the ring. This is determined by finding
/// the non-carbon atom with the highest atomic number. In the case of
/// a tie the atom with the largest valence is returned.
Atom* Ring::root() const
{
    int highestAtomicNumber = 0;
    std::vector<Atom *> candidates;

    foreach(Atom *atom, m_atoms){
        if(atom->is(Atom::Carbon))
            continue;

        if(atom->atomicNumber() > highestAtomicNumber){
            candidates.clear();
            candidates.push_back(atom);
            highestAtomicNumber = atom->atomicNumber();
        }
        else if(atom->atomicNumber() == highestAtomicNumber){
            candidates.push_back(atom);
        }
    }

    if(candidates.empty()){
        candidates = m_atoms;
    }

    Atom *root = 0;
    int highestNeighborCount = 0;

    foreach(Atom *atom, candidates){
        if(atom->neighborCount() > highestNeighborCount){
            root = atom;
            highestNeighborCount = atom->neighborCount();
        }
    }

    return root;
}

/// Returns the relative postion (distance around the ring) of atom to
/// root. If root is 0, the atom returned from root() is used.
int Ring::position(const Atom *atom, const Atom *root) const
{
    if(!root || !contains(root)){
        root = this->root();
    }

    int size = m_atoms.size();
    int index = std::distance(m_atoms.begin(), std::find(m_atoms.begin(), m_atoms.end(), atom));

    if(index == size || atom == root){
        return 0;
    }

    for(int i = 1; i <= (size / 2); i++){
        int a1 = (index + i) % size;
        int a2 = (index - i) % size;

        if(a1 < 0) a1 += size;
        if(a2 < 0) a2 += size;

        if(m_atoms[a1] == root || m_atoms[a2] == root){
            return i;
        }
    }

    // should not get here
    return 0;
}

/// Returns \c true if the ring is fused to \p ring.
bool Ring::isFusedTo(const Ring *ring) const
{
    if(ring == this)
        return false;

    Q_FOREACH(const Bond *bond, ring->bonds()){
        if(contains(bond)){
            return true;
        }
    }

    return false;
}

// --- Aromaticity --------------------------------------------------------- //
/// Returns \c true if the ring is aromatic.
bool Ring::isAromatic() const
{
    // check for planarity of all ring atoms
    if(!isPlanar()){
        return false;
    }

    // count number of pi electrons
    int piCount = 0;

    for(int i = 0; i < size(); i++){
        const Atom *atom = m_atoms[i];
        const Bond *nextBond = this->nextBond(atom);

        // double bond
        if(nextBond->order() == Bond::Double)
            piCount += 2;

        // sp2 oxygen group p-orbital lone pair
        if((atom->is(Atom::Oxygen) || atom->is(Atom::Sulfur)) &&
           atom->neighborCount() == 2 && atom->valence() == 2){
            piCount += 2;
        }

        // sp2 nitrogen group p-orbital lone pair
        else if((atom->is(Atom::Nitrogen) || atom->is(Atom::Phosphorus) || atom->is(Atom::Arsenic)) &&
                ((atom->neighborCount() == 3 && atom->valence() == 3) ||
                 (atom->neighborCount() == 2 && atom->valence() == 2))){
            piCount += 2;
        }
    }

    // check for aromaticity using huckel's rule
    if((piCount - 2) % 4 == 0){
        return true;
    }

    // add pi electrons from exocyclic double bonds
    Q_FOREACH(const Bond *bond, exocyclicBonds()){
        if(bond->order() == Bond::Double){
            piCount += 1;
        }
    }

    // round pi electron count up to next even number if it is odd
    if(piCount & 1){
        piCount++;
    }

    // check again for aromaticity using huckel's rule
    if((piCount - 2) % 4 == 0){
        return true;
    }

    return false;
}

// --- Internal Methods ---------------------------------------------------- //
bool Ring::isValid() const
{
    if(size() < 3)
        return false;

    for(int i = 0; i < size(); i++){
        if(!m_atoms[i]->isBondedTo(m_atoms[(i+1) % size()])){
            return false;
        }
    }

    return true;
}

const Atom* Ring::nextAtom(const Atom *atom) const
{
    int index = std::distance(m_atoms.begin(), std::find(m_atoms.begin(), m_atoms.end(), atom));

    return m_atoms[(index+1) % size()];
}

const Atom* Ring::previousAtom(const Atom *atom) const
{
    int index = std::distance(m_atoms.begin(), std::find(m_atoms.begin(), m_atoms.end(), atom));

    if(index == 0)
        index = size()-1;
    else
        index = (index-1) % size();

    return m_atoms[index];
}

const Bond* Ring::nextBond(const Atom *atom) const
{
    return atom->bondTo(nextAtom(atom));
}

const Bond* Ring::previousBond(const Atom *atom) const
{
    return atom->bondTo(previousAtom(atom));
}

bool Ring::isPlanar() const
{
    foreach(const Atom *atom, m_atoms){
        if(atom->is(Atom::Carbon) && atom->neighborCount() != 3){
            return false;
        }
        else if(atom->is(Atom::Oxygen) && atom->neighborCount() != 2){
            return false;
        }
        else if(atom->is(Atom::Sulfur) && atom->neighborCount() != 2){
            return false;
        }
    }

    return true;
}

int Ring::piElectronCount() const
{
    int count = 0;

    for(int i = 0; i < size(); i++){
        const Atom *atom = m_atoms[i];
        const Bond *nextBond = this->nextBond(atom);
        const Bond *previousBond = this->previousBond(atom);

        // double bond
        if(nextBond->order() == Bond::Double)
            count += 2;

        // sp2 oxygen group p-orbital lone pair
        if((atom->is(Atom::Oxygen) || atom->is(Atom::Sulfur)) &&
           atom->neighborCount() == 2 && atom->valence() == 2){
            count += 2;
        }

        // sp2 nitrogen group p-orbital lone pair
        else if((atom->is(Atom::Nitrogen) || atom->is(Atom::Phosphorus) || atom->is(Atom::Arsenic)) &&
                ((atom->neighborCount() == 3 && atom->valence() == 3) ||
                 (atom->neighborCount() == 2 && atom->valence() == 2))){
            count += 2;
        }

        else{
            // exocyclic double bonds
            Q_FOREACH(const Bond *bond, atom->bonds()){
                if(bond == nextBond || bond == previousBond){
                    // skip ring bonds
                    continue;
                }

                if(bond->order() == Bond::Double){
                    count += 1;
                }
            }
        }
    }

    return count;
}

} // end chemkit namespace
<|endoftext|>


<commit_before>/* Copyright 2015 Stanford University, NVIDIA Corporation
 *
 * 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.
 */

// Realm logging infrastructure

#include "logging.h"

#ifdef SHARED_LOWLEVEL
#define gasnet_mynode() 0
#define gasnet_nodes() 1
#else
#include "activemsg.h"
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>

#include <set>

namespace Realm {

  // abstract class for an output stream
  class LoggerOutputStream {
  public:
    LoggerOutputStream(void) {}
    virtual ~LoggerOutputStream(void) {}

    virtual void write(const char *buffer, size_t len) = 0;
    virtual void flush(void) = 0;
  };

  class LoggerFileStream : public LoggerOutputStream {
  public:
    LoggerFileStream(FILE *_f, bool _close_file)
      : f(_f), close_file(_close_file)
    {}

    virtual ~LoggerFileStream(void)
    {
      if(close_file)
	fclose(f);
    }

    virtual void write(const char *buffer, size_t len)
    {
#ifndef NDEBUG
      size_t amt =
#endif
	fwrite(buffer, 1, len, f);
      assert(amt == len);
    }

    virtual void flush(void)
    {
      fflush(f);
    }

  protected:
    FILE *f;
    bool close_file;
  };

  // use a pthread mutex to prevent simultaneous writes to a stream
  template <typename T>
  class LoggerStreamSerialized : public LoggerOutputStream {
  public:
    LoggerStreamSerialized(T *_stream, bool _delete_inner)
      : stream(_stream), delete_inner(_delete_inner)
    {
#ifndef NDEBUG
      int ret =
#endif
	pthread_mutex_init(&mutex, 0);
      assert(ret == 0);
    }

    virtual ~LoggerStreamSerialized(void)
    {
#ifndef NDEBUG
      int ret =
#endif
	pthread_mutex_destroy(&mutex);
      assert(ret == 0);
      if(delete_inner)
	delete stream;
    }

    virtual void write(const char *buffer, size_t len)
    {
#ifndef NDEBUG
      int ret;
      ret =
#endif
	pthread_mutex_lock(&mutex);
      assert(ret == 0);
      stream->write(buffer, len);
#ifndef NDEBUG
      ret =
#endif
	pthread_mutex_unlock(&mutex);
      assert(ret == 0);
    }

    virtual void flush(void)
    {
#ifndef NDEBUG
      int ret;
      ret =
#endif
	pthread_mutex_lock(&mutex);
      assert(ret == 0);
      stream->flush();
#ifndef NDEBUG
      ret =
#endif
	pthread_mutex_unlock(&mutex);
      assert(ret == 0);
    }

  protected:
    LoggerOutputStream *stream;
    bool delete_inner;
    pthread_mutex_t mutex;
  };

  class LoggerConfig {
  protected:
    LoggerConfig(void);
    ~LoggerConfig(void);

  public:
    static LoggerConfig *get_config(void);

    static void flush_all_streams(void);

    void read_command_line(int argc, const char *argv[]);

    // either configures a logger right away or remembers it to config once
    //   we know the desired settings
    void configure(Logger *logger);

  protected:
    bool cmdline_read;
    Logger::LoggingLevel level;
    char *cats_enabled;
    std::set<Logger *> pending_configs;
    LoggerOutputStream *stream;
  };

  LoggerConfig::LoggerConfig(void)
    : cmdline_read(false), level(Logger::LEVEL_PRINT), cats_enabled(0), stream(0)
  {}

  LoggerConfig::~LoggerConfig(void)
  {
    if(cats_enabled)
      free(cats_enabled);

    delete stream;
  }

  /*static*/ LoggerConfig *LoggerConfig::get_config(void)
  {
    static LoggerConfig cfg;
    return &cfg;
  }

  /*static*/ void LoggerConfig::flush_all_streams(void)
  {
    LoggerConfig *cfg = get_config();
    if(cfg->stream)
      cfg->stream->flush();
  }

  void LoggerConfig::read_command_line(int argc, const char *argv[])
  {
    const char *logname = 0;

    for(int i = 1; i < argc-1; i++) {
      if(!strcmp(argv[i], "-level")) {
	int l = atoi(argv[++i]);
	assert((l >= 0) && (l <= Logger::LEVEL_NONE));
	level = (Logger::LoggingLevel)l;
	continue;
      }

      if(!strcmp(argv[i], "-cat")) {
	if(cats_enabled)
	  free(cats_enabled);
	cats_enabled = strdup(argv[++i]);
	continue;
      }

      if(!strcmp(argv[i], "-logfile")) {
	logname = argv[++i];
	continue;
      }
    }

    // lots of choices for log output
    if(!logname || !strcmp(logname, "stdout")) {
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(stdout, false),
							    true);
    } else if(!strcmp(logname, "stderr")) {
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(stderr, false),
							    true);
    } else {
      // we're going to open a file, but key off a + for appending and
      //  look for a % for node number insertion
      bool append = false;

      if(*logname == '+') {
	append = true;
	logname++;
      }

      const char *pos = strchr(logname, '%');
      FILE *f = 0;
      if(pos == 0) {
	// no node number - everybody uses the same file
	if(gasnet_nodes() > 1) {
	  if(!append) {
	    if(gasnet_mynode() == 1)
	      fprintf(stderr, "WARNING: all ranks are logging to the same output file - appending is forced and output may be jumbled\n");
	    append = true;
	  }
	}
	f = fopen(logname, append ? "a" : "w");
	if(!f) {
	  fprintf(stderr, "could not open log file '%s': %s\n", logname, strerror(errno));
	  exit(1);
	}
      } else {
	// replace % with node number
	char filename[256];
	sprintf(filename, "%.*s%d%s", (int)(pos - logname), logname, gasnet_mynode(), pos+1);

	f = fopen(filename, append ? "a" : "w");
	if(!f) {
	  fprintf(stderr, "could not open log file '%s': %s\n", filename, strerror(errno));
	  exit(1);
	}
      }
      // TODO: consider buffering in some cases?
      setbuf(f, 0); // disable output buffering
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(f, true),
							    true);
    }

    atexit(LoggerConfig::flush_all_streams);

    cmdline_read = true;
    if(!pending_configs.empty()) {
      for(std::set<Logger *>::iterator it = pending_configs.begin();
	  it != pending_configs.end();
	  it++)
	configure(*it);
      pending_configs.clear();
    }
  }

  void LoggerConfig::configure(Logger *logger)
  {
    // if we haven't read the command line yet, remember this for later
    if(!cmdline_read) {
      pending_configs.insert(logger);
      return;
    }

    // see if this logger is one of the categories we want
    if(cats_enabled && (cats_enabled[0] != '*')) {
      bool found = false;
      const char *p = cats_enabled;
      int l = logger->get_name().length();
      const char *n = logger->get_name().c_str();
      while(*p) {
	if(((p[l] == '\0') || (p[l] == ',')) && !strcmp(p, n)) {
	  found = true;
	  break;
	}
	// skip to after next comma
	while(*p && (*p != ',')) p++;
	while(*p && (*p == ',')) p++;
      }
      if(!found) {
	//printf("'%s' not in '%s'\n", n, cats_enabled);
	return;
      }
    }

    // give this logger a copy of the global stream
    logger->add_stream(stream, level,
		       false,  /* don't delete */
		       false); /* don't flush each write */
  }

  ////////////////////////////////////////////////////////////////////////
  //
  // class Logger

  Logger::Logger(const std::string& _name)
    : name(_name), log_level(LEVEL_NONE)
  {
    LoggerConfig::get_config()->configure(this);
  }

  Logger::~Logger(void)
  {
    // go through our streams and delete any we're supposed to
    for(std::vector<LogStream>::iterator it = streams.begin();
	it != streams.end();
	it++)
      if(it->delete_when_done)
	delete it->s;

    streams.clear();
  }

  /*static*/ void Logger::configure_from_cmdline(int argc, const char *argv[])
  {
    LoggerConfig::get_config()->read_command_line(argc, argv);
  }

  void Logger::log_msg(LoggingLevel level, const std::string& msg)
  {
    // no logging of empty messages
    if(msg.length() == 0)
      return;

    // build message string, including prefix
    static const int MAXLEN = 4096;
    char buffer[MAXLEN];
    int len = snprintf(buffer, MAXLEN - 2, "[%d - %lx] {%d}{%s}: ",
		       gasnet_mynode(), (unsigned long)pthread_self(),
		       level, name.c_str());
    int amt = msg.length();
    if((len + amt) >= MAXLEN)
      amt = MAXLEN - 2 - len;
    memcpy(buffer + len, msg.data(), amt);
    len += amt;
    buffer[len++] = '\n';
    buffer[len] = 0;

    // go through all the streams
    for(std::vector<LogStream>::iterator it = streams.begin();
	it != streams.end();
	it++) {
      if(level < it->min_level)
	continue;

      it->s->write(buffer, len);

      if(it->flush_each_write)
	it->s->flush();
    }
  }

  void Logger::add_stream(LoggerOutputStream *s, LoggingLevel min_level,
			  bool delete_when_done, bool flush_each_write)
  {
    LogStream ls;
    ls.s = s;
    ls.min_level = min_level;
    ls.delete_when_done = delete_when_done;
    ls.flush_each_write = flush_each_write;
    streams.push_back(ls);

    // update our logging level if needed
    if(log_level > min_level)
      log_level = min_level;
  }

  ////////////////////////////////////////////////////////////////////////
  //
  // class LoggerMessage

  LoggerMessage& LoggerMessage::vprintf(const char *fmt, va_list args)
  {
    if(active) {
      char msg[256];
      vsnprintf(msg, 256, fmt, args);
      oss << msg;
    }
    return *this;
  }

}; // namespace Realm
<commit_msg>realm: making mutliple logging categories actually work<commit_after>/* Copyright 2015 Stanford University, NVIDIA Corporation
 *
 * 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.
 */

// Realm logging infrastructure

#include "logging.h"

#ifdef SHARED_LOWLEVEL
#define gasnet_mynode() 0
#define gasnet_nodes() 1
#else
#include "activemsg.h"
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>

#include <set>

namespace Realm {

  // abstract class for an output stream
  class LoggerOutputStream {
  public:
    LoggerOutputStream(void) {}
    virtual ~LoggerOutputStream(void) {}

    virtual void write(const char *buffer, size_t len) = 0;
    virtual void flush(void) = 0;
  };

  class LoggerFileStream : public LoggerOutputStream {
  public:
    LoggerFileStream(FILE *_f, bool _close_file)
      : f(_f), close_file(_close_file)
    {}

    virtual ~LoggerFileStream(void)
    {
      if(close_file)
	fclose(f);
    }

    virtual void write(const char *buffer, size_t len)
    {
#ifndef NDEBUG
      size_t amt =
#endif
	fwrite(buffer, 1, len, f);
      assert(amt == len);
    }

    virtual void flush(void)
    {
      fflush(f);
    }

  protected:
    FILE *f;
    bool close_file;
  };

  // use a pthread mutex to prevent simultaneous writes to a stream
  template <typename T>
  class LoggerStreamSerialized : public LoggerOutputStream {
  public:
    LoggerStreamSerialized(T *_stream, bool _delete_inner)
      : stream(_stream), delete_inner(_delete_inner)
    {
#ifndef NDEBUG
      int ret =
#endif
	pthread_mutex_init(&mutex, 0);
      assert(ret == 0);
    }

    virtual ~LoggerStreamSerialized(void)
    {
#ifndef NDEBUG
      int ret =
#endif
	pthread_mutex_destroy(&mutex);
      assert(ret == 0);
      if(delete_inner)
	delete stream;
    }

    virtual void write(const char *buffer, size_t len)
    {
#ifndef NDEBUG
      int ret;
      ret =
#endif
	pthread_mutex_lock(&mutex);
      assert(ret == 0);
      stream->write(buffer, len);
#ifndef NDEBUG
      ret =
#endif
	pthread_mutex_unlock(&mutex);
      assert(ret == 0);
    }

    virtual void flush(void)
    {
#ifndef NDEBUG
      int ret;
      ret =
#endif
	pthread_mutex_lock(&mutex);
      assert(ret == 0);
      stream->flush();
#ifndef NDEBUG
      ret =
#endif
	pthread_mutex_unlock(&mutex);
      assert(ret == 0);
    }

  protected:
    LoggerOutputStream *stream;
    bool delete_inner;
    pthread_mutex_t mutex;
  };

  class LoggerConfig {
  protected:
    LoggerConfig(void);
    ~LoggerConfig(void);

  public:
    static LoggerConfig *get_config(void);

    static void flush_all_streams(void);

    void read_command_line(int argc, const char *argv[]);

    // either configures a logger right away or remembers it to config once
    //   we know the desired settings
    void configure(Logger *logger);

  protected:
    bool cmdline_read;
    Logger::LoggingLevel level;
    char *cats_enabled;
    std::set<Logger *> pending_configs;
    LoggerOutputStream *stream;
  };

  LoggerConfig::LoggerConfig(void)
    : cmdline_read(false), level(Logger::LEVEL_PRINT), cats_enabled(0), stream(0)
  {}

  LoggerConfig::~LoggerConfig(void)
  {
    if(cats_enabled)
      free(cats_enabled);

    delete stream;
  }

  /*static*/ LoggerConfig *LoggerConfig::get_config(void)
  {
    static LoggerConfig cfg;
    return &cfg;
  }

  /*static*/ void LoggerConfig::flush_all_streams(void)
  {
    LoggerConfig *cfg = get_config();
    if(cfg->stream)
      cfg->stream->flush();
  }

  void LoggerConfig::read_command_line(int argc, const char *argv[])
  {
    const char *logname = 0;

    for(int i = 1; i < argc-1; i++) {
      if(!strcmp(argv[i], "-level")) {
	int l = atoi(argv[++i]);
	assert((l >= 0) && (l <= Logger::LEVEL_NONE));
	level = (Logger::LoggingLevel)l;
	continue;
      }

      if(!strcmp(argv[i], "-cat")) {
	if(cats_enabled)
	  free(cats_enabled);
	cats_enabled = strdup(argv[++i]);
	continue;
      }

      if(!strcmp(argv[i], "-logfile")) {
	logname = argv[++i];
	continue;
      }
    }

    // lots of choices for log output
    if(!logname || !strcmp(logname, "stdout")) {
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(stdout, false),
							    true);
    } else if(!strcmp(logname, "stderr")) {
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(stderr, false),
							    true);
    } else {
      // we're going to open a file, but key off a + for appending and
      //  look for a % for node number insertion
      bool append = false;

      if(*logname == '+') {
	append = true;
	logname++;
      }

      const char *pos = strchr(logname, '%');
      FILE *f = 0;
      if(pos == 0) {
	// no node number - everybody uses the same file
	if(gasnet_nodes() > 1) {
	  if(!append) {
	    if(gasnet_mynode() == 1)
	      fprintf(stderr, "WARNING: all ranks are logging to the same output file - appending is forced and output may be jumbled\n");
	    append = true;
	  }
	}
	f = fopen(logname, append ? "a" : "w");
	if(!f) {
	  fprintf(stderr, "could not open log file '%s': %s\n", logname, strerror(errno));
	  exit(1);
	}
      } else {
	// replace % with node number
	char filename[256];
	sprintf(filename, "%.*s%d%s", (int)(pos - logname), logname, gasnet_mynode(), pos+1);

	f = fopen(filename, append ? "a" : "w");
	if(!f) {
	  fprintf(stderr, "could not open log file '%s': %s\n", filename, strerror(errno));
	  exit(1);
	}
      }
      // TODO: consider buffering in some cases?
      setbuf(f, 0); // disable output buffering
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(f, true),
							    true);
    }

    atexit(LoggerConfig::flush_all_streams);

    cmdline_read = true;
    if(!pending_configs.empty()) {
      for(std::set<Logger *>::iterator it = pending_configs.begin();
	  it != pending_configs.end();
	  it++)
	configure(*it);
      pending_configs.clear();
    }
  }

  void LoggerConfig::configure(Logger *logger)
  {
    // if we haven't read the command line yet, remember this for later
    if(!cmdline_read) {
      pending_configs.insert(logger);
      return;
    }

    // see if this logger is one of the categories we want
    if(cats_enabled && (cats_enabled[0] != '*')) {
      bool found = false;
      const char *p = cats_enabled;
      int l = logger->get_name().length();
      const char *n = logger->get_name().c_str();
      while(*p) {
	if(((p[l] == '\0') || (p[l] == ',')) && !strncmp(p, n, l)) {
	  found = true;
	  break;
	}
	// skip to after next comma
	while(*p && (*p != ',')) p++;
	while(*p && (*p == ',')) p++;
      }
      if(!found) {
	//printf("'%s' not in '%s'\n", n, cats_enabled);
	return;
      }
    }

    // give this logger a copy of the global stream
    logger->add_stream(stream, level,
		       false,  /* don't delete */
		       false); /* don't flush each write */
  }

  ////////////////////////////////////////////////////////////////////////
  //
  // class Logger

  Logger::Logger(const std::string& _name)
    : name(_name), log_level(LEVEL_NONE)
  {
    LoggerConfig::get_config()->configure(this);
  }

  Logger::~Logger(void)
  {
    // go through our streams and delete any we're supposed to
    for(std::vector<LogStream>::iterator it = streams.begin();
	it != streams.end();
	it++)
      if(it->delete_when_done)
	delete it->s;

    streams.clear();
  }

  /*static*/ void Logger::configure_from_cmdline(int argc, const char *argv[])
  {
    LoggerConfig::get_config()->read_command_line(argc, argv);
  }

  void Logger::log_msg(LoggingLevel level, const std::string& msg)
  {
    // no logging of empty messages
    if(msg.length() == 0)
      return;

    // build message string, including prefix
    static const int MAXLEN = 4096;
    char buffer[MAXLEN];
    int len = snprintf(buffer, MAXLEN - 2, "[%d - %lx] {%d}{%s}: ",
		       gasnet_mynode(), (unsigned long)pthread_self(),
		       level, name.c_str());
    int amt = msg.length();
    if((len + amt) >= MAXLEN)
      amt = MAXLEN - 2 - len;
    memcpy(buffer + len, msg.data(), amt);
    len += amt;
    buffer[len++] = '\n';
    buffer[len] = 0;

    // go through all the streams
    for(std::vector<LogStream>::iterator it = streams.begin();
	it != streams.end();
	it++) {
      if(level < it->min_level)
	continue;

      it->s->write(buffer, len);

      if(it->flush_each_write)
	it->s->flush();
    }
  }

  void Logger::add_stream(LoggerOutputStream *s, LoggingLevel min_level,
			  bool delete_when_done, bool flush_each_write)
  {
    LogStream ls;
    ls.s = s;
    ls.min_level = min_level;
    ls.delete_when_done = delete_when_done;
    ls.flush_each_write = flush_each_write;
    streams.push_back(ls);

    // update our logging level if needed
    if(log_level > min_level)
      log_level = min_level;
  }

  ////////////////////////////////////////////////////////////////////////
  //
  // class LoggerMessage

  LoggerMessage& LoggerMessage::vprintf(const char *fmt, va_list args)
  {
    if(active) {
      char msg[256];
      vsnprintf(msg, 256, fmt, args);
      oss << msg;
    }
    return *this;
  }

}; // namespace Realm
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * 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.
 */

#include "runtime.h"

#include <signal.h>
#include <string.h>
#include <sys/utsname.h>

#include "base/logging.h"
#include "base/mutex.h"
#include "base/stringprintf.h"
#include "thread.h"
#include "utils.h"

namespace art {

struct Backtrace {
  void Dump(std::ostream& os) {
    DumpNativeStack(os, GetTid(), "\t", true);
  }
};

struct OsInfo {
  void Dump(std::ostream& os) {
    utsname info;
    uname(&info);
    // Linux 2.6.38.8-gg784 (x86_64)
    // Darwin 11.4.0 (x86_64)
    os << info.sysname << " " << info.release << " (" << info.machine << ")";
  }
};

static const char* GetSignalName(int signal_number) {
  switch (signal_number) {
    case SIGABRT: return "SIGABRT";
    case SIGBUS: return "SIGBUS";
    case SIGFPE: return "SIGFPE";
    case SIGILL: return "SIGILL";
    case SIGPIPE: return "SIGPIPE";
    case SIGSEGV: return "SIGSEGV";
#if defined(SIGSTKFLT)
    case SIGSTKFLT: return "SIGSTKFLT";
#endif
    case SIGTRAP: return "SIGTRAP";
  }
  return "??";
}

static const char* GetSignalCodeName(int signal_number, int signal_code) {
  // Try the signal-specific codes...
  switch (signal_number) {
    case SIGILL:
      switch (signal_code) {
        case ILL_ILLOPC: return "ILL_ILLOPC";
        case ILL_ILLOPN: return "ILL_ILLOPN";
        case ILL_ILLADR: return "ILL_ILLADR";
        case ILL_ILLTRP: return "ILL_ILLTRP";
        case ILL_PRVOPC: return "ILL_PRVOPC";
        case ILL_PRVREG: return "ILL_PRVREG";
        case ILL_COPROC: return "ILL_COPROC";
        case ILL_BADSTK: return "ILL_BADSTK";
      }
      break;
    case SIGBUS:
      switch (signal_code) {
        case BUS_ADRALN: return "BUS_ADRALN";
        case BUS_ADRERR: return "BUS_ADRERR";
        case BUS_OBJERR: return "BUS_OBJERR";
      }
      break;
    case SIGFPE:
      switch (signal_code) {
        case FPE_INTDIV: return "FPE_INTDIV";
        case FPE_INTOVF: return "FPE_INTOVF";
        case FPE_FLTDIV: return "FPE_FLTDIV";
        case FPE_FLTOVF: return "FPE_FLTOVF";
        case FPE_FLTUND: return "FPE_FLTUND";
        case FPE_FLTRES: return "FPE_FLTRES";
        case FPE_FLTINV: return "FPE_FLTINV";
        case FPE_FLTSUB: return "FPE_FLTSUB";
      }
      break;
    case SIGSEGV:
      switch (signal_code) {
        case SEGV_MAPERR: return "SEGV_MAPERR";
        case SEGV_ACCERR: return "SEGV_ACCERR";
      }
      break;
    case SIGTRAP:
      switch (signal_code) {
        case TRAP_BRKPT: return "TRAP_BRKPT";
        case TRAP_TRACE: return "TRAP_TRACE";
      }
      break;
  }
  // Then the other codes...
  switch (signal_code) {
    case SI_USER:     return "SI_USER";
#if defined(SI_KERNEL)
    case SI_KERNEL:   return "SI_KERNEL";
#endif
    case SI_QUEUE:    return "SI_QUEUE";
    case SI_TIMER:    return "SI_TIMER";
    case SI_MESGQ:    return "SI_MESGQ";
    case SI_ASYNCIO:  return "SI_ASYNCIO";
#if defined(SI_SIGIO)
    case SI_SIGIO:    return "SI_SIGIO";
#endif
#if defined(SI_TKILL)
    case SI_TKILL:    return "SI_TKILL";
#endif
  }
  // Then give up...
  return "?";
}

struct UContext {
  explicit UContext(void* raw_context) : context(reinterpret_cast<ucontext_t*>(raw_context)->uc_mcontext) {}

  void Dump(std::ostream& os) {
    // TODO: support non-x86 hosts (not urgent because this code doesn't run on targets).
#if defined(__APPLE__) && defined(__i386__)
    DumpRegister32(os, "eax", context->__ss.__eax);
    DumpRegister32(os, "ebx", context->__ss.__ebx);
    DumpRegister32(os, "ecx", context->__ss.__ecx);
    DumpRegister32(os, "edx", context->__ss.__edx);
    os << '\n';

    DumpRegister32(os, "edi", context->__ss.__edi);
    DumpRegister32(os, "esi", context->__ss.__esi);
    DumpRegister32(os, "ebp", context->__ss.__ebp);
    DumpRegister32(os, "esp", context->__ss.__esp);
    os << '\n';

    DumpRegister32(os, "eip", context->__ss.__eip);
    os << "                   ";
    DumpRegister32(os, "eflags", context->__ss.__eflags);
    DumpX86Flags(os, context->__ss.__eflags);
    os << '\n';

    DumpRegister32(os, "cs",  context->__ss.__cs);
    DumpRegister32(os, "ds",  context->__ss.__ds);
    DumpRegister32(os, "es",  context->__ss.__es);
    DumpRegister32(os, "fs",  context->__ss.__fs);
    os << '\n';
    DumpRegister32(os, "gs",  context->__ss.__gs);
    DumpRegister32(os, "ss",  context->__ss.__ss);
#elif defined(__linux__) && defined(__i386__)
    DumpRegister32(os, "eax", context.gregs[REG_EAX]);
    DumpRegister32(os, "ebx", context.gregs[REG_EBX]);
    DumpRegister32(os, "ecx", context.gregs[REG_ECX]);
    DumpRegister32(os, "edx", context.gregs[REG_EDX]);
    os << '\n';

    DumpRegister32(os, "edi", context.gregs[REG_EDI]);
    DumpRegister32(os, "esi", context.gregs[REG_ESI]);
    DumpRegister32(os, "ebp", context.gregs[REG_EBP]);
    DumpRegister32(os, "esp", context.gregs[REG_ESP]);
    os << '\n';

    DumpRegister32(os, "eip", context.gregs[REG_EIP]);
    os << "                   ";
    DumpRegister32(os, "eflags", context.gregs[REG_EFL]);
    DumpX86Flags(os, context.gregs[REG_EFL]);
    os << '\n';

    DumpRegister32(os, "cs",  context.gregs[REG_CS]);
    DumpRegister32(os, "ds",  context.gregs[REG_DS]);
    DumpRegister32(os, "es",  context.gregs[REG_ES]);
    DumpRegister32(os, "fs",  context.gregs[REG_FS]);
    os << '\n';
    DumpRegister32(os, "gs",  context.gregs[REG_GS]);
    DumpRegister32(os, "ss",  context.gregs[REG_SS]);
#else
    os << "Unknown architecture/word size/OS in ucontext dump";
#endif
  }

  void DumpRegister32(std::ostream& os, const char* name, uint32_t value) {
    os << StringPrintf(" %6s: 0x%08x", name, value);
  }

  void DumpX86Flags(std::ostream& os, uint32_t flags) {
    os << " [";
    if ((flags & (1 << 0)) != 0) {
      os << " CF";
    }
    if ((flags & (1 << 2)) != 0) {
      os << " PF";
    }
    if ((flags & (1 << 4)) != 0) {
      os << " AF";
    }
    if ((flags & (1 << 6)) != 0) {
      os << " ZF";
    }
    if ((flags & (1 << 7)) != 0) {
      os << " SF";
    }
    if ((flags & (1 << 8)) != 0) {
      os << " TF";
    }
    if ((flags & (1 << 9)) != 0) {
      os << " IF";
    }
    if ((flags & (1 << 10)) != 0) {
      os << " DF";
    }
    if ((flags & (1 << 11)) != 0) {
      os << " OF";
    }
    os << " ]";
  }

  mcontext_t& context;
};

void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context) {
  static bool handlingUnexpectedSignal = false;
  if (handlingUnexpectedSignal) {
    LogMessageData data(__FILE__, __LINE__, INTERNAL_FATAL, -1);
    LogMessage::LogLine(data, "HandleUnexpectedSignal reentered\n");
    _exit(1);
  }
  handlingUnexpectedSignal = true;

  gAborting++;  // set before taking any locks
  MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_);

  bool has_address = (signal_number == SIGILL || signal_number == SIGBUS ||
                      signal_number == SIGFPE || signal_number == SIGSEGV);

  OsInfo os_info;
  const char* cmd_line = GetCmdLine();
  if (cmd_line == NULL) {
    cmd_line = "<unset>";  // Because no-one called InitLogging.
  }
  pid_t tid = GetTid();
  std::string thread_name(GetThreadName(tid));
  UContext thread_context(raw_context);
  Backtrace thread_backtrace;

  LOG(INTERNAL_FATAL) << "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"
                      << StringPrintf("Fatal signal %d (%s), code %d (%s)",
                                      signal_number, GetSignalName(signal_number),
                                      info->si_code,
                                      GetSignalCodeName(signal_number, info->si_code))
                      << (has_address ? StringPrintf(" fault addr %p", info->si_addr) : "") << "\n"
                      << "OS: " << Dumpable<OsInfo>(os_info) << "\n"
                      << "Cmdline: " << cmd_line << "\n"
                      << "Thread: " << tid << " \"" << thread_name << "\"\n"
                      << "Registers:\n" << Dumpable<UContext>(thread_context) << "\n"
                      << "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace);

  if (getenv("debug_db_uid") != NULL || getenv("art_wait_for_gdb_on_crash") != NULL) {
    LOG(INTERNAL_FATAL) << "********************************************************\n"
                        << "* Process " << getpid() << " thread " << tid << " \"" << thread_name << "\""
                        << " has been suspended while crashing.\n"
                        << "* Attach gdb:\n"
                        << "*     gdb -p " << tid << "\n"
                        << "********************************************************\n";
    // Wait for debugger to attach.
    while (true) {
    }
  }

  // Remove our signal handler for this signal...
  struct sigaction action;
  memset(&action, 0, sizeof(action));
  sigemptyset(&action.sa_mask);
  action.sa_handler = SIG_DFL;
  sigaction(signal_number, &action, NULL);
  // ...and re-raise so we die with the appropriate status.
  kill(getpid(), signal_number);
}

void Runtime::InitPlatformSignalHandlers() {
  // On the host, we don't have debuggerd to dump a stack for us when something unexpected happens.
  struct sigaction action;
  memset(&action, 0, sizeof(action));
  sigemptyset(&action.sa_mask);
  action.sa_sigaction = HandleUnexpectedSignal;
  // Use the three-argument sa_sigaction handler.
  action.sa_flags |= SA_SIGINFO;
  // Use the alternate signal stack so we can catch stack overflows.
  action.sa_flags |= SA_ONSTACK;

  int rc = 0;
  rc += sigaction(SIGABRT, &action, NULL);
  rc += sigaction(SIGBUS, &action, NULL);
  rc += sigaction(SIGFPE, &action, NULL);
  rc += sigaction(SIGILL, &action, NULL);
  rc += sigaction(SIGPIPE, &action, NULL);
  rc += sigaction(SIGSEGV, &action, NULL);
#if defined(SIGSTKFLT)
  rc += sigaction(SIGSTKFLT, &action, NULL);
#endif
  rc += sigaction(SIGTRAP, &action, NULL);
  CHECK_EQ(rc, 0);
}

}  // namespace art
<commit_msg>am 64ddbc60: am 0a07f939: Merge "art: 64-bit support in UContext::Dump"<commit_after>/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * 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.
 */

#include "runtime.h"

#include <signal.h>
#include <string.h>
#include <sys/utsname.h>
#include <inttypes.h>

#include "base/logging.h"
#include "base/mutex.h"
#include "base/stringprintf.h"
#include "thread.h"
#include "utils.h"

namespace art {

struct Backtrace {
  void Dump(std::ostream& os) {
    DumpNativeStack(os, GetTid(), "\t", true);
  }
};

struct OsInfo {
  void Dump(std::ostream& os) {
    utsname info;
    uname(&info);
    // Linux 2.6.38.8-gg784 (x86_64)
    // Darwin 11.4.0 (x86_64)
    os << info.sysname << " " << info.release << " (" << info.machine << ")";
  }
};

static const char* GetSignalName(int signal_number) {
  switch (signal_number) {
    case SIGABRT: return "SIGABRT";
    case SIGBUS: return "SIGBUS";
    case SIGFPE: return "SIGFPE";
    case SIGILL: return "SIGILL";
    case SIGPIPE: return "SIGPIPE";
    case SIGSEGV: return "SIGSEGV";
#if defined(SIGSTKFLT)
    case SIGSTKFLT: return "SIGSTKFLT";
#endif
    case SIGTRAP: return "SIGTRAP";
  }
  return "??";
}

static const char* GetSignalCodeName(int signal_number, int signal_code) {
  // Try the signal-specific codes...
  switch (signal_number) {
    case SIGILL:
      switch (signal_code) {
        case ILL_ILLOPC: return "ILL_ILLOPC";
        case ILL_ILLOPN: return "ILL_ILLOPN";
        case ILL_ILLADR: return "ILL_ILLADR";
        case ILL_ILLTRP: return "ILL_ILLTRP";
        case ILL_PRVOPC: return "ILL_PRVOPC";
        case ILL_PRVREG: return "ILL_PRVREG";
        case ILL_COPROC: return "ILL_COPROC";
        case ILL_BADSTK: return "ILL_BADSTK";
      }
      break;
    case SIGBUS:
      switch (signal_code) {
        case BUS_ADRALN: return "BUS_ADRALN";
        case BUS_ADRERR: return "BUS_ADRERR";
        case BUS_OBJERR: return "BUS_OBJERR";
      }
      break;
    case SIGFPE:
      switch (signal_code) {
        case FPE_INTDIV: return "FPE_INTDIV";
        case FPE_INTOVF: return "FPE_INTOVF";
        case FPE_FLTDIV: return "FPE_FLTDIV";
        case FPE_FLTOVF: return "FPE_FLTOVF";
        case FPE_FLTUND: return "FPE_FLTUND";
        case FPE_FLTRES: return "FPE_FLTRES";
        case FPE_FLTINV: return "FPE_FLTINV";
        case FPE_FLTSUB: return "FPE_FLTSUB";
      }
      break;
    case SIGSEGV:
      switch (signal_code) {
        case SEGV_MAPERR: return "SEGV_MAPERR";
        case SEGV_ACCERR: return "SEGV_ACCERR";
      }
      break;
    case SIGTRAP:
      switch (signal_code) {
        case TRAP_BRKPT: return "TRAP_BRKPT";
        case TRAP_TRACE: return "TRAP_TRACE";
      }
      break;
  }
  // Then the other codes...
  switch (signal_code) {
    case SI_USER:     return "SI_USER";
#if defined(SI_KERNEL)
    case SI_KERNEL:   return "SI_KERNEL";
#endif
    case SI_QUEUE:    return "SI_QUEUE";
    case SI_TIMER:    return "SI_TIMER";
    case SI_MESGQ:    return "SI_MESGQ";
    case SI_ASYNCIO:  return "SI_ASYNCIO";
#if defined(SI_SIGIO)
    case SI_SIGIO:    return "SI_SIGIO";
#endif
#if defined(SI_TKILL)
    case SI_TKILL:    return "SI_TKILL";
#endif
  }
  // Then give up...
  return "?";
}

struct UContext {
  explicit UContext(void* raw_context) : context(reinterpret_cast<ucontext_t*>(raw_context)->uc_mcontext) {}

  void Dump(std::ostream& os) {
    // TODO: support non-x86 hosts (not urgent because this code doesn't run on targets).
#if defined(__APPLE__) && defined(__i386__)
    DumpRegister32(os, "eax", context->__ss.__eax);
    DumpRegister32(os, "ebx", context->__ss.__ebx);
    DumpRegister32(os, "ecx", context->__ss.__ecx);
    DumpRegister32(os, "edx", context->__ss.__edx);
    os << '\n';

    DumpRegister32(os, "edi", context->__ss.__edi);
    DumpRegister32(os, "esi", context->__ss.__esi);
    DumpRegister32(os, "ebp", context->__ss.__ebp);
    DumpRegister32(os, "esp", context->__ss.__esp);
    os << '\n';

    DumpRegister32(os, "eip", context->__ss.__eip);
    os << "                   ";
    DumpRegister32(os, "eflags", context->__ss.__eflags);
    DumpX86Flags(os, context->__ss.__eflags);
    os << '\n';

    DumpRegister32(os, "cs",  context->__ss.__cs);
    DumpRegister32(os, "ds",  context->__ss.__ds);
    DumpRegister32(os, "es",  context->__ss.__es);
    DumpRegister32(os, "fs",  context->__ss.__fs);
    os << '\n';
    DumpRegister32(os, "gs",  context->__ss.__gs);
    DumpRegister32(os, "ss",  context->__ss.__ss);
#elif defined(__linux__) && defined(__i386__)
    DumpRegister32(os, "eax", context.gregs[REG_EAX]);
    DumpRegister32(os, "ebx", context.gregs[REG_EBX]);
    DumpRegister32(os, "ecx", context.gregs[REG_ECX]);
    DumpRegister32(os, "edx", context.gregs[REG_EDX]);
    os << '\n';

    DumpRegister32(os, "edi", context.gregs[REG_EDI]);
    DumpRegister32(os, "esi", context.gregs[REG_ESI]);
    DumpRegister32(os, "ebp", context.gregs[REG_EBP]);
    DumpRegister32(os, "esp", context.gregs[REG_ESP]);
    os << '\n';

    DumpRegister32(os, "eip", context.gregs[REG_EIP]);
    os << "                   ";
    DumpRegister32(os, "eflags", context.gregs[REG_EFL]);
    DumpX86Flags(os, context.gregs[REG_EFL]);
    os << '\n';

    DumpRegister32(os, "cs",  context.gregs[REG_CS]);
    DumpRegister32(os, "ds",  context.gregs[REG_DS]);
    DumpRegister32(os, "es",  context.gregs[REG_ES]);
    DumpRegister32(os, "fs",  context.gregs[REG_FS]);
    os << '\n';
    DumpRegister32(os, "gs",  context.gregs[REG_GS]);
    DumpRegister32(os, "ss",  context.gregs[REG_SS]);
#elif defined(__linux__) && defined(__x86_64__)
    DumpRegister64(os, "rax", context.gregs[REG_RAX]);
    DumpRegister64(os, "rbx", context.gregs[REG_RBX]);
    DumpRegister64(os, "rcx", context.gregs[REG_RCX]);
    DumpRegister64(os, "rdx", context.gregs[REG_RDX]);
    os << '\n';

    DumpRegister64(os, "rdi", context.gregs[REG_RDI]);
    DumpRegister64(os, "rsi", context.gregs[REG_RSI]);
    DumpRegister64(os, "rbp", context.gregs[REG_RBP]);
    DumpRegister64(os, "rsp", context.gregs[REG_RSP]);
    os << '\n';

    DumpRegister64(os, "r8 ", context.gregs[REG_R8]);
    DumpRegister64(os, "r9 ", context.gregs[REG_R9]);
    DumpRegister64(os, "r10", context.gregs[REG_R10]);
    DumpRegister64(os, "r11", context.gregs[REG_R11]);
    os << '\n';

    DumpRegister64(os, "r12", context.gregs[REG_R12]);
    DumpRegister64(os, "r13", context.gregs[REG_R13]);
    DumpRegister64(os, "r14", context.gregs[REG_R14]);
    DumpRegister64(os, "r15", context.gregs[REG_R15]);
    os << '\n';

    DumpRegister64(os, "rip", context.gregs[REG_RIP]);
    os << "   ";
    DumpRegister32(os, "eflags", context.gregs[REG_EFL]);
    DumpX86Flags(os, context.gregs[REG_EFL]);
    os << '\n';

    DumpRegister32(os, "cs",  (context.gregs[REG_CSGSFS]) & 0x0FFFF);
    DumpRegister32(os, "gs",  (context.gregs[REG_CSGSFS] >> 16) & 0x0FFFF);
    DumpRegister32(os, "fs",  (context.gregs[REG_CSGSFS] >> 32) & 0x0FFFF);
    os << '\n';
#else
    os << "Unknown architecture/word size/OS in ucontext dump";
#endif
  }

  void DumpRegister32(std::ostream& os, const char* name, uint32_t value) {
    os << StringPrintf(" %6s: 0x%08x", name, value);
  }

  void DumpRegister64(std::ostream& os, const char* name, uint64_t value) {
    os << StringPrintf(" %6s: 0x%016" PRIx64, name, value);
  }

  void DumpX86Flags(std::ostream& os, uint32_t flags) {
    os << " [";
    if ((flags & (1 << 0)) != 0) {
      os << " CF";
    }
    if ((flags & (1 << 2)) != 0) {
      os << " PF";
    }
    if ((flags & (1 << 4)) != 0) {
      os << " AF";
    }
    if ((flags & (1 << 6)) != 0) {
      os << " ZF";
    }
    if ((flags & (1 << 7)) != 0) {
      os << " SF";
    }
    if ((flags & (1 << 8)) != 0) {
      os << " TF";
    }
    if ((flags & (1 << 9)) != 0) {
      os << " IF";
    }
    if ((flags & (1 << 10)) != 0) {
      os << " DF";
    }
    if ((flags & (1 << 11)) != 0) {
      os << " OF";
    }
    os << " ]";
  }

  mcontext_t& context;
};

void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context) {
  static bool handlingUnexpectedSignal = false;
  if (handlingUnexpectedSignal) {
    LogMessageData data(__FILE__, __LINE__, INTERNAL_FATAL, -1);
    LogMessage::LogLine(data, "HandleUnexpectedSignal reentered\n");
    _exit(1);
  }
  handlingUnexpectedSignal = true;

  gAborting++;  // set before taking any locks
  MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_);

  bool has_address = (signal_number == SIGILL || signal_number == SIGBUS ||
                      signal_number == SIGFPE || signal_number == SIGSEGV);

  OsInfo os_info;
  const char* cmd_line = GetCmdLine();
  if (cmd_line == NULL) {
    cmd_line = "<unset>";  // Because no-one called InitLogging.
  }
  pid_t tid = GetTid();
  std::string thread_name(GetThreadName(tid));
  UContext thread_context(raw_context);
  Backtrace thread_backtrace;

  LOG(INTERNAL_FATAL) << "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"
                      << StringPrintf("Fatal signal %d (%s), code %d (%s)",
                                      signal_number, GetSignalName(signal_number),
                                      info->si_code,
                                      GetSignalCodeName(signal_number, info->si_code))
                      << (has_address ? StringPrintf(" fault addr %p", info->si_addr) : "") << "\n"
                      << "OS: " << Dumpable<OsInfo>(os_info) << "\n"
                      << "Cmdline: " << cmd_line << "\n"
                      << "Thread: " << tid << " \"" << thread_name << "\"\n"
                      << "Registers:\n" << Dumpable<UContext>(thread_context) << "\n"
                      << "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace);

  if (getenv("debug_db_uid") != NULL || getenv("art_wait_for_gdb_on_crash") != NULL) {
    LOG(INTERNAL_FATAL) << "********************************************************\n"
                        << "* Process " << getpid() << " thread " << tid << " \"" << thread_name << "\""
                        << " has been suspended while crashing.\n"
                        << "* Attach gdb:\n"
                        << "*     gdb -p " << tid << "\n"
                        << "********************************************************\n";
    // Wait for debugger to attach.
    while (true) {
    }
  }

  // Remove our signal handler for this signal...
  struct sigaction action;
  memset(&action, 0, sizeof(action));
  sigemptyset(&action.sa_mask);
  action.sa_handler = SIG_DFL;
  sigaction(signal_number, &action, NULL);
  // ...and re-raise so we die with the appropriate status.
  kill(getpid(), signal_number);
}

void Runtime::InitPlatformSignalHandlers() {
  // On the host, we don't have debuggerd to dump a stack for us when something unexpected happens.
  struct sigaction action;
  memset(&action, 0, sizeof(action));
  sigemptyset(&action.sa_mask);
  action.sa_sigaction = HandleUnexpectedSignal;
  // Use the three-argument sa_sigaction handler.
  action.sa_flags |= SA_SIGINFO;
  // Use the alternate signal stack so we can catch stack overflows.
  action.sa_flags |= SA_ONSTACK;

  int rc = 0;
  rc += sigaction(SIGABRT, &action, NULL);
  rc += sigaction(SIGBUS, &action, NULL);
  rc += sigaction(SIGFPE, &action, NULL);
  rc += sigaction(SIGILL, &action, NULL);
  rc += sigaction(SIGPIPE, &action, NULL);
  rc += sigaction(SIGSEGV, &action, NULL);
#if defined(SIGSTKFLT)
  rc += sigaction(SIGSTKFLT, &action, NULL);
#endif
  rc += sigaction(SIGTRAP, &action, NULL);
  CHECK_EQ(rc, 0);
}

}  // namespace art
<|endoftext|>