Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/Log/LogManager.cpp
5668 views
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 <io.h>
35
#include "Common/Log/ConsoleListener.h"
36
#endif
37
38
#include "Common/TimeUtil.h"
39
#include "Common/Thread/ThreadUtil.h"
40
#include "Common/File/FileUtil.h"
41
#include "Common/Data/Format/IniFile.h"
42
#include "Common/StringUtils.h"
43
44
LogChannel g_log[(size_t)Log::NUMBER_OF_LOGS];
45
LogManager g_logManager;
46
47
const char *hleCurrentThreadName = nullptr;
48
49
bool g_bDummySetting = true;
50
bool *g_bLogEnabledSetting = &g_bDummySetting;
51
52
static const char level_to_char[8] = "-NEWIDV";
53
54
#if PPSSPP_PLATFORM(UWP) && defined(_DEBUG)
55
#define LOG_MSC_OUTPUTDEBUG true
56
#else
57
#define LOG_MSC_OUTPUTDEBUG false
58
#endif
59
60
#if PPSSPP_PLATFORM(ANDROID)
61
void AndroidLog(const LogMessage &message);
62
#endif
63
64
void GenericLog(Log type, LogLevel level, const char *file, int line, const char* fmt, ...) {
65
va_list args;
66
va_start(args, fmt);
67
g_logManager.LogLine(level, type, file, line, fmt, args);
68
va_end(args);
69
}
70
71
// NOTE: Needs to be kept in sync with the Log enum.
72
static const char * const g_logTypeNames[] = {
73
"SYSTEM",
74
"BOOT",
75
"COMMON",
76
"CPU",
77
"FILESYS",
78
"G3D",
79
"HLE",
80
"JIT",
81
"LOADER",
82
"MPEG",
83
"ATRAC",
84
"ME", // Rest of the media Engine
85
"MEMMAP",
86
"SASMIX",
87
"SAVESTATE",
88
"FRAMEBUF",
89
"AUDIO",
90
"IO",
91
"ACHIEVEMENTS",
92
"HTTP",
93
"PRINTF",
94
"TEXREPLACE",
95
"DEBUGGER",
96
"GEDEBUGGER",
97
"UI",
98
"IAP",
99
"SCEAUDIO",
100
"SCECTRL",
101
"SCEDISP",
102
"SCEFONT",
103
"SCEGE",
104
"SCEINTC",
105
"SCEIO",
106
"SCEKERNEL",
107
"SCEMODULE",
108
"SCENET",
109
"SCERTC",
110
"SCESAS",
111
"SCEUTIL",
112
"SCEMISC",
113
"SCEREG",
114
};
115
116
const char *LogManager::GetLogTypeName(Log type) {
117
return g_logTypeNames[(size_t)type];
118
}
119
120
// Ultra plain output, for CI and stuff.
121
void PrintfLog(const LogMessage &message);
122
123
void LogManager::Init(bool *enabledSetting, bool headless) {
124
g_bLogEnabledSetting = enabledSetting;
125
if (initialized_) {
126
// Just update the pointer, already done above.
127
return;
128
}
129
initialized_ = true;
130
131
_dbg_assert_(ARRAY_SIZE(g_logTypeNames) == (size_t)Log::NUMBER_OF_LOGS);
132
_dbg_assert_(ARRAY_SIZE(g_logTypeNames) == ARRAY_SIZE(g_log));
133
134
for (size_t i = 0; i < ARRAY_SIZE(g_log); i++) {
135
g_log[i].enabled = true;
136
g_log[i].level = LogLevel::LINFO;
137
}
138
}
139
140
void LogManager::Shutdown() {
141
if (!initialized_) {
142
// already done
143
return;
144
}
145
146
if (fp_) {
147
fclose(fp_);
148
fp_ = nullptr;
149
}
150
151
outputs_ = (LogOutput)0;
152
153
ringLog_.Clear();
154
initialized_ = false;
155
}
156
157
LogManager::LogManager() {
158
#if PPSSPP_PLATFORM(IOS) || PPSSPP_PLATFORM(UWP) || PPSSPP_PLATFORM(SWITCH)
159
stdioUseColor_ = false;
160
#elif defined(_MSC_VER)
161
stdioUseColor_ = false;
162
#elif defined(__APPLE__)
163
// Xcode builtin terminal used for debugging does not support colours.
164
// Fortunately it can be detected with a TERM env variable.
165
stdioUseColor_ = isatty(fileno(stdout)) && getenv("TERM") != NULL;
166
#else
167
stdioUseColor_ = isatty(fileno(stdout));
168
#endif
169
170
#if PPSSPP_PLATFORM(WINDOWS)
171
if (IsDebuggerPresent()) {
172
outputs_ |= LogOutput::DebugString;
173
}
174
#if !PPSSPP_PLATFORM(UWP)
175
if (!consoleLog_) {
176
consoleLog_ = new ConsoleListener();
177
}
178
outputs_ |= LogOutput::WinConsole;
179
#endif
180
#endif
181
}
182
183
LogManager::~LogManager() {
184
Shutdown();
185
186
#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
187
delete consoleLog_;
188
consoleLog_ = nullptr;
189
#endif
190
}
191
192
void LogManager::SetFileLogPath(const Path &filename) {
193
if (fp_ && filename == logFilename_) {
194
// All good
195
return;
196
}
197
198
if (fp_) {
199
fclose(fp_);
200
}
201
202
if (!filename.empty() && (outputs_ & LogOutput::File)) {
203
logFilename_ = Path(filename);
204
File::CreateFullPath(logFilename_.NavigateUp());
205
fp_ = File::OpenCFile(logFilename_, "at");
206
logFileOpenFailed_ = fp_ == nullptr;
207
if (logFileOpenFailed_) {
208
printf("Failed to open log file %s\n", filename.c_str());
209
}
210
}
211
}
212
213
void LogManager::SaveConfig(Section *section) {
214
for (int i = 0; i < (int)Log::NUMBER_OF_LOGS; i++) {
215
section->Set((std::string(g_logTypeNames[i]) + "Enabled"), g_log[i].enabled);
216
section->Set((std::string(g_logTypeNames[i]) + "Level"), (int)g_log[i].level);
217
}
218
}
219
220
void LogManager::LoadConfig(const Section *section) {
221
for (int i = 0; i < (int)Log::NUMBER_OF_LOGS; i++) {
222
// Defaults. Get now doesn't write the output if it fails.
223
bool enabled = true;
224
int level = (int)LogLevel::LERROR;
225
section->Get((std::string(g_logTypeNames[i]) + "Enabled"), &enabled);
226
section->Get((std::string(g_logTypeNames[i]) + "Level"), &level);
227
g_log[i].enabled = enabled;
228
g_log[i].level = (LogLevel)level;
229
}
230
}
231
232
void LogManager::SetOutputsEnabled(LogOutput outputs) {
233
outputs_ = outputs;
234
if (outputs & LogOutput::File) {
235
SetFileLogPath(logFilename_);
236
}
237
}
238
239
void LogManager::LogLine(LogLevel level, Log type, const char *file, int line, const char *format, va_list args) {
240
char msgBuf[1024];
241
242
const LogChannel &log = g_log[(size_t)type];
243
if (level > log.level || !log.enabled || outputs_ == (LogOutput)0) {
244
// If we get here, it should have been caught earlier.
245
return;
246
}
247
248
LogMessage message;
249
message.level = level;
250
message.log = g_logTypeNames[(size_t)type];
251
252
#ifdef _WIN32
253
static const char sep = '\\';
254
#else
255
static const char sep = '/';
256
#endif
257
const char *fileshort = strrchr(file, sep);
258
if (fileshort) {
259
do
260
--fileshort;
261
while (fileshort > file && *fileshort != sep);
262
if (fileshort != file)
263
file = fileshort + 1;
264
}
265
266
const char *threadName;
267
#if PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(MAC)
268
const char *hostThreadName = GetCurrentThreadName();
269
if ((hostThreadName && strcmp(hostThreadName, "EmuThread") != 0) || !hleCurrentThreadName) {
270
// Use the host thread name.
271
threadName = hostThreadName ? hostThreadName : "unknown";
272
} else {
273
// Use the PSP HLE thread name.
274
threadName = hleCurrentThreadName;
275
}
276
#else
277
threadName = hleCurrentThreadName;
278
#endif
279
280
if (threadName) {
281
snprintf(message.header, sizeof(message.header), "%-12.12s %c[%s]: %s:%d",
282
threadName, level_to_char[(int)level],
283
message.log,
284
file, line);
285
} else {
286
snprintf(message.header, sizeof(message.header), "%s:%d %c[%s]:",
287
file, line, level_to_char[(int)level],
288
message.log);
289
}
290
291
GetCurrentTimeFormatted(message.timestamp);
292
293
va_list args_copy;
294
295
va_copy(args_copy, args);
296
size_t neededBytes = vsnprintf(msgBuf, sizeof(msgBuf), format, args);
297
message.msg.resize(neededBytes + 1);
298
if (neededBytes > sizeof(msgBuf)) {
299
// Needed more space? Re-run vsnprintf.
300
vsnprintf(&message.msg[0], neededBytes + 1, format, args_copy);
301
} else {
302
memcpy(&message.msg[0], msgBuf, neededBytes);
303
}
304
message.msg[neededBytes] = '\n';
305
va_end(args_copy);
306
307
if (outputs_ & LogOutput::Stdio) {
308
// This has its own mutex.
309
StdioLog(message);
310
}
311
312
// OK, now go through the possible listeners in order.
313
if (outputs_ & LogOutput::File) {
314
if (fp_) {
315
std::lock_guard<std::mutex> lk(logFileLock_);
316
fprintf(fp_, "%s %s %s", message.timestamp, message.header, message.msg.c_str());
317
// Is this really necessary to do every time? I guess to catch the last message before a crash..
318
fflush(fp_);
319
}
320
}
321
322
#if PPSSPP_PLATFORM(WINDOWS)
323
if (outputs_ & LogOutput::DebugString) {
324
// No mutex needed
325
char buffer[4096];
326
// We omit the timestamp for easy copy-paste-diffing.
327
snprintf(buffer, sizeof(buffer), "%s %s", message.header, message.msg.c_str());
328
OutputDebugStringUTF8(buffer);
329
}
330
#endif
331
332
if (outputs_ & LogOutput::RingBuffer) {
333
ringLog_.Log(message);
334
}
335
336
if (outputs_ & LogOutput::Printf) {
337
PrintfLog(message);
338
}
339
340
#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
341
if (outputs_ & LogOutput::WinConsole) {
342
if (consoleLog_) {
343
consoleLog_->Log(message);
344
}
345
}
346
#endif
347
348
if (outputs_ & LogOutput::ExternalCallback) {
349
if (externalCallback_) {
350
externalCallback_(message, externalUserData_);
351
}
352
}
353
}
354
355
void RingbufferLog::Log(const LogMessage &message) {
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 = new wchar_t[65536];
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
delete[] temp;
375
}
376
377
#else
378
379
void OutputDebugStringUTF8(const char *p) {
380
INFO_LOG(Log::System, "%s", p);
381
}
382
383
#endif
384
385
#ifdef HAVE_LIBRETRO_VFS
386
#undef fprintf
387
#endif
388
389
void LogManager::StdioLog(const LogMessage &message) {
390
#if PPSSPP_PLATFORM(ANDROID)
391
#ifndef LOG_APP_NAME
392
#define LOG_APP_NAME "PPSSPP"
393
#endif
394
int mode;
395
switch (message.level) {
396
case LogLevel::LWARNING:
397
mode = ANDROID_LOG_WARN;
398
break;
399
case LogLevel::LERROR:
400
mode = ANDROID_LOG_ERROR;
401
break;
402
default:
403
mode = ANDROID_LOG_INFO;
404
break;
405
}
406
407
// Long log messages need splitting up.
408
// Not sure what the actual limit is (seems to vary), but let's be conservative.
409
const size_t maxLogLength = 512;
410
if (message.msg.length() < maxLogLength) {
411
// Log with simplified headers as Android already provides timestamp etc.
412
__android_log_print(mode, LOG_APP_NAME, "[%s] %s", message.log, message.msg.c_str());
413
} else {
414
std::string_view msg = message.msg;
415
416
// Ideally we should split at line breaks, but it's at least fairly usable anyway.
417
std::string_view first_part = msg.substr(0, maxLogLength);
418
__android_log_print(mode, LOG_APP_NAME, "[%s] %.*s", message.log, (int)first_part.size(), first_part.data());
419
msg = msg.substr(maxLogLength);
420
421
while (msg.length() > maxLogLength) {
422
std::string_view next_part = msg.substr(0, maxLogLength);
423
__android_log_print(mode, LOG_APP_NAME, "%.*s", (int)next_part.size(), next_part.data());
424
msg = msg.substr(maxLogLength);
425
}
426
// Print the final part.
427
__android_log_print(mode, LOG_APP_NAME, "%.*s", (int)msg.size(), msg.data());
428
}
429
#else
430
char text[2048];
431
snprintf(text, sizeof(text), "%s %s %s", message.timestamp, message.header, message.msg.c_str());
432
text[sizeof(text) - 2] = '\n';
433
text[sizeof(text) - 1] = '\0';
434
435
const char *colorAttr = "";
436
const char *resetAttr = "";
437
438
if (stdioUseColor_) {
439
resetAttr = "\033[0m";
440
switch (message.level) {
441
case LogLevel::LNOTICE: // light green
442
colorAttr = "\033[92m";
443
break;
444
case LogLevel::LERROR: // light red
445
colorAttr = "\033[91m";
446
break;
447
case LogLevel::LWARNING: // light yellow
448
colorAttr = "\033[93m";
449
break;
450
case LogLevel::LINFO: // cyan
451
colorAttr = "\033[96m";
452
break;
453
case LogLevel::LDEBUG: // gray
454
colorAttr = "\033[90m";
455
break;
456
default:
457
break;
458
}
459
}
460
461
std::lock_guard<std::mutex> lock(stdioLock_);
462
fprintf(stderr, "%s%s%s", colorAttr, text, resetAttr);
463
#endif
464
}
465
466
void PrintfLog(const LogMessage &message) {
467
switch (message.level) {
468
case LogLevel::LVERBOSE:
469
fprintf(stderr, "V %s", message.msg.c_str());
470
break;
471
case LogLevel::LDEBUG:
472
fprintf(stderr, "D %s", message.msg.c_str());
473
break;
474
case LogLevel::LINFO:
475
fprintf(stderr, "I %s", message.msg.c_str());
476
break;
477
case LogLevel::LERROR:
478
fprintf(stderr, "E %s", message.msg.c_str());
479
break;
480
case LogLevel::LWARNING:
481
fprintf(stderr, "W %s", message.msg.c_str());
482
break;
483
case LogLevel::LNOTICE:
484
default:
485
fprintf(stderr, "N %s", message.msg.c_str());
486
break;
487
}
488
}
489
490