blob: 5b87be480337f2226b1d1c3332fda7956cec2d0e [file] [log] [blame]
Tobin Ehlis35308dd2016-10-31 13:27:36 -06001#!/usr/bin/env python3
Dave Houltoncacef472018-05-29 13:00:42 -06002# Copyright (c) 2015-2018 The Khronos Group Inc.
3# Copyright (c) 2015-2018 Valve Corporation
4# Copyright (c) 2015-2018 LunarG, Inc.
5# Copyright (c) 2015-2018 Google Inc.
Tobin Ehlis35308dd2016-10-31 13:27:36 -06006#
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: Tobin Ehlis <tobine@google.com>
Dave Houltoncacef472018-05-29 13:00:42 -060020# Author: Dave Houlton <daveh@lunarg.com>
Tobin Ehlis35308dd2016-10-31 13:27:36 -060021
22import argparse
23import os
24import sys
Dave Houltoncacef472018-05-29 13:00:42 -060025import operator
Tobin Ehlis35308dd2016-10-31 13:27:36 -060026import platform
Dave Houltoncacef472018-05-29 13:00:42 -060027import json
28import re
29import csv
30import html
31from collections import defaultdict
Tobin Ehlis35308dd2016-10-31 13:27:36 -060032
Dave Houltoncacef472018-05-29 13:00:42 -060033verbose_mode = False
34txt_db = False
35csv_db = False
36html_db = False
37txt_filename = "validation_error_database.txt"
38csv_filename = "validation_error_database.csv"
39html_filename = "validation_error_database.html"
40# header_file = '../layers/vk_validation_error_messages.h'
41test_file = '../tests/layer_validation_tests.cpp'
42vuid_prefixes = ['VUID-', 'UNASSIGNED-']
Tobin Ehlis35308dd2016-10-31 13:27:36 -060043
Dave Houltoncacef472018-05-29 13:00:42 -060044# Hard-coded flags that could be command line args, if we decide that's useful
45# replace KHR vuids with non-KHR during consistency checking
46dealias_khr = True
47ignore_unassigned = True # These are not found in layer code unless they appear explicitly (most don't), so produce false positives
48
Mark Lobodzinski05849f02017-06-21 14:44:14 -060049generated_layer_source_directories = [
50'build',
51'dbuild',
52'release',
53]
54generated_layer_source_files = [
Mark Lobodzinskid4950072017-08-01 13:02:20 -060055'parameter_validation.cpp',
Mark Lobodzinski09fa2d42017-07-21 10:16:53 -060056'object_tracker.cpp',
Mark Lobodzinski05849f02017-06-21 14:44:14 -060057]
Tobin Ehlis35308dd2016-10-31 13:27:36 -060058layer_source_files = [
Mark Lobodzinskie3787b42017-06-21 13:41:00 -060059'../layers/core_validation.cpp',
60'../layers/descriptor_sets.cpp',
Mark Lobodzinskid4950072017-08-01 13:02:20 -060061'../layers/parameter_validation_utils.cpp',
Mark Lobodzinskib2de97f2017-07-06 15:28:11 -060062'../layers/object_tracker_utils.cpp',
Mark Lobodzinskie3787b42017-06-21 13:41:00 -060063'../layers/shader_validation.cpp',
64'../layers/buffer_validation.cpp',
Tobin Ehlis35308dd2016-10-31 13:27:36 -060065]
Tobin Ehlis35308dd2016-10-31 13:27:36 -060066
Dave Houltoncacef472018-05-29 13:00:42 -060067khr_aliases = {
68 'VUID-vkBindBufferMemory2KHR-device-parameter' : 'VUID-vkBindBufferMemory2-device-parameter',
69 'VUID-vkBindBufferMemory2KHR-pBindInfos-parameter' : 'VUID-vkBindBufferMemory2-pBindInfos-parameter',
70 'VUID-vkBindImageMemory2KHR-device-parameter' : 'VUID-vkBindImageMemory2-device-parameter',
71 'VUID-vkBindImageMemory2KHR-pBindInfos-parameter' : 'VUID-vkBindImageMemory2-pBindInfos-parameter',
72 'VUID-vkCmdDispatchBaseKHR-commandBuffer-parameter' : 'VUID-vkCmdDispatchBase-commandBuffer-parameter',
73 'VUID-vkCmdSetDeviceMaskKHR-commandBuffer-parameter' : 'VUID-vkCmdSetDeviceMask-commandBuffer-parameter',
74 'VUID-vkCreateDescriptorUpdateTemplateKHR-device-parameter' : 'VUID-vkCreateDescriptorUpdateTemplate-device-parameter',
75 'VUID-vkCreateDescriptorUpdateTemplateKHR-pDescriptorUpdateTemplate-parameter' : 'VUID-vkCreateDescriptorUpdateTemplate-pDescriptorUpdateTemplate-parameter',
76 'VUID-vkCreateSamplerYcbcrConversionKHR-device-parameter' : 'VUID-vkCreateSamplerYcbcrConversion-device-parameter',
77 'VUID-vkCreateSamplerYcbcrConversionKHR-pYcbcrConversion-parameter' : 'VUID-vkCreateSamplerYcbcrConversion-pYcbcrConversion-parameter',
78 'VUID-vkDestroyDescriptorUpdateTemplateKHR-descriptorUpdateTemplate-parameter' : 'VUID-vkDestroyDescriptorUpdateTemplate-descriptorUpdateTemplate-parameter',
79 'VUID-vkDestroyDescriptorUpdateTemplateKHR-descriptorUpdateTemplate-parent' : 'VUID-vkDestroyDescriptorUpdateTemplate-descriptorUpdateTemplate-parent',
80 'VUID-vkDestroyDescriptorUpdateTemplateKHR-device-parameter' : 'VUID-vkDestroyDescriptorUpdateTemplate-device-parameter',
81 'VUID-vkDestroySamplerYcbcrConversionKHR-device-parameter' : 'VUID-vkDestroySamplerYcbcrConversion-device-parameter',
82 'VUID-vkDestroySamplerYcbcrConversionKHR-ycbcrConversion-parameter' : 'VUID-vkDestroySamplerYcbcrConversion-ycbcrConversion-parameter',
83 'VUID-vkDestroySamplerYcbcrConversionKHR-ycbcrConversion-parent' : 'VUID-vkDestroySamplerYcbcrConversion-ycbcrConversion-parent',
84 'VUID-vkEnumeratePhysicalDeviceGroupsKHR-instance-parameter' : 'VUID-vkEnumeratePhysicalDeviceGroups-instance-parameter',
85 'VUID-vkEnumeratePhysicalDeviceGroupsKHR-pPhysicalDeviceGroupProperties-parameter' : 'VUID-vkEnumeratePhysicalDeviceGroups-pPhysicalDeviceGroupProperties-parameter',
86 'VUID-vkGetBufferMemoryRequirements2KHR-device-parameter' : 'VUID-vkGetBufferMemoryRequirements2-device-parameter',
87 'VUID-vkGetDescriptorSetLayoutSupportKHR-device-parameter' : 'VUID-vkGetDescriptorSetLayoutSupport-device-parameter',
88 'VUID-vkGetDeviceGroupPeerMemoryFeaturesKHR-device-parameter' : 'VUID-vkGetDeviceGroupPeerMemoryFeatures-device-parameter',
89 'VUID-vkGetDeviceGroupPeerMemoryFeaturesKHR-pPeerMemoryFeatures-parameter' : 'VUID-vkGetDeviceGroupPeerMemoryFeatures-pPeerMemoryFeatures-parameter',
90 'VUID-vkGetImageMemoryRequirements2KHR-device-parameter' : 'VUID-vkGetImageMemoryRequirements2-device-parameter',
91 'VUID-vkGetImageSparseMemoryRequirements2KHR-device-parameter' : 'VUID-vkGetImageSparseMemoryRequirements2-device-parameter',
92 'VUID-vkGetImageSparseMemoryRequirements2KHR-pSparseMemoryRequirements-parameter' : 'VUID-vkGetImageSparseMemoryRequirements2-pSparseMemoryRequirements-parameter',
93 'VUID-vkGetPhysicalDeviceExternalBufferPropertiesKHR-physicalDevice-parameter' : 'VUID-vkGetPhysicalDeviceExternalBufferProperties-physicalDevice-parameter',
94 'VUID-vkGetPhysicalDeviceExternalFencePropertiesKHR-physicalDevice-parameter' : 'VUID-vkGetPhysicalDeviceExternalFenceProperties-physicalDevice-parameter',
95 'VUID-vkGetPhysicalDeviceExternalSemaphorePropertiesKHR-physicalDevice-parameter' : 'VUID-vkGetPhysicalDeviceExternalSemaphoreProperties-physicalDevice-parameter',
96 'VUID-vkGetPhysicalDeviceFeatures2KHR-physicalDevice-parameter' : 'VUID-vkGetPhysicalDeviceFeatures2-physicalDevice-parameter',
97 'VUID-vkGetPhysicalDeviceFormatProperties2KHR-format-parameter' : 'VUID-vkGetPhysicalDeviceFormatProperties2-format-parameter',
98 'VUID-vkGetPhysicalDeviceFormatProperties2KHR-physicalDevice-parameter' : 'VUID-vkGetPhysicalDeviceFormatProperties2-physicalDevice-parameter',
99 'VUID-vkGetPhysicalDeviceImageFormatProperties2KHR-physicalDevice-parameter' : 'VUID-vkGetPhysicalDeviceImageFormatProperties2-physicalDevice-parameter',
100 'VUID-vkGetPhysicalDeviceMemoryProperties2KHR-physicalDevice-parameter' : 'VUID-vkGetPhysicalDeviceMemoryProperties2-physicalDevice-parameter',
101 'VUID-vkGetPhysicalDeviceProperties2KHR-physicalDevice-parameter' : 'VUID-vkGetPhysicalDeviceProperties2-physicalDevice-parameter',
102 'VUID-vkGetPhysicalDeviceQueueFamilyProperties2KHR-pQueueFamilyProperties-parameter' : 'VUID-vkGetPhysicalDeviceQueueFamilyProperties2-pQueueFamilyProperties-parameter',
103 'VUID-vkGetPhysicalDeviceSparseImageFormatProperties2KHR-pProperties-parameter' : 'VUID-vkGetPhysicalDeviceSparseImageFormatProperties2-pProperties-parameter',
104 'VUID-vkGetPhysicalDeviceSparseImageFormatProperties2KHR-physicalDevice-parameter' : 'VUID-vkGetPhysicalDeviceSparseImageFormatProperties2-physicalDevice-parameter',
105 'VUID-vkTrimCommandPoolKHR-commandPool-parameter' : 'VUID-vkTrimCommandPool-commandPool-parameter',
106 'VUID-vkTrimCommandPoolKHR-commandPool-parent' : 'VUID-vkTrimCommandPool-commandPool-parent',
107 'VUID-vkTrimCommandPoolKHR-device-parameter' : 'VUID-vkTrimCommandPool-device-parameter',
108 'VUID-vkTrimCommandPoolKHR-flags-zerobitmask' : 'VUID-vkTrimCommandPool-flags-zerobitmask',
109 'VUID-vkUpdateDescriptorSetWithTemplateKHR-descriptorSet-parameter' : 'VUID-vkUpdateDescriptorSetWithTemplate-descriptorSet-parameter',
110 'VUID-vkUpdateDescriptorSetWithTemplateKHR-descriptorUpdateTemplate-parameter' : 'VUID-vkUpdateDescriptorSetWithTemplate-descriptorUpdateTemplate-parameter',
111 'VUID-vkUpdateDescriptorSetWithTemplateKHR-descriptorUpdateTemplate-parent' : 'VUID-vkUpdateDescriptorSetWithTemplate-descriptorUpdateTemplate-parent',
112 'VUID-vkUpdateDescriptorSetWithTemplateKHR-device-parameter' : 'VUID-vkUpdateDescriptorSetWithTemplate-device-parameter',
113 'VUID-vkCreateDescriptorUpdateTemplateKHR-pCreateInfo-parameter' : 'VUID-vkCreateDescriptorUpdateTemplate-pCreateInfo-parameter',
114 'VUID-vkCreateSamplerYcbcrConversionKHR-pCreateInfo-parameter' : 'VUID-vkCreateSamplerYcbcrConversion-pCreateInfo-parameter',
115 'VUID-vkGetBufferMemoryRequirements2KHR-pInfo-parameter' : 'VUID-vkGetBufferMemoryRequirements2-pInfo-parameter',
116 'VUID-vkGetBufferMemoryRequirements2KHR-pMemoryRequirements-parameter' : 'VUID-vkGetBufferMemoryRequirements2-pMemoryRequirements-parameter',
117 'VUID-vkGetDescriptorSetLayoutSupportKHR-pCreateInfo-parameter' : 'VUID-vkGetDescriptorSetLayoutSupport-pCreateInfo-parameter',
118 'VUID-vkGetDescriptorSetLayoutSupportKHR-pSupport-parameter' : 'VUID-vkGetDescriptorSetLayoutSupport-pSupport-parameter',
119 'VUID-vkGetImageMemoryRequirements2KHR-pInfo-parameter' : 'VUID-vkGetImageMemoryRequirements2-pInfo-parameter',
120 'VUID-vkGetImageMemoryRequirements2KHR-pMemoryRequirements-parameter' : 'VUID-vkGetImageMemoryRequirements2-pMemoryRequirements-parameter',
121 'VUID-vkGetImageSparseMemoryRequirements2KHR-pInfo-parameter' : 'VUID-vkGetImageSparseMemoryRequirements2-pInfo-parameter',
122 'VUID-vkGetPhysicalDeviceExternalBufferPropertiesKHR-pExternalBufferInfo-parameter' : 'VUID-vkGetPhysicalDeviceExternalBufferProperties-pExternalBufferInfo-parameter',
123 'VUID-vkGetPhysicalDeviceExternalBufferPropertiesKHR-pExternalBufferProperties-parameter' : 'VUID-vkGetPhysicalDeviceExternalBufferProperties-pExternalBufferProperties-parameter',
124 'VUID-vkGetPhysicalDeviceExternalFencePropertiesKHR-pExternalFenceInfo-parameter' : 'VUID-vkGetPhysicalDeviceExternalFenceProperties-pExternalFenceInfo-parameter',
125 'VUID-vkGetPhysicalDeviceExternalFencePropertiesKHR-pExternalFenceProperties-parameter' : 'VUID-vkGetPhysicalDeviceExternalFenceProperties-pExternalFenceProperties-parameter',
126 'VUID-vkGetPhysicalDeviceExternalSemaphorePropertiesKHR-pExternalSemaphoreInfo-parameter' : 'VUID-vkGetPhysicalDeviceExternalSemaphoreProperties-pExternalSemaphoreInfo-parameter',
127 'VUID-vkGetPhysicalDeviceExternalSemaphorePropertiesKHR-pExternalSemaphoreProperties-parameter' : 'VUID-vkGetPhysicalDeviceExternalSemaphoreProperties-pExternalSemaphoreProperties-parameter',
128 'VUID-vkGetPhysicalDeviceFeatures2KHR-pFeatures-parameter' : 'VUID-vkGetPhysicalDeviceFeatures2-pFeatures-parameter',
129 'VUID-vkGetPhysicalDeviceFormatProperties2KHR-pFormatProperties-parameter' : 'VUID-vkGetPhysicalDeviceFormatProperties2-pFormatProperties-parameter',
130 'VUID-vkGetPhysicalDeviceImageFormatProperties2KHR-pImageFormatInfo-parameter' : 'VUID-vkGetPhysicalDeviceImageFormatProperties2-pImageFormatInfo-parameter',
131 'VUID-vkGetPhysicalDeviceImageFormatProperties2KHR-pImageFormatProperties-parameter' : 'VUID-vkGetPhysicalDeviceImageFormatProperties2-pImageFormatProperties-parameter',
132 'VUID-vkGetPhysicalDeviceMemoryProperties2KHR-pMemoryProperties-parameter' : 'VUID-vkGetPhysicalDeviceMemoryProperties2-pMemoryProperties-parameter',
133 'VUID-vkGetPhysicalDeviceProperties2KHR-pProperties-parameter' : 'VUID-vkGetPhysicalDeviceProperties2-pProperties-parameter',
134 'VUID-vkGetPhysicalDeviceSparseImageFormatProperties2KHR-pFormatInfo-parameter' : 'VUID-vkGetPhysicalDeviceSparseImageFormatProperties2-pFormatInfo-parameter' }
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600135
Dave Houltoncacef472018-05-29 13:00:42 -0600136def printHelp():
137 print ("Usage:")
138 print (" python vk_validation_stats.py <json_file>")
139 print (" [ -c ]")
140 print (" [ -todo ]")
141 print (" [ -vuid <vuid_name> ]")
142 print (" [ -text [ <text_out_filename>] ]")
143 print (" [ -csv [ <csv_out_filename>] ]")
144 print (" [ -html [ <html_out_filename>] ]")
145 print (" [ -verbose ]")
146 print (" [ -help ]")
147 print ("\n The vk_validation_stats script parses validation layer source files to")
148 print (" determine the set of valid usage checks and tests currently implemented,")
149 print (" and generates coverage values by comparing against the full set of valid")
150 print (" usage identifiers in the Vulkan-Headers registry file 'validusage.json'")
151 print ("\nArguments: ")
152 print (" <json-file> (required) registry file 'validusage.json'")
153 print (" -c report consistency warnings")
154 print (" -todo report unimplemented VUIDs")
155 print (" -vuid <vuid_name> report status of individual VUID <vuid_name>")
156 print (" -text [filename] output the error database text to <text_database_filename>,")
157 print (" defaults to 'validation_error_database.txt'")
158 print (" -csv [filename] output the error database in csv to <csv_database_filename>,")
159 print (" defaults to 'validation_error_database.csv'")
160 print (" -html [filename] output the error database in html to <html_database_filename>,")
161 print (" defaults to 'validation_error_database.html'")
162 print (" -verbose show your work (to stdout)")
163
164class ValidationJSON:
165 def __init__(self, filename):
166 self.filename = filename
167 self.explicit_vuids = set()
168 self.implicit_vuids = set()
169 self.all_vuids = set()
170 self.vuid_db = defaultdict(list) # Maps VUID string to list of json-data dicts
171 self.apiversion = ""
Dave Houltonf93bbab2018-06-26 17:21:12 -0600172 self.re_striptags = re.compile('<.*?>|&(amp;)+lt;|&(amp;)+gt;')
Dave Houltoncacef472018-05-29 13:00:42 -0600173 self.duplicate_vuids = set()
174
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600175 def read(self):
Dave Houltoncacef472018-05-29 13:00:42 -0600176 self.json_dict = {}
177 if os.path.isfile(self.filename):
178 json_file = open(self.filename, 'r')
179 self.json_dict = json.load(json_file)
180 json_file.close()
181 if len(self.json_dict) == 0:
182 print("Error: Error loading validusage.json file <%s>" % self.filename)
183 sys.exit(-1)
184 try:
185 version = self.json_dict['version info']
186 validation = self.json_dict['validation']
187 self.apiversion = version['api version']
188 except:
189 print("Error: Failure parsing validusage.json object")
190 sys.exit(-1)
191
192 # Parse vuid from json into local databases
193 for apiname in validation.keys():
194 # print("entrypoint:%s"%apiname)
195 apidict = validation[apiname]
196 for ext in apidict.keys():
197 vlist = apidict[ext]
198 for ventry in vlist:
199 vuid_string = ventry['vuid']
200 if (vuid_string[-5:-1].isdecimal()):
201 self.explicit_vuids.add(vuid_string) # explicit end in 5 numeric chars
202 vtype = 'explicit'
203 else:
204 self.implicit_vuids.add(vuid_string) # otherwise, implicit
205 vtype = 'implicit'
206 vuid_text = ventry['text']
Dave Houltonf93bbab2018-06-26 17:21:12 -0600207 #if 'amp;' in vuid_text:
208 # print(vuid_text)
209 stripped = re.sub(self.re_striptags, '', vuid_text) # strip tags & literals
210 stripped = html.unescape(stripped) # anything missed by the regex
211 #if 'amp;' in stripped:
212 # print(" %s" % stripped)
Dave Houltoncacef472018-05-29 13:00:42 -0600213 self.vuid_db[vuid_string].append({'api':apiname, 'ext':ext, 'type':vtype, 'text':stripped})
214 self.all_vuids = self.explicit_vuids | self.implicit_vuids
215 self.duplicate_vuids = set({v for v in self.vuid_db if len(self.vuid_db[v]) > 1})
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600216
217class ValidationSource:
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600218 def __init__(self, source_file_list, generated_source_file_list, generated_source_directories):
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600219 self.source_files = source_file_list
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600220 self.generated_source_files = generated_source_file_list
221 self.generated_source_dirs = generated_source_directories
Dave Houltoncacef472018-05-29 13:00:42 -0600222 self.vuid_count_dict = {} # dict of vuid values to the count of how much they're used, and location of where they're used
223 self.duplicated_checks = 0
224 self.explicit_vuids = set()
225 self.implicit_vuids = set()
226 self.unassigned_vuids = set()
227 self.all_vuids = set()
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600228
229 if len(self.generated_source_files) > 0:
230 qualified_paths = []
231 for source in self.generated_source_files:
232 for build_dir in self.generated_source_dirs:
233 filepath = '../%s/layers/%s' % (build_dir, source)
234 if os.path.isfile(filepath):
235 qualified_paths.append(filepath)
John Zulaufdde04c42018-01-16 15:32:45 -0700236 break
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600237 if len(self.generated_source_files) != len(qualified_paths):
Mark Lobodzinski2d193ac2017-06-27 09:38:15 -0600238 print("Error: Unable to locate one or more of the following source files in the %s directories" % (", ".join(generated_source_directories)))
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600239 print(self.generated_source_files)
Dave Houltoncacef472018-05-29 13:00:42 -0600240 print("Failed to locate one or more codegen files in layer source code - cannot proceed.")
John Zulaufdde04c42018-01-16 15:32:45 -0700241 exit(1)
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600242 else:
243 self.source_files.extend(qualified_paths)
244
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600245 def parse(self):
Dave Houltoncacef472018-05-29 13:00:42 -0600246 prepend = None
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600247 for sf in self.source_files:
Tobin Ehlis3d1f2bd2016-12-22 11:19:15 -0700248 line_num = 0
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600249 with open(sf) as f:
250 for line in f:
Tobin Ehlis3d1f2bd2016-12-22 11:19:15 -0700251 line_num = line_num + 1
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600252 if True in [line.strip().startswith(comment) for comment in ['//', '/*']]:
253 continue
Dave Houltoncacef472018-05-29 13:00:42 -0600254 # Find vuid strings
255 if prepend != None:
256 line = prepend[:-2] + line.lstrip().lstrip('"') # join lines skipping CR, whitespace and trailing/leading quote char
257 prepend = None
258 if any(prefix in line for prefix in vuid_prefixes):
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600259 line_list = line.split()
Dave Houltoncacef472018-05-29 13:00:42 -0600260
261 # A VUID string that has been broken by clang will start with a vuid prefix and end with -, and will be last in the list
262 broken_vuid = line_list[-1].strip('"')
263 if any(broken_vuid.startswith(prefix) for prefix in vuid_prefixes) and broken_vuid.endswith('-'):
264 prepend = line
265 continue
266
267 vuid_list = []
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600268 for str in line_list:
Dave Houltoncacef472018-05-29 13:00:42 -0600269 if any(prefix in str for prefix in vuid_prefixes):
270 vuid_list.append(str.strip(',);{}"'))
271 for vuid in vuid_list:
272 if vuid not in self.vuid_count_dict:
273 self.vuid_count_dict[vuid] = {}
274 self.vuid_count_dict[vuid]['count'] = 1
275 self.vuid_count_dict[vuid]['file_line'] = []
276 else:
277 if self.vuid_count_dict[vuid]['count'] == 1: # only count first time duplicated
278 self.duplicated_checks = self.duplicated_checks + 1
279 self.vuid_count_dict[vuid]['count'] = self.vuid_count_dict[vuid]['count'] + 1
280 self.vuid_count_dict[vuid]['file_line'].append('%s,%d' % (sf, line_num))
281 # Sort vuids by type
282 for vuid in self.vuid_count_dict.keys():
283 if (vuid.startswith('VUID-')):
284 if (vuid[-5:-1].isdecimal()):
285 self.explicit_vuids.add(vuid) # explicit end in 5 numeric chars
286 else:
287 self.implicit_vuids.add(vuid)
288 elif (vuid.startswith('UNASSIGNED-')):
289 self.unassigned_vuids.add(vuid)
290 else:
291 print("Unable to categorize VUID: %s" % vuid)
292 print("Confused while parsing VUIDs in layer source code - cannot proceed. (FIXME)")
293 exit(-1)
294 self.all_vuids = self.explicit_vuids | self.implicit_vuids | self.unassigned_vuids
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600295
296# Class to parse the validation layer test source and store testnames
Dave Houltoncacef472018-05-29 13:00:42 -0600297class ValidationTests:
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600298 def __init__(self, test_file_list, test_group_name=['VkLayerTest', 'VkPositiveLayerTest', 'VkWsiEnabledLayerTest']):
299 self.test_files = test_file_list
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600300 self.test_trigger_txt_list = []
301 for tg in test_group_name:
302 self.test_trigger_txt_list.append('TEST_F(%s' % tg)
Dave Houltoncacef472018-05-29 13:00:42 -0600303 self.explicit_vuids = set()
304 self.implicit_vuids = set()
305 self.unassigned_vuids = set()
306 self.all_vuids = set()
307 #self.test_to_vuids = {} # Map test name to VUIDs tested
308 self.vuid_to_tests = defaultdict(set) # Map VUIDs to set of test names where implemented
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600309
310 # Parse test files into internal data struct
311 def parse(self):
312 # For each test file, parse test names into set
313 grab_next_line = False # handle testname on separate line than wildcard
Tobin Ehlis9a68c982016-12-29 14:51:17 -0700314 testname = ''
Dave Houltoncacef472018-05-29 13:00:42 -0600315 prepend = None
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600316 for test_file in self.test_files:
317 with open(test_file) as tf:
318 for line in tf:
319 if True in [line.strip().startswith(comment) for comment in ['//', '/*']]:
320 continue
321
Dave Houltoncacef472018-05-29 13:00:42 -0600322 # if line ends in a broken VUID string, fix that before proceeding
323 if prepend != None:
324 line = prepend[:-2] + line.lstrip().lstrip('"') # join lines skipping CR, whitespace and trailing/leading quote char
325 prepend = None
326 if any(prefix in line for prefix in vuid_prefixes):
327 line_list = line.split()
328
329 # A VUID string that has been broken by clang will start with a vuid prefix and end with -, and will be last in the list
330 broken_vuid = line_list[-1].strip('"')
331 if any(broken_vuid.startswith(prefix) for prefix in vuid_prefixes) and broken_vuid.endswith('-'):
332 prepend = line
333 continue
334
335 if any(ttt in line for ttt in self.test_trigger_txt_list):
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600336 testname = line.split(',')[-1]
337 testname = testname.strip().strip(' {)')
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600338 if ('' == testname):
339 grab_next_line = True
340 continue
Dave Houltoncacef472018-05-29 13:00:42 -0600341 #self.test_to_vuids[testname] = []
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600342 if grab_next_line: # test name on its own line
343 grab_next_line = False
344 testname = testname.strip().strip(' {)')
Dave Houltoncacef472018-05-29 13:00:42 -0600345 #self.test_to_vuids[testname] = []
346 if any(prefix in line for prefix in vuid_prefixes):
347 line_list = re.split('[\s{}[\]()"]+',line)
Tobin Ehlis71f38c12017-01-12 14:26:56 -0700348 for sub_str in line_list:
Dave Houltoncacef472018-05-29 13:00:42 -0600349 if any(prefix in sub_str for prefix in vuid_prefixes):
350 vuid_str = sub_str.strip(',);:"')
351 self.vuid_to_tests[vuid_str].add(testname)
352 #self.test_to_vuids[testname].append(vuid_str)
353 if (vuid_str.startswith('VUID-')):
354 if (vuid_str[-5:-1].isdecimal()):
355 self.explicit_vuids.add(vuid_str) # explicit end in 5 numeric chars
356 else:
357 self.implicit_vuids.add(vuid_str)
358 elif (vuid_str.startswith('UNASSIGNED-')):
359 self.unassigned_vuids.add(vuid_str)
360 else:
361 print("Unable to categorize VUID: %s" % vuid_str)
362 print("Confused while parsing VUIDs in test code - cannot proceed. (FIXME)")
363 exit(-1)
364 self.all_vuids = self.explicit_vuids | self.implicit_vuids | self.unassigned_vuids
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600365
Dave Houltoncacef472018-05-29 13:00:42 -0600366# Class to do consistency checking
367#
368class Consistency:
369 def __init__(self, all_json, all_checks, all_tests):
370 self.valid = all_json
371 self.checks = all_checks
372 self.tests = all_tests
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600373
Dave Houltoncacef472018-05-29 13:00:42 -0600374 if (dealias_khr):
375 dk = set()
376 for vuid in self.checks:
377 if vuid in khr_aliases:
378 dk.add(khr_aliases[vuid])
379 else:
380 dk.add(vuid)
381 self.checks = dk
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600382
Dave Houltoncacef472018-05-29 13:00:42 -0600383 dk = set()
384 for vuid in self.tests:
385 if vuid in khr_aliases:
386 dk.add(khr_aliases[vuid])
387 else:
388 dk.add(vuid)
389 self.tests = dk
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600390
Dave Houltoncacef472018-05-29 13:00:42 -0600391 # Report undefined VUIDs in source code
392 def undef_vuids_in_layer_code(self):
393 undef_set = self.checks - self.valid
394 undef_set.discard('VUID-Undefined') # don't report Undefined
395 if ignore_unassigned:
396 unassigned = set({uv for uv in undef_set if uv.startswith('UNASSIGNED-')})
397 undef_set = undef_set - unassigned
398 if (len(undef_set) > 0):
399 print("\nFollowing VUIDs found in layer code are not defined in validusage.json (%d):" % len(undef_set))
400 undef = list(undef_set)
401 undef.sort()
402 for vuid in undef:
403 print(" %s" % vuid)
404 return False
405 return True
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600406
Dave Houltoncacef472018-05-29 13:00:42 -0600407 # Report undefined VUIDs in tests
408 def undef_vuids_in_tests(self):
409 undef_set = self.tests - self.valid
410 undef_set.discard('VUID-Undefined') # don't report Undefined
411 if ignore_unassigned:
412 unassigned = set({uv for uv in undef_set if uv.startswith('UNASSIGNED-')})
413 undef_set = undef_set - unassigned
414 if (len(undef_set) > 0):
415 ok = False
416 print("\nFollowing VUIDs found in layer tests are not defined in validusage.json (%d):" % len(undef_set))
417 undef = list(undef_set)
418 undef.sort()
419 for vuid in undef:
420 print(" %s" % vuid)
421 return False
422 return True
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600423
Dave Houltoncacef472018-05-29 13:00:42 -0600424 # Report vuids in tests that are not in source
425 def vuids_tested_not_checked(self):
426 undef_set = self.tests - self.checks
427 undef_set.discard('VUID-Undefined') # don't report Undefined
428 if ignore_unassigned:
429 unassigned = set()
430 for vuid in undef_set:
431 if vuid.startswith('UNASSIGNED-'):
432 unassigned.add(vuid)
433 undef_set = undef_set - unassigned
434 if (len(undef_set) > 0):
435 ok = False
436 print("\nFollowing VUIDs found in tests but are not checked in layer code (%d):" % len(undef_set))
437 undef = list(undef_set)
438 undef.sort()
439 for vuid in undef:
440 print(" %s" % vuid)
441 return False
442 return True
443
444 # TODO: Explicit checked VUIDs which have no test
445 # def explicit_vuids_checked_not_tested(self):
446
447
448# Class to output database in various flavors
449#
450class OutputDatabase:
451 def __init__(self, val_json, val_source, val_tests):
452 self.vj = val_json
453 self.vs = val_source
454 self.vt = val_tests
455
456 def dump_txt(self):
457 print("\n Dumping database to text file: %s" % txt_filename)
458 with open (txt_filename, 'w') as txt:
459 txt.write("## VUID Database\n")
460 txt.write("## Format: VUID_NAME | CHECKED | TEST | TYPE | API/STRUCT | EXTENSION | VUID_TEXT\n##\n")
461 vuid_list = list(self.vj.all_vuids)
462 vuid_list.sort()
463 for vuid in vuid_list:
464 db_list = self.vj.vuid_db[vuid]
465 db_list.sort(key=operator.itemgetter('ext')) # sort list to ease diffs of output file
466 for db_entry in db_list:
467 checked = 'N'
468 if vuid in self.vs.all_vuids:
469 checked = 'Y'
470 test = 'None'
471 if vuid in self.vt.vuid_to_tests:
472 test_list = list(self.vt.vuid_to_tests[vuid])
473 test_list.sort() # sort tests, for diff-ability
474 sep = ', '
475 test = sep.join(test_list)
476
477 txt.write("%s | %s | %s | %s | %s | %s | %s\n" % (vuid, checked, test, db_entry['type'], db_entry['api'], db_entry['ext'], db_entry['text']))
478
479 def dump_csv(self):
480 print("\n Dumping database to csv file: %s" % csv_filename)
481 with open (csv_filename, 'w', newline='') as csvfile:
482 cw = csv.writer(csvfile)
483 cw.writerow(['VUID_NAME','CHECKED','TEST','TYPE','API/STRUCT','EXTENSION','VUID_TEXT'])
484 vuid_list = list(self.vj.all_vuids)
485 vuid_list.sort()
486 for vuid in vuid_list:
487 for db_entry in self.vj.vuid_db[vuid]:
488 row = [vuid]
489 if vuid in self.vs.all_vuids:
490 row.append('Y')
491 else:
492 row.append('N')
493 test = 'None'
494 if vuid in self.vt.vuid_to_tests:
495 sep = ', '
496 test = sep.join(self.vt.vuid_to_tests[vuid])
497 row.append(test)
498 row.append(db_entry['type'])
499 row.append(db_entry['api'])
500 row.append(db_entry['ext'])
501 row.append(db_entry['text'])
502 cw.writerow(row)
503
504 def dump_html(self):
505 print("\n Dumping database to html file: %s" % html_filename)
506 preamble = '<!DOCTYPE html>\n<html>\n<head>\n<style>\ntable, th, td {\n border: 1px solid black;\n border-collapse: collapse; \n}\n</style>\n<body>\n<h2>Valid Usage Database</h2>\n<font size="2" face="Arial">\n<table style="width:100%">\n'
507 headers = '<tr><th>VUID NAME</th><th>CHECKED</th><th>TEST</th><th>TYPE</th><th>API/STRUCT</th><th>EXTENSION</th><th>VUID TEXT</th></tr>\n'
508 with open (html_filename, 'w') as hfile:
509 hfile.write(preamble)
510 hfile.write(headers)
511 vuid_list = list(self.vj.all_vuids)
512 vuid_list.sort()
513 for vuid in vuid_list:
514 for db_entry in self.vj.vuid_db[vuid]:
515 hfile.write('<tr><th>%s</th>' % vuid)
516 checked = '<span style="color:red;">N</span>'
517 if vuid in self.vs.all_vuids:
518 checked = '<span style="color:limegreen;">Y</span>'
519 hfile.write('<th>%s</th>' % checked)
520 test = 'None'
521 if vuid in self.vt.vuid_to_tests:
522 sep = ', '
523 test = sep.join(self.vt.vuid_to_tests[vuid])
524 hfile.write('<th>%s</th>' % test)
525 hfile.write('<th>%s</th>' % db_entry['type'])
526 hfile.write('<th>%s</th>' % db_entry['api'])
527 hfile.write('<th>%s</th>' % db_entry['ext'])
528 hfile.write('<th>%s</th></tr>\n' % db_entry['text'])
529 hfile.write('</table>\n</body>\n</html>\n')
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600530
Mark Lobodzinskib44c54a2017-06-12 12:02:38 -0600531def main(argv):
Dave Houltoncacef472018-05-29 13:00:42 -0600532 global verbose_mode
533 global txt_filename
534 global csv_filename
535 global html_filename
536
537 run_consistency = False
538 report_unimplemented = False
539 get_vuid_status = ''
540 txt_out = False
541 csv_out = False
542 html_out = False
543
544 if (1 > len(argv)):
545 printHelp()
546 sys.exit()
547
548 # Parse script args
549 json_filename = argv[0]
550 i = 1
551 while (i < len(argv)):
552 arg = argv[i]
553 i = i + 1
554 if (arg == '-c'):
555 run_consistency = True
556 elif (arg == '-vuid'):
557 get_vuid_status = argv[i]
558 i = i + 1
559 elif (arg == '-todo'):
560 report_unimplemented = True
Karl Schultz49d66bb2018-07-09 16:24:46 -0600561 elif (arg == '-text'):
Dave Houltoncacef472018-05-29 13:00:42 -0600562 txt_out = True
563 # Set filename if supplied, else use default
564 if i < len(argv) and not argv[i].startswith('-'):
565 txt_filename = argv[i]
566 i = i + 1
567 elif (arg == '-csv'):
568 csv_out = True
569 # Set filename if supplied, else use default
570 if i < len(argv) and not argv[i].startswith('-'):
571 csv_filename = argv[i]
572 i = i + 1
573 elif (arg == '-html'):
574 html_out = True
575 # Set filename if supplied, else use default
576 if i < len(argv) and not argv[i].startswith('-'):
577 html_filename = argv[i]
578 i = i + 1
579 elif (arg in ['-verbose']):
580 verbose_mode = True
581 elif (arg in ['-help', '-h']):
582 printHelp()
583 sys.exit()
584 else:
585 print("Unrecognized argument: %s\n" % arg)
586 printHelp()
587 sys.exit()
588
Tobin Ehlis20e32582016-12-05 14:50:03 -0700589 result = 0 # Non-zero result indicates an error case
Dave Houltoncacef472018-05-29 13:00:42 -0600590
591 # Parse validusage json
592 val_json = ValidationJSON(json_filename)
593 val_json.read()
594 exp_json = len(val_json.explicit_vuids)
595 imp_json = len(val_json.implicit_vuids)
596 all_json = len(val_json.all_vuids)
597 if verbose_mode:
598 print("Found %d unique error vuids in validusage.json file." % all_json)
599 print(" %d explicit" % exp_json)
600 print(" %d implicit" % imp_json)
601 if len(val_json.duplicate_vuids) > 0:
602 print("%d VUIDs appear in validusage.json more than once." % len(val_json.duplicate_vuids))
603 for vuid in val_json.duplicate_vuids:
604 print(" %s" % vuid)
605 for ext in val_json.vuid_db[vuid]:
606 print(" with extension: %s" % ext['ext'])
607
608 # Parse layer source files
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600609 val_source = ValidationSource(layer_source_files, generated_layer_source_files, generated_layer_source_directories)
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600610 val_source.parse()
Dave Houltoncacef472018-05-29 13:00:42 -0600611 exp_checks = len(val_source.explicit_vuids)
612 imp_checks = len(val_source.implicit_vuids)
613 all_checks = len(val_source.vuid_count_dict.keys())
614 if verbose_mode:
615 print("Found %d unique vuid checks in layer source code." % all_checks)
616 print(" %d explicit" % exp_checks)
617 print(" %d implicit" % imp_checks)
618 print(" %d unassigned" % len(val_source.unassigned_vuids))
619 print(" %d checks are implemented more that once" % val_source.duplicated_checks)
620
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600621 # Parse test files
Dave Houltoncacef472018-05-29 13:00:42 -0600622 val_tests = ValidationTests([test_file, ])
623 val_tests.parse()
624 exp_tests = len(val_tests.explicit_vuids)
625 imp_tests = len(val_tests.implicit_vuids)
626 all_tests = len(val_tests.all_vuids)
Mark Lobodzinski060a8e32018-01-08 09:08:06 -0700627 if verbose_mode:
Dave Houltoncacef472018-05-29 13:00:42 -0600628 print("Found %d unique error vuids in test file %s." % (all_tests, test_file))
629 print(" %d explicit" % exp_tests)
630 print(" %d implicit" % imp_tests)
631 print(" %d unassigned" % len(val_tests.unassigned_vuids))
Mike Weiblenfe186122017-02-03 12:44:53 -0700632
Dave Houltoncacef472018-05-29 13:00:42 -0600633 # Process stats
634 print("\nValidation Statistics (using validusage.json version %s)" % val_json.apiversion)
635 print(" VUIDs defined in JSON file: %04d explicit, %04d implicit, %04d total." % (exp_json, imp_json, all_json))
636 print(" VUIDs checked in layer code: %04d explicit, %04d implicit, %04d total." % (exp_checks, imp_checks, all_checks))
637 print(" VUIDs tested in layer tests: %04d explicit, %04d implicit, %04d total." % (exp_tests, imp_tests, all_tests))
638
639 print("\nVUID check coverage")
640 print(" Explicit VUIDs checked: %.1f%% (%d checked vs %d defined)" % ((100.0 * exp_checks / exp_json), exp_checks, exp_json))
641 print(" Implicit VUIDs checked: %.1f%% (%d checked vs %d defined)" % ((100.0 * imp_checks / imp_json), imp_checks, imp_json))
642 print(" Overall VUIDs checked: %.1f%% (%d checked vs %d defined)" % ((100.0 * all_checks / all_json), all_checks, all_json))
Mike Weiblenfe186122017-02-03 12:44:53 -0700643
Dave Houltoncacef472018-05-29 13:00:42 -0600644 print("\nVUID test coverage")
645 print(" Explicit VUIDs tested: %.1f%% (%d tested vs %d checks)" % ((100.0 * exp_tests / exp_checks), exp_tests, exp_checks))
646 print(" Implicit VUIDs tested: %.1f%% (%d tested vs %d checks)" % ((100.0 * imp_tests / imp_checks), imp_tests, imp_checks))
647 print(" Overall VUIDs tested: %.1f%% (%d tested vs %d checks)" % ((100.0 * all_tests / all_checks), all_tests, all_checks))
Mike Weiblenfe186122017-02-03 12:44:53 -0700648
Dave Houltoncacef472018-05-29 13:00:42 -0600649 # Report status of a single VUID
650 if len(get_vuid_status) > 1:
651 print("\n\nChecking status of <%s>" % get_vuid_status);
652 if get_vuid_status not in val_json.all_vuids:
653 print(' Not a valid VUID string.')
654 else:
655 if get_vuid_status in val_source.explicit_vuids:
656 print(' Implemented!')
657 line_list = val_source.vuid_count_dict[get_vuid_status]['file_line']
658 for line in line_list:
659 print(' => %s' % line)
Dave Houltonf93bbab2018-06-26 17:21:12 -0600660 elif get_vuid_status in val_source.implicit_vuids:
661 print(' Implemented! (Implicit)')
662 line_list = val_source.vuid_count_dict[get_vuid_status]['file_line']
663 for line in line_list:
664 print(' => %s' % line)
Tobin Ehlis9a68c982016-12-29 14:51:17 -0700665 else:
Dave Houltoncacef472018-05-29 13:00:42 -0600666 print(' Not implemented.')
Dave Houltonf93bbab2018-06-26 17:21:12 -0600667 if get_vuid_status in val_tests.all_vuids:
Dave Houltoncacef472018-05-29 13:00:42 -0600668 print(' Has a test!')
669 test_list = val_tests.vuid_to_tests[get_vuid_status]
670 for test in test_list:
671 print(' => %s' % test)
672 else:
673 print(' Not tested.')
Mike Weiblenfe186122017-02-03 12:44:53 -0700674
Dave Houltoncacef472018-05-29 13:00:42 -0600675 # Report unimplemented explicit VUIDs
676 if report_unimplemented:
677 unim_explicit = val_json.explicit_vuids - val_source.explicit_vuids
678 print("\n\n%d explicit VUID checks remain unimplemented:" % len(unim_explicit))
679 ulist = list(unim_explicit)
680 ulist.sort()
681 for vuid in ulist:
682 print(" => %s" % vuid)
683
684 # Consistency tests
685 if run_consistency:
686 print("\n\nRunning consistency tests...")
687 con = Consistency(val_json.all_vuids, val_source.all_vuids, val_tests.all_vuids)
688 ok = con.undef_vuids_in_layer_code()
689 ok &= con.undef_vuids_in_tests()
690 ok &= con.vuids_tested_not_checked()
691
692 if ok:
693 print(" OK! No inconsistencies found.")
694
695 # Output database in requested format(s)
696 db_out = OutputDatabase(val_json, val_source, val_tests)
697 if txt_out:
698 db_out.dump_txt()
699 if csv_out:
700 db_out.dump_csv()
701 if html_out:
702 db_out.dump_html()
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600703
Tobin Ehlis20e32582016-12-05 14:50:03 -0700704 return result
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600705
706if __name__ == "__main__":
Mark Lobodzinskib44c54a2017-06-12 12:02:38 -0600707 sys.exit(main(sys.argv[1:]))
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600708