1 /**************************************************************************
2 *
3 * Copyright 2014-2019 Valve Software
4 * Copyright 2015-2019 Google Inc.
5 * Copyright 2019 LunarG, Inc.
6 * All Rights Reserved.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 * Author: Jon Ashburn <jon@lunarg.com>
21 * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
22 * Author: Tobin Ehlis <tobin@lunarg.com>
23 * Author: Mark Lobodzinski <mark@lunarg.com>
24 **************************************************************************/
25 #include "vk_layer_config.h"
26 #include "vulkan/vk_sdk_platform.h"
27 #include <fstream>
28 #include <iostream>
29 #include <map>
30 #include <sstream>
31 #include <string.h>
32 #include <string>
33 #include <sys/stat.h>
34 #include <vulkan/vk_layer.h>
35
36 #if defined(_WIN32)
37 #include <windows.h>
38 #endif
39
40 #define MAX_CHARS_PER_LINE 4096
41
42 class ConfigFile {
43 public:
44 ConfigFile();
45 ~ConfigFile();
46
47 const char *getOption(const std::string &_option);
48 void setOption(const std::string &_option, const std::string &_val);
49 std::string vk_layer_disables_env_var{};
50
51 private:
52 bool m_fileIsParsed;
53 std::map<std::string, std::string> m_valueMap;
54
55 std::string FindSettings();
56 void parseFile(const char *filename);
57 };
58
59 static ConfigFile g_configFileObj;
60
getEnvironment(const char * variable)61 std::string getEnvironment(const char *variable) {
62 #if !defined(__ANDROID__) && !defined(_WIN32)
63 const char *output = getenv(variable);
64 return output == NULL ? "" : output;
65 #elif defined(_WIN32)
66 int size = GetEnvironmentVariable(variable, NULL, 0);
67 if (size == 0) {
68 return "";
69 }
70 char *buffer = new char[size];
71 GetEnvironmentVariable(variable, buffer, size);
72 std::string output = buffer;
73 delete[] buffer;
74 return output;
75 #else
76 return "";
77 #endif
78 }
79
getLayerOption(const char * _option)80 VK_LAYER_EXPORT const char *getLayerOption(const char *_option) { return g_configFileObj.getOption(_option); }
GetLayerEnvVar(const char * _option)81 VK_LAYER_EXPORT const char *GetLayerEnvVar(const char *_option) {
82 g_configFileObj.vk_layer_disables_env_var = getEnvironment(_option);
83 return g_configFileObj.vk_layer_disables_env_var.c_str();
84 }
85
86 // If option is NULL or stdout, return stdout, otherwise try to open option
87 // as a filename. If successful, return file handle, otherwise stdout
getLayerLogOutput(const char * _option,const char * layerName)88 VK_LAYER_EXPORT FILE *getLayerLogOutput(const char *_option, const char *layerName) {
89 FILE *log_output = NULL;
90 if (!_option || !strcmp("stdout", _option))
91 log_output = stdout;
92 else {
93 log_output = fopen(_option, "w");
94 if (log_output == NULL) {
95 if (_option)
96 std::cout << std::endl
97 << layerName << " ERROR: Bad output filename specified: " << _option << ". Writing to STDOUT instead"
98 << std::endl
99 << std::endl;
100 log_output = stdout;
101 }
102 }
103 return log_output;
104 }
105
106 // Map option strings to flag enum values
GetLayerOptionFlags(std::string _option,std::unordered_map<std::string,VkFlags> const & enum_data,uint32_t option_default)107 VK_LAYER_EXPORT VkFlags GetLayerOptionFlags(std::string _option, std::unordered_map<std::string, VkFlags> const &enum_data,
108 uint32_t option_default) {
109 VkDebugReportFlagsEXT flags = option_default;
110 std::string option_list = g_configFileObj.getOption(_option.c_str());
111
112 while (option_list.length() != 0) {
113 // Find length of option string
114 std::size_t option_length = option_list.find(",");
115 if (option_length == option_list.npos) {
116 option_length = option_list.size();
117 }
118
119 // Get first option in list
120 const std::string option = option_list.substr(0, option_length);
121
122 auto enum_value = enum_data.find(option);
123 if (enum_value != enum_data.end()) {
124 flags |= enum_value->second;
125 }
126
127 // Remove first option from option_list
128 option_list.erase(0, option_length);
129 // Remove possible comma separator
130 std::size_t char_position = option_list.find(",");
131 if (char_position == 0) {
132 option_list.erase(char_position, 1);
133 }
134 // Remove possible space
135 char_position = option_list.find(" ");
136 if (char_position == 0) {
137 option_list.erase(char_position, 1);
138 }
139 }
140 return flags;
141 }
142
setLayerOption(const char * _option,const char * _val)143 VK_LAYER_EXPORT void setLayerOption(const char *_option, const char *_val) { g_configFileObj.setOption(_option, _val); }
144
145 // Constructor for ConfigFile. Initialize layers to log error messages to stdout by default. If a vk_layer_settings file is present,
146 // its settings will override the defaults.
ConfigFile()147 ConfigFile::ConfigFile() : m_fileIsParsed(false) {
148 m_valueMap["khronos_validation.report_flags"] = "error";
149 m_valueMap["lunarg_core_validation.report_flags"] = "error";
150 m_valueMap["lunarg_object_tracker.report_flags"] = "error";
151 m_valueMap["lunarg_parameter_validation.report_flags"] = "error";
152 m_valueMap["google_threading.report_flags"] = "error";
153 m_valueMap["google_unique_objects.report_flags"] = "error";
154
155 #ifdef WIN32
156 // For Windows, enable message logging AND OutputDebugString
157 m_valueMap["khronos_validation.debug_action"] =
158 "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
159 m_valueMap["lunarg_core_validation.debug_action"] =
160 "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
161 m_valueMap["lunarg_object_tracker.debug_action"] =
162 "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
163 m_valueMap["lunarg_parameter_validation.debug_action"] =
164 "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
165 m_valueMap["google_threading.debug_action"] =
166 "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
167 m_valueMap["google_unique_objects.debug_action"] =
168 "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
169 #else // WIN32
170 m_valueMap["khronos_validation.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
171 m_valueMap["lunarg_core_validation.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
172 m_valueMap["lunarg_object_tracker.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
173 m_valueMap["lunarg_parameter_validation.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
174 m_valueMap["google_threading.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
175 m_valueMap["google_unique_objects.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
176 #endif // WIN32
177 m_valueMap["khronos_validation.log_filename"] = "stdout";
178 m_valueMap["lunarg_core_validation.log_filename"] = "stdout";
179 m_valueMap["lunarg_object_tracker.log_filename"] = "stdout";
180 m_valueMap["lunarg_parameter_validation.log_filename"] = "stdout";
181 m_valueMap["google_threading.log_filename"] = "stdout";
182 m_valueMap["google_unique_objects.log_filename"] = "stdout";
183 }
184
~ConfigFile()185 ConfigFile::~ConfigFile() {}
186
getOption(const std::string & _option)187 const char *ConfigFile::getOption(const std::string &_option) {
188 std::map<std::string, std::string>::const_iterator it;
189 if (!m_fileIsParsed) {
190 std::string settings_file = FindSettings();
191 parseFile(settings_file.c_str());
192 }
193
194 if ((it = m_valueMap.find(_option)) == m_valueMap.end())
195 return "";
196 else
197 return it->second.c_str();
198 }
199
setOption(const std::string & _option,const std::string & _val)200 void ConfigFile::setOption(const std::string &_option, const std::string &_val) {
201 if (!m_fileIsParsed) {
202 std::string settings_file = FindSettings();
203 parseFile(settings_file.c_str());
204 }
205
206 m_valueMap[_option] = _val;
207 }
208
FindSettings()209 std::string ConfigFile::FindSettings() {
210 struct stat info;
211
212 #if defined(WIN32)
213 HKEY hive;
214 LSTATUS err = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Khronos\\Vulkan\\Settings", 0, KEY_READ, &hive);
215 if (err == ERROR_SUCCESS) {
216 char name[2048];
217 DWORD i = 0, name_size = sizeof(name), type, value, value_size = sizeof(value);
218 while (ERROR_SUCCESS ==
219 RegEnumValue(hive, i++, name, &name_size, nullptr, &type, reinterpret_cast<LPBYTE>(&value), &value_size)) {
220 // Check if the registry entry is a dword with a value of zero
221 if (type != REG_DWORD || value != 0) {
222 continue;
223 }
224
225 // Check if this actually points to a file
226 if ((stat(name, &info) != 0) || !(info.st_mode & S_IFREG)) {
227 continue;
228 }
229
230 // Use this file
231 RegCloseKey(hive);
232 return name;
233 }
234
235 RegCloseKey(hive);
236 }
237 #else
238 std::string search_path = getEnvironment("XDG_DATA_HOME");
239 if (search_path == "") {
240 search_path = getEnvironment("HOME");
241 if (search_path != "") {
242 search_path += "/.local/share";
243 }
244 }
245
246 // Use the vk_layer_settings.txt file from here, if it is present
247 if (search_path != "") {
248 std::string home_file = search_path + "/vulkan/settings.d/vk_layer_settings.txt";
249 if (stat(home_file.c_str(), &info) == 0) {
250 if (info.st_mode & S_IFREG) {
251 return home_file;
252 }
253 }
254 }
255
256 #endif
257
258 std::string env_path = getEnvironment("VK_LAYER_SETTINGS_PATH");
259
260 // If the path exists use it, else use vk_layer_settings
261 if (stat(env_path.c_str(), &info) == 0) {
262 // If this is a directory, look for vk_layer_settings within the directory
263 if (info.st_mode & S_IFDIR) {
264 return env_path + "/vk_layer_settings.txt";
265 }
266 return env_path;
267 }
268 return "vk_layer_settings.txt";
269 }
270
parseFile(const char * filename)271 void ConfigFile::parseFile(const char *filename) {
272 std::ifstream file;
273 char buf[MAX_CHARS_PER_LINE];
274
275 m_fileIsParsed = true;
276
277 file.open(filename);
278 if (!file.good()) {
279 return;
280 }
281
282 // read tokens from the file and form option, value pairs
283 file.getline(buf, MAX_CHARS_PER_LINE);
284 while (!file.eof()) {
285 char option[512];
286 char value[512];
287
288 char *pComment;
289
290 // discard any comments delimited by '#' in the line
291 pComment = strchr(buf, '#');
292 if (pComment) *pComment = '\0';
293
294 if (sscanf(buf, " %511[^\n\t =] = %511[^\n \t]", option, value) == 2) {
295 std::string optStr(option);
296 std::string valStr(value);
297 m_valueMap[optStr] = valStr;
298 }
299 file.getline(buf, MAX_CHARS_PER_LINE);
300 }
301 }
302
PrintMessageFlags(VkFlags vk_flags,char * msg_flags)303 VK_LAYER_EXPORT void PrintMessageFlags(VkFlags vk_flags, char *msg_flags) {
304 bool separator = false;
305
306 msg_flags[0] = 0;
307 if (vk_flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
308 strcat(msg_flags, "DEBUG");
309 separator = true;
310 }
311 if (vk_flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
312 if (separator) strcat(msg_flags, ",");
313 strcat(msg_flags, "INFO");
314 separator = true;
315 }
316 if (vk_flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
317 if (separator) strcat(msg_flags, ",");
318 strcat(msg_flags, "WARN");
319 separator = true;
320 }
321 if (vk_flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
322 if (separator) strcat(msg_flags, ",");
323 strcat(msg_flags, "PERF");
324 separator = true;
325 }
326 if (vk_flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
327 if (separator) strcat(msg_flags, ",");
328 strcat(msg_flags, "ERROR");
329 }
330 }
331
PrintMessageSeverity(VkFlags vk_flags,char * msg_flags)332 VK_LAYER_EXPORT void PrintMessageSeverity(VkFlags vk_flags, char *msg_flags) {
333 bool separator = false;
334
335 msg_flags[0] = 0;
336 if (vk_flags & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
337 strcat(msg_flags, "VERBOSE");
338 separator = true;
339 }
340 if (vk_flags & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
341 if (separator) strcat(msg_flags, ",");
342 strcat(msg_flags, "INFO");
343 separator = true;
344 }
345 if (vk_flags & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
346 if (separator) strcat(msg_flags, ",");
347 strcat(msg_flags, "WARN");
348 separator = true;
349 }
350 if (vk_flags & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
351 if (separator) strcat(msg_flags, ",");
352 strcat(msg_flags, "ERROR");
353 }
354 }
355
PrintMessageType(VkFlags vk_flags,char * msg_flags)356 VK_LAYER_EXPORT void PrintMessageType(VkFlags vk_flags, char *msg_flags) {
357 bool separator = false;
358
359 msg_flags[0] = 0;
360 if (vk_flags & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) {
361 strcat(msg_flags, "GEN");
362 separator = true;
363 }
364 if (vk_flags & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) {
365 strcat(msg_flags, "SPEC");
366 separator = true;
367 }
368 if (vk_flags & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) {
369 if (separator) strcat(msg_flags, ",");
370 strcat(msg_flags, "PERF");
371 separator = true;
372 }
373 }
374