Coverage for /Syzygy/integration_tests/integration_tests_harness.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
62.0%621000.C++source

Line-by-line coverage:

   1    :  // Copyright 2014 Google Inc. All Rights Reserved.
   2    :  //
   3    :  // Licensed under the Apache License, Version 2.0 (the "License");
   4    :  // you may not use this file except in compliance with the License.
   5    :  // You may obtain a copy of the License at
   6    :  //
   7    :  //     http://www.apache.org/licenses/LICENSE-2.0
   8    :  //
   9    :  // Unless required by applicable law or agreed to in writing, software
  10    :  // distributed under the License is distributed on an "AS IS" BASIS,
  11    :  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12    :  // See the License for the specific language governing permissions and
  13    :  // limitations under the License.
  14    :  //
  15    :  // A harness for loading integration_tests_dll, and calling a test function
  16    :  // within it. This is intended for use with instrumented versions of the
  17    :  // DLL, and is required for certain tests that raise exceptions. The test has
  18    :  // to be moved to a separate process so as to avoid gtest interference in
  19    :  // exception handling.
  20    :  
  21    :  #include "base/bind.h"
  22    :  #include "base/command_line.h"
  23    :  #include "base/logging.h"
  24    :  #include "base/files/file_path.h"
  25    :  #include "base/files/file_util.h"
  26    :  #include "base/strings/string_number_conversions.h"
  27    :  #include "syzygy/agent/asan/block.h"  // Solely for some typedefs.
  28    :  #include "syzygy/common/com_utils.h"
  29    :  #include "syzygy/integration_tests/integration_tests_dll.h"
  30    :  
  31    :  namespace {
  32    :  
  33    :  typedef unsigned int (__stdcall* EndToEndTestFunction)(unsigned int);
  34    :  
  35    :  #define _STRINGIFY(s) #s
  36    :  #define STRINGIFY(s) _STRINGIFY(s)
  37    :  
  38    :  // An array of test names. The test integer ID is the position of the name in
  39    :  // the array.
  40    :  const char* kTestNames[] = {
  41    :  #define DEFINE_TEST_NAME(enum_name, function_name) STRINGIFY(enum_name),
  42    :    END_TO_END_TEST_ID_TABLE(DEFINE_TEST_NAME)
  43    :  #undef DEFINE_TEST_NAME
  44    :  };
  45    :  
  46    :  // Top level configuration and parameters.
  47    :  LPTOP_LEVEL_EXCEPTION_FILTER previous_unhandled_exception_filter = NULL;
  48  E :  base::FilePath dll;
  49    :  size_t test_id = 0;
  50    :  bool expect_exception = false;
  51    :  
  52  E :  bool ParseTestId(base::CommandLine* cmd_line) {
  53  E :    DCHECK_NE(reinterpret_cast<base::CommandLine*>(NULL), cmd_line);
  54    :  
  55  E :    std::string test = cmd_line->GetSwitchValueASCII("test");
  56  E :    if (test.empty()) {
  57  i :      LOG(ERROR) << "Must specify --test.";
  58  i :      return false;
  59    :    }
  60    :  
  61    :    // Search for the test by name
  62  E :    for (size_t i = 0; i < arraysize(kTestNames); ++i) {
  63  E :      if (test == kTestNames[i]) {
  64  i :        test_id = i;
  65  i :        return true;
  66    :      }
  67  E :    }
  68    :  
  69    :    // Try to convert the string to an integer.
  70  E :    if (!base::StringToSizeT(test, &test_id)) {
  71  i :      LOG(ERROR) << "Invalid test name or id: " << test;
  72  i :      return false;
  73    :    }
  74    :  
  75    :    // If integer parsing worked then ensure it's a valid test id.
  76  E :    if (test_id >= arraysize(kTestNames)) {
  77  i :      LOG(ERROR) << "Invalid test id: " << test_id;
  78  i :      return false;
  79    :    }
  80    :  
  81  E :    return true;
  82  E :  }
  83    :  
  84  E :  bool ParseCommandLine(base::CommandLine* cmd_line) {
  85  E :    DCHECK_NE(reinterpret_cast<base::CommandLine*>(NULL), cmd_line);
  86    :  
  87    :    // Parse and validate the path to the DLL.
  88  E :    dll = cmd_line->GetSwitchValuePath("dll");
  89  E :    if (dll.empty()) {
  90  i :      LOG(ERROR) << "Must specify --dll.";
  91  i :      return false;
  92    :    }
  93  E :    if (!base::PathExists(dll)) {
  94  i :      LOG(ERROR) << "File does not exist: " << dll.value();
  95  i :      return false;
  96    :    }
  97    :  
  98    :    // Parse the test ID.
  99  E :    if (!ParseTestId(cmd_line))
 100  i :      return false;
 101    :  
 102  E :    expect_exception = cmd_line->HasSwitch("expect-exception");
 103    :  
 104  E :    return true;
 105  E :  }
 106    :  
 107    :  // A utility function for terminating the process with a given return code.
 108  E :  void Exit(UINT code) {
 109  E :    if (code != 0) {
 110  i :      LOG(ERROR) << "Exiting with an error.";
 111  i :    } else {
 112  E :      VLOG(1) << "Terminating successfully.";
 113    :    }
 114  E :    ::TerminateProcess(::GetCurrentProcess(), code);
 115  E :  }
 116    :  
 117    :  // The base unhandled exception filter. If an exception is raised then this is
 118    :  // our exit path.
 119  E :  LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS* exception) {
 120  E :    VLOG(1) << "Entering UnhandledExceptionFilter.";
 121    :  
 122  E :    if (!expect_exception) {
 123  i :      LOG(ERROR) << "An exception was raised, but none was expected.";
 124  i :      Exit(1);
 125    :    }
 126  E :    Exit(0);
 127    :  
 128  E :    LOG(ERROR) << "Something went terribly wrong.";
 129  i :    return EXCEPTION_EXECUTE_HANDLER;
 130  i :  }
 131    :  
 132  i :  void AsanOnExceptionCallback(EXCEPTION_POINTERS* exception) {
 133  i :    LOG(ERROR) << "AsanOnExceptionCallback fired.";
 134  i :    Exit(1);
 135  i :  }
 136    :  
 137  E :  void SetAsanOnExceptionCallback() {
 138    :    typedef void (*OnExceptionCallback)(EXCEPTION_POINTERS*);
 139    :    typedef void (WINAPI *SetOnExceptionCallback)(OnExceptionCallback);
 140    :  
 141  E :    HMODULE asan_module = GetModuleHandle(L"syzyasan_rtl.dll");
 142  E :    if (asan_module == nullptr)
 143  E :      return;
 144    :  
 145    :    SetOnExceptionCallback set_callback =
 146    :        reinterpret_cast<SetOnExceptionCallback>(
 147  i :            ::GetProcAddress(asan_module, "asan_SetOnExceptionCallback"));
 148  i :    DCHECK(set_callback != NULL);
 149  i :    set_callback(&AsanOnExceptionCallback);
 150  E :  }
 151    :  
 152    :  }  // namespace
 153    :  
 154  E :  int main(int argc, char** argv) {
 155    :    // Initialize the command-line.
 156  E :    base::CommandLine::Init(argc, argv);
 157  E :    base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
 158    :  
 159    :    // Initialize logging.
 160  E :    logging::LoggingSettings settings;
 161  E :    settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
 162  E :    settings.lock_log = logging::DONT_LOCK_LOG_FILE;
 163  E :    settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
 164  E :    logging::InitLogging(settings);
 165  E :    logging::SetMinLogLevel(logging::LOG_ERROR);
 166  E :    if (cmd_line->HasSwitch("verbose"))
 167  i :      logging::SetMinLogLevel(logging::LOG_VERBOSE);
 168    :  
 169    :    // Parse the command-line.
 170  E :    if (!ParseCommandLine(cmd_line))
 171  i :      return 1;
 172    :  
 173    :    // Prevent dialog boxes from popping up.
 174  E :    ::SetErrorMode(SEM_FAILCRITICALERRORS);
 175    :  
 176  E :    VLOG(1) << "Registering unhandled exception filter and callback.";
 177    :    previous_unhandled_exception_filter = ::SetUnhandledExceptionFilter(
 178  E :        &MyUnhandledExceptionFilter);
 179    :  
 180    :    // If syzyasan_rtl.dll is in memory then register an OnException handler.
 181    :    // This gracefully does nothing if SyzyASan is not in memory.
 182  E :    SetAsanOnExceptionCallback();
 183    :  
 184    :    // Load the module.
 185  E :    LOG(INFO) << "Loading module: " << dll.value();
 186  E :    HMODULE module = ::LoadLibrary(dll.value().c_str());
 187  E :    if (module == NULL) {
 188  i :      DWORD error = ::GetLastError();
 189  i :      LOG(ERROR) << "LoadLibrary failed: " << common::LogWe(error);
 190  i :      return 1;
 191    :    }
 192    :  
 193    :    // Get the EndToEndTest function. It is the entry point for calling
 194    :    // the various tests.
 195  E :    LOG(INFO) << "Looking up EndToEndTest function.";
 196    :    EndToEndTestFunction func = reinterpret_cast<EndToEndTestFunction>(
 197  E :        ::GetProcAddress(module, "EndToEndTest"));
 198  E :    if (func == NULL) {
 199  i :      LOG(ERROR) << "Failed to find EndToEndTest function.";
 200  i :      return 1;
 201    :    }
 202    :  
 203    :    // Invoke the test function.
 204  E :    LOG(INFO) << "Invoking test " << test_id << ".";
 205  E :    size_t ret = func(test_id);
 206    :  
 207  E :    if (expect_exception) {
 208  i :      LOG(ERROR) << "Expected an exception, but none was raised.";
 209  i :      LOG(ERROR) << "Command-line: " << cmd_line->GetCommandLineString();
 210  i :      Exit(1);
 211    :    }
 212  E :    Exit(0);
 213    :  
 214  E :    LOG(ERROR) << "Something went terribly wrong.";
 215  i :    return 1;
 216  i :  }

Coverage information generated Thu Jan 14 17:40:38 2016.