xref: /aosp_15_r20/external/deqp/framework/common/tcuWaiverUtil.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * Vulkan CTS Framework
3  * --------------------
4  *
5  * Copyright (c) 2020 The Khronos Group Inc.
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  *//*!
20  * \file
21  * \brief Waiver mechanism implementation.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "tcuWaiverUtil.hpp"
25 #include <fstream>
26 #include <iostream>
27 #include <sstream>
28 #include <iomanip>
29 #include "deString.h"
30 #include "deStringUtil.hpp"
31 #include "xeXMLParser.hpp"
32 #include "tcuCommandLine.hpp"
33 
34 namespace tcu
35 {
36 
SessionInfo(uint32_t vendorId,uint32_t deviceId,const std::string & deviceName,const std::string & cmdLine)37 SessionInfo::SessionInfo(uint32_t vendorId, uint32_t deviceId, const std::string &deviceName,
38                          const std::string &cmdLine)
39     : m_cmdLine(cmdLine)
40 {
41     m_info << std::hex << "#sessionInfo vendorID 0x" << vendorId << "\n"
42            << "#sessionInfo deviceID 0x" << deviceId << "\n"
43            << "#sessionInfo deviceName " << deviceName << "\n";
44 }
45 
SessionInfo(std::string vendor,std::string renderer,const std::string & cmdLine)46 SessionInfo::SessionInfo(std::string vendor, std::string renderer, const std::string &cmdLine) : m_cmdLine(cmdLine)
47 {
48     m_info << "#sessionInfo vendor \"" << vendor << "\"\n"
49            << "#sessionInfo renderer \"" << renderer << "\"\n";
50 }
51 
get()52 std::string SessionInfo::get()
53 {
54     if (!m_waiverUrls.empty())
55     {
56         m_info << "#sessionInfo waiverUrls \"" << m_waiverUrls << "\"\n";
57         m_waiverUrls.clear();
58     }
59     if (!m_cmdLine.empty())
60     {
61         m_info << "#sessionInfo commandLineParameters \"" << m_cmdLine << "\"\n";
62         m_cmdLine.clear();
63     }
64     return m_info.str();
65 }
66 
67 // Base class for GL and VK waiver tree builders
68 class WaiverTreeBuilder
69 {
70 public:
71     typedef WaiverUtil::WaiverComponent WaiverComponent;
72 
73 public:
74     WaiverTreeBuilder(const std::string &waiverFile, const std::string &packageName, const char *vendorTag,
75                       const char *deviceTag, SessionInfo &sessionInfo, std::vector<WaiverComponent> &waiverTree);
76 
77     virtual ~WaiverTreeBuilder();
78 
79     void build(void);
80 
81 protected:
82     // structure representing component during tree construction
83     struct BuilComponent
84     {
85         std::string name;
86         uint32_t parentIndex;                // index in allComponents vector
87         std::vector<uint32_t> childrenIndex; // index in allComponents vector
88 
BuilComponenttcu::WaiverTreeBuilder::BuilComponent89         BuilComponent(std::string n, uint32_t p) : name(std::move(n)), parentIndex(p)
90         {
91         }
92     };
93 
94     // parse waiver.xml and read list of waived tests defined
95     // specificly for current device id and current vendor id
96     void readWaivedTestsFromXML(void);
97 
98     // use list of paths to build a temporary tree which
99     // consists of BuilComponents that help with tree construction
100     void buildTreeFromPathList(void);
101 
102     // use temporary tree to create final tree containing
103     // only things that are needed during searches
104     void constructFinalTree(void);
105 
106     // helper methods used to identify if proper waiver for vendor was found
107     virtual bool matchVendor(const std::string &vendor) const = 0;
108 
109     // helper methods used after waiver for current vendor was found to check
110     // if it is defined also for currend deviceId/renderer
111     virtual bool matchDevice(const std::string &device) const = 0;
112 
113     // helper method used in buildTreeFromPathList; returns index
114     // of component having same ancestors as the component specified
115     // in the argument or 0 when build tree does not include this component
116     uint32_t findComponentInBuildTree(const std::vector<std::string> &pathComponents, uint32_t index) const;
117 
118 private:
119     const std::string &m_waiverFile;
120     const std::string &m_packageName;
121 
122     const char *m_vendorTag;
123     const char *m_deviceTag;
124 
125     // helper attributes used during construction
126     std::vector<std::string> m_testList;
127     std::vector<BuilComponent> m_buildTree;
128 
129     // reference to object containing information about used waivers
130     SessionInfo &m_sessionInfo;
131 
132     // reference to vector containing final tree
133     std::vector<WaiverComponent> &m_finalTree;
134 };
135 
WaiverTreeBuilder(const std::string & waiverFile,const std::string & packageName,const char * vendorTag,const char * deviceTag,SessionInfo & sessionInfo,std::vector<WaiverComponent> & waiverTree)136 WaiverTreeBuilder::WaiverTreeBuilder(const std::string &waiverFile, const std::string &packageName,
137                                      const char *vendorTag, const char *deviceTag, SessionInfo &sessionInfo,
138                                      std::vector<WaiverComponent> &waiverTree)
139     : m_waiverFile(waiverFile)
140     , m_packageName(packageName)
141     , m_vendorTag(vendorTag)
142     , m_deviceTag(deviceTag)
143     , m_sessionInfo(sessionInfo)
144     , m_finalTree(waiverTree)
145 {
146 }
147 
~WaiverTreeBuilder()148 WaiverTreeBuilder::~WaiverTreeBuilder()
149 {
150 }
151 
build(void)152 void WaiverTreeBuilder::build(void)
153 {
154     readWaivedTestsFromXML();
155     buildTreeFromPathList();
156     constructFinalTree();
157 }
158 
readWaivedTestsFromXML()159 void WaiverTreeBuilder::readWaivedTestsFromXML()
160 {
161     std::ifstream iStream(m_waiverFile);
162     if (!iStream.is_open())
163         return;
164 
165     // get whole waiver file content
166     std::stringstream buffer;
167     buffer << iStream.rdbuf();
168     std::string wholeContent = buffer.str();
169 
170     // feed parser with xml content
171     xe::xml::Parser xmlParser;
172     xmlParser.feed(reinterpret_cast<const uint8_t *>(wholeContent.c_str()), static_cast<int>(wholeContent.size()));
173     xmlParser.advance();
174 
175     // first we find matching vendor, then search for matching device/renderer and then memorize cases
176     bool vendorFound  = false;
177     bool deviceFound  = false;
178     bool scanDevice   = false;
179     bool memorizeCase = false;
180     std::string waiverUrl;
181     std::vector<std::string> waiverTestList;
182 
183     while (true)
184     {
185         // we are grabing elements one by one - depth-first traversal in pre-order
186         xe::xml::Element currElement = xmlParser.getElement();
187 
188         // stop if there is parsing error or we didnt found
189         // waiver for current vendor id and device id/renderer
190         if (currElement == xe::xml::ELEMENT_INCOMPLETE || currElement == xe::xml::ELEMENT_END_OF_STRING)
191             break;
192 
193         const char *elemName = xmlParser.getElementName();
194         switch (currElement)
195         {
196         case xe::xml::ELEMENT_START:
197             if (vendorFound)
198             {
199                 if (!deviceFound)
200                 {
201                     // if we found proper vendor and are reading deviceIds/rendererers list then allow it
202                     scanDevice = deStringEqual(elemName, m_deviceTag); // e.g. "d"
203                     if (scanDevice)
204                         break;
205                 }
206 
207                 // if we found waiver for current vendor and are reading test case names then allow it
208                 memorizeCase = deStringEqual(elemName, "t");
209                 break;
210             }
211 
212             // we are searching for waiver definition for current vendor, till we find
213             // it we skip everythingh; we also skip tags that we don't need eg. description
214             if (!deStringEqual(elemName, "waiver"))
215                 break;
216 
217             // we found waiver tag, check if it is deffined for current vendor
218             waiverTestList.clear();
219             if (xmlParser.hasAttribute(m_vendorTag))
220             {
221                 vendorFound = matchVendor(xmlParser.getAttribute(m_vendorTag));
222                 // if waiver vendor matches current one then memorize waiver url
223                 // it will be needed when deviceId/renderer will match current one
224                 if (vendorFound)
225                     waiverUrl = xmlParser.getAttribute("url");
226             }
227             break;
228 
229         case xe::xml::ELEMENT_DATA:
230             if (scanDevice)
231             {
232                 // check if device read from xml matches current device/renderer
233                 std::string waivedDevice;
234                 xmlParser.getDataStr(waivedDevice);
235                 deviceFound = matchDevice(waivedDevice);
236             }
237             else if (memorizeCase)
238             {
239                 // memorize whats betwean <t></t> tags when case name starts with current package name
240                 // note: waiver tree is constructed per package
241                 std::string waivedCaseName;
242                 xmlParser.getDataStr(waivedCaseName);
243                 if (waivedCaseName.find(m_packageName) == 0)
244                     waiverTestList.push_back(waivedCaseName);
245             }
246             break;
247 
248         case xe::xml::ELEMENT_END:
249             memorizeCase = false;
250             scanDevice   = false;
251             if (deStringEqual(elemName, "waiver"))
252             {
253                 // when we found proper waiver we can copy memorized cases and update waiver info
254                 if (vendorFound && deviceFound)
255                 {
256                     DE_ASSERT(m_testList.empty() || waiverUrl.empty());
257 
258                     std::string &urls = m_sessionInfo.m_waiverUrls;
259                     m_testList.insert(m_testList.end(), waiverTestList.begin(), waiverTestList.end());
260 
261                     // if m_waiverUrls is not empty then we found another waiver
262                     // definition that should be applyed for this device; we need to
263                     // add space to urls attribute to separate new url from previous one
264                     if (!urls.empty())
265                         urls.append(" ");
266                     urls.append(waiverUrl);
267                 }
268                 vendorFound = false;
269                 deviceFound = false;
270             }
271             break;
272 
273         default:
274             DE_ASSERT(false);
275         }
276 
277         xmlParser.advance();
278     }
279 }
280 
findComponentInBuildTree(const std::vector<std::string> & pathComponents,uint32_t index) const281 uint32_t WaiverTreeBuilder::findComponentInBuildTree(const std::vector<std::string> &pathComponents,
282                                                      uint32_t index) const
283 {
284     const std::string &checkedName = pathComponents[index];
285 
286     // check if same component is already in the build tree; we start from 1 - skiping root
287     for (uint32_t componentIndex = 1; componentIndex < m_buildTree.size(); ++componentIndex)
288     {
289         const BuilComponent &componentInTree = m_buildTree[componentIndex];
290         if (componentInTree.name != checkedName)
291             continue;
292 
293         // names match so we need to make sure that all their ancestors match too;
294         uint32_t reverseLevel        = index;
295         uint32_t ancestorInTreeIndex = componentInTree.parentIndex;
296 
297         // if this component is the next after root then there is no ancestors to check
298         if (reverseLevel == 1)
299             return componentIndex;
300 
301         while (--reverseLevel > 0)
302         {
303             // names dont match - we can move to searching other build tree items
304             if (pathComponents[reverseLevel] != m_buildTree[ancestorInTreeIndex].name)
305                 break;
306 
307             // when previous path component matches ancestor name then we need do check earlier path component
308             ancestorInTreeIndex = m_buildTree[ancestorInTreeIndex].parentIndex;
309 
310             // we reached root
311             if (ancestorInTreeIndex == 0)
312             {
313                 // if next level would be root then ancestors match
314                 if (reverseLevel == 1)
315                     return componentIndex;
316                 // if next level is not root then ancestors dont match
317                 break;
318             }
319         }
320     }
321 
322     // searched path component is not in the tree
323     return 0;
324 }
325 
buildTreeFromPathList(void)326 void WaiverTreeBuilder::buildTreeFromPathList(void)
327 {
328     if (m_testList.empty())
329         return;
330 
331     uint32_t parentIndex = 0;
332 
333     // construct root node
334     m_buildTree.emplace_back("root", 0);
335 
336     for (const auto &path : m_testList)
337     {
338         const std::vector<std::string> pathComponents = de::splitString(path, '.');
339 
340         // first component is parented to root
341         parentIndex = 0;
342 
343         // iterate over all components of current path, but skip first one (e.g. "dEQP-VK", "KHR-GLES31")
344         for (uint32_t level = 1; level < pathComponents.size(); ++level)
345         {
346             // check if same component is already in the tree and we dont need to add it
347             uint32_t componentIndex = findComponentInBuildTree(pathComponents, level);
348             if (componentIndex)
349             {
350                 parentIndex = componentIndex;
351                 continue;
352             }
353 
354             // component is not in the tree, add it
355             const std::string componentName = pathComponents[level];
356             m_buildTree.emplace_back(componentName, parentIndex);
357 
358             // add current component as a child to its parent and assume
359             // that this component will be parent of next component
360             componentIndex = static_cast<uint32_t>(m_buildTree.size() - 1);
361             m_buildTree[parentIndex].childrenIndex.push_back(componentIndex);
362             parentIndex = componentIndex;
363         }
364     }
365 }
366 
constructFinalTree(void)367 void WaiverTreeBuilder::constructFinalTree(void)
368 {
369     if (m_buildTree.empty())
370         return;
371 
372     // translate vector of BuilComponents to vector of WaiverComponents
373     m_finalTree.resize(m_buildTree.size());
374     for (uint32_t i = 0; i < m_finalTree.size(); ++i)
375     {
376         BuilComponent &buildCmponent     = m_buildTree[i];
377         WaiverComponent &waiverComponent = m_finalTree[i];
378 
379         waiverComponent.name = std::move(buildCmponent.name);
380         waiverComponent.children.resize(buildCmponent.childrenIndex.size());
381 
382         // set pointers for children
383         for (uint32_t j = 0; j < buildCmponent.childrenIndex.size(); ++j)
384         {
385             uint32_t childIndexInTree   = buildCmponent.childrenIndex[j];
386             waiverComponent.children[j] = &m_finalTree[childIndexInTree];
387         }
388     }
389 }
390 
391 // Class that builds a tree out of waiver definitions for OpenGL tests.
392 // Most of functionalities are shared betwean VK and GL builders and they
393 // were extracted to WaiverTreeBuilder base class.
394 class GLWaiverTreeBuilder : public WaiverTreeBuilder
395 {
396 public:
397     GLWaiverTreeBuilder(const std::string &waiverFile, const std::string &packageName, const std::string &currentVendor,
398                         const std::string &currentRenderer, SessionInfo &sessionInfo,
399                         std::vector<WaiverComponent> &waiverTree);
400 
401     bool matchVendor(const std::string &vendor) const override;
402     bool matchDevice(const std::string &device) const override;
403 
404 private:
405     const std::string m_currentVendor;
406     const std::string m_currentRenderer;
407 };
408 
GLWaiverTreeBuilder(const std::string & waiverFile,const std::string & packageName,const std::string & currentVendor,const std::string & currentRenderer,SessionInfo & sessionInfo,std::vector<WaiverComponent> & waiverTree)409 GLWaiverTreeBuilder::GLWaiverTreeBuilder(const std::string &waiverFile, const std::string &packageName,
410                                          const std::string &currentVendor, const std::string &currentRenderer,
411                                          SessionInfo &sessionInfo, std::vector<WaiverComponent> &waiverTree)
412     : WaiverTreeBuilder(waiverFile, packageName, "vendor", "r", sessionInfo, waiverTree)
413     , m_currentVendor(currentVendor)
414     , m_currentRenderer(currentRenderer)
415 {
416 }
417 
matchVendor(const std::string & vendor) const418 bool GLWaiverTreeBuilder::matchVendor(const std::string &vendor) const
419 {
420     return tcu::matchWildcards(vendor.cbegin(), vendor.cend(), m_currentVendor.cbegin(), m_currentVendor.cend(), false);
421 }
422 
matchDevice(const std::string & device) const423 bool GLWaiverTreeBuilder::matchDevice(const std::string &device) const
424 {
425     // make sure that renderer name in .xml is not within "", those extra characters should be removed
426     DE_ASSERT(device[0] != '\"');
427 
428     return tcu::matchWildcards(device.cbegin(), device.cend(), m_currentRenderer.cbegin(), m_currentRenderer.cend(),
429                                false);
430 }
431 
432 // Class that builds a tree out of waiver definitions for Vulkan tests.
433 // Most of functionalities are shared betwean VK and GL builders and they
434 // were extracted to WaiverTreeBuilder base class.
435 class VKWaiverTreeBuilder : public WaiverTreeBuilder
436 {
437 public:
438     VKWaiverTreeBuilder(const std::string &waiverFile, const std::string &packageName, const uint32_t currentVendor,
439                         const uint32_t currentRenderer, SessionInfo &sessionInfo,
440                         std::vector<WaiverComponent> &waiverTree);
441 
442     bool matchVendor(const std::string &vendor) const override;
443     bool matchDevice(const std::string &device) const override;
444 
445 private:
446     const uint32_t m_currentVendorId;
447     const uint32_t m_currentDeviceId;
448 };
449 
VKWaiverTreeBuilder(const std::string & waiverFile,const std::string & packageName,const uint32_t currentVendor,const uint32_t currentRenderer,SessionInfo & sessionInfo,std::vector<WaiverComponent> & waiverTree)450 VKWaiverTreeBuilder::VKWaiverTreeBuilder(const std::string &waiverFile, const std::string &packageName,
451                                          const uint32_t currentVendor, const uint32_t currentRenderer,
452                                          SessionInfo &sessionInfo, std::vector<WaiverComponent> &waiverTree)
453     : WaiverTreeBuilder(waiverFile, packageName, "vendorId", "d", sessionInfo, waiverTree)
454     , m_currentVendorId(currentVendor)
455     , m_currentDeviceId(currentRenderer)
456 {
457 }
458 
matchVendor(const std::string & vendor) const459 bool VKWaiverTreeBuilder::matchVendor(const std::string &vendor) const
460 {
461     return (m_currentVendorId == static_cast<uint32_t>(std::stoul(vendor, 0, 0)));
462 }
463 
matchDevice(const std::string & device) const464 bool VKWaiverTreeBuilder::matchDevice(const std::string &device) const
465 {
466     return (m_currentDeviceId == static_cast<uint32_t>(std::stoul(device, 0, 0)));
467 }
468 
setup(const std::string waiverFile,std::string packageName,uint32_t vendorId,uint32_t deviceId,SessionInfo & sessionInfo)469 void WaiverUtil::setup(const std::string waiverFile, std::string packageName, uint32_t vendorId, uint32_t deviceId,
470                        SessionInfo &sessionInfo)
471 {
472     VKWaiverTreeBuilder(waiverFile, packageName, vendorId, deviceId, sessionInfo, m_waiverTree).build();
473 }
474 
setup(const std::string waiverFile,std::string packageName,std::string vendor,std::string renderer,SessionInfo & sessionInfo)475 void WaiverUtil::setup(const std::string waiverFile, std::string packageName, std::string vendor, std::string renderer,
476                        SessionInfo &sessionInfo)
477 {
478     GLWaiverTreeBuilder(waiverFile, packageName, vendor, renderer, sessionInfo, m_waiverTree).build();
479 }
480 
isOnWaiverList(const std::string & casePath) const481 bool WaiverUtil::isOnWaiverList(const std::string &casePath) const
482 {
483     if (m_waiverTree.empty())
484         return false;
485 
486     // skip root e.g. "dEQP-VK"
487     size_t firstDotPos                         = casePath.find('.');
488     std::string::const_iterator componentStart = casePath.cbegin() + firstDotPos + 1;
489     std::string::const_iterator componentEnd   = componentStart;
490     std::string::const_iterator pathEnd        = casePath.cend();
491     const WaiverComponent *waiverComponent     = m_waiverTree.data();
492 
493     // check path component by component
494     while (true)
495     {
496         // find the last character of next component
497         ++componentEnd;
498         for (; componentEnd < pathEnd; ++componentEnd)
499         {
500             if (*componentEnd == '.')
501                 break;
502         }
503 
504         // check if one of children has the same component name
505         for (const auto &c : waiverComponent->children)
506         {
507             bool matchFound =
508                 tcu::matchWildcards(c->name.cbegin(), c->name.cend(), componentStart, componentEnd, false);
509 
510             // current waiver component matches curent path component - go to next component
511             if (matchFound)
512             {
513                 waiverComponent = c;
514                 break;
515             }
516         }
517 
518         // we checked all components - if our pattern was a leaf then this test should be waived
519         if (componentEnd == pathEnd)
520             return waiverComponent->children.empty();
521 
522         // go to next test path component
523         componentStart = componentEnd + 1;
524     }
525     return false;
526 }
527 
528 } // namespace tcu
529