summaryrefslogtreecommitdiff
path: root/src/common/logging
diff options
context:
space:
mode:
authorGravatar Yuri Kunde Schlesner2014-10-28 05:36:00 -0200
committerGravatar Yuri Kunde Schlesner2014-12-13 01:59:52 -0200
commit616d87444313db865c60fbeee36ebe5250ef301e (patch)
treefb99bf8bebfdf8c825c5d3e4f01fb4779ceaba68 /src/common/logging
parentAdd SCOPE_EXIT macro to conveniently execute cleanup actions (diff)
downloadyuzu-616d87444313db865c60fbeee36ebe5250ef301e.tar.gz
yuzu-616d87444313db865c60fbeee36ebe5250ef301e.tar.xz
yuzu-616d87444313db865c60fbeee36ebe5250ef301e.zip
New logging system
Diffstat (limited to '')
-rw-r--r--src/common/logging/backend.cpp151
-rw-r--r--src/common/logging/backend.h134
-rw-r--r--src/common/logging/log.h115
-rw-r--r--src/common/logging/text_formatter.cpp47
-rw-r--r--src/common/logging/text_formatter.h26
5 files changed, 473 insertions, 0 deletions
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
new file mode 100644
index 000000000..e79b84604
--- /dev/null
+++ b/src/common/logging/backend.cpp
@@ -0,0 +1,151 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "common/log.h" // For _dbg_assert_
8
9#include "common/logging/backend.h"
10#include "common/logging/log.h"
11#include "common/logging/text_formatter.h"
12
13namespace Log {
14
15static std::shared_ptr<Logger> global_logger;
16
17/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
18#define ALL_LOG_CLASSES() \
19 CLS(Log) \
20 CLS(Common) \
21 SUB(Common, Filesystem) \
22 SUB(Common, Memory) \
23 CLS(Core) \
24 SUB(Core, ARM11) \
25 CLS(Config) \
26 CLS(Debug) \
27 SUB(Debug, Emulated) \
28 SUB(Debug, GPU) \
29 SUB(Debug, Breakpoint) \
30 CLS(Kernel) \
31 SUB(Kernel, SVC) \
32 CLS(Service) \
33 SUB(Service, SRV) \
34 SUB(Service, FS) \
35 SUB(Service, APT) \
36 SUB(Service, GSP) \
37 SUB(Service, AC) \
38 SUB(Service, PTM) \
39 SUB(Service, CFG) \
40 SUB(Service, DSP) \
41 SUB(Service, HID) \
42 CLS(HW) \
43 SUB(HW, Memory) \
44 SUB(HW, GPU) \
45 CLS(Frontend) \
46 CLS(Render) \
47 SUB(Render, Software) \
48 SUB(Render, OpenGL) \
49 CLS(Loader)
50
51Logger::Logger() {
52 // Register logging classes so that they can be queried at runtime
53 size_t parent_class;
54 all_classes.reserve((size_t)Class::Count);
55
56#define CLS(x) \
57 all_classes.push_back(Class::x); \
58 parent_class = all_classes.size() - 1;
59#define SUB(x, y) \
60 all_classes.push_back(Class::x##_##y); \
61 all_classes[parent_class].num_children += 1;
62
63 ALL_LOG_CLASSES()
64#undef CLS
65#undef SUB
66
67 // Ensures that ALL_LOG_CLASSES isn't missing any entries.
68 _dbg_assert_(Log, all_classes.size() == (size_t)Class::Count);
69}
70
71// GetClassName is a macro defined by Windows.h, grrr...
72const char* Logger::GetLogClassName(Class log_class) {
73 switch (log_class) {
74#define CLS(x) case Class::x: return #x;
75#define SUB(x, y) case Class::x##_##y: return #x "." #y;
76 ALL_LOG_CLASSES()
77#undef CLS
78#undef SUB
79 }
80 return "Unknown";
81}
82
83const char* Logger::GetLevelName(Level log_level) {
84#define LVL(x) case Level::x: return #x
85 switch (log_level) {
86 LVL(Trace);
87 LVL(Debug);
88 LVL(Info);
89 LVL(Warning);
90 LVL(Error);
91 LVL(Critical);
92 }
93 return "Unknown";
94#undef LVL
95}
96
97void Logger::LogMessage(Entry entry) {
98 ring_buffer.Push(std::move(entry));
99}
100
101size_t Logger::GetEntries(Entry* out_buffer, size_t buffer_len) {
102 return ring_buffer.BlockingPop(out_buffer, buffer_len);
103}
104
105std::shared_ptr<Logger> InitGlobalLogger() {
106 global_logger = std::make_shared<Logger>();
107 return global_logger;
108}
109
110Entry CreateEntry(Class log_class, Level log_level,
111 const char* filename, unsigned int line_nr, const char* function,
112 const char* format, va_list args) {
113 using std::chrono::steady_clock;
114 using std::chrono::duration_cast;
115
116 static steady_clock::time_point time_origin = steady_clock::now();
117
118 std::array<char, 4 * 1024> formatting_buffer;
119
120 Entry entry;
121 entry.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin);
122 entry.log_class = log_class;
123 entry.log_level = log_level;
124
125 snprintf(formatting_buffer.data(), formatting_buffer.size(), "%s:%s:%u", filename, function, line_nr);
126 entry.location = std::string(formatting_buffer.data());
127
128 vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args);
129 entry.message = std::string(formatting_buffer.data());
130
131 return std::move(entry);
132}
133
134void LogMessage(Class log_class, Level log_level,
135 const char* filename, unsigned int line_nr, const char* function,
136 const char* format, ...) {
137 va_list args;
138 va_start(args, format);
139 Entry entry = CreateEntry(log_class, log_level,
140 filename, line_nr, function, format, args);
141 va_end(args);
142
143 if (global_logger != nullptr && !global_logger->IsClosed()) {
144 global_logger->LogMessage(std::move(entry));
145 } else {
146 // Fall back to directly printing to stderr
147 PrintMessage(entry);
148 }
149}
150
151}
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
new file mode 100644
index 000000000..ae270efe8
--- /dev/null
+++ b/src/common/logging/backend.h
@@ -0,0 +1,134 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstdarg>
8#include <memory>
9#include <vector>
10
11#include "common/concurrent_ring_buffer.h"
12
13#include "common/logging/log.h"
14
15namespace Log {
16
17/**
18 * A log entry. Log entries are store in a structured format to permit more varied output
19 * formatting on different frontends, as well as facilitating filtering and aggregation.
20 */
21struct Entry {
22 std::chrono::microseconds timestamp;
23 Class log_class;
24 Level log_level;
25 std::string location;
26 std::string message;
27
28 Entry() = default;
29
30 // TODO(yuriks) Use defaulted move constructors once MSVC supports them
31#define MOVE(member) member(std::move(o.member))
32 Entry(Entry&& o)
33 : MOVE(timestamp), MOVE(log_class), MOVE(log_level),
34 MOVE(location), MOVE(message)
35 {}
36#undef MOVE
37
38 Entry& operator=(const Entry&& o) {
39#define MOVE(member) member = std::move(o.member)
40 MOVE(timestamp);
41 MOVE(log_class);
42 MOVE(log_level);
43 MOVE(location);
44 MOVE(message);
45#undef MOVE
46 return *this;
47 }
48};
49
50struct ClassInfo {
51 Class log_class;
52
53 /**
54 * Total number of (direct or indirect) sub classes this class has. If any, they follow in
55 * sequence after this class in the class list.
56 */
57 unsigned int num_children = 0;
58
59 ClassInfo(Class log_class) : log_class(log_class) {}
60};
61
62/**
63 * Logging management class. This class has the dual purpose of acting as an exchange point between
64 * the logging clients and the log outputter, as well as containing reflection info about available
65 * log classes.
66 */
67class Logger {
68private:
69 using Buffer = Common::ConcurrentRingBuffer<Entry, 16 * 1024 / sizeof(Entry)>;
70
71public:
72 static const size_t QUEUE_CLOSED = Buffer::QUEUE_CLOSED;
73
74 Logger();
75
76 /**
77 * Returns a list of all vector classes and subclasses. The sequence returned is a pre-order of
78 * classes and subclasses, which together with the `num_children` field in ClassInfo, allows
79 * you to recover the hierarchy.
80 */
81 const std::vector<ClassInfo>& GetClasses() const { return all_classes; }
82
83 /**
84 * Returns the name of the passed log class as a C-string. Subclasses are separated by periods
85 * instead of underscores as in the enumeration.
86 */
87 static const char* GetLogClassName(Class log_class);
88
89 /**
90 * Returns the name of the passed log level as a C-string.
91 */
92 static const char* GetLevelName(Level log_level);
93
94 /**
95 * Appends a messages to the log buffer.
96 * @note This function is thread safe.
97 */
98 void LogMessage(Entry entry);
99
100 /**
101 * Retrieves a batch of messages from the log buffer, blocking until they are available.
102 * @note This function is thread safe.
103 *
104 * @param out_buffer Destination buffer that will receive the log entries.
105 * @param buffer_len The maximum size of `out_buffer`.
106 * @return The number of entries stored. In case the logger is shutting down, `QUEUE_CLOSED` is
107 * returned, no entries are stored and the logger should shutdown.
108 */
109 size_t GetEntries(Entry* out_buffer, size_t buffer_len);
110
111 /**
112 * Initiates a shutdown of the logger. This will indicate to log output clients that they
113 * should shutdown.
114 */
115 void Close() { ring_buffer.Close(); }
116
117 /**
118 * Returns true if Close() has already been called on the Logger.
119 */
120 bool IsClosed() const { return ring_buffer.IsClosed(); }
121
122private:
123 Buffer ring_buffer;
124 std::vector<ClassInfo> all_classes;
125};
126
127/// Creates a log entry by formatting the given source location, and message.
128Entry CreateEntry(Class log_class, Level log_level,
129 const char* filename, unsigned int line_nr, const char* function,
130 const char* format, va_list args);
131/// Initializes the default Logger.
132std::shared_ptr<Logger> InitGlobalLogger();
133
134}
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
new file mode 100644
index 000000000..1eec34fbb
--- /dev/null
+++ b/src/common/logging/log.h
@@ -0,0 +1,115 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cassert>
8#include <chrono>
9#include <string>
10
11#include "common/common_types.h"
12
13namespace Log {
14
15/// Specifies the severity or level of detail of the log message.
16enum class Level : u8 {
17 Trace, ///< Extremely detailed and repetitive debugging information that is likely to
18 /// pollute logs.
19 Debug, ///< Less detailed debugging information.
20 Info, ///< Status information from important points during execution.
21 Warning, ///< Minor or potential problems found during execution of a task.
22 Error, ///< Major problems found during execution of a task that prevent it from being
23 /// completed.
24 Critical, ///< Major problems during execution that threathen the stability of the entire
25 /// application.
26
27 Count ///< Total number of logging levels
28};
29
30typedef u8 ClassType;
31
32/**
33 * Specifies the sub-system that generated the log message.
34 *
35 * @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in log.cpp.
36 */
37enum class Class : ClassType {
38 Log, ///< Messages about the log system itself
39 Common, ///< Library routines
40 Common_Filesystem, ///< Filesystem interface library
41 Common_Memory, ///< Memory mapping and management functions
42 Core, ///< LLE emulation core
43 Core_ARM11, ///< ARM11 CPU core
44 Config, ///< Emulator configuration (including commandline)
45 Debug, ///< Debugging tools
46 Debug_Emulated, ///< Debug messages from the emulated programs
47 Debug_GPU, ///< GPU debugging tools
48 Debug_Breakpoint, ///< Logging breakpoints and watchpoints
49 Kernel, ///< The HLE implementation of the CTR kernel
50 Kernel_SVC, ///< Kernel system calls
51 Service, ///< HLE implementation of system services. Each major service
52 /// should have its own subclass.
53 Service_SRV, ///< The SRV (Service Directory) implementation
54 Service_FS, ///< The FS (Filesystem) service implementation
55 Service_APT, ///< The APT (Applets) service
56 Service_GSP, ///< The GSP (GPU control) service
57 Service_AC, ///< The AC (WiFi status) service
58 Service_PTM, ///< The PTM (Power status & misc.) service
59 Service_CFG, ///< The CFG (Configuration) service
60 Service_DSP, ///< The DSP (DSP control) service
61 Service_HID, ///< The HID (User input) service
62 HW, ///< Low-level hardware emulation
63 HW_Memory, ///< Memory-map and address translation
64 HW_GPU, ///< GPU control emulation
65 Frontend, ///< Emulator UI
66 Render, ///< Emulator video output and hardware acceleration
67 Render_Software, ///< Software renderer backend
68 Render_OpenGL, ///< OpenGL backend
69 Loader, ///< ROM loader
70
71 Count ///< Total number of logging classes
72};
73
74/**
75 * Level below which messages are simply discarded without buffering regardless of the display
76 * settings.
77 */
78const Level MINIMUM_LEVEL =
79#ifdef _DEBUG
80 Level::Trace;
81#else
82 Level::Debug;
83#endif
84
85/**
86 * Logs a message to the global logger. This proxy exists to avoid exposing the details of the
87 * Logger class, including the ConcurrentRingBuffer template, to all files that desire to log
88 * messages, reducing unecessary recompilations.
89 */
90void LogMessage(Class log_class, Level log_level,
91 const char* filename, unsigned int line_nr, const char* function,
92#ifdef _MSC_VER
93 _Printf_format_string_
94#endif
95 const char* format, ...)
96#ifdef __GNUC__
97 __attribute__((format(printf, 6, 7)))
98#endif
99 ;
100
101} // namespace Log
102
103#define LOG_GENERIC(log_class, log_level, ...) \
104 do { \
105 if (::Log::Level::log_level >= ::Log::MINIMUM_LEVEL) \
106 ::Log::LogMessage(::Log::Class::log_class, ::Log::Level::log_level, \
107 __FILE__, __LINE__, __func__, __VA_ARGS__); \
108 } while (0)
109
110#define LOG_TRACE( log_class, ...) LOG_GENERIC(log_class, Trace, __VA_ARGS__)
111#define LOG_DEBUG( log_class, ...) LOG_GENERIC(log_class, Debug, __VA_ARGS__)
112#define LOG_INFO( log_class, ...) LOG_GENERIC(log_class, Info, __VA_ARGS__)
113#define LOG_WARNING( log_class, ...) LOG_GENERIC(log_class, Warning, __VA_ARGS__)
114#define LOG_ERROR( log_class, ...) LOG_GENERIC(log_class, Error, __VA_ARGS__)
115#define LOG_CRITICAL(log_class, ...) LOG_GENERIC(log_class, Critical, __VA_ARGS__)
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp
new file mode 100644
index 000000000..01c355bb6
--- /dev/null
+++ b/src/common/logging/text_formatter.cpp
@@ -0,0 +1,47 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <array>
6#include <cstdio>
7
8#include "common/logging/backend.h"
9#include "common/logging/log.h"
10#include "common/logging/text_formatter.h"
11
12namespace Log {
13
14void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len) {
15 unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000);
16 unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000);
17
18 const char* class_name = Logger::GetLogClassName(entry.log_class);
19 const char* level_name = Logger::GetLevelName(entry.log_level);
20
21 snprintf(out_text, text_len, "[%4u.%06u] %s <%s> %s: %s",
22 time_seconds, time_fractional, class_name, level_name,
23 entry.location.c_str(), entry.message.c_str());
24}
25
26void PrintMessage(const Entry& entry) {
27 std::array<char, 4 * 1024> format_buffer;
28 FormatLogMessage(entry, format_buffer.data(), format_buffer.size());
29 fputs(format_buffer.data(), stderr);
30 fputc('\n', stderr);
31}
32
33void TextLoggingLoop(std::shared_ptr<Logger> logger) {
34 std::array<Entry, 256> entry_buffer;
35
36 while (true) {
37 size_t num_entries = logger->GetEntries(entry_buffer.data(), entry_buffer.size());
38 if (num_entries == Logger::QUEUE_CLOSED) {
39 break;
40 }
41 for (size_t i = 0; i < num_entries; ++i) {
42 PrintMessage(entry_buffer[i]);
43 }
44 }
45}
46
47}
diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h
new file mode 100644
index 000000000..6c2a6f1ea
--- /dev/null
+++ b/src/common/logging/text_formatter.h
@@ -0,0 +1,26 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstddef>
8#include <memory>
9
10namespace Log {
11
12class Logger;
13struct Entry;
14
15/// Formats a log entry into the provided text buffer.
16void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len);
17/// Formats and prints a log entry to stderr.
18void PrintMessage(const Entry& entry);
19
20/**
21 * Logging loop that repeatedly reads messages from the provided logger and prints them to the
22 * console. It is the baseline barebones log outputter.
23 */
24void TextLoggingLoop(std::shared_ptr<Logger> logger);
25
26}