Coverage for /Syzygy/refinery/unittest_util.cc

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

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