blob: 00bdd2ee7322f6e152736c3f2c9ba7decf8edeea [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 = ""
172 self.re_striptags = re.compile('<.*?>')
173 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']
207 stripped = re.sub(self.re_striptags, '', vuid_text) # strip tags
208 stripped = html.unescape(stripped) # unescape html literals (only partially works - because asciiDoctor?)
209 self.vuid_db[vuid_string].append({'api':apiname, 'ext':ext, 'type':vtype, 'text':stripped})
210 self.all_vuids = self.explicit_vuids | self.implicit_vuids
211 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 -0600212
213class ValidationSource:
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600214 def __init__(self, source_file_list, generated_source_file_list, generated_source_directories):
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600215 self.source_files = source_file_list
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600216 self.generated_source_files = generated_source_file_list
217 self.generated_source_dirs = generated_source_directories
Dave Houltoncacef472018-05-29 13:00:42 -0600218 self.vuid_count_dict = {} # dict of vuid values to the count of how much they're used, and location of where they're used
219 self.duplicated_checks = 0
220 self.explicit_vuids = set()
221 self.implicit_vuids = set()
222 self.unassigned_vuids = set()
223 self.all_vuids = set()
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600224
225 if len(self.generated_source_files) > 0:
226 qualified_paths = []
227 for source in self.generated_source_files:
228 for build_dir in self.generated_source_dirs:
229 filepath = '../%s/layers/%s' % (build_dir, source)
230 if os.path.isfile(filepath):
231 qualified_paths.append(filepath)
John Zulaufdde04c42018-01-16 15:32:45 -0700232 break
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600233 if len(self.generated_source_files) != len(qualified_paths):
Mark Lobodzinski2d193ac2017-06-27 09:38:15 -0600234 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 -0600235 print(self.generated_source_files)
Dave Houltoncacef472018-05-29 13:00:42 -0600236 print("Failed to locate one or more codegen files in layer source code - cannot proceed.")
John Zulaufdde04c42018-01-16 15:32:45 -0700237 exit(1)
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600238 else:
239 self.source_files.extend(qualified_paths)
240
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600241 def parse(self):
Dave Houltoncacef472018-05-29 13:00:42 -0600242 prepend = None
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600243 for sf in self.source_files:
Tobin Ehlis3d1f2bd2016-12-22 11:19:15 -0700244 line_num = 0
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600245 with open(sf) as f:
246 for line in f:
Tobin Ehlis3d1f2bd2016-12-22 11:19:15 -0700247 line_num = line_num + 1
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600248 if True in [line.strip().startswith(comment) for comment in ['//', '/*']]:
249 continue
Dave Houltoncacef472018-05-29 13:00:42 -0600250 # Find vuid strings
251 if prepend != None:
252 line = prepend[:-2] + line.lstrip().lstrip('"') # join lines skipping CR, whitespace and trailing/leading quote char
253 prepend = None
254 if any(prefix in line for prefix in vuid_prefixes):
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600255 line_list = line.split()
Dave Houltoncacef472018-05-29 13:00:42 -0600256
257 # 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
258 broken_vuid = line_list[-1].strip('"')
259 if any(broken_vuid.startswith(prefix) for prefix in vuid_prefixes) and broken_vuid.endswith('-'):
260 prepend = line
261 continue
262
263 vuid_list = []
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600264 for str in line_list:
Dave Houltoncacef472018-05-29 13:00:42 -0600265 if any(prefix in str for prefix in vuid_prefixes):
266 vuid_list.append(str.strip(',);{}"'))
267 for vuid in vuid_list:
268 if vuid not in self.vuid_count_dict:
269 self.vuid_count_dict[vuid] = {}
270 self.vuid_count_dict[vuid]['count'] = 1
271 self.vuid_count_dict[vuid]['file_line'] = []
272 else:
273 if self.vuid_count_dict[vuid]['count'] == 1: # only count first time duplicated
274 self.duplicated_checks = self.duplicated_checks + 1
275 self.vuid_count_dict[vuid]['count'] = self.vuid_count_dict[vuid]['count'] + 1
276 self.vuid_count_dict[vuid]['file_line'].append('%s,%d' % (sf, line_num))
277 # Sort vuids by type
278 for vuid in self.vuid_count_dict.keys():
279 if (vuid.startswith('VUID-')):
280 if (vuid[-5:-1].isdecimal()):
281 self.explicit_vuids.add(vuid) # explicit end in 5 numeric chars
282 else:
283 self.implicit_vuids.add(vuid)
284 elif (vuid.startswith('UNASSIGNED-')):
285 self.unassigned_vuids.add(vuid)
286 else:
287 print("Unable to categorize VUID: %s" % vuid)
288 print("Confused while parsing VUIDs in layer source code - cannot proceed. (FIXME)")
289 exit(-1)
290 self.all_vuids = self.explicit_vuids | self.implicit_vuids | self.unassigned_vuids
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600291
292# Class to parse the validation layer test source and store testnames
Dave Houltoncacef472018-05-29 13:00:42 -0600293class ValidationTests:
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600294 def __init__(self, test_file_list, test_group_name=['VkLayerTest', 'VkPositiveLayerTest', 'VkWsiEnabledLayerTest']):
295 self.test_files = test_file_list
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600296 self.test_trigger_txt_list = []
297 for tg in test_group_name:
298 self.test_trigger_txt_list.append('TEST_F(%s' % tg)
Dave Houltoncacef472018-05-29 13:00:42 -0600299 self.explicit_vuids = set()
300 self.implicit_vuids = set()
301 self.unassigned_vuids = set()
302 self.all_vuids = set()
303 #self.test_to_vuids = {} # Map test name to VUIDs tested
304 self.vuid_to_tests = defaultdict(set) # Map VUIDs to set of test names where implemented
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600305
306 # Parse test files into internal data struct
307 def parse(self):
308 # For each test file, parse test names into set
309 grab_next_line = False # handle testname on separate line than wildcard
Tobin Ehlis9a68c982016-12-29 14:51:17 -0700310 testname = ''
Dave Houltoncacef472018-05-29 13:00:42 -0600311 prepend = None
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600312 for test_file in self.test_files:
313 with open(test_file) as tf:
314 for line in tf:
315 if True in [line.strip().startswith(comment) for comment in ['//', '/*']]:
316 continue
317
Dave Houltoncacef472018-05-29 13:00:42 -0600318 # if line ends in a broken VUID string, fix that before proceeding
319 if prepend != None:
320 line = prepend[:-2] + line.lstrip().lstrip('"') # join lines skipping CR, whitespace and trailing/leading quote char
321 prepend = None
322 if any(prefix in line for prefix in vuid_prefixes):
323 line_list = line.split()
324
325 # 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
326 broken_vuid = line_list[-1].strip('"')
327 if any(broken_vuid.startswith(prefix) for prefix in vuid_prefixes) and broken_vuid.endswith('-'):
328 prepend = line
329 continue
330
331 if any(ttt in line for ttt in self.test_trigger_txt_list):
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600332 testname = line.split(',')[-1]
333 testname = testname.strip().strip(' {)')
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600334 if ('' == testname):
335 grab_next_line = True
336 continue
Dave Houltoncacef472018-05-29 13:00:42 -0600337 #self.test_to_vuids[testname] = []
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600338 if grab_next_line: # test name on its own line
339 grab_next_line = False
340 testname = testname.strip().strip(' {)')
Dave Houltoncacef472018-05-29 13:00:42 -0600341 #self.test_to_vuids[testname] = []
342 if any(prefix in line for prefix in vuid_prefixes):
343 line_list = re.split('[\s{}[\]()"]+',line)
Tobin Ehlis71f38c12017-01-12 14:26:56 -0700344 for sub_str in line_list:
Dave Houltoncacef472018-05-29 13:00:42 -0600345 if any(prefix in sub_str for prefix in vuid_prefixes):
346 vuid_str = sub_str.strip(',);:"')
347 self.vuid_to_tests[vuid_str].add(testname)
348 #self.test_to_vuids[testname].append(vuid_str)
349 if (vuid_str.startswith('VUID-')):
350 if (vuid_str[-5:-1].isdecimal()):
351 self.explicit_vuids.add(vuid_str) # explicit end in 5 numeric chars
352 else:
353 self.implicit_vuids.add(vuid_str)
354 elif (vuid_str.startswith('UNASSIGNED-')):
355 self.unassigned_vuids.add(vuid_str)
356 else:
357 print("Unable to categorize VUID: %s" % vuid_str)
358 print("Confused while parsing VUIDs in test code - cannot proceed. (FIXME)")
359 exit(-1)
360 self.all_vuids = self.explicit_vuids | self.implicit_vuids | self.unassigned_vuids
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600361
Dave Houltoncacef472018-05-29 13:00:42 -0600362# Class to do consistency checking
363#
364class Consistency:
365 def __init__(self, all_json, all_checks, all_tests):
366 self.valid = all_json
367 self.checks = all_checks
368 self.tests = all_tests
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600369
Dave Houltoncacef472018-05-29 13:00:42 -0600370 if (dealias_khr):
371 dk = set()
372 for vuid in self.checks:
373 if vuid in khr_aliases:
374 dk.add(khr_aliases[vuid])
375 else:
376 dk.add(vuid)
377 self.checks = dk
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600378
Dave Houltoncacef472018-05-29 13:00:42 -0600379 dk = set()
380 for vuid in self.tests:
381 if vuid in khr_aliases:
382 dk.add(khr_aliases[vuid])
383 else:
384 dk.add(vuid)
385 self.tests = dk
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600386
Dave Houltoncacef472018-05-29 13:00:42 -0600387 # Report undefined VUIDs in source code
388 def undef_vuids_in_layer_code(self):
389 undef_set = self.checks - self.valid
390 undef_set.discard('VUID-Undefined') # don't report Undefined
391 if ignore_unassigned:
392 unassigned = set({uv for uv in undef_set if uv.startswith('UNASSIGNED-')})
393 undef_set = undef_set - unassigned
394 if (len(undef_set) > 0):
395 print("\nFollowing VUIDs found in layer code are not defined in validusage.json (%d):" % len(undef_set))
396 undef = list(undef_set)
397 undef.sort()
398 for vuid in undef:
399 print(" %s" % vuid)
400 return False
401 return True
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600402
Dave Houltoncacef472018-05-29 13:00:42 -0600403 # Report undefined VUIDs in tests
404 def undef_vuids_in_tests(self):
405 undef_set = self.tests - self.valid
406 undef_set.discard('VUID-Undefined') # don't report Undefined
407 if ignore_unassigned:
408 unassigned = set({uv for uv in undef_set if uv.startswith('UNASSIGNED-')})
409 undef_set = undef_set - unassigned
410 if (len(undef_set) > 0):
411 ok = False
412 print("\nFollowing VUIDs found in layer tests are not defined in validusage.json (%d):" % len(undef_set))
413 undef = list(undef_set)
414 undef.sort()
415 for vuid in undef:
416 print(" %s" % vuid)
417 return False
418 return True
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600419
Dave Houltoncacef472018-05-29 13:00:42 -0600420 # Report vuids in tests that are not in source
421 def vuids_tested_not_checked(self):
422 undef_set = self.tests - self.checks
423 undef_set.discard('VUID-Undefined') # don't report Undefined
424 if ignore_unassigned:
425 unassigned = set()
426 for vuid in undef_set:
427 if vuid.startswith('UNASSIGNED-'):
428 unassigned.add(vuid)
429 undef_set = undef_set - unassigned
430 if (len(undef_set) > 0):
431 ok = False
432 print("\nFollowing VUIDs found in tests but are not checked in layer code (%d):" % len(undef_set))
433 undef = list(undef_set)
434 undef.sort()
435 for vuid in undef:
436 print(" %s" % vuid)
437 return False
438 return True
439
440 # TODO: Explicit checked VUIDs which have no test
441 # def explicit_vuids_checked_not_tested(self):
442
443
444# Class to output database in various flavors
445#
446class OutputDatabase:
447 def __init__(self, val_json, val_source, val_tests):
448 self.vj = val_json
449 self.vs = val_source
450 self.vt = val_tests
451
452 def dump_txt(self):
453 print("\n Dumping database to text file: %s" % txt_filename)
454 with open (txt_filename, 'w') as txt:
455 txt.write("## VUID Database\n")
456 txt.write("## Format: VUID_NAME | CHECKED | TEST | TYPE | API/STRUCT | EXTENSION | VUID_TEXT\n##\n")
457 vuid_list = list(self.vj.all_vuids)
458 vuid_list.sort()
459 for vuid in vuid_list:
460 db_list = self.vj.vuid_db[vuid]
461 db_list.sort(key=operator.itemgetter('ext')) # sort list to ease diffs of output file
462 for db_entry in db_list:
463 checked = 'N'
464 if vuid in self.vs.all_vuids:
465 checked = 'Y'
466 test = 'None'
467 if vuid in self.vt.vuid_to_tests:
468 test_list = list(self.vt.vuid_to_tests[vuid])
469 test_list.sort() # sort tests, for diff-ability
470 sep = ', '
471 test = sep.join(test_list)
472
473 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']))
474
475 def dump_csv(self):
476 print("\n Dumping database to csv file: %s" % csv_filename)
477 with open (csv_filename, 'w', newline='') as csvfile:
478 cw = csv.writer(csvfile)
479 cw.writerow(['VUID_NAME','CHECKED','TEST','TYPE','API/STRUCT','EXTENSION','VUID_TEXT'])
480 vuid_list = list(self.vj.all_vuids)
481 vuid_list.sort()
482 for vuid in vuid_list:
483 for db_entry in self.vj.vuid_db[vuid]:
484 row = [vuid]
485 if vuid in self.vs.all_vuids:
486 row.append('Y')
487 else:
488 row.append('N')
489 test = 'None'
490 if vuid in self.vt.vuid_to_tests:
491 sep = ', '
492 test = sep.join(self.vt.vuid_to_tests[vuid])
493 row.append(test)
494 row.append(db_entry['type'])
495 row.append(db_entry['api'])
496 row.append(db_entry['ext'])
497 row.append(db_entry['text'])
498 cw.writerow(row)
499
500 def dump_html(self):
501 print("\n Dumping database to html file: %s" % html_filename)
502 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'
503 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'
504 with open (html_filename, 'w') as hfile:
505 hfile.write(preamble)
506 hfile.write(headers)
507 vuid_list = list(self.vj.all_vuids)
508 vuid_list.sort()
509 for vuid in vuid_list:
510 for db_entry in self.vj.vuid_db[vuid]:
511 hfile.write('<tr><th>%s</th>' % vuid)
512 checked = '<span style="color:red;">N</span>'
513 if vuid in self.vs.all_vuids:
514 checked = '<span style="color:limegreen;">Y</span>'
515 hfile.write('<th>%s</th>' % checked)
516 test = 'None'
517 if vuid in self.vt.vuid_to_tests:
518 sep = ', '
519 test = sep.join(self.vt.vuid_to_tests[vuid])
520 hfile.write('<th>%s</th>' % test)
521 hfile.write('<th>%s</th>' % db_entry['type'])
522 hfile.write('<th>%s</th>' % db_entry['api'])
523 hfile.write('<th>%s</th>' % db_entry['ext'])
524 hfile.write('<th>%s</th></tr>\n' % db_entry['text'])
525 hfile.write('</table>\n</body>\n</html>\n')
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600526
Mark Lobodzinskib44c54a2017-06-12 12:02:38 -0600527def main(argv):
Dave Houltoncacef472018-05-29 13:00:42 -0600528 global verbose_mode
529 global txt_filename
530 global csv_filename
531 global html_filename
532
533 run_consistency = False
534 report_unimplemented = False
535 get_vuid_status = ''
536 txt_out = False
537 csv_out = False
538 html_out = False
539
540 if (1 > len(argv)):
541 printHelp()
542 sys.exit()
543
544 # Parse script args
545 json_filename = argv[0]
546 i = 1
547 while (i < len(argv)):
548 arg = argv[i]
549 i = i + 1
550 if (arg == '-c'):
551 run_consistency = True
552 elif (arg == '-vuid'):
553 get_vuid_status = argv[i]
554 i = i + 1
555 elif (arg == '-todo'):
556 report_unimplemented = True
557 elif (arg == '-txt'):
558 txt_out = True
559 # Set filename if supplied, else use default
560 if i < len(argv) and not argv[i].startswith('-'):
561 txt_filename = argv[i]
562 i = i + 1
563 elif (arg == '-csv'):
564 csv_out = True
565 # Set filename if supplied, else use default
566 if i < len(argv) and not argv[i].startswith('-'):
567 csv_filename = argv[i]
568 i = i + 1
569 elif (arg == '-html'):
570 html_out = True
571 # Set filename if supplied, else use default
572 if i < len(argv) and not argv[i].startswith('-'):
573 html_filename = argv[i]
574 i = i + 1
575 elif (arg in ['-verbose']):
576 verbose_mode = True
577 elif (arg in ['-help', '-h']):
578 printHelp()
579 sys.exit()
580 else:
581 print("Unrecognized argument: %s\n" % arg)
582 printHelp()
583 sys.exit()
584
Tobin Ehlis20e32582016-12-05 14:50:03 -0700585 result = 0 # Non-zero result indicates an error case
Dave Houltoncacef472018-05-29 13:00:42 -0600586
587 # Parse validusage json
588 val_json = ValidationJSON(json_filename)
589 val_json.read()
590 exp_json = len(val_json.explicit_vuids)
591 imp_json = len(val_json.implicit_vuids)
592 all_json = len(val_json.all_vuids)
593 if verbose_mode:
594 print("Found %d unique error vuids in validusage.json file." % all_json)
595 print(" %d explicit" % exp_json)
596 print(" %d implicit" % imp_json)
597 if len(val_json.duplicate_vuids) > 0:
598 print("%d VUIDs appear in validusage.json more than once." % len(val_json.duplicate_vuids))
599 for vuid in val_json.duplicate_vuids:
600 print(" %s" % vuid)
601 for ext in val_json.vuid_db[vuid]:
602 print(" with extension: %s" % ext['ext'])
603
604 # Parse layer source files
Mark Lobodzinski05849f02017-06-21 14:44:14 -0600605 val_source = ValidationSource(layer_source_files, generated_layer_source_files, generated_layer_source_directories)
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600606 val_source.parse()
Dave Houltoncacef472018-05-29 13:00:42 -0600607 exp_checks = len(val_source.explicit_vuids)
608 imp_checks = len(val_source.implicit_vuids)
609 all_checks = len(val_source.vuid_count_dict.keys())
610 if verbose_mode:
611 print("Found %d unique vuid checks in layer source code." % all_checks)
612 print(" %d explicit" % exp_checks)
613 print(" %d implicit" % imp_checks)
614 print(" %d unassigned" % len(val_source.unassigned_vuids))
615 print(" %d checks are implemented more that once" % val_source.duplicated_checks)
616
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600617 # Parse test files
Dave Houltoncacef472018-05-29 13:00:42 -0600618 val_tests = ValidationTests([test_file, ])
619 val_tests.parse()
620 exp_tests = len(val_tests.explicit_vuids)
621 imp_tests = len(val_tests.implicit_vuids)
622 all_tests = len(val_tests.all_vuids)
Mark Lobodzinski060a8e32018-01-08 09:08:06 -0700623 if verbose_mode:
Dave Houltoncacef472018-05-29 13:00:42 -0600624 print("Found %d unique error vuids in test file %s." % (all_tests, test_file))
625 print(" %d explicit" % exp_tests)
626 print(" %d implicit" % imp_tests)
627 print(" %d unassigned" % len(val_tests.unassigned_vuids))
Mike Weiblenfe186122017-02-03 12:44:53 -0700628
Dave Houltoncacef472018-05-29 13:00:42 -0600629 # Process stats
630 print("\nValidation Statistics (using validusage.json version %s)" % val_json.apiversion)
631 print(" VUIDs defined in JSON file: %04d explicit, %04d implicit, %04d total." % (exp_json, imp_json, all_json))
632 print(" VUIDs checked in layer code: %04d explicit, %04d implicit, %04d total." % (exp_checks, imp_checks, all_checks))
633 print(" VUIDs tested in layer tests: %04d explicit, %04d implicit, %04d total." % (exp_tests, imp_tests, all_tests))
634
635 print("\nVUID check coverage")
636 print(" Explicit VUIDs checked: %.1f%% (%d checked vs %d defined)" % ((100.0 * exp_checks / exp_json), exp_checks, exp_json))
637 print(" Implicit VUIDs checked: %.1f%% (%d checked vs %d defined)" % ((100.0 * imp_checks / imp_json), imp_checks, imp_json))
638 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 -0700639
Dave Houltoncacef472018-05-29 13:00:42 -0600640 print("\nVUID test coverage")
641 print(" Explicit VUIDs tested: %.1f%% (%d tested vs %d checks)" % ((100.0 * exp_tests / exp_checks), exp_tests, exp_checks))
642 print(" Implicit VUIDs tested: %.1f%% (%d tested vs %d checks)" % ((100.0 * imp_tests / imp_checks), imp_tests, imp_checks))
643 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 -0700644
Dave Houltoncacef472018-05-29 13:00:42 -0600645 # Report status of a single VUID
646 if len(get_vuid_status) > 1:
647 print("\n\nChecking status of <%s>" % get_vuid_status);
648 if get_vuid_status not in val_json.all_vuids:
649 print(' Not a valid VUID string.')
650 else:
651 if get_vuid_status in val_source.explicit_vuids:
652 print(' Implemented!')
653 line_list = val_source.vuid_count_dict[get_vuid_status]['file_line']
654 for line in line_list:
655 print(' => %s' % line)
Tobin Ehlis9a68c982016-12-29 14:51:17 -0700656 else:
Dave Houltoncacef472018-05-29 13:00:42 -0600657 print(' Not implemented.')
658 if get_vuid_status in val_tests.explicit_vuids:
659 print(' Has a test!')
660 test_list = val_tests.vuid_to_tests[get_vuid_status]
661 for test in test_list:
662 print(' => %s' % test)
663 else:
664 print(' Not tested.')
Mike Weiblenfe186122017-02-03 12:44:53 -0700665
Dave Houltoncacef472018-05-29 13:00:42 -0600666 # Report unimplemented explicit VUIDs
667 if report_unimplemented:
668 unim_explicit = val_json.explicit_vuids - val_source.explicit_vuids
669 print("\n\n%d explicit VUID checks remain unimplemented:" % len(unim_explicit))
670 ulist = list(unim_explicit)
671 ulist.sort()
672 for vuid in ulist:
673 print(" => %s" % vuid)
674
675 # Consistency tests
676 if run_consistency:
677 print("\n\nRunning consistency tests...")
678 con = Consistency(val_json.all_vuids, val_source.all_vuids, val_tests.all_vuids)
679 ok = con.undef_vuids_in_layer_code()
680 ok &= con.undef_vuids_in_tests()
681 ok &= con.vuids_tested_not_checked()
682
683 if ok:
684 print(" OK! No inconsistencies found.")
685
686 # Output database in requested format(s)
687 db_out = OutputDatabase(val_json, val_source, val_tests)
688 if txt_out:
689 db_out.dump_txt()
690 if csv_out:
691 db_out.dump_csv()
692 if html_out:
693 db_out.dump_html()
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600694
Tobin Ehlis20e32582016-12-05 14:50:03 -0700695 return result
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600696
697if __name__ == "__main__":
Mark Lobodzinskib44c54a2017-06-12 12:02:38 -0600698 sys.exit(main(sys.argv[1:]))
Tobin Ehlis35308dd2016-10-31 13:27:36 -0600699