CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/Log/LogManager.cpp
Views: 1401
1
// Copyright (C) 2003 Dolphin Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official SVN repository and contact information can be found at
16
// http://code.google.com/p/dolphin-emu/
17
18
#include "ppsspp_config.h"
19
20
#if PPSSPP_PLATFORM(ANDROID)
21
22
#include <android/log.h>
23
24
#endif
25
26
#include <algorithm>
27
#include <cstring>
28
29
#include "Common/Data/Encoding/Utf8.h"
30
31
#include "Common/Log/LogManager.h"
32
33
#if PPSSPP_PLATFORM(WINDOWS)
34
#include "Common/Log/ConsoleListener.h"
35
#endif
36
37
#include "Common/Log/StdioListener.h"
38
#include "Common/TimeUtil.h"
39
#include "Common/File/FileUtil.h"
40
#include "Common/StringUtils.h"
41
42
// Don't need to savestate this.
43
const char *hleCurrentThreadName = nullptr;
44
45
bool *g_bLogEnabledSetting = nullptr;
46
47
static const char level_to_char[8] = "-NEWIDV";
48
49
#if PPSSPP_PLATFORM(UWP) && defined(_DEBUG)
50
#define LOG_MSC_OUTPUTDEBUG true
51
#else
52
#define LOG_MSC_OUTPUTDEBUG false
53
#endif
54
55
void GenericLog(LogLevel level, Log type, const char *file, int line, const char* fmt, ...) {
56
if (g_bLogEnabledSetting && !(*g_bLogEnabledSetting))
57
return;
58
va_list args;
59
va_start(args, fmt);
60
LogManager *instance = LogManager::GetInstance();
61
if (instance) {
62
instance->LogLine(level, type, file, line, fmt, args);
63
} else {
64
// Fall back to printf or direct android logger with a small buffer if the log manager hasn't been initialized yet.
65
#if PPSSPP_PLATFORM(ANDROID)
66
char temp[512];
67
vsnprintf(temp, sizeof(temp), fmt, args);
68
__android_log_print(ANDROID_LOG_INFO, "PPSSPP", "EARLY: %s", temp);
69
#else
70
vprintf(fmt, args);
71
printf("\n");
72
#endif
73
}
74
va_end(args);
75
}
76
77
bool GenericLogEnabled(LogLevel level, Log type) {
78
if (LogManager::GetInstance())
79
return (*g_bLogEnabledSetting) && LogManager::GetInstance()->IsEnabled(level, type);
80
return false;
81
}
82
83
LogManager *LogManager::logManager_ = NULL;
84
85
// NOTE: Needs to be kept in sync with the Log enum.
86
static const char * const g_logTypeNames[] = {
87
"SYSTEM",
88
"BOOT",
89
"COMMON",
90
"CPU",
91
"FILESYS",
92
"G3D",
93
"HLE",
94
"JIT",
95
"LOADER",
96
"ME", // Media Engine
97
"MEMMAP",
98
"SASMIX",
99
"SAVESTATE",
100
"FRAMEBUF",
101
"AUDIO",
102
"IO",
103
"ACHIEVEMENTS",
104
"HTTP",
105
"PRINTF",
106
107
"SCEAUDIO",
108
"SCECTRL",
109
"SCEDISP",
110
"SCEFONT",
111
"SCEGE",
112
"SCEINTC",
113
"SCEIO",
114
"SCEKERNEL",
115
"SCEMODULE",
116
"SCENET",
117
"SCERTC",
118
"SCESAS",
119
"SCEUTIL",
120
"SCEMISC",
121
};
122
123
LogManager::LogManager(bool *enabledSetting) {
124
g_bLogEnabledSetting = enabledSetting;
125
126
_dbg_assert_(ARRAY_SIZE(g_logTypeNames) == (size_t)Log::NUMBER_OF_LOGS);
127
128
for (size_t i = 0; i < ARRAY_SIZE(g_logTypeNames); i++) {
129
truncate_cpy(log_[i].m_shortName, g_logTypeNames[i]);
130
log_[i].enabled = true;
131
#if defined(_DEBUG)
132
log_[i].level = LogLevel::LDEBUG;
133
#else
134
log_[i].level = LogLevel::LINFO;
135
#endif
136
}
137
138
// Remove file logging on small devices in Release mode.
139
#if PPSSPP_PLATFORM(UWP)
140
if (IsDebuggerPresent())
141
debuggerLog_ = new OutputDebugStringLogListener();
142
#else
143
#if !defined(MOBILE_DEVICE) || defined(_DEBUG)
144
fileLog_ = new FileLogListener("");
145
#if PPSSPP_PLATFORM(WINDOWS)
146
#if !PPSSPP_PLATFORM(UWP)
147
consoleLog_ = new ConsoleListener();
148
#endif
149
if (IsDebuggerPresent())
150
debuggerLog_ = new OutputDebugStringLogListener();
151
#else
152
stdioLog_ = new StdioListener();
153
#endif
154
#endif
155
ringLog_ = new RingbufferLogListener();
156
#endif
157
158
#if !defined(MOBILE_DEVICE) || defined(_DEBUG)
159
AddListener(fileLog_);
160
#if PPSSPP_PLATFORM(WINDOWS)
161
#if !PPSSPP_PLATFORM(UWP)
162
AddListener(consoleLog_);
163
#endif
164
#else
165
AddListener(stdioLog_);
166
#endif
167
#if defined(_MSC_VER) && (defined(USING_WIN_UI) || PPSSPP_PLATFORM(UWP))
168
if (IsDebuggerPresent() && debuggerLog_ && LOG_MSC_OUTPUTDEBUG)
169
AddListener(debuggerLog_);
170
#endif
171
AddListener(ringLog_);
172
#endif
173
}
174
175
LogManager::~LogManager() {
176
for (int i = 0; i < (int)Log::NUMBER_OF_LOGS; ++i) {
177
#if !defined(MOBILE_DEVICE) || defined(_DEBUG)
178
RemoveListener(fileLog_);
179
#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
180
RemoveListener(consoleLog_);
181
#endif
182
RemoveListener(stdioLog_);
183
#if defined(_MSC_VER) && defined(USING_WIN_UI)
184
RemoveListener(debuggerLog_);
185
#endif
186
#endif
187
}
188
189
// Make sure we don't shutdown while logging. RemoveListener locks too, but there are gaps.
190
std::lock_guard<std::mutex> listeners_lock(listeners_lock_);
191
192
delete fileLog_;
193
#if !defined(MOBILE_DEVICE) || defined(_DEBUG)
194
#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
195
delete consoleLog_;
196
#endif
197
delete stdioLog_;
198
delete debuggerLog_;
199
#endif
200
delete ringLog_;
201
}
202
203
void LogManager::ChangeFileLog(const char *filename) {
204
if (fileLog_) {
205
RemoveListener(fileLog_);
206
delete fileLog_;
207
fileLog_ = nullptr;
208
}
209
210
if (filename) {
211
fileLog_ = new FileLogListener(filename);
212
AddListener(fileLog_);
213
}
214
}
215
216
void LogManager::SaveConfig(Section *section) {
217
for (int i = 0; i < (int)Log::NUMBER_OF_LOGS; i++) {
218
section->Set((std::string(log_[i].m_shortName) + "Enabled").c_str(), log_[i].enabled);
219
section->Set((std::string(log_[i].m_shortName) + "Level").c_str(), (int)log_[i].level);
220
}
221
}
222
223
void LogManager::LoadConfig(const Section *section, bool debugDefaults) {
224
for (int i = 0; i < (int)Log::NUMBER_OF_LOGS; i++) {
225
bool enabled = false;
226
int level = 0;
227
section->Get((std::string(log_[i].m_shortName) + "Enabled").c_str(), &enabled, true);
228
section->Get((std::string(log_[i].m_shortName) + "Level").c_str(), &level, (int)(debugDefaults ? LogLevel::LDEBUG : LogLevel::LERROR));
229
log_[i].enabled = enabled;
230
log_[i].level = (LogLevel)level;
231
}
232
}
233
234
void LogManager::LogLine(LogLevel level, Log type, const char *file, int line, const char *format, va_list args) {
235
const LogChannel &log = log_[(size_t)type];
236
if (level > log.level || !log.enabled)
237
return;
238
239
LogMessage message;
240
message.level = level;
241
message.log = log.m_shortName;
242
243
#ifdef _WIN32
244
static const char sep = '\\';
245
#else
246
static const char sep = '/';
247
#endif
248
const char *fileshort = strrchr(file, sep);
249
if (fileshort != NULL) {
250
do
251
--fileshort;
252
while (fileshort > file && *fileshort != sep);
253
if (fileshort != file)
254
file = fileshort + 1;
255
}
256
257
GetCurrentTimeFormatted(message.timestamp);
258
259
if (hleCurrentThreadName) {
260
snprintf(message.header, sizeof(message.header), "%-12.12s %c[%s]: %s:%d",
261
hleCurrentThreadName, level_to_char[(int)level],
262
log.m_shortName,
263
file, line);
264
} else {
265
snprintf(message.header, sizeof(message.header), "%s:%d %c[%s]:",
266
file, line, level_to_char[(int)level],
267
log.m_shortName);
268
}
269
270
char msgBuf[1024];
271
va_list args_copy;
272
273
va_copy(args_copy, args);
274
size_t neededBytes = vsnprintf(msgBuf, sizeof(msgBuf), format, args);
275
message.msg.resize(neededBytes + 1);
276
if (neededBytes > sizeof(msgBuf)) {
277
// Needed more space? Re-run vsnprintf.
278
vsnprintf(&message.msg[0], neededBytes + 1, format, args_copy);
279
} else {
280
memcpy(&message.msg[0], msgBuf, neededBytes);
281
}
282
message.msg[neededBytes] = '\n';
283
va_end(args_copy);
284
285
std::lock_guard<std::mutex> listeners_lock(listeners_lock_);
286
for (auto &iter : listeners_) {
287
iter->Log(message);
288
}
289
}
290
291
bool LogManager::IsEnabled(LogLevel level, Log type) {
292
LogChannel &log = log_[(size_t)type];
293
if (level > log.level || !log.enabled)
294
return false;
295
return true;
296
}
297
298
void LogManager::Init(bool *enabledSetting) {
299
_assert_(logManager_ == nullptr);
300
logManager_ = new LogManager(enabledSetting);
301
}
302
303
void LogManager::Shutdown() {
304
delete logManager_;
305
logManager_ = NULL;
306
}
307
308
void LogManager::AddListener(LogListener *listener) {
309
if (!listener)
310
return;
311
std::lock_guard<std::mutex> lk(listeners_lock_);
312
listeners_.push_back(listener);
313
}
314
315
void LogManager::RemoveListener(LogListener *listener) {
316
if (!listener)
317
return;
318
std::lock_guard<std::mutex> lk(listeners_lock_);
319
auto iter = std::find(listeners_.begin(), listeners_.end(), listener);
320
if (iter != listeners_.end())
321
listeners_.erase(iter);
322
}
323
324
FileLogListener::FileLogListener(const char *filename) {
325
if (strlen(filename) > 0) {
326
fp_ = File::OpenCFile(Path(std::string(filename)), "at");
327
}
328
SetEnabled(fp_ != nullptr);
329
}
330
331
FileLogListener::~FileLogListener() {
332
if (fp_)
333
fclose(fp_);
334
}
335
336
void FileLogListener::Log(const LogMessage &message) {
337
if (!IsEnabled() || !IsValid())
338
return;
339
340
std::lock_guard<std::mutex> lk(m_log_lock);
341
fprintf(fp_, "%s %s %s", message.timestamp, message.header, message.msg.c_str());
342
fflush(fp_);
343
}
344
345
void OutputDebugStringLogListener::Log(const LogMessage &message) {
346
char buffer[4096];
347
snprintf(buffer, sizeof(buffer), "%s %s %s", message.timestamp, message.header, message.msg.c_str());
348
#if _MSC_VER
349
OutputDebugStringUTF8(buffer);
350
#endif
351
}
352
353
void RingbufferLogListener::Log(const LogMessage &message) {
354
if (!enabled_)
355
return;
356
messages_[curMessage_] = message;
357
curMessage_++;
358
if (curMessage_ >= MAX_LOGS)
359
curMessage_ -= MAX_LOGS;
360
count_++;
361
}
362
363
#ifdef _WIN32
364
365
void OutputDebugStringUTF8(const char *p) {
366
wchar_t temp[16384*4];
367
368
int len = std::min(16383*4, (int)strlen(p));
369
int size = (int)MultiByteToWideChar(CP_UTF8, 0, p, len, NULL, 0);
370
MultiByteToWideChar(CP_UTF8, 0, p, len, temp, size);
371
temp[size] = 0;
372
373
OutputDebugString(temp);
374
}
375
376
#else
377
378
void OutputDebugStringUTF8(const char *p) {
379
INFO_LOG(Log::System, "%s", p);
380
}
381
382
#endif
383
384