Coverage for /Syzygy/refinery/unittest_util.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
0.0%00575.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/refinery/unittest_util.h"
  16    :  
  17    :  #include <windows.h>  // NOLINT
  18    :  #include <dbghelp.h>
  19    :  
  20    :  #include <cstring>
  21    :  #include <vector>
  22    :  
  23    :  #include "base/base_switches.h"
  24    :  #include "base/command_line.h"
  25    :  #include "base/logging.h"
  26    :  #include "base/files/file_util.h"
  27    :  #include "base/files/scoped_temp_dir.h"
  28    :  #include "base/numerics/safe_math.h"
  29    :  #include "base/strings/string_number_conversions.h"
  30    :  #include "base/strings/utf_string_conversions.h"
  31    :  #include "base/test/multiprocess_test.h"
  32    :  #include "base/test/test_timeouts.h"
  33    :  #include "gtest/gtest.h"
  34    :  #include "syzygy/common/com_utils.h"
  35    :  #include "testing/multiprocess_func_list.h"
  36    :  
  37  m :  namespace testing {
  38    :  
  39  m :  namespace {
  40    :  
  41  m :  void PopulateContext(std::string* context_bytes, int base_offset) {
  42  m :    DCHECK(context_bytes != nullptr);
  43    :  
  44  m :    context_bytes->resize(sizeof(CONTEXT));
  45  m :    CONTEXT* ctx = reinterpret_cast<CONTEXT*>(&context_bytes->at(0));
  46  m :    ctx->ContextFlags = CONTEXT_SEGMENTS | CONTEXT_INTEGER | CONTEXT_CONTROL;
  47  m :    ctx->SegGs = base_offset + 1;
  48  m :    ctx->SegFs = base_offset + 2;
  49  m :    ctx->SegEs = base_offset + 3;
  50  m :    ctx->SegDs = base_offset + 4;
  51  m :    ctx->Edi = base_offset + 11;
  52  m :    ctx->Esi = base_offset + 12;
  53  m :    ctx->Ebx = base_offset + 13;
  54  m :    ctx->Edx = base_offset + 14;
  55  m :    ctx->Ecx = base_offset + 15;
  56  m :    ctx->Eax = base_offset + 16;
  57  m :    ctx->Ebp = base_offset + 21;
  58  m :    ctx->Eip = base_offset + 22;
  59  m :    ctx->SegCs = base_offset + 23;
  60  m :    ctx->EFlags = base_offset + 24;
  61  m :    ctx->Esp = base_offset + 25;
  62  m :    ctx->SegSs = base_offset + 26;
  63  m :  }
  64    :  
  65  m :  using MemorySpecification = MinidumpSpecification::MemorySpecification;
  66  m :  using ThreadSpecification = MinidumpSpecification::ThreadSpecification;
  67  m :  using ExceptionSpecification = MinidumpSpecification::ExceptionSpecification;
  68  m :  using ModuleSpecification = MinidumpSpecification::ModuleSpecification;
  69    :  
  70    :  // TODO(manzagop): ensure on destruction or finalizing that the allocations are
  71    :  // actually reflected in the file. At this point, allocating without writing
  72    :  // would leave the file short.
  73  m :  class MinidumpSerializer {
  74  m :   public:
  75  m :    typedef RVA Position;
  76    :  
  77  m :    MinidumpSerializer();
  78    :  
  79    :    // @pre @p dir must be a valid directory.
  80  m :    bool Initialize(const base::ScopedTempDir& dir);
  81  m :    bool SerializeThreads(const std::vector<ThreadSpecification>& threads);
  82  m :    bool SerializeMemory(const std::vector<MemorySpecification>& regions);
  83  m :    bool SerializeModules(const std::vector<ModuleSpecification>& modules);
  84  m :    bool SerializeExceptions(
  85  m :        const std::vector<ExceptionSpecification>& exceptions);
  86  m :    bool Finalize();
  87    :  
  88  m :    const base::FilePath& path() const { return path_; }
  89    :  
  90  m :   private:
  91  m :    static const Position kHeaderPos = 0U;
  92    :  
  93  m :    void SerializeDirectory();
  94  m :    void SerializeHeader();
  95    :  
  96  m :    Position Allocate(size_t size_bytes);
  97    :  
  98    :    // Allocate new space and write to it.
  99  m :    template <class DataType>
 100  m :    Position Append(const DataType& data);
 101    :    // @pre @p data must not be empty.
 102  m :    template <class DataType>
 103  m :    Position AppendVec(const std::vector<DataType>& data);
 104    :    // @pre @p elements must not be empty.
 105  m :    template <class DataType>
 106  m :    Position AppendListStream(MINIDUMP_STREAM_TYPE type,
 107  m :                              const std::vector<DataType>& elements);
 108  m :    Position AppendBytes(base::StringPiece data);
 109  m :    Position AppendMinidumpString(base::StringPiece utf8);
 110    :  
 111    :    // Write to already allocated space.
 112  m :    template <class DataType>
 113  m :    void Write(Position pos, const DataType& data);
 114  m :    void WriteBytes(Position pos, size_t size_bytes, const void* data);
 115    :  
 116  m :    bool IncrementCursor(size_t size_bytes);
 117    :  
 118    :    // Gets the position of an address range which is fully contained in a
 119    :    // serialized range.
 120    :    // @pre is_serialize_memory_invoked_ must be true.
 121    :    // @param range the range for which to get the rva.
 122    :    // @param pos the returned position.
 123    :    // returns true on success, false otherwise.
 124  m :    bool GetPos(const refinery::AddressRange& range, Position* pos) const;
 125    :  
 126  m :    void AddDirectoryEntry(MINIDUMP_STREAM_TYPE type, Position pos, size_t size);
 127    :  
 128  m :    bool succeeded() const { return !failed_; }
 129    :  
 130  m :    bool failed_;
 131  m :    bool is_serialize_memory_invoked_;
 132    :  
 133  m :    std::vector<MINIDUMP_DIRECTORY> directory_;
 134    :  
 135  m :    Position cursor_;  // The next allocatable position.
 136  m :    base::FilePath path_;
 137  m :    base::ScopedFILE file_;
 138    :  
 139  m :    std::map<refinery::AddressRange, Position> memory_positions_;
 140  m :  };
 141    :  
 142  m :  MinidumpSerializer::MinidumpSerializer()
 143  m :      : failed_(true), is_serialize_memory_invoked_(false), cursor_(0U) {
 144  m :  }
 145    :  
 146  m :  bool MinidumpSerializer::Initialize(const base::ScopedTempDir& dir) {
 147  m :    DCHECK(dir.IsValid());
 148    :  
 149  m :    failed_ = false;
 150  m :    is_serialize_memory_invoked_ = false;
 151  m :    directory_.clear();
 152    :  
 153    :    // Create the backing file.
 154  m :    cursor_ = 0U;
 155  m :    if (!CreateTemporaryFileInDir(dir.path(), &path_)) {
 156  m :      failed_ = true;
 157  m :      return false;
 158  m :    }
 159    :  
 160  m :    file_.reset(base::OpenFile(path_, "wb"));
 161  m :    if (file_.get() == nullptr)
 162  m :      failed_ = true;
 163    :  
 164    :    // Allocate the header.
 165  m :    Position pos = Allocate(sizeof(MINIDUMP_HEADER));
 166  m :    DCHECK_EQ(kHeaderPos, pos);
 167    :  
 168  m :    return succeeded();
 169  m :  }
 170    :  
 171  m :  bool MinidumpSerializer::SerializeThreads(
 172  m :      const std::vector<ThreadSpecification>& specs) {
 173  m :    if (specs.empty())
 174  m :      return succeeded();
 175    :  
 176  m :    std::vector<MINIDUMP_THREAD> threads;
 177  m :    threads.resize(specs.size());
 178    :  
 179  m :    for (int i = 0; i < specs.size(); ++i) {
 180  m :      const ThreadSpecification& spec = specs[i];
 181    :  
 182    :      // Write the context.
 183  m :      DCHECK_EQ(sizeof(CONTEXT), spec.context_data.size());
 184  m :      Position pos = AppendBytes(spec.context_data);
 185    :  
 186    :      // Copy thread to vector for more efficient serialization, then set Rvas.
 187  m :      DCHECK_EQ(sizeof(MINIDUMP_THREAD), spec.thread_data.size());
 188  m :      memcpy(&threads.at(i), spec.thread_data.c_str(), sizeof(MINIDUMP_THREAD));
 189    :  
 190  m :      MINIDUMP_THREAD& thread = threads[i];
 191  m :      refinery::AddressRange stack_range(thread.Stack.StartOfMemoryRange,
 192  m :                                         thread.Stack.Memory.DataSize);
 193  m :      if (!GetPos(stack_range, &thread.Stack.Memory.Rva))
 194  m :        failed_ = true;
 195  m :      thread.ThreadContext.Rva = pos;
 196  m :    }
 197    :  
 198  m :    AppendListStream(ThreadListStream, threads);
 199  m :    return succeeded();
 200  m :  }
 201    :  
 202  m :  bool MinidumpSerializer::SerializeMemory(
 203  m :      const std::vector<MemorySpecification>& regions) {
 204    :    // Signal that memory serialization has occured, and regions now have
 205    :    // associated positions in the minidump.
 206  m :    is_serialize_memory_invoked_ = true;
 207    :  
 208  m :    if (regions.empty())
 209  m :      return succeeded();
 210    :  
 211    :    // Write bytes data and create the memory descriptors.
 212  m :    std::vector<MINIDUMP_MEMORY_DESCRIPTOR> memory_descriptors;
 213  m :    memory_descriptors.resize(regions.size());
 214  m :    size_t idx = 0;
 215  m :    for (const auto& region : regions) {
 216  m :      refinery::AddressRange range(region.address, region.buffer.size());
 217  m :      DCHECK(range.IsValid());
 218    :  
 219  m :      Position pos = AppendBytes(region.buffer);
 220  m :      auto inserted = memory_positions_.insert(std::make_pair(range, pos));
 221  m :      DCHECK_EQ(true, inserted.second);
 222    :  
 223  m :      memory_descriptors[idx].StartOfMemoryRange = range.start();
 224  m :      memory_descriptors[idx].Memory.DataSize = range.size();
 225  m :      memory_descriptors[idx].Memory.Rva = pos;
 226    :  
 227  m :      ++idx;
 228  m :    }
 229    :  
 230    :    // Write descriptors and create directory entry.
 231  m :    AppendListStream(MemoryListStream, memory_descriptors);
 232    :  
 233  m :    return succeeded();
 234  m :  }
 235    :  
 236  m :  bool MinidumpSerializer::SerializeModules(
 237  m :      const std::vector<ModuleSpecification>& module_specs) {
 238  m :    if (module_specs.empty())
 239  m :      return succeeded();
 240    :  
 241  m :    std::vector<MINIDUMP_MODULE> modules;
 242  m :    modules.resize(module_specs.size());
 243    :  
 244  m :    for (int i = 0; i < module_specs.size(); ++i) {
 245  m :      modules[i].BaseOfImage = module_specs[i].addr;
 246  m :      modules[i].SizeOfImage = module_specs[i].size;
 247  m :      modules[i].CheckSum = module_specs[i].checksum;
 248  m :      modules[i].TimeDateStamp = module_specs[i].timestamp;
 249  m :      modules[i].ModuleNameRva = AppendMinidumpString(module_specs[i].name);
 250  m :    }
 251    :  
 252  m :    AppendListStream(ModuleListStream, modules);
 253    :  
 254  m :    return succeeded();
 255  m :  }
 256    :  
 257  m :  bool MinidumpSerializer::SerializeExceptions(
 258  m :      const std::vector<ExceptionSpecification>& exception_specs) {
 259  m :    if (exception_specs.empty())
 260  m :      return succeeded();
 261    :  
 262  m :    for (const ExceptionSpecification& spec : exception_specs) {
 263    :      // Write the context.
 264  m :      DCHECK_EQ(sizeof(CONTEXT), spec.context_data.size());
 265  m :      Position pos = AppendBytes(spec.context_data);
 266    :  
 267    :      // Write the MINIDUMP_EXCEPTION_STREAM.
 268  m :      ULONG32 thread_id = spec.thread_id;
 269  m :      Position rva = Append(thread_id);
 270  m :      ULONG32 dummy_alignment = 0U;
 271  m :      Append(dummy_alignment);
 272    :  
 273  m :      MINIDUMP_EXCEPTION exception = {0};
 274  m :      exception.ExceptionCode = spec.exception_code;
 275  m :      exception.ExceptionFlags = spec.exception_flags;
 276  m :      exception.ExceptionRecord = spec.exception_record;
 277  m :      exception.ExceptionAddress = spec.exception_address;
 278  m :      exception.NumberParameters = spec.exception_information.size();
 279  m :      DCHECK(exception.NumberParameters <= EXCEPTION_MAXIMUM_PARAMETERS);
 280  m :      for (size_t i = 0; i < spec.exception_information.size(); ++i) {
 281  m :        exception.ExceptionInformation[i] = spec.exception_information[i];
 282  m :      }
 283  m :      Append(exception);
 284    :  
 285  m :      MINIDUMP_LOCATION_DESCRIPTOR context_loc = {};
 286  m :      context_loc.DataSize = sizeof(CONTEXT);
 287  m :      context_loc.Rva = pos;
 288  m :      Append(context_loc);
 289    :  
 290  m :      AddDirectoryEntry(ExceptionStream, rva, sizeof(MINIDUMP_EXCEPTION_STREAM));
 291  m :    }
 292    :  
 293  m :    return succeeded();
 294  m :  }
 295    :  
 296  m :  bool MinidumpSerializer::Finalize() {
 297    :    // Serialize the directory.
 298  m :    Position pos = AppendVec(directory_);
 299    :  
 300    :    // Serialize the header.
 301  m :    MINIDUMP_HEADER hdr;
 302  m :    hdr.Signature = MINIDUMP_SIGNATURE;
 303  m :    hdr.NumberOfStreams = directory_.size();
 304  m :    hdr.StreamDirectoryRva = pos;
 305  m :    Write(kHeaderPos, hdr);
 306    :  
 307  m :    return succeeded();
 308  m :  }
 309    :  
 310  m :  MinidumpSerializer::Position MinidumpSerializer::Allocate(size_t size_bytes) {
 311  m :    Position pos = cursor_;
 312  m :    if (!IncrementCursor(size_bytes))
 313  m :      failed_ = true;
 314  m :    return pos;
 315  m :  }
 316    :  
 317  m :  template <class DataType>
 318  m :  MinidumpSerializer::Position MinidumpSerializer::Append(const DataType& data) {
 319  m :    Position pos = Allocate(sizeof(data));
 320  m :    Write(pos, data);
 321  m :    return pos;
 322  m :  }
 323    :  
 324  m :  template <class DataType>
 325  m :  MinidumpSerializer::Position MinidumpSerializer::AppendVec(
 326  m :      const std::vector<DataType>& data) {
 327  m :    DCHECK(!data.empty());
 328    :  
 329  m :    size_t size_bytes = sizeof(DataType) * data.size();
 330  m :    Position pos = Allocate(size_bytes);
 331  m :    WriteBytes(pos, size_bytes, &data.at(0));
 332  m :    return pos;
 333  m :  }
 334    :  
 335  m :  template <class DataType>
 336  m :  MinidumpSerializer::Position MinidumpSerializer::AppendListStream(
 337  m :      MINIDUMP_STREAM_TYPE type,
 338  m :      const std::vector<DataType>& elements) {
 339  m :    DCHECK(!elements.empty());
 340    :  
 341    :    // Append the stream
 342  m :    ULONG32 num_elements = elements.size();
 343  m :    Position pos = Append(num_elements);
 344  m :    AppendVec(elements);
 345    :  
 346    :    // Create its directory entry.
 347  m :    size_t size_bytes = sizeof(ULONG32) + elements.size() * sizeof(DataType);
 348  m :    AddDirectoryEntry(type, pos, size_bytes);
 349    :  
 350  m :    return pos;
 351  m :  }
 352    :  
 353  m :  MinidumpSerializer::Position MinidumpSerializer::AppendBytes(
 354  m :      base::StringPiece data) {
 355  m :    Position pos = Allocate(data.length());
 356  m :    WriteBytes(pos, data.length(), data.data());
 357  m :    return pos;
 358  m :  }
 359    :  
 360  m :  MinidumpSerializer::Position MinidumpSerializer::AppendMinidumpString(
 361  m :      base::StringPiece utf8) {
 362  m :    std::wstring wide = base::UTF8ToWide(utf8);
 363  m :    ULONG32 size_bytes = wide.length() * sizeof(std::wstring::value_type);
 364    :  
 365  m :    Position pos = Append(size_bytes);
 366    :    // Note: write the null termination character.
 367  m :    size_bytes += sizeof(std::wstring::value_type);
 368  m :    Position string_pos = Allocate(size_bytes);
 369  m :    WriteBytes(string_pos, size_bytes, wide.c_str());
 370  m :    return pos;
 371  m :  }
 372    :  
 373  m :  template <class DataType>
 374  m :  void MinidumpSerializer::Write(Position pos, const DataType& data) {
 375  m :    WriteBytes(pos, sizeof(data), &data);
 376  m :  }
 377    :  
 378  m :  void MinidumpSerializer::WriteBytes(Position pos,
 379  m :                                      size_t size_bytes,
 380  m :                                      const void* data) {
 381  m :    if (failed_)
 382  m :      return;
 383    :  
 384    :    // Validate the write does not go past the cursor.
 385  m :    base::CheckedNumeric<Position> pos_end = pos;
 386  m :    pos_end += size_bytes;
 387  m :    DCHECK(pos_end.IsValid());
 388  m :    DCHECK(pos_end.ValueOrDie() <= cursor_);
 389    :  
 390  m :    DCHECK(file_.get() != nullptr);
 391    :  
 392    :    // Seek and write.
 393  m :    if (fseek(file_.get(), pos, SEEK_SET) != 0) {
 394  m :      failed_ = true;
 395  m :      return;
 396  m :    }
 397  m :    if (fwrite(data, sizeof(char), size_bytes, file_.get()) != size_bytes) {
 398  m :      failed_ = true;
 399  m :      return;
 400  m :    }
 401  m :  }
 402    :  
 403  m :  bool MinidumpSerializer::IncrementCursor(size_t size_bytes) {
 404  m :    base::CheckedNumeric<Position> cur = cursor_;
 405  m :    cur += size_bytes;
 406  m :    if (!cur.IsValid())
 407  m :      return false;
 408    :  
 409  m :    cursor_ += size_bytes;
 410  m :    return true;
 411  m :  }
 412    :  
 413  m :  bool MinidumpSerializer::GetPos(const refinery::AddressRange& range,
 414  m :                                  Position* pos) const {
 415  m :    DCHECK(range.IsValid());
 416  m :    DCHECK(pos != nullptr);
 417  m :    DCHECK(is_serialize_memory_invoked_);
 418    :  
 419  m :    auto it = memory_positions_.upper_bound(range);
 420  m :    if (it == memory_positions_.begin())
 421  m :      return false;
 422    :  
 423    :    // Note: given that memory ranges do not overlap, only the immediate
 424    :    // predecessor is a candidate match.
 425  m :    --it;
 426  m :    if (it->first.Contains(range)) {
 427  m :      *pos = it->second;
 428  m :      return true;
 429  m :    }
 430    :  
 431  m :    return false;
 432  m :  }
 433    :  
 434  m :  void MinidumpSerializer::AddDirectoryEntry(MINIDUMP_STREAM_TYPE type,
 435  m :                                             Position pos,
 436  m :                                             size_t size_bytes) {
 437  m :    MINIDUMP_DIRECTORY directory = {0};
 438  m :    directory.StreamType = type;
 439  m :    directory.Location.Rva = pos;
 440  m :    directory.Location.DataSize = size_bytes;
 441  m :    directory_.push_back(directory);
 442  m :  }
 443    :  
 444  m :  }  // namespace
 445    :  
 446  m :  const uint32_t ScopedMinidump::kMinidumpWithStacks =
 447  m :      MiniDumpWithProcessThreadData |  // Get PEB and TEB.
 448  m :      MiniDumpWithUnloadedModules;     // Get unloaded modules when available.
 449    :  
 450  m :  const uint32_t ScopedMinidump::kMinidumpWithData =
 451  m :      kMinidumpWithStacks |
 452  m :      MiniDumpWithIndirectlyReferencedMemory;  // Get referenced memory.
 453    :  
 454  m :  MinidumpSpecification::MinidumpSpecification() : allow_memory_overlap_(false) {
 455  m :  }
 456    :  
 457  m :  MinidumpSpecification::MinidumpSpecification(AllowMemoryOverlap dummy)
 458  m :      : allow_memory_overlap_(true) {
 459  m :  }
 460    :  
 461  m :  bool MinidumpSpecification::AddThread(const ThreadSpecification& spec) {
 462  m :    DCHECK_EQ(sizeof(MINIDUMP_THREAD), spec.thread_data.size());
 463  m :    DCHECK_EQ(sizeof(CONTEXT), spec.context_data.size());
 464  m :    DCHECK_GT(spec.thread_data.size(), 0U);
 465  m :    DCHECK_GT(spec.context_data.size(), 0U);
 466    :  
 467  m :    threads_.push_back(spec);
 468    :  
 469  m :    return true;
 470  m :  }
 471    :  
 472  m :  bool MinidumpSpecification::AddMemoryRegion(const MemorySpecification& spec) {
 473  m :    refinery::Address address = spec.address;
 474  m :    refinery::Size size_bytes = spec.buffer.size();
 475    :  
 476    :    // Ensure range validity.
 477  m :    refinery::AddressRange range(address, size_bytes);
 478  m :    if (!range.IsValid())
 479  m :      return false;
 480    :  
 481  m :    if (!allow_memory_overlap_) {
 482    :      // Overlap is not allowed in this instance - check with successor and
 483    :      // predecessor.
 484  m :      auto it = region_sizes_.upper_bound(address);
 485    :  
 486  m :      if (it != region_sizes_.end()) {
 487  m :        refinery::AddressRange post_range(it->first, it->second);
 488  m :        DCHECK(post_range.IsValid());
 489  m :        if (range.Intersects(post_range))
 490  m :          return false;
 491  m :      }
 492    :  
 493  m :      if (it != region_sizes_.begin()) {
 494  m :        --it;
 495  m :        refinery::AddressRange pre_range(it->first, it->second);
 496  m :        DCHECK(pre_range.IsValid());
 497  m :        if (range.Intersects(pre_range))
 498  m :          return false;
 499  m :      }
 500    :  
 501    :      // Add the specification.
 502  m :      auto inserted = region_sizes_.insert(std::make_pair(address, size_bytes));
 503  m :      if (!inserted.second)
 504  m :        return false;
 505  m :    }
 506    :  
 507  m :    memory_regions_.push_back(spec);
 508    :  
 509  m :    return true;
 510  m :  }
 511    :  
 512  m :  bool MinidumpSpecification::AddModule(const ModuleSpecification& module) {
 513  m :    modules_.push_back(module);
 514  m :    return true;
 515  m :  }
 516    :  
 517  m :  bool MinidumpSpecification::AddException(
 518  m :      const ExceptionSpecification& exception) {
 519  m :    exceptions_.push_back(exception);
 520  m :    return true;
 521  m :  }
 522    :  
 523  m :  bool MinidumpSpecification::Serialize(const base::ScopedTempDir& dir,
 524  m :                                        base::FilePath* path) const {
 525  m :    MinidumpSerializer serializer;
 526  m :    bool success = serializer.Initialize(dir) &&
 527  m :                   serializer.SerializeMemory(memory_regions_) &&
 528  m :                   serializer.SerializeThreads(threads_) &&
 529  m :                   serializer.SerializeModules(modules_) &&
 530  m :                   serializer.SerializeExceptions(exceptions_) &&
 531  m :                   serializer.Finalize();
 532  m :    *path = serializer.path();
 533  m :    return success;
 534  m :  }
 535    :  
 536  m :  MinidumpSpecification::MemorySpecification::MemorySpecification()
 537  m :      : address(0ULL) {
 538  m :  }
 539    :  
 540  m :  MinidumpSpecification::MemorySpecification::MemorySpecification(
 541  m :      refinery::Address addr,
 542  m :      base::StringPiece data)
 543  m :      : address(addr) {
 544  m :    data.CopyToString(&buffer);
 545  m :  }
 546    :  
 547  m :  MinidumpSpecification::ThreadSpecification::ThreadSpecification(
 548  m :      size_t thread_id,
 549  m :      refinery::Address stack_address,
 550  m :      refinery::Size stack_size) {
 551    :    // Generate the MINIDUMP_THREAD.
 552  m :    thread_data.resize(sizeof(MINIDUMP_THREAD));
 553  m :    MINIDUMP_THREAD* thread =
 554  m :        reinterpret_cast<MINIDUMP_THREAD*>(&thread_data.at(0));
 555  m :    thread->ThreadId = thread_id;
 556  m :    thread->SuspendCount = 2;
 557  m :    thread->PriorityClass = 3;
 558  m :    thread->Priority = 4;
 559  m :    thread->Teb = 5U;
 560  m :    thread->Stack.StartOfMemoryRange = stack_address;
 561  m :    thread->Stack.Memory.DataSize = stack_size;
 562  m :    thread->ThreadContext.DataSize = sizeof(CONTEXT);
 563    :    // Note: thread.Stack.Memory.Rva and thread.ThreadContext.Rva are set during
 564    :    // serialization.
 565    :  
 566  m :    PopulateContext(&context_data, 0);
 567  m :  }
 568    :  
 569  m :  void MinidumpSpecification::ThreadSpecification::SetTebAddress(
 570  m :      refinery::Address addr) {
 571  m :    DCHECK(thread_data.size() == sizeof(MINIDUMP_THREAD));
 572  m :    MINIDUMP_THREAD* thread =
 573  m :        reinterpret_cast<MINIDUMP_THREAD*>(&thread_data.at(0));
 574  m :    thread->Teb = addr;
 575  m :  }
 576    :  
 577  m :  void MinidumpSpecification::ThreadSpecification::FillStackMemorySpecification(
 578  m :      MinidumpSpecification::MemorySpecification* spec) const {
 579  m :    DCHECK(spec);
 580  m :    DCHECK_EQ(sizeof(MINIDUMP_THREAD), thread_data.size());
 581  m :    const MINIDUMP_THREAD* thread =
 582  m :        reinterpret_cast<const MINIDUMP_THREAD*>(&thread_data.at(0));
 583    :  
 584    :    // The stack is a range of 'S' padded at either end with a single 'P'.
 585  m :    DCHECK_GT(thread->Stack.StartOfMemoryRange, 0U);
 586  m :    const ULONG32 kStackMaxSize = static_cast<ULONG32>(-1) - 1;
 587  m :    DCHECK(thread->Stack.Memory.DataSize < kStackMaxSize);
 588  m :    spec->address = thread->Stack.StartOfMemoryRange - 1;
 589  m :    spec->buffer.resize(thread->Stack.Memory.DataSize + 2, 'S');
 590  m :    spec->buffer[0] = 'P';
 591  m :    spec->buffer[thread->Stack.Memory.DataSize + 1] = 'P';
 592  m :  }
 593    :  
 594  m :  MinidumpSpecification::ExceptionSpecification::ExceptionSpecification(
 595  m :      uint32_t thread_identifier) {
 596  m :    thread_id = thread_identifier;
 597  m :    exception_code = EXCEPTION_ACCESS_VIOLATION;
 598  m :    exception_flags = EXCEPTION_NONCONTINUABLE;
 599  m :    exception_record = 0ULL;
 600  m :    exception_address = 1111ULL;
 601  m :    exception_information.push_back(1);
 602  m :    exception_information.push_back(2222ULL);
 603    :  
 604  m :    PopulateContext(&context_data, 100);
 605  m :  }
 606    :  
 607  m :  MinidumpSpecification::ModuleSpecification::ModuleSpecification() {
 608  m :    addr = 12345ULL;
 609  m :    size = 75U;
 610  m :    checksum = 23U;
 611  m :    timestamp = 42U;
 612  m :    name = "someModule";
 613  m :  }
 614    :  
 615  m :  namespace {
 616    :  
 617    :  // Minidump.
 618  m :  const wchar_t kMinidumpFileName[] = L"minidump.dmp";
 619  m :  const char kSwitchExceptionPtrs[] = "exception-ptrs";
 620  m :  const char kSwitchPid[] = "dump-pid";
 621  m :  const char kSwitchMinidumpPath[] = "dump-path";
 622  m :  const char kSwitchTid[] = "exception-thread-id";
 623  m :  const char kSwitchMinidumpType[] = "minidump-type";
 624    :  
 625  m :  __declspec(noinline) DWORD GetEip() {
 626  m :    return reinterpret_cast<DWORD>(_ReturnAddress());
 627  m :  }
 628    :  
 629  m :  }  // namespace
 630    :  
 631  m :  MULTIPROCESS_TEST_MAIN(MinidumpDumperProcess) {
 632    :    // Retrieve information from the command line.
 633  m :    base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
 634  m :    if (!cmd_line->HasSwitch(kSwitchPid) || !cmd_line->HasSwitch(kSwitchTid) ||
 635  m :        !cmd_line->HasSwitch(kSwitchExceptionPtrs) ||
 636  m :        !cmd_line->HasSwitch(kSwitchMinidumpPath)) {
 637  m :      return 1;
 638  m :    }
 639    :  
 640  m :    std::string pid_string = cmd_line->GetSwitchValueASCII(kSwitchPid);
 641  m :    unsigned pid_uint = 0U;
 642  m :    if (!base::StringToUint(pid_string, &pid_uint))
 643  m :      return 1;
 644  m :    base::ProcessId pid = static_cast<base::ProcessId>(pid_uint);
 645    :  
 646  m :    std::string thread_id_string = cmd_line->GetSwitchValueASCII(kSwitchTid);
 647  m :    unsigned thread_id = 0U;
 648  m :    if (!base::StringToUint(thread_id_string, &thread_id))
 649  m :      return 1;
 650    :  
 651  m :    std::string exception_ptrs_string =
 652  m :        cmd_line->GetSwitchValueASCII(kSwitchExceptionPtrs);
 653  m :    unsigned exception_ptrs = 0ULL;
 654  m :    if (!base::StringToUint(exception_ptrs_string, &exception_ptrs))
 655  m :      return 1;
 656  m :    std::string minidump_type_string =
 657  m :        cmd_line->GetSwitchValueASCII(kSwitchMinidumpType);
 658  m :    uint32_t minidump_type = 0U;
 659  m :    if (!base::StringToUint(minidump_type_string, &minidump_type))
 660  m :      return 1;
 661    :  
 662  m :    base::FilePath minidump_path =
 663  m :        cmd_line->GetSwitchValuePath(kSwitchMinidumpPath);
 664    :  
 665    :    // Get handles to dumpee and dump file.
 666  m :    base::Process dumpee_process = base::Process::OpenWithAccess(
 667  m :        pid, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ);
 668  m :    if (!dumpee_process.IsValid()) {
 669  m :      LOG(ERROR) << "Failed to open process: " << ::common::LogWe() << ".";
 670  m :      return 1;
 671  m :    }
 672    :  
 673  m :    base::File minidump_file(minidump_path,
 674  m :                             base::File::FLAG_CREATE | base::File::FLAG_WRITE);
 675  m :    if (!minidump_file.IsValid()) {
 676  m :      LOG(ERROR) << "Failed to create minidump file: " << minidump_path.value();
 677  m :      return 1;
 678  m :    }
 679    :  
 680    :    // Build the dump related information.
 681  m :    MINIDUMP_EXCEPTION_INFORMATION exception_information = {};
 682  m :    exception_information.ThreadId = static_cast<DWORD>(thread_id);
 683  m :    exception_information.ExceptionPointers =
 684  m :        reinterpret_cast<PEXCEPTION_POINTERS>(exception_ptrs);
 685  m :    exception_information.ClientPointers = true;
 686    :  
 687  m :    MINIDUMP_USER_STREAM_INFORMATION* user_info = nullptr;
 688  m :    MINIDUMP_CALLBACK_INFORMATION* callback_info = nullptr;
 689    :  
 690    :    // Take the minidump.
 691  m :    if (::MiniDumpWriteDump(
 692  m :            dumpee_process.Handle(), pid, minidump_file.GetPlatformFile(),
 693  m :            static_cast<MINIDUMP_TYPE>(minidump_type), &exception_information,
 694  m :            user_info, callback_info) == FALSE) {
 695  m :      LOG(ERROR) << "MiniDumpWriteDump failed: " << ::common::LogWe() << ".";
 696  m :      return 1;
 697  m :    }
 698    :  
 699  m :    return 0;
 700  m :  }
 701    :  
 702  m :  bool ScopedMinidump::GenerateMinidump(uint32_t minidump_type) {
 703    :    // Determine minidump path.
 704  m :    if (!temp_dir_.CreateUniqueTempDir())
 705  m :      return false;
 706    :  
 707  m :    minidump_path_ = temp_dir_.path().Append(kMinidumpFileName);
 708    :  
 709    :    // Grab a context. RtlCaptureContext sets the instruction pointer, stack
 710    :    // pointer and base pointer to values from this function's callee (similar
 711    :    // to _ReturnAddress). Override them so they actually match the context.
 712    :    // TODO(manzagop): package this to a utility function.
 713  m :    CONTEXT context = {};
 714  m :    ::RtlCaptureContext(&context);
 715  m :    __asm {
 716  m :      mov context.Ebp, ebp
 717  m :      mov context.Esp, esp
 718  m :    }
 719  m :    context.Eip = GetEip();
 720    :  
 721    :    // Build the exception information.
 722  m :    EXCEPTION_RECORD exception = {};
 723  m :    exception.ExceptionCode = 0xCAFEBABE;  // Note: a random error code.
 724  m :    exception.ExceptionAddress = reinterpret_cast<PVOID>(context.Eip);
 725    :  
 726  m :    EXCEPTION_POINTERS exception_pointers = {&exception, &context};
 727    :  
 728    :    // Build the dumper's command line.
 729  m :    base::CommandLine dumper_command_line(
 730  m :        base::GetMultiProcessTestChildBaseCommandLine());
 731  m :    dumper_command_line.AppendSwitchASCII(switches::kTestChildProcess,
 732  m :                                          "MinidumpDumperProcess");
 733  m :    base::Process current_process = base::Process::Current();
 734  m :    dumper_command_line.AppendSwitchASCII(
 735  m :        kSwitchPid, base::UintToString(current_process.Pid()));
 736  m :    dumper_command_line.AppendSwitchASCII(
 737  m :        kSwitchTid, base::UintToString(::GetCurrentThreadId()));
 738  m :    unsigned exception_pointers_uint =
 739  m :        reinterpret_cast<unsigned>(&exception_pointers);
 740  m :    dumper_command_line.AppendSwitchASCII(
 741  m :        kSwitchExceptionPtrs, base::UintToString(exception_pointers_uint));
 742  m :    dumper_command_line.AppendSwitchASCII(kSwitchMinidumpType,
 743  m :                                          base::UintToString(minidump_type));
 744  m :    dumper_command_line.AppendSwitchPath(kSwitchMinidumpPath, minidump_path());
 745    :  
 746    :    // Launch the dumper.
 747  m :    base::Process dumper_process =
 748  m :        base::LaunchProcess(dumper_command_line, base::LaunchOptions());
 749  m :    int exit_code = 0;
 750  m :    bool success = dumper_process.WaitForExitWithTimeout(
 751  m :        TestTimeouts::action_timeout(), &exit_code);
 752  m :    if (!success) {
 753  m :      dumper_process.Terminate(0, true);
 754  m :      return false;
 755  m :    }
 756    :  
 757  m :    return exit_code == 0;
 758  m :  }
 759    :  
 760  m :  ScopedHeap::ScopedHeap() : heap_(nullptr) {
 761  m :  }
 762    :  
 763  m :  ScopedHeap::~ScopedHeap() {
 764  m :    if (heap_ != nullptr) {
 765  m :      EXPECT_TRUE(::HeapDestroy(heap_));
 766  m :      heap_ = nullptr;
 767  m :    }
 768  m :  }
 769    :  
 770  m :  bool ScopedHeap::Create() {
 771  m :    CHECK(heap_ == nullptr);
 772  m :    heap_ = ::HeapCreate(0, 0, 0);
 773  m :    return heap_ != nullptr;
 774  m :  }
 775    :  
 776  m :  void* ScopedHeap::Allocate(size_t block_size) {
 777  m :    CHECK(heap_ != nullptr);
 778    :  
 779  m :    return ::HeapAlloc(heap_, 0, block_size);
 780  m :  }
 781    :  
 782  m :  bool ScopedHeap::Free(void* block) {
 783  m :    CHECK(heap_ != nullptr);
 784    :  
 785  m :    return ::HeapFree(heap_, 0, block);
 786  m :  }
 787    :  
 788  m :  bool ScopedHeap::IsLFHBlock(const void* block) {
 789  m :    const uint32_t* ptr = reinterpret_cast<const uint32_t*>(block);
 790  m :    __try {
 791    :      // Search back a bounded distance for the LFH bin signature.
 792  m :      for (size_t i = 0; i < 32; ++i) {
 793  m :        const uint32_t kLFHBinSignature = 0xF0E0D0C0;
 794  m :        if (*ptr-- == kLFHBinSignature)
 795  m :          return true;
 796  m :      }
 797  m :    } __except (EXCEPTION_EXECUTE_HANDLER) {  // NOLINT
 798    :      // Note that git cl format yields the above, which is then contrary to
 799    :      // our presubmit checks.
 800    :      // On exception, we conclude this isn't an LFH block.
 801  m :    }
 802    :  
 803  m :    return false;
 804  m :  }
 805    :  
 806  m :  refinery::Address ToAddress(const void* ptr) {
 807  m :    return static_cast<refinery::Address>(reinterpret_cast<uintptr_t>(ptr));
 808  m :  }
 809    :  
 810  m :  bool IsAppVerifierActive() {
 811    :    // TODO(siggi): This seems like a solid proxy for verifier present.
 812  m :    return ::GetModuleHandle(L"verifier.dll") != nullptr;
 813  m :  }
 814    :  
 815  m :  }  // namespace testing

Coverage information generated Fri Jul 29 11:00:21 2016.