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