Home | History | Annotate | Download | only in vulkan
      1 /*-------------------------------------------------------------------------
      2  * Vulkan Conformance Tests
      3  * ------------------------
      4  *
      5  * Copyright (c) 2016 Google 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 Utility for pre-compiling source programs to SPIR-V
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "tcuDefs.hpp"
     25 #include "tcuCommandLine.hpp"
     26 #include "tcuPlatform.hpp"
     27 #include "tcuResource.hpp"
     28 #include "tcuTestLog.hpp"
     29 #include "tcuTestHierarchyIterator.hpp"
     30 #include "deUniquePtr.hpp"
     31 #include "vkPrograms.hpp"
     32 #include "vkBinaryRegistry.hpp"
     33 #include "vktTestCase.hpp"
     34 #include "vktTestPackage.hpp"
     35 #include "deUniquePtr.hpp"
     36 #include "deCommandLine.hpp"
     37 #include "deSharedPtr.hpp"
     38 #include "deThread.hpp"
     39 #include "deThreadSafeRingBuffer.hpp"
     40 #include "dePoolArray.hpp"
     41 
     42 #include <iostream>
     43 
     44 using std::vector;
     45 using std::string;
     46 using de::UniquePtr;
     47 using de::MovePtr;
     48 using de::SharedPtr;
     49 
     50 namespace vkt
     51 {
     52 
     53 namespace // anonymous
     54 {
     55 
     56 typedef de::SharedPtr<glu::ProgramSources>	ProgramSourcesSp;
     57 typedef de::SharedPtr<vk::SpirVAsmSource>	SpirVAsmSourceSp;
     58 typedef de::SharedPtr<vk::ProgramBinary>	ProgramBinarySp;
     59 
     60 class Task
     61 {
     62 public:
     63 	virtual void	execute		(void) = 0;
     64 };
     65 
     66 typedef de::ThreadSafeRingBuffer<Task*>	TaskQueue;
     67 
     68 class TaskExecutorThread : public de::Thread
     69 {
     70 public:
     71 	TaskExecutorThread (TaskQueue& tasks)
     72 		: m_tasks(tasks)
     73 	{
     74 		start();
     75 	}
     76 
     77 	void run (void)
     78 	{
     79 		for (;;)
     80 		{
     81 			Task* const	task	= m_tasks.popBack();
     82 
     83 			if (task)
     84 				task->execute();
     85 			else
     86 				break; // End of tasks - time to terminate
     87 		}
     88 	}
     89 
     90 private:
     91 	TaskQueue&	m_tasks;
     92 };
     93 
     94 class TaskExecutor
     95 {
     96 public:
     97 								TaskExecutor		(deUint32 numThreads);
     98 								~TaskExecutor		(void);
     99 
    100 	void						submit				(Task* task);
    101 	void						waitForComplete		(void);
    102 
    103 private:
    104 	typedef de::SharedPtr<TaskExecutorThread>	ExecThreadSp;
    105 
    106 	std::vector<ExecThreadSp>	m_threads;
    107 	TaskQueue					m_tasks;
    108 };
    109 
    110 TaskExecutor::TaskExecutor (deUint32 numThreads)
    111 	: m_threads	(numThreads)
    112 	, m_tasks	(m_threads.size() * 1024u)
    113 {
    114 	for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
    115 		m_threads[ndx] = ExecThreadSp(new TaskExecutorThread(m_tasks));
    116 }
    117 
    118 TaskExecutor::~TaskExecutor (void)
    119 {
    120 	for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
    121 		m_tasks.pushFront(DE_NULL);
    122 
    123 	for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
    124 		m_threads[ndx]->join();
    125 }
    126 
    127 void TaskExecutor::submit (Task* task)
    128 {
    129 	DE_ASSERT(task);
    130 	m_tasks.pushFront(task);
    131 }
    132 
    133 class SyncTask : public Task
    134 {
    135 public:
    136 	SyncTask (de::Semaphore* enterBarrier, de::Semaphore* inBarrier, de::Semaphore* leaveBarrier)
    137 		: m_enterBarrier	(enterBarrier)
    138 		, m_inBarrier		(inBarrier)
    139 		, m_leaveBarrier	(leaveBarrier)
    140 	{}
    141 
    142 	SyncTask (void)
    143 		: m_enterBarrier	(DE_NULL)
    144 		, m_inBarrier		(DE_NULL)
    145 		, m_leaveBarrier	(DE_NULL)
    146 	{}
    147 
    148 	void execute (void)
    149 	{
    150 		m_enterBarrier->increment();
    151 		m_inBarrier->decrement();
    152 		m_leaveBarrier->increment();
    153 	}
    154 
    155 private:
    156 	de::Semaphore*	m_enterBarrier;
    157 	de::Semaphore*	m_inBarrier;
    158 	de::Semaphore*	m_leaveBarrier;
    159 };
    160 
    161 void TaskExecutor::waitForComplete (void)
    162 {
    163 	de::Semaphore			enterBarrier	(0);
    164 	de::Semaphore			inBarrier		(0);
    165 	de::Semaphore			leaveBarrier	(0);
    166 	std::vector<SyncTask>	syncTasks		(m_threads.size());
    167 
    168 	for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
    169 	{
    170 		syncTasks[ndx] = SyncTask(&enterBarrier, &inBarrier, &leaveBarrier);
    171 		submit(&syncTasks[ndx]);
    172 	}
    173 
    174 	for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
    175 		enterBarrier.decrement();
    176 
    177 	for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
    178 		inBarrier.increment();
    179 
    180 	for (size_t ndx = 0; ndx < m_threads.size(); ++ndx)
    181 		leaveBarrier.decrement();
    182 }
    183 
    184 struct Program
    185 {
    186 	enum Status
    187 	{
    188 		STATUS_NOT_COMPLETED = 0,
    189 		STATUS_FAILED,
    190 		STATUS_PASSED,
    191 
    192 		STATUS_LAST
    193 	};
    194 
    195 	vk::ProgramIdentifier	id;
    196 
    197 	Status					buildStatus;
    198 	std::string				buildLog;
    199 	ProgramBinarySp			binary;
    200 
    201 	Status					validationStatus;
    202 	std::string				validationLog;
    203 
    204 	vk::SpirvVersion		spirvVersion;
    205 
    206 	explicit				Program		(const vk::ProgramIdentifier& id_, const vk::SpirvVersion spirvVersion_)
    207 								: id				(id_)
    208 								, buildStatus		(STATUS_NOT_COMPLETED)
    209 								, validationStatus	(STATUS_NOT_COMPLETED)
    210 								, spirvVersion		(spirvVersion_)
    211 							{}
    212 							Program		(void)
    213 								: id				("", "")
    214 								, buildStatus		(STATUS_NOT_COMPLETED)
    215 								, validationStatus	(STATUS_NOT_COMPLETED)
    216 								, spirvVersion		(vk::SPIRV_VERSION_LAST)
    217 							{}
    218 };
    219 
    220 void writeBuildLogs (const glu::ShaderProgramInfo& buildInfo, std::ostream& dst)
    221 {
    222 	for (size_t shaderNdx = 0; shaderNdx < buildInfo.shaders.size(); shaderNdx++)
    223 	{
    224 		const glu::ShaderInfo&	shaderInfo	= buildInfo.shaders[shaderNdx];
    225 		const char* const		shaderName	= getShaderTypeName(shaderInfo.type);
    226 
    227 		dst << shaderName << " source:\n"
    228 			<< "---\n"
    229 			<< shaderInfo.source << "\n"
    230 			<< "---\n"
    231 			<< shaderName << " compile log:\n"
    232 			<< "---\n"
    233 			<< shaderInfo.infoLog << "\n"
    234 			<< "---\n";
    235 	}
    236 
    237 	dst << "link log:\n"
    238 		<< "---\n"
    239 		<< buildInfo.program.infoLog << "\n"
    240 		<< "---\n";
    241 }
    242 
    243 template <typename Source>
    244 class BuildHighLevelShaderTask : public Task
    245 {
    246 public:
    247 
    248 	BuildHighLevelShaderTask (const Source& source, Program* program)
    249 		: m_source	(source)
    250 		, m_program	(program)
    251 	{}
    252 
    253 	BuildHighLevelShaderTask (void) : m_program(DE_NULL) {}
    254 
    255 	void execute (void)
    256 	{
    257 		glu::ShaderProgramInfo buildInfo;
    258 
    259 		try
    260 		{
    261 			DE_ASSERT(m_source.buildOptions.targetVersion < vk::SPIRV_VERSION_LAST);
    262 
    263 			m_program->binary		= ProgramBinarySp(vk::buildProgram(m_source, &buildInfo));
    264 			m_program->buildStatus	= Program::STATUS_PASSED;
    265 		}
    266 		catch (const tcu::Exception&)
    267 		{
    268 			std::ostringstream log;
    269 
    270 			writeBuildLogs(buildInfo, log);
    271 
    272 			m_program->buildStatus	= Program::STATUS_FAILED;
    273 			m_program->buildLog		= log.str();
    274 		}
    275 	}
    276 
    277 private:
    278 	Source		m_source;
    279 	Program*	m_program;
    280 };
    281 
    282 void writeBuildLogs (const vk::SpirVProgramInfo& buildInfo, std::ostream& dst)
    283 {
    284 	dst << "source:\n"
    285 		<< "---\n"
    286 		<< buildInfo.source << "\n"
    287 		<< "---\n";
    288 }
    289 
    290 class BuildSpirVAsmTask : public Task
    291 {
    292 public:
    293 	BuildSpirVAsmTask (const vk::SpirVAsmSource& source, Program* program)
    294 		: m_source	(source)
    295 		, m_program	(program)
    296 	{}
    297 
    298 	BuildSpirVAsmTask (void) : m_program(DE_NULL) {}
    299 
    300 	void execute (void)
    301 	{
    302 		vk::SpirVProgramInfo buildInfo;
    303 
    304 		try
    305 		{
    306 			DE_ASSERT(m_source.buildOptions.targetVersion < vk::SPIRV_VERSION_LAST);
    307 
    308 			m_program->binary		= ProgramBinarySp(vk::assembleProgram(m_source, &buildInfo));
    309 			m_program->buildStatus	= Program::STATUS_PASSED;
    310 		}
    311 		catch (const tcu::Exception&)
    312 		{
    313 			std::ostringstream log;
    314 
    315 			writeBuildLogs(buildInfo, log);
    316 
    317 			m_program->buildStatus	= Program::STATUS_FAILED;
    318 			m_program->buildLog		= log.str();
    319 		}
    320 	}
    321 
    322 private:
    323 	vk::SpirVAsmSource	m_source;
    324 	Program*			m_program;
    325 };
    326 
    327 class ValidateBinaryTask : public Task
    328 {
    329 public:
    330 	ValidateBinaryTask (Program* program)
    331 		: m_program(program)
    332 	{}
    333 
    334 	void execute (void)
    335 	{
    336 		DE_ASSERT(m_program->buildStatus == Program::STATUS_PASSED);
    337 		DE_ASSERT(m_program->binary->getFormat() == vk::PROGRAM_FORMAT_SPIRV);
    338 
    339 		std::ostringstream			validationLog;
    340 
    341 		if (vk::validateProgram(*m_program->binary, &validationLog))
    342 			m_program->validationStatus = Program::STATUS_PASSED;
    343 		else
    344 			m_program->validationStatus = Program::STATUS_FAILED;
    345 	}
    346 
    347 private:
    348 	Program*	m_program;
    349 };
    350 
    351 tcu::TestPackageRoot* createRoot (tcu::TestContext& testCtx)
    352 {
    353 	vector<tcu::TestNode*>	children;
    354 	children.push_back(new TestPackage(testCtx));
    355 	return new tcu::TestPackageRoot(testCtx, children);
    356 }
    357 
    358 } // anonymous
    359 
    360 struct BuildStats
    361 {
    362 	int		numSucceeded;
    363 	int		numFailed;
    364 	int		notSupported;
    365 
    366 	BuildStats (void)
    367 		: numSucceeded	(0)
    368 		, numFailed		(0)
    369 		, notSupported	(0)
    370 	{
    371 	}
    372 };
    373 
    374 BuildStats buildPrograms (tcu::TestContext&			testCtx,
    375 						  const std::string&		dstPath,
    376 						  const bool				validateBinaries,
    377 						  const deUint32			usedVulkanVersion,
    378 						  const vk::SpirvVersion	baselineSpirvVersion,
    379 						  const vk::SpirvVersion	maxSpirvVersion)
    380 {
    381 	const deUint32						numThreads			= deGetNumAvailableLogicalCores();
    382 
    383 	TaskExecutor						executor			(numThreads);
    384 
    385 	// de::PoolArray<> is faster to build than std::vector
    386 	de::MemPool							programPool;
    387 	de::PoolArray<Program>				programs			(&programPool);
    388 	int									notSupported		= 0;
    389 
    390 	{
    391 		de::MemPool							tmpPool;
    392 		de::PoolArray<BuildHighLevelShaderTask<vk::GlslSource> >	buildGlslTasks		(&tmpPool);
    393 		de::PoolArray<BuildHighLevelShaderTask<vk::HlslSource> >	buildHlslTasks		(&tmpPool);
    394 		de::PoolArray<BuildSpirVAsmTask>	buildSpirvAsmTasks	(&tmpPool);
    395 
    396 		// Collect build tasks
    397 		{
    398 			const UniquePtr<tcu::TestPackageRoot>	root			(createRoot(testCtx));
    399 			tcu::DefaultHierarchyInflater			inflater		(testCtx);
    400 			de::MovePtr<tcu::CaseListFilter>		caseListFilter	(testCtx.getCommandLine().createCaseListFilter(testCtx.getArchive()));
    401 			tcu::TestHierarchyIterator				iterator		(*root, inflater, *caseListFilter);
    402 
    403 			while (iterator.getState() != tcu::TestHierarchyIterator::STATE_FINISHED)
    404 			{
    405 				if (iterator.getState() == tcu::TestHierarchyIterator::STATE_ENTER_NODE &&
    406 					tcu::isTestNodeTypeExecutable(iterator.getNode()->getNodeType()))
    407 				{
    408 					const TestCase* const		testCase					= dynamic_cast<TestCase*>(iterator.getNode());
    409 					const string				casePath					= iterator.getNodePath();
    410 					vk::ShaderBuildOptions		defaultGlslBuildOptions		(baselineSpirvVersion, 0u);
    411 					vk::ShaderBuildOptions		defaultHlslBuildOptions		(baselineSpirvVersion, 0u);
    412 					vk::SpirVAsmBuildOptions	defaultSpirvAsmBuildOptions	(baselineSpirvVersion);
    413 					vk::SourceCollections		sourcePrograms				(usedVulkanVersion, defaultGlslBuildOptions, defaultHlslBuildOptions, defaultSpirvAsmBuildOptions);
    414 
    415 					try
    416 					{
    417 						testCase->initPrograms(sourcePrograms);
    418 					}
    419 					catch (const tcu::NotSupportedError& )
    420 					{
    421 						notSupported++;
    422 						iterator.next();
    423 						continue;
    424 					}
    425 
    426 					for (vk::GlslSourceCollection::Iterator progIter = sourcePrograms.glslSources.begin();
    427 						 progIter != sourcePrograms.glslSources.end();
    428 						 ++progIter)
    429 					{
    430 						// Source program requires higher SPIR-V version than available: skip it to avoid fail
    431 						if (progIter.getProgram().buildOptions.targetVersion > maxSpirvVersion)
    432 							continue;
    433 
    434 						programs.pushBack(Program(vk::ProgramIdentifier(casePath, progIter.getName()), progIter.getProgram().buildOptions.targetVersion));
    435 						buildGlslTasks.pushBack(BuildHighLevelShaderTask<vk::GlslSource>(progIter.getProgram(), &programs.back()));
    436 						executor.submit(&buildGlslTasks.back());
    437 					}
    438 
    439 					for (vk::HlslSourceCollection::Iterator progIter = sourcePrograms.hlslSources.begin();
    440 						 progIter != sourcePrograms.hlslSources.end();
    441 						 ++progIter)
    442 					{
    443 						// Source program requires higher SPIR-V version than available: skip it to avoid fail
    444 						if (progIter.getProgram().buildOptions.targetVersion > maxSpirvVersion)
    445 							continue;
    446 
    447 						programs.pushBack(Program(vk::ProgramIdentifier(casePath, progIter.getName()), progIter.getProgram().buildOptions.targetVersion));
    448 						buildHlslTasks.pushBack(BuildHighLevelShaderTask<vk::HlslSource>(progIter.getProgram(), &programs.back()));
    449 						executor.submit(&buildHlslTasks.back());
    450 					}
    451 
    452 					for (vk::SpirVAsmCollection::Iterator progIter = sourcePrograms.spirvAsmSources.begin();
    453 						 progIter != sourcePrograms.spirvAsmSources.end();
    454 						 ++progIter)
    455 					{
    456 						// Source program requires higher SPIR-V version than available: skip it to avoid fail
    457 						if (progIter.getProgram().buildOptions.targetVersion > maxSpirvVersion)
    458 							continue;
    459 
    460 						programs.pushBack(Program(vk::ProgramIdentifier(casePath, progIter.getName()), progIter.getProgram().buildOptions.targetVersion));
    461 						buildSpirvAsmTasks.pushBack(BuildSpirVAsmTask(progIter.getProgram(), &programs.back()));
    462 						executor.submit(&buildSpirvAsmTasks.back());
    463 					}
    464 				}
    465 
    466 				iterator.next();
    467 			}
    468 		}
    469 
    470 		// Need to wait until tasks completed before freeing task memory
    471 		executor.waitForComplete();
    472 	}
    473 
    474 	if (validateBinaries)
    475 	{
    476 		std::vector<ValidateBinaryTask>	validationTasks;
    477 
    478 		validationTasks.reserve(programs.size());
    479 
    480 		for (de::PoolArray<Program>::iterator progIter = programs.begin(); progIter != programs.end(); ++progIter)
    481 		{
    482 			if (progIter->buildStatus == Program::STATUS_PASSED)
    483 			{
    484 				validationTasks.push_back(ValidateBinaryTask(&*progIter));
    485 				executor.submit(&validationTasks.back());
    486 			}
    487 		}
    488 
    489 		executor.waitForComplete();
    490 	}
    491 
    492 	{
    493 		vk::BinaryRegistryWriter	registryWriter		(dstPath);
    494 
    495 		for (de::PoolArray<Program>::iterator progIter = programs.begin(); progIter != programs.end(); ++progIter)
    496 		{
    497 			if (progIter->buildStatus == Program::STATUS_PASSED)
    498 				registryWriter.addProgram(progIter->id, *progIter->binary);
    499 		}
    500 
    501 		registryWriter.write();
    502 	}
    503 
    504 	{
    505 		BuildStats	stats;
    506 		stats.notSupported = notSupported;
    507 		for (de::PoolArray<Program>::iterator progIter = programs.begin(); progIter != programs.end(); ++progIter)
    508 		{
    509 			const bool	buildOk			= progIter->buildStatus == Program::STATUS_PASSED;
    510 			const bool	validationOk	= progIter->validationStatus != Program::STATUS_FAILED;
    511 
    512 			if (buildOk && validationOk)
    513 				stats.numSucceeded += 1;
    514 			else
    515 			{
    516 				stats.numFailed += 1;
    517 				tcu::print("ERROR: %s / %s: %s failed\n",
    518 						   progIter->id.testCasePath.c_str(),
    519 						   progIter->id.programName.c_str(),
    520 						   (buildOk ? "validation" : "build"));
    521 				tcu::print("%s\n", (buildOk ? progIter->validationLog.c_str() : progIter->buildLog.c_str()));
    522 			}
    523 		}
    524 
    525 		return stats;
    526 	}
    527 }
    528 
    529 } // vkt
    530 
    531 namespace opt
    532 {
    533 
    534 DE_DECLARE_COMMAND_LINE_OPT(DstPath,		std::string);
    535 DE_DECLARE_COMMAND_LINE_OPT(Cases,			std::string);
    536 DE_DECLARE_COMMAND_LINE_OPT(Validate,		bool);
    537 DE_DECLARE_COMMAND_LINE_OPT(VulkanVersion,	deUint32);
    538 
    539 void registerOptions (de::cmdline::Parser& parser)
    540 {
    541 	using de::cmdline::Option;
    542 	using de::cmdline::NamedValue;
    543 
    544 	static const NamedValue<deUint32> s_vulkanVersion[] =
    545 	{
    546 		{ "1.0",	VK_MAKE_VERSION(1, 0, 0)	},
    547 		{ "1.1",	VK_MAKE_VERSION(1, 1, 0)	},
    548 	};
    549 
    550 	DE_STATIC_ASSERT(vk::SPIRV_VERSION_1_3 + 1 == vk::SPIRV_VERSION_LAST);
    551 
    552 	parser << Option<opt::DstPath>				("d", "dst-path",				"Destination path",	"out")
    553 		   << Option<opt::Cases>				("n", "deqp-case",				"Case path filter (works as in test binaries)")
    554 		   << Option<opt::Validate>				("v", "validate-spv",			"Validate generated SPIR-V binaries")
    555 		   << Option<opt::VulkanVersion>		("t", "target-vulkan-version",	"Target Vulkan version", s_vulkanVersion, "1.1");
    556 }
    557 
    558 } // opt
    559 
    560 int main (int argc, const char* argv[])
    561 {
    562 	de::cmdline::CommandLine	cmdLine;
    563 	tcu::CommandLine			deqpCmdLine;
    564 
    565 	{
    566 		de::cmdline::Parser		parser;
    567 		opt::registerOptions(parser);
    568 		if (!parser.parse(argc, argv, &cmdLine, std::cerr))
    569 		{
    570 			parser.help(std::cout);
    571 			return -1;
    572 		}
    573 	}
    574 
    575 	{
    576 		vector<const char*> deqpArgv;
    577 
    578 		deqpArgv.push_back("unused");
    579 
    580 		if (cmdLine.hasOption<opt::Cases>())
    581 		{
    582 			deqpArgv.push_back("--deqp-case");
    583 			deqpArgv.push_back(cmdLine.getOption<opt::Cases>().c_str());
    584 		}
    585 
    586 		if (!deqpCmdLine.parse((int)deqpArgv.size(), &deqpArgv[0]))
    587 			return -1;
    588 	}
    589 
    590 	try
    591 	{
    592 		tcu::DirArchive			archive					(".");
    593 		tcu::TestLog			log						(deqpCmdLine.getLogFileName(), deqpCmdLine.getLogFlags());
    594 		tcu::Platform			platform;
    595 		tcu::TestContext		testCtx					(platform, archive, log, deqpCmdLine, DE_NULL);
    596 		vk::SpirvVersion		baselineSpirvVersion	= vk::getBaselineSpirvVersion(cmdLine.getOption<opt::VulkanVersion>());
    597 		vk::SpirvVersion		maxSpirvVersion			= vk::getMaxSpirvVersionForGlsl(cmdLine.getOption<opt::VulkanVersion>());
    598 
    599 		tcu::print("SPIR-V versions: baseline: %s, max supported: %s\n",
    600 					getSpirvVersionName(baselineSpirvVersion).c_str(),
    601 					getSpirvVersionName(maxSpirvVersion).c_str());
    602 
    603 		const vkt::BuildStats	stats		= vkt::buildPrograms(testCtx,
    604 																 cmdLine.getOption<opt::DstPath>(),
    605 																 cmdLine.getOption<opt::Validate>(),
    606 																 cmdLine.getOption<opt::VulkanVersion>(),
    607 																 baselineSpirvVersion,
    608 																 maxSpirvVersion);
    609 
    610 		tcu::print("DONE: %d passed, %d failed, %d not supported\n", stats.numSucceeded, stats.numFailed, stats.notSupported);
    611 
    612 		return stats.numFailed == 0 ? 0 : -1;
    613 	}
    614 	catch (const std::exception& e)
    615 	{
    616 		tcu::die("%s", e.what());
    617 	}
    618 }
    619