blob: 28d15037d29b6019fbe47f858a830f7f236ca261 [file] [log] [blame]
// Copyright 2015 Google Inc. 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/log.h"
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <string>
#include "starboard/client_porting/poem/string_poem.h"
#include "starboard/mutex.h"
#include "starboard/system.h"
#include "starboard/thread.h"
#include "starboard/time.h"
namespace starboard {
namespace logging {
namespace {
SbLogPriority g_min_log_level = kSbLogPriorityUnknown;
SbMutex g_log_mutex = SB_MUTEX_INITIALIZER;
#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
}
void Break() {
SbSystemBreakIntoDebugger();
}
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;
}
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());
SbMutexAcquire(&g_log_mutex);
SbLog(priority_, str_newline.c_str());
SbMutexRelease(&g_log_mutex);
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();
}
}
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() << ':';
stream_ << SbTimeGetMonotonicNow() << ':';
stream_ << log_priority_names[priority_];
stream_ << ":" << filename << "(" << line << ")] ";
message_start_ = stream_.tellp();
}
} // namespace logging
} // namespace starboard