1/**************************************************************************
2 *
3 * Copyright 2014 Valve Software
4 * Copyright 2015 Google Inc.
5 * All Rights Reserved.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *     http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * Author: Jon Ashburn <jon@lunarg.com>
20 * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
21 * Author: Tobin Ehlis <tobin@lunarg.com>
22 * Author: Mark Lobodzinski <mark@lunarg.com>
23 **************************************************************************/
24#include "vk_layer_config.h"
25#include "vulkan/vk_sdk_platform.h"
26#include <fstream>
27#include <iostream>
28#include <map>
29#include <string.h>
30#include <string>
31#include <sys/stat.h>
32#include <vulkan/vk_layer.h>
33
34#define MAX_CHARS_PER_LINE 4096
35
36class ConfigFile {
37  public:
38    ConfigFile();
39    ~ConfigFile();
40
41    const char *getOption(const std::string &_option);
42    void setOption(const std::string &_option, const std::string &_val);
43
44  private:
45    bool m_fileIsParsed;
46    std::map<std::string, std::string> m_valueMap;
47
48    void parseFile(const char *filename);
49};
50
51static ConfigFile g_configFileObj;
52
53std::string getEnvironment(const char *variable) {
54#if !defined(__ANDROID__) && !defined(_WIN32)
55    const char *output = getenv(variable);
56    return output == NULL ? "" : output;
57#elif defined(_WIN32)
58    int size = GetEnvironmentVariable(variable, NULL, 0);
59    if (size == 0) {
60        return "";
61    }
62    char *buffer = new char[size];
63    GetEnvironmentVariable(variable, buffer, size);
64    std::string output = buffer;
65    delete[] buffer;
66    return output;
67#else
68    return "";
69#endif
70}
71
72const char *getLayerOption(const char *_option) { return g_configFileObj.getOption(_option); }
73
74// If option is NULL or stdout, return stdout, otherwise try to open option
75// as a filename. If successful, return file handle, otherwise stdout
76FILE *getLayerLogOutput(const char *_option, const char *layerName) {
77    FILE *log_output = NULL;
78    if (!_option || !strcmp("stdout", _option))
79        log_output = stdout;
80    else {
81        log_output = fopen(_option, "w");
82        if (log_output == NULL) {
83            if (_option)
84                std::cout << std::endl
85                          << layerName << " ERROR: Bad output filename specified: " << _option << ". Writing to STDOUT instead"
86                          << std::endl
87                          << std::endl;
88            log_output = stdout;
89        }
90    }
91    return log_output;
92}
93
94// Map option strings to flag enum values
95VkFlags GetLayerOptionFlags(std::string _option, std::unordered_map<std::string, VkFlags> const &enum_data,
96                            uint32_t option_default) {
97    VkDebugReportFlagsEXT flags = option_default;
98    std::string option_list = g_configFileObj.getOption(_option.c_str());
99
100    while (option_list.length() != 0) {
101
102        // Find length of option string
103        std::size_t option_length = option_list.find(",");
104        if (option_length == option_list.npos) {
105            option_length = option_list.size();
106        }
107
108        // Get first option in list
109        const std::string option = option_list.substr(0, option_length);
110
111        auto enum_value = enum_data.find(option);
112        if (enum_value != enum_data.end()) {
113            flags |= enum_value->second;
114        }
115
116        // Remove first option from option_list
117        option_list.erase(0, option_length);
118        // Remove possible comma separator
119        std::size_t char_position = option_list.find(",");
120        if (char_position == 0) {
121            option_list.erase(char_position, 1);
122        }
123        // Remove possible space
124        char_position = option_list.find(" ");
125        if (char_position == 0) {
126            option_list.erase(char_position, 1);
127        }
128    }
129    return flags;
130}
131
132void setLayerOption(const char *_option, const char *_val) { g_configFileObj.setOption(_option, _val); }
133
134// Constructor for ConfigFile. Initialize layers to log error messages to stdout by default. If a vk_layer_settings file is present,
135// its settings will override the defaults.
136ConfigFile::ConfigFile() : m_fileIsParsed(false) {
137    m_valueMap["lunarg_core_validation.report_flags"] = "error";
138    m_valueMap["lunarg_image.report_flags"] = "error";
139    m_valueMap["lunarg_object_tracker.report_flags"] = "error";
140    m_valueMap["lunarg_parameter_validation.report_flags"] = "error";
141    m_valueMap["lunarg_swapchain.report_flags"] = "error";
142    m_valueMap["google_threading.report_flags"] = "error";
143    m_valueMap["google_unique_objects.report_flags"] = "error";
144
145#ifdef WIN32
146    // For Windows, enable message logging AND OutputDebugString
147    m_valueMap["lunarg_core_validation.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
148    m_valueMap["lunarg_image.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
149    m_valueMap["lunarg_object_tracker.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
150    m_valueMap["lunarg_parameter_validation.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
151    m_valueMap["lunarg_swapchain.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
152    m_valueMap["google_threading.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
153    m_valueMap["google_unique_objects.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG,VK_DBG_LAYER_ACTION_DEBUG_OUTPUT";
154#else  // WIN32
155    m_valueMap["lunarg_core_validation.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
156    m_valueMap["lunarg_image.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
157    m_valueMap["lunarg_object_tracker.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
158    m_valueMap["lunarg_parameter_validation.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
159    m_valueMap["lunarg_swapchain.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
160    m_valueMap["google_threading.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
161    m_valueMap["google_unique_objects.debug_action"] = "VK_DBG_LAYER_ACTION_DEFAULT,VK_DBG_LAYER_ACTION_LOG_MSG";
162#endif // WIN32
163
164    m_valueMap["lunarg_core_validation.log_filename"] = "stdout";
165    m_valueMap["lunarg_image.log_filename"] = "stdout";
166    m_valueMap["lunarg_object_tracker.log_filename"] = "stdout";
167    m_valueMap["lunarg_parameter_validation.log_filename"] = "stdout";
168    m_valueMap["lunarg_swapchain.log_filename"] = "stdout";
169    m_valueMap["google_threading.log_filename"] = "stdout";
170    m_valueMap["google_unique_objects.log_filename"] = "stdout";
171}
172
173ConfigFile::~ConfigFile() {}
174
175const char *ConfigFile::getOption(const std::string &_option) {
176    std::map<std::string, std::string>::const_iterator it;
177    if (!m_fileIsParsed) {
178        std::string envPath = getEnvironment("VK_LAYER_SETTINGS_PATH");
179
180        // If the path exists use it, else use vk_layer_settings
181        struct stat info;
182        if (stat(envPath.c_str(), &info) == 0) {
183            // If this is a directory, look for vk_layer_settings within the directory
184            if (info.st_mode & S_IFDIR) {
185                envPath += "/vk_layer_settings.txt";
186            }
187            parseFile(envPath.c_str());
188        } else {
189            parseFile("vk_layer_settings.txt");
190        }
191    }
192
193    if ((it = m_valueMap.find(_option)) == m_valueMap.end())
194        return "";
195    else
196        return it->second.c_str();
197}
198
199void ConfigFile::setOption(const std::string &_option, const std::string &_val) {
200    if (!m_fileIsParsed) {
201        std::string envPath = getEnvironment("VK_LAYER_SETTINGS_PATH");
202
203        // If the path exists use it, else use vk_layer_settings
204        struct stat info;
205        if (stat(envPath.c_str(), &info) == 0) {
206            // If this is a directory, look for vk_layer_settings within the directory
207            if (info.st_mode & S_IFDIR) {
208                envPath += "/vk_layer_settings.txt";
209            }
210            parseFile(envPath.c_str());
211        } else {
212            parseFile("vk_layer_settings.txt");
213        }
214    }
215
216    m_valueMap[_option] = _val;
217}
218
219void ConfigFile::parseFile(const char *filename) {
220    std::ifstream file;
221    char buf[MAX_CHARS_PER_LINE];
222
223    m_fileIsParsed = true;
224
225    file.open(filename);
226    if (!file.good()) {
227        return;
228    }
229
230
231    // read tokens from the file and form option, value pairs
232    file.getline(buf, MAX_CHARS_PER_LINE);
233    while (!file.eof()) {
234        char option[512];
235        char value[512];
236
237        char *pComment;
238
239        // discard any comments delimited by '#' in the line
240        pComment = strchr(buf, '#');
241        if (pComment)
242            *pComment = '\0';
243
244        if (sscanf(buf, " %511[^\n\t =] = %511[^\n \t]", option, value) == 2) {
245            std::string optStr(option);
246            std::string valStr(value);
247            m_valueMap[optStr] = valStr;
248        }
249        file.getline(buf, MAX_CHARS_PER_LINE);
250    }
251}
252
253void print_msg_flags(VkFlags msgFlags, char *msg_flags) {
254    bool separator = false;
255
256    msg_flags[0] = 0;
257    if (msgFlags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
258        strcat(msg_flags, "DEBUG");
259        separator = true;
260    }
261    if (msgFlags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
262        if (separator)
263            strcat(msg_flags, ",");
264        strcat(msg_flags, "INFO");
265        separator = true;
266    }
267    if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
268        if (separator)
269            strcat(msg_flags, ",");
270        strcat(msg_flags, "WARN");
271        separator = true;
272    }
273    if (msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
274        if (separator)
275            strcat(msg_flags, ",");
276        strcat(msg_flags, "PERF");
277        separator = true;
278    }
279    if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
280        if (separator)
281            strcat(msg_flags, ",");
282        strcat(msg_flags, "ERROR");
283    }
284}
285