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
32namespace {
33void 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
73class 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
85class 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
101class 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
112class 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
122OstreamLoaderLogRecorder::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
128bool 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
142DebugUtilsLogRecorder::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
154bool 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
190bool 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
200static 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
213LogcatLoaderLogRecorder::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
222bool 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
239DebuggerLoaderLogRecorder::DebuggerLoaderLogRecorder(void* user_data, XrLoaderLogMessageSeverityFlags flags)
240 : LoaderLogRecorder(XR_LOADER_LOG_DEBUGGER, user_data, flags, 0xFFFFFFFFUL) {
241 // Automatically start
242 Start();
243}
244
245bool 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
262std::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
267std::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
273std::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__
280std::unique_ptr<LoaderLogRecorder> MakeLogcatLoaderLogRecorder() {
281 std::unique_ptr<LoaderLogRecorder> recorder(new LogcatLoaderLogRecorder());
282 return recorder;
283}
284#endif
285
286#ifdef _WIN32
287std::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