CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Common/Log/LogManager.cpp
Views: 1401
// Copyright (C) 2003 Dolphin Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official SVN repository and contact information can be found at15// http://code.google.com/p/dolphin-emu/1617#include "ppsspp_config.h"1819#if PPSSPP_PLATFORM(ANDROID)2021#include <android/log.h>2223#endif2425#include <algorithm>26#include <cstring>2728#include "Common/Data/Encoding/Utf8.h"2930#include "Common/Log/LogManager.h"3132#if PPSSPP_PLATFORM(WINDOWS)33#include "Common/Log/ConsoleListener.h"34#endif3536#include "Common/Log/StdioListener.h"37#include "Common/TimeUtil.h"38#include "Common/File/FileUtil.h"39#include "Common/StringUtils.h"4041// Don't need to savestate this.42const char *hleCurrentThreadName = nullptr;4344bool *g_bLogEnabledSetting = nullptr;4546static const char level_to_char[8] = "-NEWIDV";4748#if PPSSPP_PLATFORM(UWP) && defined(_DEBUG)49#define LOG_MSC_OUTPUTDEBUG true50#else51#define LOG_MSC_OUTPUTDEBUG false52#endif5354void GenericLog(LogLevel level, Log type, const char *file, int line, const char* fmt, ...) {55if (g_bLogEnabledSetting && !(*g_bLogEnabledSetting))56return;57va_list args;58va_start(args, fmt);59LogManager *instance = LogManager::GetInstance();60if (instance) {61instance->LogLine(level, type, file, line, fmt, args);62} else {63// Fall back to printf or direct android logger with a small buffer if the log manager hasn't been initialized yet.64#if PPSSPP_PLATFORM(ANDROID)65char temp[512];66vsnprintf(temp, sizeof(temp), fmt, args);67__android_log_print(ANDROID_LOG_INFO, "PPSSPP", "EARLY: %s", temp);68#else69vprintf(fmt, args);70printf("\n");71#endif72}73va_end(args);74}7576bool GenericLogEnabled(LogLevel level, Log type) {77if (LogManager::GetInstance())78return (*g_bLogEnabledSetting) && LogManager::GetInstance()->IsEnabled(level, type);79return false;80}8182LogManager *LogManager::logManager_ = NULL;8384// NOTE: Needs to be kept in sync with the Log enum.85static const char * const g_logTypeNames[] = {86"SYSTEM",87"BOOT",88"COMMON",89"CPU",90"FILESYS",91"G3D",92"HLE",93"JIT",94"LOADER",95"ME", // Media Engine96"MEMMAP",97"SASMIX",98"SAVESTATE",99"FRAMEBUF",100"AUDIO",101"IO",102"ACHIEVEMENTS",103"HTTP",104"PRINTF",105106"SCEAUDIO",107"SCECTRL",108"SCEDISP",109"SCEFONT",110"SCEGE",111"SCEINTC",112"SCEIO",113"SCEKERNEL",114"SCEMODULE",115"SCENET",116"SCERTC",117"SCESAS",118"SCEUTIL",119"SCEMISC",120};121122LogManager::LogManager(bool *enabledSetting) {123g_bLogEnabledSetting = enabledSetting;124125_dbg_assert_(ARRAY_SIZE(g_logTypeNames) == (size_t)Log::NUMBER_OF_LOGS);126127for (size_t i = 0; i < ARRAY_SIZE(g_logTypeNames); i++) {128truncate_cpy(log_[i].m_shortName, g_logTypeNames[i]);129log_[i].enabled = true;130#if defined(_DEBUG)131log_[i].level = LogLevel::LDEBUG;132#else133log_[i].level = LogLevel::LINFO;134#endif135}136137// Remove file logging on small devices in Release mode.138#if PPSSPP_PLATFORM(UWP)139if (IsDebuggerPresent())140debuggerLog_ = new OutputDebugStringLogListener();141#else142#if !defined(MOBILE_DEVICE) || defined(_DEBUG)143fileLog_ = new FileLogListener("");144#if PPSSPP_PLATFORM(WINDOWS)145#if !PPSSPP_PLATFORM(UWP)146consoleLog_ = new ConsoleListener();147#endif148if (IsDebuggerPresent())149debuggerLog_ = new OutputDebugStringLogListener();150#else151stdioLog_ = new StdioListener();152#endif153#endif154ringLog_ = new RingbufferLogListener();155#endif156157#if !defined(MOBILE_DEVICE) || defined(_DEBUG)158AddListener(fileLog_);159#if PPSSPP_PLATFORM(WINDOWS)160#if !PPSSPP_PLATFORM(UWP)161AddListener(consoleLog_);162#endif163#else164AddListener(stdioLog_);165#endif166#if defined(_MSC_VER) && (defined(USING_WIN_UI) || PPSSPP_PLATFORM(UWP))167if (IsDebuggerPresent() && debuggerLog_ && LOG_MSC_OUTPUTDEBUG)168AddListener(debuggerLog_);169#endif170AddListener(ringLog_);171#endif172}173174LogManager::~LogManager() {175for (int i = 0; i < (int)Log::NUMBER_OF_LOGS; ++i) {176#if !defined(MOBILE_DEVICE) || defined(_DEBUG)177RemoveListener(fileLog_);178#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)179RemoveListener(consoleLog_);180#endif181RemoveListener(stdioLog_);182#if defined(_MSC_VER) && defined(USING_WIN_UI)183RemoveListener(debuggerLog_);184#endif185#endif186}187188// Make sure we don't shutdown while logging. RemoveListener locks too, but there are gaps.189std::lock_guard<std::mutex> listeners_lock(listeners_lock_);190191delete fileLog_;192#if !defined(MOBILE_DEVICE) || defined(_DEBUG)193#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)194delete consoleLog_;195#endif196delete stdioLog_;197delete debuggerLog_;198#endif199delete ringLog_;200}201202void LogManager::ChangeFileLog(const char *filename) {203if (fileLog_) {204RemoveListener(fileLog_);205delete fileLog_;206fileLog_ = nullptr;207}208209if (filename) {210fileLog_ = new FileLogListener(filename);211AddListener(fileLog_);212}213}214215void LogManager::SaveConfig(Section *section) {216for (int i = 0; i < (int)Log::NUMBER_OF_LOGS; i++) {217section->Set((std::string(log_[i].m_shortName) + "Enabled").c_str(), log_[i].enabled);218section->Set((std::string(log_[i].m_shortName) + "Level").c_str(), (int)log_[i].level);219}220}221222void LogManager::LoadConfig(const Section *section, bool debugDefaults) {223for (int i = 0; i < (int)Log::NUMBER_OF_LOGS; i++) {224bool enabled = false;225int level = 0;226section->Get((std::string(log_[i].m_shortName) + "Enabled").c_str(), &enabled, true);227section->Get((std::string(log_[i].m_shortName) + "Level").c_str(), &level, (int)(debugDefaults ? LogLevel::LDEBUG : LogLevel::LERROR));228log_[i].enabled = enabled;229log_[i].level = (LogLevel)level;230}231}232233void LogManager::LogLine(LogLevel level, Log type, const char *file, int line, const char *format, va_list args) {234const LogChannel &log = log_[(size_t)type];235if (level > log.level || !log.enabled)236return;237238LogMessage message;239message.level = level;240message.log = log.m_shortName;241242#ifdef _WIN32243static const char sep = '\\';244#else245static const char sep = '/';246#endif247const char *fileshort = strrchr(file, sep);248if (fileshort != NULL) {249do250--fileshort;251while (fileshort > file && *fileshort != sep);252if (fileshort != file)253file = fileshort + 1;254}255256GetCurrentTimeFormatted(message.timestamp);257258if (hleCurrentThreadName) {259snprintf(message.header, sizeof(message.header), "%-12.12s %c[%s]: %s:%d",260hleCurrentThreadName, level_to_char[(int)level],261log.m_shortName,262file, line);263} else {264snprintf(message.header, sizeof(message.header), "%s:%d %c[%s]:",265file, line, level_to_char[(int)level],266log.m_shortName);267}268269char msgBuf[1024];270va_list args_copy;271272va_copy(args_copy, args);273size_t neededBytes = vsnprintf(msgBuf, sizeof(msgBuf), format, args);274message.msg.resize(neededBytes + 1);275if (neededBytes > sizeof(msgBuf)) {276// Needed more space? Re-run vsnprintf.277vsnprintf(&message.msg[0], neededBytes + 1, format, args_copy);278} else {279memcpy(&message.msg[0], msgBuf, neededBytes);280}281message.msg[neededBytes] = '\n';282va_end(args_copy);283284std::lock_guard<std::mutex> listeners_lock(listeners_lock_);285for (auto &iter : listeners_) {286iter->Log(message);287}288}289290bool LogManager::IsEnabled(LogLevel level, Log type) {291LogChannel &log = log_[(size_t)type];292if (level > log.level || !log.enabled)293return false;294return true;295}296297void LogManager::Init(bool *enabledSetting) {298_assert_(logManager_ == nullptr);299logManager_ = new LogManager(enabledSetting);300}301302void LogManager::Shutdown() {303delete logManager_;304logManager_ = NULL;305}306307void LogManager::AddListener(LogListener *listener) {308if (!listener)309return;310std::lock_guard<std::mutex> lk(listeners_lock_);311listeners_.push_back(listener);312}313314void LogManager::RemoveListener(LogListener *listener) {315if (!listener)316return;317std::lock_guard<std::mutex> lk(listeners_lock_);318auto iter = std::find(listeners_.begin(), listeners_.end(), listener);319if (iter != listeners_.end())320listeners_.erase(iter);321}322323FileLogListener::FileLogListener(const char *filename) {324if (strlen(filename) > 0) {325fp_ = File::OpenCFile(Path(std::string(filename)), "at");326}327SetEnabled(fp_ != nullptr);328}329330FileLogListener::~FileLogListener() {331if (fp_)332fclose(fp_);333}334335void FileLogListener::Log(const LogMessage &message) {336if (!IsEnabled() || !IsValid())337return;338339std::lock_guard<std::mutex> lk(m_log_lock);340fprintf(fp_, "%s %s %s", message.timestamp, message.header, message.msg.c_str());341fflush(fp_);342}343344void OutputDebugStringLogListener::Log(const LogMessage &message) {345char buffer[4096];346snprintf(buffer, sizeof(buffer), "%s %s %s", message.timestamp, message.header, message.msg.c_str());347#if _MSC_VER348OutputDebugStringUTF8(buffer);349#endif350}351352void RingbufferLogListener::Log(const LogMessage &message) {353if (!enabled_)354return;355messages_[curMessage_] = message;356curMessage_++;357if (curMessage_ >= MAX_LOGS)358curMessage_ -= MAX_LOGS;359count_++;360}361362#ifdef _WIN32363364void OutputDebugStringUTF8(const char *p) {365wchar_t temp[16384*4];366367int len = std::min(16383*4, (int)strlen(p));368int size = (int)MultiByteToWideChar(CP_UTF8, 0, p, len, NULL, 0);369MultiByteToWideChar(CP_UTF8, 0, p, len, temp, size);370temp[size] = 0;371372OutputDebugString(temp);373}374375#else376377void OutputDebugStringUTF8(const char *p) {378INFO_LOG(Log::System, "%s", p);379}380381#endif382383384