blob: b7a6ab0db3a131afd9592aa03b9231a084663186 [file] [log] [blame]
// Copyright 2015 The Cobalt Authors. All Rights Reserved.
//
// 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 "starboard/common/log.h"
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <string>
#include "starboard/client_porting/eztime/eztime.h"
#include "starboard/client_porting/poem/string_poem.h"
#include "starboard/configuration.h"
#include "starboard/system.h"
#include "starboard/thread.h"
#include "starboard/time.h"
#if SB_API_VERSION < 11
#include "starboard/common/mutex.h"
#include "starboard/common/recursive_mutex.h"
#include "starboard/once.h"
#endif // SB_API_VERSION < 11
namespace starboard {
namespace logging {
namespace {
SbLogPriority g_min_log_level = kSbLogPriorityUnknown;
#if SB_API_VERSION < 11
SB_ONCE_INITIALIZE_FUNCTION(RecursiveMutex, g_log_mutex);
#endif // SB_API_VERSION < 11
#if defined(COMPILER_MSVC)
#pragma optimize("", off)
#endif
void Alias(const void* var) {}
#if defined(COMPILER_MSVC)
#pragma optimize("", on)
#endif
const char* log_priority_names[] = {
"UNKNOWN", "INFO", "WARNING", "ERROR", "FATAL",
};
} // namespace
void SetMinLogLevel(SbLogPriority priority) {
g_min_log_level = priority;
}
SbLogPriority GetMinLogLevel() {
#if SB_LOGGING_IS_OFFICIAL_BUILD
return SB_LOG_FATAL;
#else
return g_min_log_level;
#endif
}
SbLogPriority StringToLogLevel(const std::string& log_level) {
// The command-line arguments are lower-case, so we need to compare against
// the lower-case logging level string equivalents.
if (log_level == "warning") {
return SB_LOG_WARNING;
} else if (log_level == "error") {
return SB_LOG_ERROR;
} else if (log_level == "fatal") {
return SB_LOG_FATAL;
}
return SB_LOG_INFO;
}
void Break() {
SbSystemBreakIntoDebugger();
}
Stack::Stack(int skip_frames) : skip_frames(skip_frames) {}
std::ostream& operator<<(std::ostream& out, const Stack& stack_token) {
int skip_frames = stack_token.skip_frames;
if (skip_frames < 0) {
skip_frames = 0;
}
void* stack[256];
int count = SbSystemGetStack(stack, SB_ARRAY_SIZE_INT(stack));
// Skip over DumpStack's stack frame, plus any more requested by the caller.
for (int i = 1 + skip_frames; i < count; ++i) {
char symbol[512];
bool result =
SbSystemSymbolize(stack[i], symbol, SB_ARRAY_SIZE_INT(symbol));
out << "\t";
if (result) {
out << symbol;
} else {
out << "<unknown>";
}
out << " [" << stack[i] << "]\n";
}
return out;
}
std::ostream& operator<<(std::ostream& out, const wchar_t* wstr) {
// We don't have any good cross-platform wide character to UTF8 converter at
// this level in the stack, so just throwing out non-ASCII characters.
// TODO: Convert to UTF8.
size_t len = wcslen(wstr);
char* buffer = new char[len + 1];
size_t pos = 0;
for (size_t i = 0; i < len; ++i) {
if (wstr[i] < 128) {
buffer[pos++] = static_cast<char>(wstr[i]);
}
}
buffer[pos] = '\0';
out << buffer;
delete[] buffer;
return out;
}
std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) {
return out << wstr.c_str();
}
#if defined(__cplusplus_winrt)
std::ostream& operator<<(std::ostream& out, ::Platform::String ^ str) {
return out << std::wstring(str->Begin(), str->End());
}
#endif
LogMessage::LogMessage(const char* file, int line, SbLogPriority priority)
: priority_(priority), file_(file), line_(line) {
Init(file, line);
}
LogMessage::~LogMessage() {
stream_ << std::endl;
// Output the stack trace on Fatal messages.
if (priority_ == kSbLogPriorityFatal) {
stream_ << Stack(1);
}
std::string str_newline(stream_.str());
#if SB_API_VERSION < 11
g_log_mutex()->Acquire();
SbLog(priority_, str_newline.c_str());
g_log_mutex()->Release();
#else // SB_API_VERSION >= 11
SbLog(priority_, str_newline.c_str());
#endif // SB_API_VERSION < 11
if (priority_ == kSbLogPriorityFatal) {
// Ensure the first characters of the string are on the stack so they
// are contained in minidumps for diagnostic purposes.
char str_stack[1024];
const size_t copy_bytes =
std::min(SB_ARRAY_SIZE(str_stack), str_newline.length() + 1);
PoemStringCopyN(str_stack, str_newline.c_str(),
static_cast<int>(copy_bytes));
Alias(str_stack);
Break();
}
}
std::ostream& LogMessage::stream() {
return stream_;
}
void LogMessage::Init(const char* file, int line) {
std::string filename(file);
size_t last_slash_pos = filename.find_last_of("\\/");
if (last_slash_pos != std::string::npos) {
filename.erase(0, last_slash_pos + 1);
}
stream_ << '[';
stream_ << SbThreadGetId() << ':';
EzTimeValue time_value;
EzTimeValueGetNow(&time_value, NULL);
EzTimeT t = time_value.tv_sec;
struct EzTimeExploded local_time = {0};
EzTimeTExplodeLocal(&t, &local_time);
struct EzTimeExploded* tm_time = &local_time;
stream_ << std::setfill('0') << std::setw(2) << 1 + tm_time->tm_mon
<< std::setw(2) << tm_time->tm_mday << '/' << std::setw(2)
<< tm_time->tm_hour << std::setw(2) << tm_time->tm_min << std::setw(2)
<< tm_time->tm_sec << '.' << std::setw(6) << time_value.tv_usec
<< ':';
stream_ << log_priority_names[priority_];
stream_ << ":" << filename << "(" << line << ")] ";
message_start_ = stream_.tellp();
}
LogMessageVoidify::LogMessageVoidify() {}
void LogMessageVoidify::operator&(std::ostream&) {}
} // namespace logging
} // namespace starboard