vkbench: initial commit

This is the initial commit of vkbench. And contains a example test,
submitTest, which submit empty command buffers into the queue.

BUG=chromium:936705
TEST=build and run 'vkbench --notemp'

Change-Id: Ica55d97ac2348da7527cd17418fe4a989d30748e
Signed-off-by: Po-Hsien Wang <pwang@chromium.org>
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..f1503a0
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.7)
+project(vkbench)
+
+set(CMAKE_VERBOSE_MAKEFILE:BOOL ON)
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_BUILD_TYPE Debug)
+
+find_package(Vulkan REQUIRED)
+
+include_directories(.)
+include_directories(${Vulkan_INCLUDE_DIRS})
+link_libraries(${Vulkan_LIBRARIES})
+
+add_executable(vkbench src/main.cc src/utils.cc src/testBase.cc src/testUtil.cc
+        src/submitTest.cc src/submitTest.h)
diff --git a/src/main.cc b/src/main.cc
new file mode 100644
index 0000000..2027207
--- /dev/null
+++ b/src/main.cc
@@ -0,0 +1,207 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <getopt.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <algorithm>
+#include <iterator>
+#include <map>
+#include <vector>
+
+#include "submitTest.h"
+#include "utils.h"
+
+// g_iteration determines the total iteration to run for each test
+int g_iteration = 1;
+// g_verbose determine the logging level to print into the screen.
+int g_verbose = false;
+// g_vlayer enables the vulkan verification layer if it is set.
+int g_vlayer = false;
+// g_notemp skips the temperature checking if it is set.
+int g_notemp = false;
+
+// all_tests list all the available tests.
+std::vector<vkbench::testBase*> all_tests = {
+    new vkbench::SubmitTest(10),
+    new vkbench::SubmitTest(100),
+    new vkbench::SubmitTest(1000),
+    new vkbench::SubmitTest(10000),
+};
+// kLongOptions defines the options for argument options.
+const static struct option kLongOptions[] = {
+    {"iterations", required_argument, nullptr, 'i'},
+    {"tests", required_argument, nullptr, 't'},
+    {"blacklist", required_argument, nullptr, 'b'},
+    {"list", no_argument, nullptr, 'l'},
+    {"help", no_argument, nullptr, 'h'},
+    {"notemp", no_argument, &g_notemp, 1},
+    {"vlayer", no_argument, &g_vlayer, 1},
+    {"verbose", no_argument, &g_verbose, 1},
+    {0, 0, 0, 0}};
+
+// TimeTest times the test by looping it iteration times.
+inline uint64_t TimeTest(vkbench::testBase* test, uint64_t iteration) {
+  uint64_t start = GetUTime();
+  bool passed = true;
+  try {
+    test->TestSetup();
+    for (int i = 0; i < iteration && passed; i++) {
+      passed = test->TestRun();
+    }
+    test->TestCleanup();
+  } catch (std::system_error e) {
+    DEBUG("Error while timing test %s.", test->Name())
+    throw e;
+  }
+  if (!passed) {
+    return std::numeric_limits<uint64_t>::max();
+  }
+  return GetUTime() - start;
+}
+
+// Run the test and pre/post processes.
+// @param target_temperature: The test would try to start until the machine get
+//                            to this temperature unless notemp is set.
+// @param duration_us: The test would to iterate till duration_us is reached.
+// @return: true if the test passes.
+bool Run(vkbench::testBase* test,
+         const double init_temperature,
+         const uint64_t duration_us = 1000000) {
+  try {
+    test->VkInitialization();
+    test->TestInitialization();
+    if (!g_notemp) {
+      double temperature = 0;
+      double cooldown_temperature = std::min(45.0, init_temperature + 6.0);
+      double
+          wait = WaitForCoolMachine(cooldown_temperature, 30.0, &temperature);
+      LOG("Waited %f second for machine temperature to cool down to %f.", wait,
+          temperature)
+    }
+    // Do some iterations since initial timings may vary.
+    TimeTest(test, 2);
+    // Target minimum iteration is 1s for each test.
+    uint64_t time = 0;
+    uint64_t iteration = 1;
+    double score = -1.f;
+    do {
+      time = TimeTest(test, iteration);
+      DEBUG("iterations: %llu, time: %llu us, time/iter: %llu us", iteration,
+            time, time / iteration)
+      if (time > duration_us) {
+        if (time == std::numeric_limits<uint64_t>::max()) {
+          return false;
+        } else {
+          score = time / iteration;
+          break;
+        }
+      }
+      iteration = iteration * 2;
+    } while ((1ull << 40) > iteration);
+//     Returns 0.0 if it ran max iterations in less than test time.
+    if (score <= 0.01f) LOG("%s: measurement may not be accurate.",
+                            test->Name())
+    score = test->FormatMeasurement(score);
+    LOG("@RESULT: %46s = %10.2f %-15s", test->Name(), score, test->Unit())
+
+  } catch (std::system_error e) {
+    DEBUG("Error while running test: %s", e.what())
+    return false;
+  }
+  test->TestDestroy();
+  test->VkDestroy();
+  return true;
+}
+
+void PrintTests() {
+  for (const auto& test : all_tests) {
+    LOG("%s: %s", test->Name(), test->Desp())
+  }
+}
+
+void PrintHelp() {
+  LOG(R",(
+Usage: vkbench [OPTIONS]
+  -i, --iterations=N     Specify the iterations to run the tests.
+  -t, --tests=TESTS      Tests to run in colon separated form.
+  -b, --blacklist=TESTS  Tests to not run in colon separated form.
+  --list                 List the tests available.
+  --verbose              Show verbose messages.
+  --vlayer               Enable vulkan verification layer.
+  --notemp               Do not wait for temperature to cool down.),")
+}
+
+bool ParseArgv(int argc, char** argv) {
+  std::vector<std::string> enabled_tests;
+  std::vector<std::string> disabled_tests;
+  int c;
+  int option_index = 0;
+  while ((c = getopt_long(argc, argv, "i:t:b:", kLongOptions, &option_index)) !=
+      -1) {
+    if (c == 'i') {
+      g_iteration = atoi(optarg);
+    } else if (c == 't') {
+      enabled_tests = SplitString(std::string(optarg), ':');
+    } else if (c == 'b') {
+      disabled_tests = SplitString(std::string(optarg), ':');
+    } else if (c == 'l') {
+      PrintTests();
+      return false;
+    } else if (c == '?' || c == 'h') {
+      PrintHelp();
+      return false;
+    }
+  }
+
+  if (optind < argc) {
+    ERROR("Unknown argv: ")
+    while (optind < argc) ERROR("%s ", argv[optind++])
+    return false;
+  }
+
+  all_tests.erase(
+      remove_if(
+          all_tests.begin(), all_tests.end(),
+          [enabled_tests, disabled_tests](const vkbench::testBase* test) {
+            bool isDisabled =
+                !IsItemInVector(enabled_tests, test->Name(), true) ||
+                    IsItemInVector(disabled_tests, test->Name(), false);
+            if (isDisabled)
+              delete test;
+            return isDisabled;
+          }),
+      all_tests.end());
+  return true;
+}
+
+int main(int argc, char* argv[]) {
+  if (!ParseArgv(argc, argv))
+    return 0;
+
+  double init_temperature = g_notemp ? 40 : GetMachineTemperature();
+  std::map<const char*, int> failed_test;
+  LOG("@TEST_BEGIN")
+  PrintDateTime();
+  for (auto i = 0; i<all_tests.size(); i++ ){
+    auto &test = all_tests[i];
+    for (auto iter = 0; iter < g_iteration; i++) {
+      if (!Run(test, init_temperature, 1000000 )) {
+        failed_test[test->Name()] += 1;
+      }
+    }
+  }
+  PrintDateTime();
+  LOG("@TEST_END")
+
+  for (auto& keyval : failed_test) {
+    LOG("%s failed %d times.", keyval.first, keyval.second)
+  }
+
+  for (auto& test : all_tests) {
+    delete test;
+  }
+
+  return 0;
+}
\ No newline at end of file
diff --git a/src/submitTest.cc b/src/submitTest.cc
new file mode 100644
index 0000000..efba1b4
--- /dev/null
+++ b/src/submitTest.cc
@@ -0,0 +1,44 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "submitTest.h"
+#include "testUtil.h"
+
+namespace vkbench {
+void SubmitTest::TestInitialization() {
+  fences_.push_back(device_.createFence({vk::FenceCreateFlagBits::eSignaled}));
+  // Create empty cmdBuffer
+  cmd_buffers_ = device_.allocateCommandBuffers(
+      {cmd_pool_, vk::CommandBufferLevel::ePrimary, 1});
+  cmd_buffers_[0].begin({vk::CommandBufferUsageFlagBits::eSimultaneousUse});
+  cmd_buffers_[0].end();
+
+  for (auto& t : smt_infos_) {
+    t.setCommandBufferCount(1);
+    t.setPCommandBuffers(&cmd_buffers_[0]);
+  }
+}
+
+bool SubmitTest::TestRun() {
+  device_.waitForFences(fences_, VK_TRUE, std::numeric_limits<uint64_t>::max());
+  device_.resetFences(fences_);
+  gfx_queue_.submit(smt_infos_, fences_[0], {});
+  return true;
+}
+
+void SubmitTest::TestCleanup() {
+  device_.waitForFences(fences_, VK_TRUE, std::numeric_limits<uint64_t>::max());
+}
+
+void SubmitTest::TestDestroy() {
+  if (!device_)
+    return;
+
+  if (cmd_pool_)
+    device_.freeCommandBuffers(cmd_pool_, cmd_buffers_);
+  for (auto &fence : fences_) {
+    device_.destroyFence(fence);
+  }
+}
+}  // namespace vkbench
diff --git a/src/submitTest.h b/src/submitTest.h
new file mode 100644
index 0000000..8a5c647
--- /dev/null
+++ b/src/submitTest.h
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VKBENCH_SUBMITTEST_H
+#define VKBENCH_SUBMITTEST_H
+
+#include "testBase.h"
+
+namespace vkbench {
+class SubmitTest : public testBase {
+ public:
+  SubmitTest(uint64_t submitCnt){
+    sprintf(name_, "SubmitTest@%lu", submitCnt);
+    sprintf(desp_, "Times the time used when submitting %lu empty calls.",
+            submitCnt);
+    smt_infos_.resize(submitCnt);
+  }
+  const char* Name() const override { return name_; }
+  const char* Desp() const override { return desp_; }
+  ~SubmitTest() override = default;
+  const char* Unit() const override { return "us"; }
+
+ protected:
+  void TestInitialization() override;
+  bool TestRun() override;
+  void TestCleanup() override;
+  void TestDestroy() override;
+
+ private:
+  char name_[100];
+  char desp_[1024];
+  std::vector<vk::Fence> fences_;
+  std::vector<vk::SubmitInfo> smt_infos_;
+  std::vector<vk::CommandBuffer> cmd_buffers_;
+  DISALLOW_COPY_AND_ASSIGN(SubmitTest);
+};
+}  // namespace vkbench
+#endif  // VKBENCH_SUBMITTEST_H
diff --git a/src/testBase.cc b/src/testBase.cc
new file mode 100644
index 0000000..6c2834a
--- /dev/null
+++ b/src/testBase.cc
@@ -0,0 +1,131 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testBase.h"
+#include <unistd.h>
+#include <limits>
+#include <vulkan/vulkan.hpp>
+#include "utils.h"
+#include "testUtil.h"
+
+extern int g_vlayer;
+
+namespace vkbench {
+void testBase::VkInitialization() {
+  CreateInstance();
+  ChoosePhysical_device_();
+  CreateLogicalDevice();
+  CreateCommandPool();
+}
+
+VKAPI_ATTR VkBool32 VKAPI_CALL
+ValidationCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
+                   VkDebugUtilsMessageTypeFlagsEXT messageType,
+                   const VkDebugUtilsMessengerCallbackDataEXT* kPcallbackData,
+                   void* pUserData) {
+  UNUSED(messageType);
+  UNUSED(pUserData);
+  if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
+    DEBUG("%s", kPcallbackData->pMessage)
+  } else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT ||
+      messageSeverity ==
+          VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
+    LOG("%s", kPcallbackData->pMessage)
+  } else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
+    ERROR("%s", kPcallbackData->pMessage)
+  } else {
+    ABORT(1, "%s", kPcallbackData->pMessage)
+  }
+  return VK_FALSE;
+}
+
+bool testBase::CreateInstance() {
+  const std::vector<const char*> kDebugExtension = {"VK_EXT_debug_utils"};
+  vk::ApplicationInfo
+      appInfo("vkbench", 1, "Vulkan.hpp", 1, VK_API_VERSION_1_1);
+  vk::InstanceCreateInfo createInfo({}, &appInfo);
+
+  if (g_vlayer) {
+    if (!IsLayersSupported(kValidation_layers_) ||
+        !IsExtensionSupported(kDebugExtension)) {
+      DEBUG("Validation layer is not supported. Less log will be printed.")
+      enable_validation_layer_ = false;
+    } else {
+      createInfo.setEnabledExtensionCount(kDebugExtension.size())
+          .setPpEnabledExtensionNames(kDebugExtension.data())
+          .setEnabledLayerCount(kValidation_layers_.size())
+          .setPpEnabledLayerNames(kValidation_layers_.data());
+      enable_validation_layer_ = true;
+    }
+  }
+  instance_ = vk::createInstance(createInfo);
+  if (enable_validation_layer_) {
+    vk::DebugUtilsMessengerCreateInfoEXT debugCreateInfo;
+    debugCreateInfo
+        .setMessageSeverity(
+            vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning
+                | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError)
+        .setMessageType(vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral
+                            | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation)
+        .setPfnUserCallback(ValidationCallback);
+    CreateDebugUtilsMessengerEXT(instance_,
+                                 &debugCreateInfo,
+                                 nullptr,
+                                 &debug_messenger_);
+  }
+  return true;
+}
+
+bool testBase::ChoosePhysical_device_() {
+  if (!instance_)
+    return false;
+  std::vector<vk::PhysicalDevice>
+      physical_devices = instance_.enumeratePhysicalDevices();
+  if (physical_devices.size() == 0) {
+    ERROR("No available Vk physical_device_.")
+    return false;
+  }
+  // Choose the first device we found.
+  physical_device_ = physical_devices[0];
+  gfx_queue_idx_ = ChooseGFXQueueFamilies(physical_device_);
+  return true;
+}
+
+bool testBase::CreateLogicalDevice() {
+  float tmp = 1.0f;
+  vk::DeviceQueueCreateInfo queueCreateInfo({}, gfx_queue_idx_, 1, &tmp);
+  vk::DeviceCreateInfo deviceCreateInfo({}, 1, &queueCreateInfo);
+  if (enable_validation_layer_) {
+    deviceCreateInfo.setEnabledLayerCount(kValidation_layers_.size());
+    deviceCreateInfo.setPpEnabledLayerNames(kValidation_layers_.data());
+  }
+  device_ = physical_device_.createDevice(deviceCreateInfo);
+  gfx_queue_ = device_.getQueue(gfx_queue_idx_, /* Queue Index */ 0);
+  return true;
+}
+
+bool testBase::CreateCommandPool() {
+  cmd_pool_ =
+      device_.createCommandPool({vk::CommandPoolCreateFlags(), gfx_queue_idx_});
+  return true;
+}
+
+void testBase::VkDestroy() {
+  if (!instance_) {
+    ERROR("Can't destroy any resource without a valid vulkan instance.")
+    return;
+  }
+  if (device_) {
+    if (cmd_pool_) {
+      device_.destroy(cmd_pool_, nullptr);
+    }
+    device_.destroy();
+  }
+  if (enable_validation_layer_) {
+    DestroyDebugUtilsMessengerEXT(instance_, debug_messenger_, nullptr);
+  }
+  instance_.destroy();
+}
+
+}  // namespace vkbench
\ No newline at end of file
diff --git a/src/testBase.h b/src/testBase.h
new file mode 100644
index 0000000..e5cf210
--- /dev/null
+++ b/src/testBase.h
@@ -0,0 +1,68 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef __VKBENCH_TESTBASE_H__
+#define __VKBENCH_TESTBASE_H__
+
+#include <string>
+#include <vector>
+#include <vulkan/vulkan.hpp>
+
+#include "utils.h"
+
+namespace vkbench {
+class testBase {
+ public:
+  virtual ~testBase() = default;
+  // Name of test.
+  virtual const char* Name() const = 0;
+  // Description of the test.
+  virtual const char* Desp() const = 0;
+  // Unit for formatted measurement.
+  virtual const char* Unit() const = 0;
+  // Given time elapse per iteration, format it into meaningful numbers.
+  virtual double FormatMeasurement(double time) { return time; }
+
+  // Vulkan initialization for allocating universal resources. Must be called
+  // before TestInitialization.
+  virtual void VkInitialization();
+  // Test related resources allocation. The resources allocated here would be
+  // shared by all TestRun iterations. Time spent here will not be recorded.
+  virtual void TestInitialization(){}
+  // Test configuration before running the test. This will run once before
+  // each TestRun loop. Time spent here will be recorded.
+  virtual void TestSetup(){}
+  // Test body. Time spent will be recorded.
+  virtual bool TestRun() = 0;
+  // Test cleanup after looping TestRun. Time spent here will be recorded.
+  virtual void TestCleanup() = 0;
+  // Free and Destroy any resources allocated during TestInitialization. Time
+  // spent here will not be recorded.
+  virtual void TestDestroy() = 0;
+  // Free and Destroy any general vulkan resources
+  virtual void VkDestroy();
+
+ protected:
+  // Vulkan general method.
+  virtual bool CreateInstance();
+  virtual bool ChoosePhysical_device_();
+  virtual bool CreateLogicalDevice();
+  virtual bool CreateCommandPool();
+
+  // Vulkan handles
+  const std::vector<const char*> kValidation_layers_ = {
+      "VK_LAYER_LUNARG_standard_validation", "VK_LAYER_GOOGLE_threading"};
+  bool enable_validation_layer_ = false;
+
+  vk::DebugUtilsMessengerEXT debug_messenger_;
+  vk::Instance instance_;
+  vk::PhysicalDevice physical_device_;
+  vk::Device device_;
+  vk::Queue gfx_queue_;
+  vk::CommandPool cmd_pool_;
+  uint32_t gfx_queue_idx_ = -1;
+};
+
+}  // namespace vkbench
+#endif
diff --git a/src/testUtil.cc b/src/testUtil.cc
new file mode 100644
index 0000000..6e84530
--- /dev/null
+++ b/src/testUtil.cc
@@ -0,0 +1,97 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <limits>
+#include <vulkan/vulkan.hpp>
+#include "testUtil.h"
+#include "utils.h"
+
+bool vkbench::IsLayersSupported(const std::vector<const char*>& kLayers) {
+  std::vector<vk::LayerProperties>
+      availLayers = vk::enumerateInstanceLayerProperties();
+  for (const auto& layer : kLayers) {
+    bool found = false;
+    for (const auto availLayer : availLayers) {
+      if (!strcmp(availLayer.layerName, layer)) {
+        DEBUG("%s Layer is support.", layer)
+        found = true;
+        break;
+      }
+    }
+    if (!found) {
+      DEBUG("Layer %s is not support.", layer)
+      return false;
+    }
+  }
+  return true;
+}
+
+bool vkbench::IsExtensionSupported(
+    const std::vector<const char*>& kExtensions) {
+  std::vector<vk::ExtensionProperties>
+      availExtensions = vk::enumerateInstanceExtensionProperties();
+  for (const auto extension : kExtensions) {
+    bool found = false;
+    for (const auto availExtension : availExtensions) {
+      if (!strcmp(availExtension.extensionName, extension)) {
+        found = true;
+        break;
+      }
+    }
+    if (!found) {
+      DEBUG("Extension %s is not supported.", extension)
+      return false;
+    }
+  }
+  return true;
+}
+
+vk::Result vkbench::CreateDebugUtilsMessengerEXT(
+    vk::Instance instance,
+    const vk::DebugUtilsMessengerCreateInfoEXT* kPcreateInfo,
+    const vk::AllocationCallbacks* kPallocator,
+    vk::DebugUtilsMessengerEXT* pdebug_messengeer) {
+  auto func = (PFN_vkCreateDebugUtilsMessengerEXT) instance.getProcAddr(
+      "vkCreateDebugUtilsMessengerEXT");
+  if (func != nullptr) {
+    return static_cast<vk::Result>(func(
+        instance,
+        reinterpret_cast<const VkDebugUtilsMessengerCreateInfoEXT*>(
+            kPcreateInfo),
+        reinterpret_cast<const VkAllocationCallbacks*>(kPallocator),
+        reinterpret_cast<VkDebugUtilsMessengerEXT*>(pdebug_messengeer)));
+  } else {
+    return vk::Result::eErrorExtensionNotPresent;
+  }
+}
+
+void vkbench::DestroyDebugUtilsMessengerEXT(
+    vk::Instance instance,
+    vk::DebugUtilsMessengerEXT debug_messengeer,
+    const vk::AllocationCallbacks* kPAllocator) {
+  auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) instance.getProcAddr(
+      "vkDestroyDebugUtilsMessengerEXT");
+  if (func != nullptr) {
+    func(instance,
+         static_cast<VkDebugUtilsMessengerEXT>(debug_messengeer),
+         reinterpret_cast<const VkAllocationCallbacks*>(
+             static_cast<const vk::AllocationCallbacks*>(kPAllocator)));
+  }
+}
+
+uint32_t vkbench::ChooseGFXQueueFamilies(
+    const vk::PhysicalDevice& kPhysical_device) {
+  uint32_t gfxQueueIdx = UINT32_MAX;
+  std::vector<vk::QueueFamilyProperties>
+      props = kPhysical_device.getQueueFamilyProperties();
+  for (uint32_t i = 0; i < props.size(); i++) {
+    if (props[i].queueCount <= 0)
+      continue;
+    if (props[i].queueFlags & vk::QueueFlagBits::eGraphics) {
+      gfxQueueIdx = i;
+      break;
+    }
+  }
+  return gfxQueueIdx;
+}
\ No newline at end of file
diff --git a/src/testUtil.h b/src/testUtil.h
new file mode 100644
index 0000000..0a87ebd
--- /dev/null
+++ b/src/testUtil.h
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef __VKBENCH_TESTUTIL_H__
+#define __VKBENCH_TESTUTIL_H__
+#include <string>
+#include <vulkan/vulkan.hpp>
+
+namespace vkbench {
+bool IsLayersSupported(const std::vector<const char*>& kLayers);
+bool IsExtensionSupported(const std::vector<const char*>& kExtensions);
+uint32_t ChooseGFXQueueFamilies(const vk::PhysicalDevice& kPhysical_device);
+vk::Result CreateDebugUtilsMessengerEXT(
+    vk::Instance instance,
+    const vk::DebugUtilsMessengerCreateInfoEXT* kPcreateInfo,
+    const vk::AllocationCallbacks* kPallocator,
+    vk::DebugUtilsMessengerEXT* pdebug_messengeer);
+void DestroyDebugUtilsMessengerEXT(vk::Instance instance,
+                                   vk::DebugUtilsMessengerEXT debug_messengeer,
+                                   const vk::AllocationCallbacks* kPAllocator);
+}  // namespace vkbench
+#endif
diff --git a/src/utils.cc b/src/utils.cc
new file mode 100644
index 0000000..6d665a8
--- /dev/null
+++ b/src/utils.cc
@@ -0,0 +1,101 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "utils.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <chrono>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+const std::string kTemperatureScript =
+    "/usr/local/autotest/bin/temperature.py --maximum";
+
+void PrintDateTime() {
+  time_t timer;
+  char buffer[26];
+  time(&timer);
+  struct tm* tm_info = localtime(&timer);
+  strftime(buffer, 26, "%Y-%m-%d %H:%M:%S", tm_info);
+  LOG("# DateTime: %s", buffer)
+}
+
+/* Execute a shell command and return its file descriptor for reading output. */
+/* @param command: command to be run. */
+/* @param result: the stdout of the command output. */
+/* @return true if the command is executed successfully. */
+bool ExecuteCommand(const std::string& kCommand,
+                    std::string* result = nullptr) {
+  FILE* fd = popen(kCommand.c_str(), "r");
+  if (!fd) {
+    return false;
+  }
+
+  if (result) {
+    fseek(fd, 0, SEEK_END);
+    long size = ftell(fd);
+    fseek(fd, 0, SEEK_SET);
+    fread(&result[0], sizeof(char), size, fd);
+  }
+  return pclose(fd) == 0;
+}
+
+std::vector<std::string> SplitString(const std::string& kInput,
+                                     char delimiter) {
+  std::vector<std::string> result;
+  std::stringstream srcStream(kInput);
+
+  std::string token;
+  while (getline(srcStream, token, delimiter)) {
+    result.push_back(token);
+  }
+  return result;
+}
+
+// Returns currently measured temperature.
+double GetMachineTemperature() {
+  std::string temp_str;
+  double temperature;
+  if (!ExecuteCommand(kTemperatureScript, &temp_str)) {
+    return 10000;
+  }
+  temperature = strtod(temp_str.c_str(), nullptr);
+  if (temperature < 10.0 || temperature > 150.0) {
+    DEBUG("Warning: ignoring temperature reading of %f'C.", temperature)
+  }
+  return temperature;
+}
+
+// Waits up to timeout seconds to reach cold_temperature in Celsius.
+// @param cold_temperature: the target temperature in celsius.
+// @param timeout: the timeout to wait.
+// @param temperature: the final temperature of the machine.
+// @return: the total second of wait time.
+double WaitForCoolMachine(double cold_temperature,
+                          double timeout,
+                          double* temperature) {
+  uint64_t start, now, end;
+  start = now = GetUTime();
+  end = now + 1e6 * timeout;
+  do {
+    *temperature = GetMachineTemperature();
+    if (*temperature < cold_temperature)
+      break;
+    sleep(1.0);
+    now = GetUTime();
+  } while (now < end);
+  return 1.0e-6 * (now - start);
+}
+
+bool IsItemInVector(const std::vector<std::string>& list,
+                    const char* value,
+                    bool empty_value = false) {
+  if (list.empty())
+    return empty_value;
+  return !(find(list.begin(), list.end(), std::string(value)) == list.end());
+}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..9f16811
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,84 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef __UTILS_H__
+#define __UTILS_H__
+
+#include <stdarg.h>
+#include <sys/time.h>
+#include <string>
+#include <vector>
+#include <vulkan/vulkan.hpp>
+
+extern int g_verbose;
+void PrintDateTime();
+std::vector<std::string> SplitString(const std::string& kInput, char delimiter);
+// For thermal monitoring of system.
+double GetMachineTemperature();
+// Wait for machine to cool with temperature in Celsius and timeout in seconds.
+// Returns the time spent waiting and sets the last observed temperature.
+double WaitForCoolMachine(double cold_temperature,
+                          double timeout,
+                          double* temperature);
+
+inline uint64_t GetUTime() {
+  struct timeval tv = {};
+  gettimeofday(&tv, nullptr);
+  return tv.tv_usec + 1000000ULL * static_cast<uint64_t>(tv.tv_sec);
+}
+
+bool IsItemInVector(const std::vector<std::string>& list,
+                    const char* value,
+                    bool empty_value);
+
+inline void DbgPrintf(const char* filename,
+                      int line,
+                      FILE* fileid,
+                      const char* format,
+                      ...) {
+  va_list args;
+  va_start(args, format);
+  char string_format[strlen(format) + 1024];
+  char debug_header[1024] = "\0";
+  if (g_verbose) {
+    sprintf(debug_header, "[%s:%d]", filename, line);
+    sprintf(string_format, "%30s %s\n", debug_header, format);
+  } else {
+    sprintf(string_format, "%s\n", format);
+  }
+  vfprintf(fileid, string_format, args);
+  va_end(args);
+}
+
+#define DEBUG(fmt, ...)        \
+  {                            \
+    if (g_verbose) {           \
+      LOG(fmt, ##__VA_ARGS__); \
+    }                          \
+  }
+#define LOG(fmt, ...) \
+  { DbgPrintf(__FILE__, __LINE__, stdout, fmt, ##__VA_ARGS__); }
+#define ERROR(fmt, ...) \
+  { DbgPrintf(__FILE__, __LINE__, stderr, fmt, ##__VA_ARGS__); }
+#define ABORT(code, fmt, ...)  \
+  {                            \
+    ERROR(fmt, ##__VA_ARGS__); \
+    exit(code);                \
+  }
+
+// Put this in the declarations for a class to be uncopyable.
+#define DISALLOW_COPY(TypeName) TypeName(const TypeName&) = delete
+
+// Put this in the declarations for a class to be unassignable.
+#define DISALLOW_ASSIGN(TypeName) TypeName& operator=(const TypeName&) = delete
+
+// Put this in the declarations for a class to be uncopyable and unassignable.
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  DISALLOW_COPY(TypeName);                 \
+  DISALLOW_ASSIGN(TypeName)
+
+// Ignore warning for linter for unused variable.
+#define UNUSED(x) (void)(x)
+
+#endif