213 lines
4.8 KiB
C++
213 lines
4.8 KiB
C++
/*
|
|
* nextpnr -- Next Generation Place and Route
|
|
*
|
|
* Copyright (C) 2018 Claire Xenia Wolf <claire@yosyshq.com>
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
#include <list>
|
|
#include <map>
|
|
#include <set>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <vector>
|
|
|
|
#include "log.h"
|
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
NPNR_NORETURN void logv_error(const char *format, va_list ap) NPNR_ATTRIBUTE(noreturn);
|
|
|
|
std::vector<std::pair<std::ostream *, LogLevel>> log_streams;
|
|
log_write_type log_write_function = nullptr;
|
|
|
|
std::string log_last_error;
|
|
void (*log_error_atexit)() = NULL;
|
|
|
|
dict<LogLevel, int, loglevel_hash_ops> message_count_by_level;
|
|
static int log_newline_count = 0;
|
|
bool had_nonfatal_error = false;
|
|
bool log_warn_as_error = false;
|
|
|
|
std::string stringf(const char *fmt, ...)
|
|
{
|
|
std::string string;
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
string = vstringf(fmt, ap);
|
|
va_end(ap);
|
|
|
|
return string;
|
|
}
|
|
|
|
std::string vstringf(const char *fmt, va_list ap)
|
|
{
|
|
std::string string;
|
|
char *str = NULL;
|
|
|
|
#if defined(_WIN32) || defined(__CYGWIN__)
|
|
int sz = 64 + strlen(fmt), rc;
|
|
while (1) {
|
|
va_list apc;
|
|
va_copy(apc, ap);
|
|
str = (char *)realloc(str, sz);
|
|
rc = vsnprintf(str, sz, fmt, apc);
|
|
va_end(apc);
|
|
if (rc >= 0 && rc < sz)
|
|
break;
|
|
sz *= 2;
|
|
}
|
|
#else
|
|
if (vasprintf(&str, fmt, ap) < 0)
|
|
str = NULL;
|
|
#endif
|
|
|
|
if (str != NULL) {
|
|
string = str;
|
|
free(str);
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
void logv(const char *format, va_list ap, LogLevel level = LogLevel::LOG_MSG)
|
|
{
|
|
//
|
|
// Trim newlines from the beginning
|
|
while (format[0] == '\n' && format[1] != 0) {
|
|
log_always("\n");
|
|
format++;
|
|
}
|
|
|
|
std::string str = vstringf(format, ap);
|
|
|
|
if (str.empty())
|
|
return;
|
|
|
|
size_t nnl_pos = str.find_last_not_of('\n');
|
|
if (nnl_pos == std::string::npos)
|
|
log_newline_count += str.size();
|
|
else
|
|
log_newline_count = str.size() - nnl_pos - 1;
|
|
|
|
for (auto f : log_streams)
|
|
if (f.second <= level)
|
|
*f.first << str;
|
|
if (log_write_function)
|
|
log_write_function(str);
|
|
}
|
|
|
|
void log_with_level(LogLevel level, const char *format, ...)
|
|
{
|
|
message_count_by_level[level]++;
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
logv(format, ap, level);
|
|
va_end(ap);
|
|
}
|
|
|
|
void logv_prefixed(const char *prefix, const char *format, va_list ap, LogLevel level)
|
|
{
|
|
std::string message = vstringf(format, ap);
|
|
|
|
log_with_level(level, "%s%s", prefix, message.c_str());
|
|
log_flush();
|
|
}
|
|
|
|
void logv_nonfatal_error(const char *format, va_list ap)
|
|
{
|
|
logv_prefixed("ERROR: ", format, ap, LogLevel::ERROR_MSG);
|
|
had_nonfatal_error = true;
|
|
}
|
|
|
|
void logv_error(const char *format, va_list ap)
|
|
{
|
|
logv_prefixed("ERROR: ", format, ap, LogLevel::ERROR_MSG);
|
|
|
|
if (log_error_atexit)
|
|
log_error_atexit();
|
|
|
|
throw log_execution_error_exception();
|
|
}
|
|
|
|
void log_always(const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
logv(format, ap, LogLevel::ALWAYS_MSG);
|
|
va_end(ap);
|
|
}
|
|
|
|
void log(const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
logv(format, ap, LogLevel::LOG_MSG);
|
|
va_end(ap);
|
|
}
|
|
|
|
void log_info(const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
logv_prefixed("Info: ", format, ap, LogLevel::INFO_MSG);
|
|
va_end(ap);
|
|
}
|
|
|
|
void log_warning(const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
if (log_warn_as_error)
|
|
logv_nonfatal_error(format, ap);
|
|
else
|
|
logv_prefixed("Warning: ", format, ap, LogLevel::WARNING_MSG);
|
|
va_end(ap);
|
|
}
|
|
|
|
void log_error(const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
logv_error(format, ap);
|
|
}
|
|
|
|
void log_break()
|
|
{
|
|
if (log_newline_count < 2)
|
|
log("\n");
|
|
if (log_newline_count < 2)
|
|
log("\n");
|
|
}
|
|
|
|
void log_nonfatal_error(const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
logv_nonfatal_error(format, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
void log_flush()
|
|
{
|
|
for (auto f : log_streams)
|
|
f.first->flush();
|
|
}
|
|
|
|
NEXTPNR_NAMESPACE_END
|