1 /* Copyright (c) 2015-2017 The Khronos Group Inc. 2 * Copyright (c) 2015-2017 Valve Corporation 3 * Copyright (c) 2015-2017 LunarG, Inc. 4 * Copyright (C) 2015-2017 Google Inc. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 * Author: Mark Lobodzinski <mark (at) lunarg.com> 19 * Author: Jon Ashburn <jon (at) lunarg.com> 20 * Author: Tobin Ehlis <tobin (at) lunarg.com> 21 */ 22 23 #include <mutex> 24 #include <cinttypes> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <unordered_map> 29 30 #include "vk_loader_platform.h" 31 #include "vulkan/vulkan.h" 32 #include "vk_layer_config.h" 33 #include "vk_layer_data.h" 34 #include "vk_layer_logging.h" 35 #include "vk_layer_table.h" 36 #include "vk_object_types.h" 37 #include "vulkan/vk_layer.h" 38 #include "vk_object_types.h" 39 #include "vk_enum_string_helper.h" 40 #include "vk_layer_extension_utils.h" 41 #include "vk_layer_table.h" 42 #include "vk_layer_utils.h" 43 #include "vulkan/vk_layer.h" 44 #include "vk_dispatch_table_helper.h" 45 #include "vk_validation_error_messages.h" 46 47 namespace object_tracker { 48 49 // Object Tracker ERROR codes 50 enum ObjectTrackerError { 51 OBJTRACK_NONE, // Used for INFO & other non-error messages 52 OBJTRACK_UNKNOWN_OBJECT, // Updating uses of object that's not in global object list 53 OBJTRACK_INTERNAL_ERROR, // Bug with data tracking within the layer 54 OBJTRACK_OBJECT_LEAK, // OBJECT was not correctly freed/destroyed 55 }; 56 57 // Object Status -- used to track state of individual objects 58 typedef VkFlags ObjectStatusFlags; 59 enum ObjectStatusFlagBits { 60 OBJSTATUS_NONE = 0x00000000, // No status is set 61 OBJSTATUS_FENCE_IS_SUBMITTED = 0x00000001, // Fence has been submitted 62 OBJSTATUS_VIEWPORT_BOUND = 0x00000002, // Viewport state object has been bound 63 OBJSTATUS_RASTER_BOUND = 0x00000004, // Viewport state object has been bound 64 OBJSTATUS_COLOR_BLEND_BOUND = 0x00000008, // Viewport state object has been bound 65 OBJSTATUS_DEPTH_STENCIL_BOUND = 0x00000010, // Viewport state object has been bound 66 OBJSTATUS_GPU_MEM_MAPPED = 0x00000020, // Memory object is currently mapped 67 OBJSTATUS_COMMAND_BUFFER_SECONDARY = 0x00000040, // Command Buffer is of type SECONDARY 68 OBJSTATUS_CUSTOM_ALLOCATOR = 0x00000080, // Allocated with custom allocator 69 }; 70 71 // Object and state information structure 72 struct ObjTrackState { 73 uint64_t handle; // Object handle (new) 74 VulkanObjectType object_type; // Object type identifier 75 ObjectStatusFlags status; // Object state 76 uint64_t parent_object; // Parent object 77 }; 78 79 // Track Queue information 80 struct ObjTrackQueueInfo { 81 uint32_t queue_node_index; 82 VkQueue queue; 83 }; 84 85 // Layer name string to be logged with validation messages. 86 const char LayerName[] = "ObjectTracker"; 87 88 typedef std::unordered_map<uint64_t, ObjTrackState *> object_map_type; 89 90 struct layer_data { 91 VkInstance instance; 92 VkPhysicalDevice physical_device; 93 94 uint64_t num_objects[kVulkanObjectTypeMax + 1]; 95 uint64_t num_total_objects; 96 97 debug_report_data *report_data; 98 std::vector<VkDebugReportCallbackEXT> logging_callback; 99 // The following are for keeping track of the temporary callbacks that can 100 // be used in vkCreateInstance and vkDestroyInstance: 101 uint32_t num_tmp_callbacks; 102 VkDebugReportCallbackCreateInfoEXT *tmp_dbg_create_infos; 103 VkDebugReportCallbackEXT *tmp_callbacks; 104 105 std::vector<VkQueueFamilyProperties> queue_family_properties; 106 107 // Vector of unordered_maps per object type to hold ObjTrackState info 108 std::vector<object_map_type> object_map; 109 // Special-case map for swapchain images 110 std::unordered_map<uint64_t, ObjTrackState *> swapchainImageMap; 111 // Map of queue information structures, one per queue 112 std::unordered_map<VkQueue, ObjTrackQueueInfo *> queue_info_map; 113 114 VkLayerDispatchTable dispatch_table; 115 // Default constructor 116 layer_data() 117 : instance(nullptr), 118 physical_device(nullptr), 119 num_objects{}, 120 num_total_objects(0), 121 report_data(nullptr), 122 num_tmp_callbacks(0), 123 tmp_dbg_create_infos(nullptr), 124 tmp_callbacks(nullptr), 125 object_map{}, 126 dispatch_table{} { 127 object_map.resize(kVulkanObjectTypeMax + 1); 128 } 129 }; 130 131 extern std::unordered_map<void *, layer_data *> layer_data_map; 132 extern device_table_map ot_device_table_map; 133 extern instance_table_map ot_instance_table_map; 134 extern std::mutex global_lock; 135 extern uint64_t object_track_index; 136 extern uint32_t loader_layer_if_version; 137 extern const std::unordered_map<std::string, void *> name_to_funcptr_map; 138 139 void DeviceReportUndestroyedObjects(VkDevice device, VulkanObjectType object_type, enum UNIQUE_VALIDATION_ERROR_CODE error_code); 140 void CreateQueue(VkDevice device, VkQueue vkObj); 141 void AddQueueInfo(VkDevice device, uint32_t queue_node_index, VkQueue queue); 142 void ValidateQueueFlags(VkQueue queue, const char *function); 143 void AllocateCommandBuffer(VkDevice device, const VkCommandPool command_pool, const VkCommandBuffer command_buffer, 144 VkCommandBufferLevel level); 145 void AllocateDescriptorSet(VkDevice device, VkDescriptorPool descriptor_pool, VkDescriptorSet descriptor_set); 146 void CreateSwapchainImageObject(VkDevice dispatchable_object, VkImage swapchain_image, VkSwapchainKHR swapchain); 147 void ReportUndestroyedObjects(VkDevice device, UNIQUE_VALIDATION_ERROR_CODE error_code); 148 bool ValidateDeviceObject(uint64_t device_handle, enum UNIQUE_VALIDATION_ERROR_CODE invalid_handle_code, 149 enum UNIQUE_VALIDATION_ERROR_CODE wrong_device_code); 150 151 template <typename T1, typename T2> 152 bool ValidateObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type, bool null_allowed, 153 enum UNIQUE_VALIDATION_ERROR_CODE invalid_handle_code, enum UNIQUE_VALIDATION_ERROR_CODE wrong_device_code) { 154 if (null_allowed && (object == VK_NULL_HANDLE)) { 155 return false; 156 } 157 auto object_handle = HandleToUint64(object); 158 159 if (object_type == kVulkanObjectTypeDevice) { 160 return ValidateDeviceObject(object_handle, invalid_handle_code, wrong_device_code); 161 } 162 163 VkDebugReportObjectTypeEXT debug_object_type = get_debug_report_enum[object_type]; 164 165 layer_data *device_data = GetLayerDataPtr(get_dispatch_key(dispatchable_object), layer_data_map); 166 // Look for object in device object map 167 if (device_data->object_map[object_type].find(object_handle) == device_data->object_map[object_type].end()) { 168 // If object is an image, also look for it in the swapchain image map 169 if ((object_type != kVulkanObjectTypeImage) || 170 (device_data->swapchainImageMap.find(object_handle) == device_data->swapchainImageMap.end())) { 171 // Object not found, look for it in other device object maps 172 for (auto other_device_data : layer_data_map) { 173 if (other_device_data.second != device_data) { 174 if (other_device_data.second->object_map[object_type].find(object_handle) != 175 other_device_data.second->object_map[object_type].end() || 176 (object_type == kVulkanObjectTypeImage && other_device_data.second->swapchainImageMap.find(object_handle) != 177 other_device_data.second->swapchainImageMap.end())) { 178 // Object found on other device, report an error if object has a device parent error code 179 if ((wrong_device_code != VALIDATION_ERROR_UNDEFINED) && (object_type != kVulkanObjectTypeSurfaceKHR)) { 180 return log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, 181 object_handle, __LINE__, wrong_device_code, LayerName, 182 "Object 0x%" PRIxLEAST64 183 " was not created, allocated or retrieved from the correct device. %s", 184 object_handle, validation_error_map[wrong_device_code]); 185 } else { 186 return false; 187 } 188 } 189 } 190 } 191 // Report an error if object was not found anywhere 192 return log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle, __LINE__, 193 invalid_handle_code, LayerName, "Invalid %s Object 0x%" PRIxLEAST64 ". %s", object_string[object_type], 194 object_handle, validation_error_map[invalid_handle_code]); 195 } 196 } 197 return false; 198 } 199 200 template <typename T1, typename T2> 201 void CreateObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type, const VkAllocationCallbacks *pAllocator) { 202 layer_data *instance_data = GetLayerDataPtr(get_dispatch_key(dispatchable_object), layer_data_map); 203 204 auto object_handle = HandleToUint64(object); 205 bool custom_allocator = pAllocator != nullptr; 206 207 if (!instance_data->object_map[object_type].count(object_handle)) { 208 VkDebugReportObjectTypeEXT debug_object_type = get_debug_report_enum[object_type]; 209 log_msg(instance_data->report_data, VK_DEBUG_REPORT_INFORMATION_BIT_EXT, debug_object_type, object_handle, __LINE__, 210 OBJTRACK_NONE, LayerName, "OBJ[0x%" PRIxLEAST64 "] : CREATE %s object 0x%" PRIxLEAST64, object_track_index++, 211 object_string[object_type], object_handle); 212 213 ObjTrackState *pNewObjNode = new ObjTrackState; 214 pNewObjNode->object_type = object_type; 215 pNewObjNode->status = custom_allocator ? OBJSTATUS_CUSTOM_ALLOCATOR : OBJSTATUS_NONE; 216 pNewObjNode->handle = object_handle; 217 218 instance_data->object_map[object_type][object_handle] = pNewObjNode; 219 instance_data->num_objects[object_type]++; 220 instance_data->num_total_objects++; 221 } 222 } 223 224 template <typename T1, typename T2> 225 void DestroyObject(T1 dispatchable_object, T2 object, VulkanObjectType object_type, const VkAllocationCallbacks *pAllocator, 226 enum UNIQUE_VALIDATION_ERROR_CODE expected_custom_allocator_code, 227 enum UNIQUE_VALIDATION_ERROR_CODE expected_default_allocator_code) { 228 layer_data *device_data = GetLayerDataPtr(get_dispatch_key(dispatchable_object), layer_data_map); 229 230 auto object_handle = HandleToUint64(object); 231 bool custom_allocator = pAllocator != nullptr; 232 VkDebugReportObjectTypeEXT debug_object_type = get_debug_report_enum[object_type]; 233 234 if (object_handle != VK_NULL_HANDLE) { 235 auto item = device_data->object_map[object_type].find(object_handle); 236 if (item != device_data->object_map[object_type].end()) { 237 ObjTrackState *pNode = item->second; 238 assert(device_data->num_total_objects > 0); 239 device_data->num_total_objects--; 240 assert(device_data->num_objects[pNode->object_type] > 0); 241 device_data->num_objects[pNode->object_type]--; 242 243 log_msg(device_data->report_data, VK_DEBUG_REPORT_INFORMATION_BIT_EXT, debug_object_type, object_handle, __LINE__, 244 OBJTRACK_NONE, LayerName, 245 "OBJ_STAT Destroy %s obj 0x%" PRIxLEAST64 " (%" PRIu64 " total objs remain & %" PRIu64 " %s objs).", 246 object_string[object_type], HandleToUint64(object), device_data->num_total_objects, 247 device_data->num_objects[pNode->object_type], object_string[object_type]); 248 249 auto allocated_with_custom = (pNode->status & OBJSTATUS_CUSTOM_ALLOCATOR) ? true : false; 250 if (allocated_with_custom && !custom_allocator && expected_custom_allocator_code != VALIDATION_ERROR_UNDEFINED) { 251 // This check only verifies that custom allocation callbacks were provided to both Create and Destroy calls, 252 // it cannot verify that these allocation callbacks are compatible with each other. 253 log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle, __LINE__, 254 expected_custom_allocator_code, LayerName, 255 "Custom allocator not specified while destroying %s obj 0x%" PRIxLEAST64 " but specified at creation. %s", 256 object_string[object_type], object_handle, validation_error_map[expected_custom_allocator_code]); 257 } else if (!allocated_with_custom && custom_allocator && 258 expected_default_allocator_code != VALIDATION_ERROR_UNDEFINED) { 259 log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, debug_object_type, object_handle, __LINE__, 260 expected_default_allocator_code, LayerName, 261 "Custom allocator specified while destroying %s obj 0x%" PRIxLEAST64 " but not specified at creation. %s", 262 object_string[object_type], object_handle, validation_error_map[expected_default_allocator_code]); 263 } 264 265 delete pNode; 266 device_data->object_map[object_type].erase(item); 267 } else { 268 log_msg(device_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, object_handle, 269 __LINE__, OBJTRACK_UNKNOWN_OBJECT, LayerName, 270 "Unable to remove %s obj 0x%" PRIxLEAST64 ". Was it created? Has it already been destroyed?", 271 object_string[object_type], object_handle); 272 } 273 } 274 } 275 276 } // namespace object_tracker 277