Coverage for /Syzygy/kasko/minidump_unittest.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
100.0%1761760.C++test

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    :  #include "syzygy/kasko/minidump.h"
  16    :  
  17    :  #include <Windows.h>  // NOLINT
  18    :  #include <Dbgeng.h>
  19    :  #include <DbgHelp.h>
  20    :  #include <Psapi.h>
  21    :  
  22    :  #include <cstring>
  23    :  #include <vector>
  24    :  
  25    :  #include "base/base_switches.h"
  26    :  #include "base/bind.h"
  27    :  #include "base/command_line.h"
  28    :  #include "base/macros.h"
  29    :  #include "base/files/file_path.h"
  30    :  #include "base/files/file_util.h"
  31    :  #include "base/files/memory_mapped_file.h"
  32    :  #include "base/files/scoped_temp_dir.h"
  33    :  #include "base/process/kill.h"
  34    :  #include "base/process/launch.h"
  35    :  #include "base/strings/string16.h"
  36    :  #include "base/strings/string_number_conversions.h"
  37    :  #include "base/strings/utf_string_conversions.h"
  38    :  #include "base/test/multiprocess_test.h"
  39    :  #include "gtest/gtest.h"
  40    :  #include "syzygy/kasko/loader_lock.h"
  41    :  #include "syzygy/kasko/minidump_request.h"
  42    :  #include "syzygy/kasko/testing/minidump_unittest_helpers.h"
  43    :  #include "syzygy/kasko/testing/safe_pipe_reader.h"
  44    :  #include "syzygy/minidump/minidump.h"
  45    :  #include "testing/multiprocess_func_list.h"
  46    :  
  47    :  // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
  48    :  extern "C" IMAGE_DOS_HEADER __ImageBase;
  49    :  
  50    :  namespace kasko {
  51    :  
  52    :  namespace {
  53    :  
  54    :  const char kPipeHandleSwitch[] = "pipe-handle";
  55    :  
  56    :  // Signals an event named by kReadyEventSwitch, then blocks indefinitely.
  57  E :  MULTIPROCESS_TEST_MAIN(MinidumpTestBlockingProcess) {
  58    :    // Read the caller-supplied parameters.
  59  E :    base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
  60    :    std::string pipe_handle_string =
  61  E :        cmd_line->GetSwitchValueASCII(kPipeHandleSwitch);
  62  E :    unsigned handle_value = 0;
  63  E :    CHECK(base::StringToUint(pipe_handle_string, &handle_value));
  64  E :    base::win::ScopedHandle pipe(reinterpret_cast<HANDLE>(handle_value));
  65  E :    DWORD written = 0;
  66  E :    uint32_t image_base = reinterpret_cast<uint32_t>(&__ImageBase);
  67    :    PCHECK(WriteFile(pipe.Get(), &image_base, sizeof(image_base), &written,
  68  E :                     nullptr));
  69  E :    CHECK_EQ(sizeof(void*), written);
  70  E :    pipe.Close();
  71  E :    ::Sleep(INFINITE);
  72  E :    return 0;
  73  E :  }
  74    :  
  75    :  const char kGlobalString[] = "a global string";
  76    :  
  77    :  const char kCustomStreamContents[] = "hello world";
  78    :  uint32_t kCustomStreamType = LastReservedStream + 2468;
  79    :  
  80    :  void ValidateMinidump(IDebugClient4* debug_client,
  81    :                        IDebugControl* debug_control,
  82  E :                        IDebugSymbols* debug_symbols) {
  83    :    ASSERT_HRESULT_SUCCEEDED(
  84  E :        debug_symbols->GetModuleByModuleName("kasko_unittests", 0, NULL, NULL));
  85  E :  }
  86    :  
  87    :  }  // namespace
  88    :  
  89    :  class MinidumpTest : public ::testing::Test {
  90    :   public:
  91  E :    MinidumpTest() {}
  92    :  
  93  E :    ~MinidumpTest() override {}
  94    :  
  95    :    // ::testing::Test implementation.
  96  E :    void SetUp() override { temp_dir_.CreateUniqueTempDir(); }
  97    :  
  98    :    // Launches a child process, waits until it has loaded, and then invokes
  99    :    // GenerateMinidump for the child.
 100    :    // The contents of |request().memory_ranges| must be within the current image
 101    :    // (kasko_unittests.exe). They will be adjusted so as to read the same offset
 102    :    // (from the image base) in the child process.
 103    :    void CallGenerateMinidump(const base::FilePath& dump_file_path,
 104  E :                              bool* result) {
 105  E :      testing::SafePipeReader pipe_reader;
 106    :      base::CommandLine child_command_line =
 107  E :          base::GetMultiProcessTestChildBaseCommandLine();
 108    :      child_command_line.AppendSwitchASCII(switches::kTestChildProcess,
 109  E :                                           "MinidumpTestBlockingProcess");
 110    :      child_command_line.AppendSwitchASCII(
 111    :          kPipeHandleSwitch, base::UintToString(reinterpret_cast<unsigned>(
 112  E :                                 pipe_reader.write_handle())));
 113  E :      base::LaunchOptions options;
 114  E :      options.inherit_handles = true;
 115    :      base::Process child_process =
 116  E :          base::LaunchProcess(child_command_line, options);
 117  E :      ASSERT_TRUE(child_process.IsValid());
 118  E :      uint32_t child_image_base = 0;
 119  E :      ASSERT_TRUE(pipe_reader.ReadData(base::TimeDelta::FromSeconds(15),
 120    :                                       sizeof(child_image_base),
 121    :                                       &child_image_base));
 122    :  
 123  E :      MinidumpRequest adjusted_request = request_;
 124  E :      for (auto& range : request_.user_selected_memory_ranges) {
 125    :        range = range.Offset(child_image_base -
 126  E :                             reinterpret_cast<uint32_t>(&__ImageBase));
 127    :      }
 128    :      *result = kasko::GenerateMinidump(dump_file_path, child_process.Handle(), 0,
 129  E :                                        adjusted_request);
 130    :  
 131  E :      ASSERT_TRUE(child_process.Terminate(0, true));
 132  E :    }
 133    :  
 134    :   protected:
 135  E :    base::FilePath temp_dir() { return temp_dir_.path(); }
 136  E :    MinidumpRequest& request() { return request_; }
 137    :  
 138    :   private:
 139    :    MinidumpRequest request_;
 140    :    base::ScopedTempDir temp_dir_;
 141    :    DISALLOW_COPY_AND_ASSIGN(MinidumpTest);
 142    :  };
 143    :  
 144  E :  TEST_F(MinidumpTest, GenerateAndLoad) {
 145    :    // Generate a minidump for the current process.
 146  E :    base::FilePath dump_file_path = temp_dir().Append(L"test.dump");
 147  E :    bool result = false;
 148  E :    ASSERT_NO_FATAL_FAILURE(CallGenerateMinidump(dump_file_path, &result));
 149  E :    ASSERT_TRUE(result);
 150    :  
 151    :    ASSERT_HRESULT_SUCCEEDED(
 152  E :        testing::VisitMinidump(dump_file_path, base::Bind(&ValidateMinidump)));
 153  E :  }
 154    :  
 155  E :  TEST_F(MinidumpTest, CustomStream) {
 156    :    // Generate a minidump for the current process.
 157  E :    base::FilePath dump_file_path = temp_dir().Append(L"test.dump");
 158    :    MinidumpRequest::CustomStream custom_stream = {
 159  E :        kCustomStreamType, kCustomStreamContents, sizeof(kCustomStreamContents)};
 160  E :    request().custom_streams.push_back(custom_stream);
 161  E :    bool result = false;
 162  E :    ASSERT_NO_FATAL_FAILURE(CallGenerateMinidump(dump_file_path, &result));
 163  E :    ASSERT_TRUE(result);
 164    :  
 165    :    // Open the minidump file.
 166  E :    base::MemoryMappedFile memory_mapped_file;
 167  E :    ASSERT_TRUE(memory_mapped_file.Initialize(dump_file_path));
 168    :  
 169    :    // Access the custom stream.
 170  E :    MINIDUMP_DIRECTORY* dir = nullptr;
 171  E :    void* stream = nullptr;
 172  E :    ULONG stream_length = 0;
 173    :    ASSERT_TRUE(::MiniDumpReadDumpStream(
 174    :        const_cast<uint8*>(memory_mapped_file.data()), kCustomStreamType, &dir,
 175  E :        &stream, &stream_length));
 176    :  
 177    :    // Assert that the custom stream is what we expected.
 178  E :    ASSERT_EQ(sizeof(kCustomStreamContents), stream_length);
 179  E :    ASSERT_EQ(0, memcmp(stream, kCustomStreamContents, stream_length));
 180  E :  }
 181    :  
 182  E :  TEST_F(MinidumpTest, MinidumpType) {
 183    :    // Generate a minidump for the current process.
 184  E :    base::FilePath small_dump_file_path = temp_dir().Append(L"small.dump");
 185  E :    base::FilePath larger_dump_file_path = temp_dir().Append(L"larger.dump");
 186  E :    base::FilePath full_dump_file_path = temp_dir().Append(L"full.dump");
 187    :  
 188  E :    bool result = false;
 189  E :    request().type = MinidumpRequest::SMALL_DUMP_TYPE;
 190  E :    ASSERT_NO_FATAL_FAILURE(CallGenerateMinidump(small_dump_file_path, &result));
 191  E :    ASSERT_TRUE(result);
 192  E :    request().type = MinidumpRequest::LARGER_DUMP_TYPE;
 193  E :    ASSERT_NO_FATAL_FAILURE(CallGenerateMinidump(larger_dump_file_path, &result));
 194  E :    ASSERT_TRUE(result);
 195  E :    request().type = MinidumpRequest::FULL_DUMP_TYPE;
 196  E :    ASSERT_NO_FATAL_FAILURE(CallGenerateMinidump(full_dump_file_path, &result));
 197  E :    ASSERT_TRUE(result);
 198    :  
 199    :    // Use the relative file sizes to infer that the correct minidump type was
 200    :    // respected.
 201    :    // Other approaches (testing the memory ranges included in the dump) were
 202    :    // rejected due to the difficulty of deterministically knowing what should and
 203    :    // shouldn't be included in the various dump types.
 204  E :    int64 small_dump_size = 0;
 205  E :    int64 larger_dump_size = 0;
 206  E :    int64 full_dump_size = 0;
 207    :  
 208  E :    ASSERT_TRUE(base::GetFileSize(small_dump_file_path, &small_dump_size));
 209  E :    ASSERT_TRUE(base::GetFileSize(larger_dump_file_path, &larger_dump_size));
 210  E :    ASSERT_TRUE(base::GetFileSize(full_dump_file_path, &full_dump_size));
 211    :  
 212  E :    EXPECT_GT(full_dump_size, larger_dump_size);
 213  E :    EXPECT_GT(larger_dump_size, small_dump_size);
 214  E :  }
 215    :  
 216  E :  TEST_F(MinidumpTest, MemoryRanges) {
 217    :    // Generate a minidump for the current process.
 218  E :    base::FilePath default_dump_file_path = temp_dir().Append(L"default.dump");
 219    :    base::FilePath dump_with_memory_range_file_path =
 220  E :        temp_dir().Append(L"with_range.dump");
 221    :  
 222  E :    bool result = false;
 223    :    ASSERT_NO_FATAL_FAILURE(
 224  E :        CallGenerateMinidump(default_dump_file_path, &result));
 225  E :    ASSERT_TRUE(result);
 226    :  
 227    :    MinidumpRequest::MemoryRange range = {
 228  E :        reinterpret_cast<uint32_t>(kGlobalString), sizeof(kGlobalString)};
 229  E :    request().user_selected_memory_ranges.push_back(range);
 230    :    ASSERT_NO_FATAL_FAILURE(
 231  E :        CallGenerateMinidump(dump_with_memory_range_file_path, &result));
 232  E :    ASSERT_TRUE(result);
 233    :  
 234  E :    std::string default_dump;
 235  E :    std::string dump_with_memory_range;
 236  E :    ASSERT_TRUE(base::ReadFileToString(default_dump_file_path, &default_dump));
 237    :    ASSERT_TRUE(base::ReadFileToString(dump_with_memory_range_file_path,
 238  E :                                       &dump_with_memory_range));
 239    :  
 240  E :    ASSERT_EQ(std::string::npos, default_dump.find(kGlobalString));
 241  E :    ASSERT_NE(std::string::npos, dump_with_memory_range.find(kGlobalString));
 242  E :  }
 243    :  
 244  E :  TEST_F(MinidumpTest, OverwriteExistingFile) {
 245  E :    base::ScopedTempDir temp_dir;
 246  E :    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 247  E :    base::FilePath dump_file_path;
 248  E :    ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &dump_file_path));
 249    :  
 250  E :    bool result = false;
 251  E :    ASSERT_NO_FATAL_FAILURE(CallGenerateMinidump(dump_file_path, &result));
 252  E :    ASSERT_TRUE(result);
 253    :  
 254    :    ASSERT_HRESULT_SUCCEEDED(
 255  E :        testing::VisitMinidump(dump_file_path, base::Bind(&ValidateMinidump)));
 256  E :  }
 257    :  
 258  E :  TEST_F(MinidumpTest, NonexistantTargetDirectory) {
 259  E :    base::ScopedTempDir temp_dir;
 260  E :    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 261  E :    bool result = false;
 262    :    ASSERT_NO_FATAL_FAILURE(CallGenerateMinidump(
 263  E :        temp_dir.path().Append(L"Foobar").Append(L"HelloWorld"), &result));
 264  E :    ASSERT_FALSE(result);
 265  E :  }
 266    :  
 267    :  // Tests that the ranges for loader lock and the loader lock debug info is
 268    :  // included in the minidump.
 269  E :  TEST_F(MinidumpTest, LoaderLock) {
 270    :    // Generate a minidump for the current process.
 271  E :    base::FilePath default_dump_file_path = temp_dir().Append(L"default.dump");
 272  E :    bool result = false;
 273    :    ASSERT_NO_FATAL_FAILURE(
 274  E :        CallGenerateMinidump(default_dump_file_path, &result));
 275  E :    ASSERT_TRUE(result);
 276    :  
 277  E :    minidump::Minidump minidump;
 278  E :    ASSERT_TRUE(minidump.Open(default_dump_file_path));
 279    :  
 280    :    minidump::Minidump::Stream stream =
 281  E :        minidump.FindNextStream(nullptr, MemoryListStream);
 282  E :    ASSERT_TRUE(stream.IsValid());
 283    :  
 284  E :    bool loader_lock_found = false;
 285  E :    bool debug_info_found = false;
 286    :    core::AddressRange<uint32_t, uint32_t> loader_lock_range(
 287  E :        reinterpret_cast<uint32_t>(GetLoaderLock()), sizeof(CRITICAL_SECTION));
 288    :    core::AddressRange<uint32_t, uint32_t> debug_info_range(
 289    :        reinterpret_cast<uint32_t>(GetLoaderLock()->DebugInfo),
 290  E :        sizeof(CRITICAL_SECTION_DEBUG));
 291    :  
 292  E :    ULONG32 num_memory_descriptors = 0;
 293  E :    stream.ReadElement(&num_memory_descriptors);
 294  E :    for (size_t i = 0;
 295  E :         i < num_memory_descriptors && !(loader_lock_found && debug_info_found);
 296  E :         ++i) {
 297  E :      MINIDUMP_MEMORY_DESCRIPTOR memory_descriptor = {};
 298  E :      stream.ReadElement(&memory_descriptor);
 299    :      core::AddressRange<uint32_t, uint32_t> descriptor_range(
 300    :          memory_descriptor.StartOfMemoryRange,
 301  E :          memory_descriptor.Memory.DataSize);
 302    :  
 303    :      // It is possible that adjacent ranges have been merged in the minidump so
 304    :      // comparing start address and size might not work.
 305  E :      if (!loader_lock_found && descriptor_range.Contains(loader_lock_range))
 306  E :        loader_lock_found = true;
 307  E :      if (!debug_info_found && descriptor_range.Contains(debug_info_range))
 308  E :        debug_info_found = true;
 309  E :    }
 310    :  
 311  E :    ASSERT_TRUE(loader_lock_found && debug_info_found);
 312  E :  }
 313    :  
 314    :  // When generating the minidump, it is assumed that ntdll is always loaded at
 315    :  // the same address in all processes on the system. This test is to make sure
 316    :  // the assertion never changes in the future.
 317  E :  TEST_F(MinidumpTest, NtdllLoadAddress) {
 318    :    // Generate a minidump for the current process.
 319  E :    base::FilePath dump_file_path = temp_dir().Append(L"default.dump");
 320  E :    bool result = false;
 321  E :    ASSERT_NO_FATAL_FAILURE(CallGenerateMinidump(dump_file_path, &result));
 322  E :    ASSERT_TRUE(result);
 323    :  
 324  E :    minidump::Minidump minidump;
 325  E :    ASSERT_TRUE(minidump.Open(dump_file_path));
 326    :  
 327    :    // Retrieve the unique module list stream.
 328    :    minidump::Minidump::Stream module_list =
 329  E :        minidump.FindNextStream(nullptr, ModuleListStream);
 330  E :    ASSERT_TRUE(module_list.IsValid());
 331    :  
 332  E :    ULONG32 num_modules = 0;
 333  E :    ASSERT_TRUE(module_list.ReadElement(&num_modules));
 334    :  
 335  E :    bool ntdll_found = false;
 336  E :    for (size_t i = 0; i < num_modules; ++i) {
 337  E :      MINIDUMP_MODULE module = {};
 338  E :      ASSERT_TRUE(module_list.ReadElement(&module));
 339    :  
 340    :      // Get the module name. The length of the name is included in the stream.
 341    :      MINIDUMP_LOCATION_DESCRIPTOR name_location = {static_cast<ULONG32>(-1),
 342  E :                                                    module.ModuleNameRva};
 343    :      minidump::Minidump::Stream name_stream =
 344  E :          minidump.GetStreamFor(name_location);
 345  E :      ASSERT_TRUE(name_stream.IsValid());
 346    :  
 347  E :      std::wstring module_name;
 348  E :      ASSERT_TRUE(name_stream.ReadString(&module_name));
 349    :  
 350  E :      if (module_name.find(L"ntdll.dll") != -1) {
 351  E :        MODULEINFO ntdll_module_info = {};
 352    :        ASSERT_TRUE(::GetModuleInformation(
 353    :            ::GetCurrentProcess(), ::GetModuleHandle(L"ntdll.dll"),
 354  E :            &ntdll_module_info, sizeof(MODULEINFO)));
 355    :        ASSERT_EQ(reinterpret_cast<uintptr_t>(ntdll_module_info.lpBaseOfDll),
 356  E :                  module.BaseOfImage);
 357  E :        ntdll_found = true;
 358  E :        break;
 359    :      }
 360  E :    }
 361    :    // Don't succeed if the address hasn't been checked.
 362  E :    ASSERT_TRUE(ntdll_found);
 363  E :  }
 364    :  
 365    :  }  // namespace kasko

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