1 | // Copyright (c) 2017-2023, The Khronos Group Inc. |
2 | // Copyright (c) 2017-2019 Valve Corporation |
3 | // Copyright (c) 2017-2019 LunarG, Inc. |
4 | // |
5 | // SPDX-License-Identifier: Apache-2.0 OR MIT |
6 | // |
7 | // Initial Author: Mark Young <marky@lunarg.com> |
8 | // |
9 | |
10 | #include "loader_logger_recorders.hpp" |
11 | |
12 | #include "hex_and_handles.h" |
13 | #include "loader_logger.hpp" |
14 | |
15 | #include <openxr/openxr.h> |
16 | |
17 | #include <memory> |
18 | #include <string> |
19 | #include <vector> |
20 | #include <iostream> |
21 | #include <sstream> |
22 | |
23 | #ifdef __ANDROID__ |
24 | #include "android/log.h" |
25 | #endif |
26 | |
27 | #ifdef _WIN32 |
28 | #include <windows.h> |
29 | #endif |
30 | |
31 | // Anonymous namespace to keep these types private |
32 | namespace { |
33 | void OutputMessageToStream(std::ostream& os, XrLoaderLogMessageSeverityFlagBits message_severity, |
34 | XrLoaderLogMessageTypeFlags message_type, const XrLoaderLogMessengerCallbackData* callback_data) { |
35 | if (XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT > message_severity) { |
36 | os << "Verbose [" ; |
37 | } else if (XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT > message_severity) { |
38 | os << "Info [" ; |
39 | } else if (XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT > message_severity) { |
40 | os << "Warning [" ; |
41 | } else { |
42 | os << "Error [" ; |
43 | } |
44 | switch (message_type) { |
45 | case XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT: |
46 | os << "GENERAL" ; |
47 | break; |
48 | case XR_LOADER_LOG_MESSAGE_TYPE_SPECIFICATION_BIT: |
49 | os << "SPEC" ; |
50 | break; |
51 | case XR_LOADER_LOG_MESSAGE_TYPE_PERFORMANCE_BIT: |
52 | os << "PERF" ; |
53 | break; |
54 | default: |
55 | os << "UNKNOWN" ; |
56 | break; |
57 | } |
58 | os << " | " << callback_data->command_name << " | " << callback_data->message_id << "] : " << callback_data->message |
59 | << std::endl; |
60 | |
61 | for (uint32_t obj = 0; obj < callback_data->object_count; ++obj) { |
62 | os << " Object[" << obj << "] = " << callback_data->objects[obj].ToString(); |
63 | os << std::endl; |
64 | } |
65 | for (uint32_t label = 0; label < callback_data->session_labels_count; ++label) { |
66 | os << " SessionLabel[" << std::to_string(label) << "] = " << callback_data->session_labels[label].labelName; |
67 | os << std::endl; |
68 | } |
69 | } |
70 | |
71 | // With std::cerr: Standard Error logger, always on for now |
72 | // With std::cout: Standard Output logger used with XR_LOADER_DEBUG |
73 | class OstreamLoaderLogRecorder : public LoaderLogRecorder { |
74 | public: |
75 | OstreamLoaderLogRecorder(std::ostream& os, void* user_data, XrLoaderLogMessageSeverityFlags flags); |
76 | |
77 | bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type, |
78 | const XrLoaderLogMessengerCallbackData* callback_data) override; |
79 | |
80 | private: |
81 | std::ostream& os_; |
82 | }; |
83 | |
84 | // Debug Utils logger used with XR_EXT_debug_utils |
85 | class DebugUtilsLogRecorder : public LoaderLogRecorder { |
86 | public: |
87 | DebugUtilsLogRecorder(const XrDebugUtilsMessengerCreateInfoEXT* create_info, XrDebugUtilsMessengerEXT debug_messenger); |
88 | |
89 | bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type, |
90 | const XrLoaderLogMessengerCallbackData* callback_data) override; |
91 | |
92 | // Extension-specific logging functions |
93 | bool LogDebugUtilsMessage(XrDebugUtilsMessageSeverityFlagsEXT message_severity, XrDebugUtilsMessageTypeFlagsEXT message_type, |
94 | const XrDebugUtilsMessengerCallbackDataEXT* callback_data) override; |
95 | |
96 | private: |
97 | PFN_xrDebugUtilsMessengerCallbackEXT _user_callback; |
98 | }; |
99 | #ifdef __ANDROID__ |
100 | |
101 | class LogcatLoaderLogRecorder : public LoaderLogRecorder { |
102 | public: |
103 | LogcatLoaderLogRecorder(); |
104 | |
105 | bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type, |
106 | const XrLoaderLogMessengerCallbackData* callback_data) override; |
107 | }; |
108 | #endif |
109 | |
110 | #ifdef _WIN32 |
111 | // Output to debugger |
112 | class DebuggerLoaderLogRecorder : public LoaderLogRecorder { |
113 | public: |
114 | DebuggerLoaderLogRecorder(void* user_data, XrLoaderLogMessageSeverityFlags flags); |
115 | |
116 | bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type, |
117 | const XrLoaderLogMessengerCallbackData* callback_data) override; |
118 | }; |
119 | #endif |
120 | |
121 | // Unified stdout/stderr logger |
122 | OstreamLoaderLogRecorder::OstreamLoaderLogRecorder(std::ostream& os, void* user_data, XrLoaderLogMessageSeverityFlags flags) |
123 | : LoaderLogRecorder(XR_LOADER_LOG_STDOUT, user_data, flags, 0xFFFFFFFFUL), os_(os) { |
124 | // Automatically start |
125 | Start(); |
126 | } |
127 | |
128 | bool OstreamLoaderLogRecorder::LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, |
129 | XrLoaderLogMessageTypeFlags message_type, |
130 | const XrLoaderLogMessengerCallbackData* callback_data) { |
131 | if (_active && 0 != (_message_severities & message_severity) && 0 != (_message_types & message_type)) { |
132 | OutputMessageToStream(os_, message_severity, message_type, callback_data); |
133 | } |
134 | |
135 | // Return of "true" means that we should exit the application after the logged message. We |
136 | // don't want to do that for our internal logging. Only let a user return true. |
137 | return false; |
138 | } |
139 | |
140 | // A logger associated with the XR_EXT_debug_utils extension |
141 | |
142 | DebugUtilsLogRecorder::DebugUtilsLogRecorder(const XrDebugUtilsMessengerCreateInfoEXT* create_info, |
143 | XrDebugUtilsMessengerEXT debug_messenger) |
144 | : LoaderLogRecorder(XR_LOADER_LOG_DEBUG_UTILS, static_cast<void*>(create_info->userData), |
145 | DebugUtilsSeveritiesToLoaderLogMessageSeverities(create_info->messageSeverities), |
146 | DebugUtilsMessageTypesToLoaderLogMessageTypes(create_info->messageTypes)), |
147 | _user_callback(create_info->userCallback) { |
148 | // Use the debug messenger value to uniquely identify this logger with that messenger |
149 | _unique_id = MakeHandleGeneric(debug_messenger); |
150 | Start(); |
151 | } |
152 | |
153 | // Extension-specific logging functions |
154 | bool DebugUtilsLogRecorder::LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, |
155 | XrLoaderLogMessageTypeFlags message_type, |
156 | const XrLoaderLogMessengerCallbackData* callback_data) { |
157 | bool should_exit = false; |
158 | if (_active && 0 != (_message_severities & message_severity) && 0 != (_message_types & message_type)) { |
159 | XrDebugUtilsMessageSeverityFlagsEXT utils_severity = DebugUtilsSeveritiesToLoaderLogMessageSeverities(message_severity); |
160 | XrDebugUtilsMessageTypeFlagsEXT utils_type = LoaderLogMessageTypesToDebugUtilsMessageTypes(message_type); |
161 | |
162 | // Convert the loader log message into the debug utils log message information |
163 | XrDebugUtilsMessengerCallbackDataEXT utils_callback_data{}; |
164 | utils_callback_data.type = XR_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT; |
165 | utils_callback_data.messageId = callback_data->message_id; |
166 | utils_callback_data.functionName = callback_data->command_name; |
167 | utils_callback_data.message = callback_data->message; |
168 | |
169 | XrDebugUtilsObjectNameInfoEXT example_utils_info{}; |
170 | example_utils_info.type = XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; |
171 | std::vector<XrDebugUtilsObjectNameInfoEXT> utils_objects(callback_data->object_count, example_utils_info); |
172 | for (uint8_t object = 0; object < callback_data->object_count; ++object) { |
173 | utils_objects[object].objectHandle = callback_data->objects[object].handle; |
174 | utils_objects[object].objectType = callback_data->objects[object].type; |
175 | utils_objects[object].objectName = callback_data->objects[object].name.c_str(); |
176 | } |
177 | utils_callback_data.objectCount = callback_data->object_count; |
178 | utils_callback_data.objects = utils_objects.data(); |
179 | utils_callback_data.sessionLabelCount = callback_data->session_labels_count; |
180 | utils_callback_data.sessionLabels = callback_data->session_labels; |
181 | |
182 | // Call the user callback with the appropriate info |
183 | // Return of "true" means that we should exit the application after the logged message. |
184 | should_exit = (_user_callback(utils_severity, utils_type, &utils_callback_data, _user_data) == XR_TRUE); |
185 | } |
186 | |
187 | return should_exit; |
188 | } |
189 | |
190 | bool DebugUtilsLogRecorder::LogDebugUtilsMessage(XrDebugUtilsMessageSeverityFlagsEXT message_severity, |
191 | XrDebugUtilsMessageTypeFlagsEXT message_type, |
192 | const XrDebugUtilsMessengerCallbackDataEXT* callback_data) { |
193 | // Call the user callback with the appropriate info |
194 | // Return of "true" means that we should exit the application after the logged message. |
195 | return (_user_callback(message_severity, message_type, callback_data, _user_data) == XR_TRUE); |
196 | } |
197 | |
198 | #ifdef __ANDROID__ |
199 | |
200 | static inline android_LogPriority LoaderToAndroidLogPriority(XrLoaderLogMessageSeverityFlags message_severity) { |
201 | if (0 != (message_severity & XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT)) { |
202 | return ANDROID_LOG_ERROR; |
203 | } |
204 | if (0 != (message_severity & XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT)) { |
205 | return ANDROID_LOG_WARN; |
206 | } |
207 | if (0 != (message_severity & XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT)) { |
208 | return ANDROID_LOG_INFO; |
209 | } |
210 | return ANDROID_LOG_VERBOSE; |
211 | } |
212 | |
213 | LogcatLoaderLogRecorder::LogcatLoaderLogRecorder() |
214 | : LoaderLogRecorder(XR_LOADER_LOG_LOGCAT, nullptr, |
215 | XR_LOADER_LOG_MESSAGE_SEVERITY_VERBOSE_BIT | XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT | |
216 | XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT | XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT, |
217 | 0xFFFFFFFFUL) { |
218 | // Automatically start |
219 | Start(); |
220 | } |
221 | |
222 | bool LogcatLoaderLogRecorder::LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, |
223 | XrLoaderLogMessageTypeFlags message_type, |
224 | const XrLoaderLogMessengerCallbackData* callback_data) { |
225 | if (_active && 0 != (_message_severities & message_severity) && 0 != (_message_types & message_type)) { |
226 | std::stringstream ss; |
227 | OutputMessageToStream(ss, message_severity, message_type, callback_data); |
228 | __android_log_write(LoaderToAndroidLogPriority(message_severity), "OpenXR-Loader" , ss.str().c_str()); |
229 | } |
230 | |
231 | // Return of "true" means that we should exit the application after the logged message. We |
232 | // don't want to do that for our internal logging. Only let a user return true. |
233 | return false; |
234 | } |
235 | #endif // __ANDROID__ |
236 | |
237 | #ifdef _WIN32 |
238 | // Unified stdout/stderr logger |
239 | DebuggerLoaderLogRecorder::DebuggerLoaderLogRecorder(void* user_data, XrLoaderLogMessageSeverityFlags flags) |
240 | : LoaderLogRecorder(XR_LOADER_LOG_DEBUGGER, user_data, flags, 0xFFFFFFFFUL) { |
241 | // Automatically start |
242 | Start(); |
243 | } |
244 | |
245 | bool DebuggerLoaderLogRecorder::LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, |
246 | XrLoaderLogMessageTypeFlags message_type, |
247 | const XrLoaderLogMessengerCallbackData* callback_data) { |
248 | if (_active && 0 != (_message_severities & message_severity) && 0 != (_message_types & message_type)) { |
249 | std::stringstream ss; |
250 | OutputMessageToStream(ss, message_severity, message_type, callback_data); |
251 | |
252 | OutputDebugStringA(ss.str().c_str()); |
253 | } |
254 | |
255 | // Return of "true" means that we should exit the application after the logged message. We |
256 | // don't want to do that for our internal logging. Only let a user return true. |
257 | return false; |
258 | } |
259 | #endif |
260 | } // namespace |
261 | |
262 | std::unique_ptr<LoaderLogRecorder> MakeStdOutLoaderLogRecorder(void* user_data, XrLoaderLogMessageSeverityFlags flags) { |
263 | std::unique_ptr<LoaderLogRecorder> recorder(new OstreamLoaderLogRecorder(std::cout, user_data, flags)); |
264 | return recorder; |
265 | } |
266 | |
267 | std::unique_ptr<LoaderLogRecorder> MakeStdErrLoaderLogRecorder(void* user_data) { |
268 | std::unique_ptr<LoaderLogRecorder> recorder( |
269 | new OstreamLoaderLogRecorder(std::cerr, user_data, XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT)); |
270 | return recorder; |
271 | } |
272 | |
273 | std::unique_ptr<LoaderLogRecorder> MakeDebugUtilsLoaderLogRecorder(const XrDebugUtilsMessengerCreateInfoEXT* create_info, |
274 | XrDebugUtilsMessengerEXT debug_messenger) { |
275 | std::unique_ptr<LoaderLogRecorder> recorder(new DebugUtilsLogRecorder(create_info, debug_messenger)); |
276 | return recorder; |
277 | } |
278 | |
279 | #ifdef __ANDROID__ |
280 | std::unique_ptr<LoaderLogRecorder> MakeLogcatLoaderLogRecorder() { |
281 | std::unique_ptr<LoaderLogRecorder> recorder(new LogcatLoaderLogRecorder()); |
282 | return recorder; |
283 | } |
284 | #endif |
285 | |
286 | #ifdef _WIN32 |
287 | std::unique_ptr<LoaderLogRecorder> MakeDebuggerLoaderLogRecorder(void* user_data) { |
288 | std::unique_ptr<LoaderLogRecorder> recorder(new DebuggerLoaderLogRecorder(user_data, XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT)); |
289 | return recorder; |
290 | } |
291 | #endif |
292 | |