Coverage for /Syzygy/grinder/grinders/sample_grinder_unittest.cc

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

Line-by-line coverage:

   1    :  // Copyright 2013 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/grinder/grinders/sample_grinder.h"
  16    :  
  17    :  #include "base/string_util.h"
  18    :  #include "base/strings/utf_string_conversions.h"
  19    :  #include "gmock/gmock.h"
  20    :  #include "gtest/gtest.h"
  21    :  #include "syzygy/core/unittest_util.h"
  22    :  #include "syzygy/pe/pe_file.h"
  23    :  #include "syzygy/pe/unittest_util.h"
  24    :  #include "syzygy/sampler/unittest_util.h"
  25    :  #include "syzygy/trace/common/clock.h"
  26    :  #include "syzygy/trace/common/unittest_util.h"
  27    :  #include "syzygy/trace/protocol/call_trace_defs.h"
  28    :  
  29    :  namespace grinder {
  30    :  namespace grinders {
  31    :  
  32    :  namespace {
  33    :  
  34    :  const wchar_t kTestDllLabelTestFuncAsm[] =
  35    :      L"syzygy\\pe\\test_dll_label_test_func.asm";
  36    :  
  37    :  // SampleGrinder with some internal details exposed for testing.
  38    :  class TestSampleGrinder : public SampleGrinder {
  39    :   public:
  40    :    // Functions.
  41    :    using SampleGrinder::UpsampleModuleData;
  42    :    using SampleGrinder::IncrementModuleData;
  43    :    using SampleGrinder::IncrementHeatMapFromModuleData;
  44    :    using SampleGrinder::RollUpByName;
  45    :  
  46    :    // Members.
  47    :    using SampleGrinder::aggregation_level_;
  48    :    using SampleGrinder::image_path_;
  49    :    using SampleGrinder::parser_;
  50    :    using SampleGrinder::heat_map_;
  51    :    using SampleGrinder::name_heat_map_;
  52    :    using SampleGrinder::line_info_;
  53    :  };
  54    :  
  55    :  class SampleGrinderTest : public testing::PELibUnitTest {
  56    :   public:
  57    :    SampleGrinderTest()
  58    :        : cmd_line_(base::FilePath(L"sample_grinder.exe")),
  59  E :          sample_data_(NULL) {
  60  E :      trace::common::GetClockInfo(&clock_info_);
  61  E :    }
  62    :  
  63  E :    virtual void SetUp() OVERRIDE {
  64  E :      testing::PELibUnitTest::SetUp();
  65  E :      test_dll_path_ = testing::GetOutputRelativePath(testing::kTestDllName);
  66  E :      ASSERT_TRUE(test_dll_pe_file_.Init(test_dll_path_));
  67  E :      test_dll_pe_file_.GetSignature(&test_dll_pe_sig_);
  68  E :    }
  69    :  
  70  E :    void PrepareDummySampleDataBuffer(size_t bucket_count) {
  71  E :      ASSERT_EQ(0u, buffer_.size());
  72  E :      ASSERT_TRUE(sample_data_ == NULL);
  73    :  
  74    :      buffer_.resize(offsetof(TraceSampleData, buckets) +
  75  E :                         sizeof(uint32) * bucket_count);
  76  E :      sample_data_ = reinterpret_cast<TraceSampleData*>(buffer_.data());
  77  E :    }
  78    :  
  79  E :    void WriteDummySampleData() {
  80  E :      ASSERT_FALSE(test_dll_path_.empty());
  81  E :      ASSERT_TRUE(temp_dir_.empty());
  82  E :      ASSERT_TRUE(trace_file_path_.empty());
  83    :  
  84  E :      this->CreateTemporaryDir(&temp_dir_);
  85    :  
  86  E :      trace_file_path_ = temp_dir_.AppendASCII("sample.bin");
  87  E :      ASSERT_NO_FATAL_FAILURE(testing::WriteDummySamplerTraceFile(
  88    :          trace_file_path_));
  89  E :    }
  90    :  
  91  E :    void InitParser(trace::parser::ParseEventHandlerImpl* handler) {
  92  E :      ASSERT_TRUE(handler != NULL);
  93  E :      ASSERT_TRUE(parser_.Init(handler));
  94  E :      ASSERT_TRUE(parser_.OpenTraceFile(trace_file_path_));
  95  E :    }
  96    :  
  97    :    void GrindSucceeds(SampleGrinder::AggregationLevel aggregation_level,
  98  E :                       bool specify_image) {
  99  E :      TestSampleGrinder g;
 100    :  
 101  E :      if (specify_image)
 102  E :        cmd_line_.AppendSwitchPath(SampleGrinder::kImage, test_dll_path_);
 103    :  
 104    :      cmd_line_.AppendSwitchASCII(
 105    :          SampleGrinder::kAggregationLevel,
 106  E :          SampleGrinder::kAggregationLevelNames[aggregation_level]);
 107  E :      ASSERT_TRUE(g.ParseCommandLine(&cmd_line_));
 108    :  
 109  E :      ASSERT_NO_FATAL_FAILURE(WriteDummySampleData());
 110  E :      ASSERT_NO_FATAL_FAILURE(InitParser(&g));
 111  E :      g.SetParser(&parser_);
 112  E :      ASSERT_TRUE(parser_.Consume());
 113    :  
 114  E :      ASSERT_TRUE(g.Grind());
 115    :  
 116    :      // 1000 samples at a rate of 0.01 samples/sec = 10 seconds of heat.
 117  E :      const double expected_heat = 10.0;
 118  E :      double total_heat = 0;
 119    :  
 120    :      // Check that the output has gone to the right intermediate representation
 121    :      // after grinding. We also check that there was a non-zero amount of
 122    :      // 'heat' distributed.
 123  E :      if (aggregation_level == SampleGrinder::kBasicBlock) {
 124  E :        ASSERT_FALSE(g.heat_map_.empty());
 125  E :        ASSERT_TRUE(g.name_heat_map_.empty());
 126  E :        ASSERT_TRUE(g.line_info_.source_lines().empty());
 127    :  
 128    :        TestSampleGrinder::HeatMap::const_iterator it =
 129  E :            g.heat_map_.begin();
 130  E :        for (; it != g.heat_map_.end(); ++it) {
 131  E :          if (it->second.heat > 0)
 132  E :            LOG(INFO) << ".";
 133  E :          total_heat += it->second.heat;
 134  E :        }
 135    :  
 136  E :        EXPECT_DOUBLE_EQ(expected_heat, total_heat);
 137  E :      } else if (aggregation_level == SampleGrinder::kCompiland ||
 138  E :                 aggregation_level == SampleGrinder::kFunction) {
 139  E :        ASSERT_TRUE(g.heat_map_.empty());
 140  E :        ASSERT_FALSE(g.name_heat_map_.empty());
 141  E :        ASSERT_TRUE(g.line_info_.source_lines().empty());
 142    :  
 143    :        // Look through the NameHeatMap output.
 144  E :        bool compiland_seen = false;
 145  E :        bool function_seen = false;
 146    :        TestSampleGrinder::NameHeatMap::const_iterator it =
 147  E :            g.name_heat_map_.begin();
 148  E :        for (; it != g.name_heat_map_.end(); ++it) {
 149  E :          base::FilePath path(UTF8ToWide(*it->first));
 150  E :          if (path.BaseName().value() == L"test_dll_label_test_func.obj")
 151  E :            compiland_seen = true;
 152  E :          if (*it->first == "_LabelTestFunc")
 153  E :            function_seen = true;
 154  E :          if (it->second > 0)
 155  E :            LOG(INFO) << ".";
 156  E :          total_heat += it->second;
 157  E :        }
 158    :  
 159  E :        if (aggregation_level == SampleGrinder::kCompiland) {
 160  E :          EXPECT_TRUE(compiland_seen);
 161  E :          EXPECT_FALSE(function_seen);
 162  E :        } else {
 163  E :          EXPECT_FALSE(compiland_seen);
 164  E :          EXPECT_TRUE(function_seen);
 165    :        }
 166  E :      } else {
 167  E :        ASSERT_EQ(SampleGrinder::kLine, aggregation_level);
 168  E :        ASSERT_TRUE(g.heat_map_.empty());
 169  E :        ASSERT_TRUE(g.name_heat_map_.empty());
 170  E :        ASSERT_FALSE(g.line_info_.source_lines().empty());
 171    :  
 172    :        // Get the path to the source file where all of the heat should land.
 173    :        base::FilePath source_file_path =
 174  E :            testing::GetSrcRelativePath(kTestDllLabelTestFuncAsm);
 175  E :        std::string source_file = WideToUTF8(source_file_path.value());
 176    :  
 177    :        // All of the heat is in the first 4-byte bucket of the LabelTestFunc.
 178    :        // Thus, it will be spread evenly across the source ranges in those 4
 179    :        // bytes, with the lowest value scaled to 1. The scaling makes the visit
 180    :        // count the same as the encoded instruction size.
 181    :        typedef std::map<size_t, uint32> LineVisitCountMap;
 182  E :        LineVisitCountMap expected, actual;
 183  E :        expected[61] = 1;  // Label. Ends up being a 1 byte source range.
 184  E :        expected[64] = 1;  // push ebp (1 byte).
 185  E :        expected[65] = 2;  // mov ebp, esp (2 bytes).
 186  E :        expected[66] = 1;  // push ecx (1 byte).
 187    :  
 188  E :        uint32 min_visit_count = 0xFFFFFFFF;
 189  E :        for (size_t i = 0; i < g.line_info_.source_lines().size(); ++i) {
 190  E :          const LineInfo::SourceLine& line = g.line_info_.source_lines()[i];
 191  E :          if (line.visit_count == 0)
 192  E :            continue;
 193    :  
 194  E :          if (line.visit_count < min_visit_count)
 195  E :            min_visit_count = line.visit_count;
 196    :  
 197  E :          EXPECT_EQ(StringToLowerASCII(source_file),
 198    :                    StringToLowerASCII(*line.source_file_name));
 199  E :          actual[line.line_number] = line.visit_count;
 200  E :        }
 201  E :        EXPECT_EQ(1u, min_visit_count);
 202    :  
 203  E :        EXPECT_THAT(expected, testing::ContainerEq(actual));
 204    :  
 205    :        // We can't say anything concrete about the total heat, as it has been
 206    :        // scaled such that the smallest non-zero value is a 1.
 207  E :        total_heat = 10.0;
 208  E :      }
 209  E :      EXPECT_DOUBLE_EQ(10.0, total_heat);
 210    :  
 211    :      // Produce the output.
 212  E :      base::FilePath csv_path = temp_dir_.Append(L"output.csv");
 213  E :      file_util::ScopedFILE csv_file(file_util::OpenFile(csv_path, "wb"));
 214  E :      ASSERT_TRUE(csv_file.get() != NULL);
 215  E :      ASSERT_TRUE(g.OutputData(csv_file.get()));
 216  E :      csv_file.reset();
 217    :  
 218    :      // Ensure output was produced.
 219  E :      int64 file_size = 0;
 220  E :      ASSERT_TRUE(file_util::GetFileSize(csv_path, &file_size));
 221  E :      ASSERT_LT(0u, file_size);
 222  E :    }
 223    :  
 224    :    base::FilePath test_dll_path_;
 225    :    pe::PEFile test_dll_pe_file_;
 226    :    pe::PEFile::Signature test_dll_pe_sig_;
 227    :  
 228    :    base::FilePath temp_dir_;
 229    :    base::FilePath trace_file_path_;
 230    :  
 231    :    CommandLine cmd_line_;
 232    :    trace::parser::Parser parser_;
 233    :  
 234    :    std::vector<uint8> buffer_;
 235    :    TraceSampleData* sample_data_;
 236    :  
 237    :    trace::common::ClockInfo clock_info_;
 238    :  };
 239    :  
 240  E :  double BucketSum(const SampleGrinder::ModuleData& module_data) {
 241  E :    double sum = 0;
 242  E :    for (size_t i = 0; i < module_data.buckets.size(); ++i)
 243  E :      sum += module_data.buckets[i];
 244  E :    return sum;
 245  E :  }
 246    :  
 247    :  }  // namespace
 248    :  
 249  E :  TEST_F(SampleGrinderTest, UpsampleModuleData) {
 250  E :    SampleGrinder::ModuleData module_data;
 251  E :    EXPECT_EQ(0u, module_data.buckets.size());
 252  E :    EXPECT_EQ(0u, module_data.bucket_size);
 253    :  
 254    :    // UpsampleModuleData only cares about bucket_size and bucket_count, so no
 255    :    // need to worry about filling out a full TraceSampleData object.
 256  E :    TraceSampleData sample_data = {};
 257  E :    sample_data.bucket_count = 1000;
 258  E :    sample_data.bucket_size = 8;
 259  E :    TestSampleGrinder::UpsampleModuleData(&sample_data, &module_data);
 260  E :    ASSERT_EQ(1000u, module_data.buckets.size());
 261  E :    EXPECT_EQ(8u, module_data.bucket_size);
 262  E :    module_data.buckets[0] = 2.0;
 263  E :    EXPECT_DOUBLE_EQ(2.0, BucketSum(module_data));
 264    :  
 265  E :    TestSampleGrinder::UpsampleModuleData(&sample_data, &module_data);
 266  E :    ASSERT_EQ(1000u, module_data.buckets.size());
 267  E :    EXPECT_EQ(8u, module_data.bucket_size);
 268  E :    EXPECT_DOUBLE_EQ(2.0, module_data.buckets[0]);
 269  E :    EXPECT_DOUBLE_EQ(2.0, BucketSum(module_data));
 270    :  
 271  E :    sample_data.bucket_count = 500;
 272  E :    sample_data.bucket_size = 16;
 273  E :    TestSampleGrinder::UpsampleModuleData(&sample_data, &module_data);
 274  E :    ASSERT_EQ(1000u, module_data.buckets.size());
 275  E :    EXPECT_EQ(8u, module_data.bucket_size);
 276  E :    EXPECT_DOUBLE_EQ(2.0, module_data.buckets[0]);
 277  E :    EXPECT_DOUBLE_EQ(2.0, BucketSum(module_data));
 278    :  
 279  E :    sample_data.bucket_count = 2000;
 280  E :    sample_data.bucket_size = 4;
 281  E :    TestSampleGrinder::UpsampleModuleData(&sample_data, &module_data);
 282  E :    ASSERT_EQ(2000u, module_data.buckets.size());
 283  E :    EXPECT_EQ(4u, module_data.bucket_size);
 284  E :    EXPECT_DOUBLE_EQ(1.0, module_data.buckets[0]);
 285  E :    EXPECT_DOUBLE_EQ(1.0, module_data.buckets[1]);
 286  E :    EXPECT_DOUBLE_EQ(2.0, BucketSum(module_data));
 287  E :  }
 288    :  
 289  E :  TEST_F(SampleGrinderTest, IncrementModuleData) {
 290  E :    ASSERT_NO_FATAL_FAILURE(PrepareDummySampleDataBuffer(5));
 291  E :    ASSERT_TRUE(sample_data_ != NULL);
 292    :  
 293    :    // We make our sampling interval 1/10th of the clock rate, so that each
 294    :    // sample is worth 0.1 'seconds'.
 295  E :    uint64 sampling_interval = clock_info_.tsc_info.frequency / 10;
 296  E :    uint32 bucket_start = 0x00011000;
 297    :  
 298  E :    sample_data_->module_base_addr = reinterpret_cast<ModuleAddr>(0x00100000);
 299  E :    sample_data_->module_size = 0x00010000;
 300  E :    sample_data_->module_checksum = 0xAAAAAAAA;
 301  E :    sample_data_->module_time_date_stamp = 0xBBBBBBBB;
 302  E :    sample_data_->bucket_size = 8;
 303  E :    sample_data_->bucket_start = reinterpret_cast<ModuleAddr>(bucket_start);
 304  E :    sample_data_->bucket_count = 5;
 305  E :    sample_data_->sampling_start_time = 0;
 306  E :    sample_data_->sampling_end_time = sampling_interval * 5;
 307  E :    sample_data_->sampling_interval = sampling_interval;
 308  E :    sample_data_->buckets[0] = 3;
 309  E :    sample_data_->buckets[1] = 1;
 310  E :    sample_data_->buckets[2] = 1;
 311    :  
 312  E :    SampleGrinder::ModuleData module_data;
 313  E :    module_data.bucket_start.set_value(bucket_start);
 314  E :    TestSampleGrinder::UpsampleModuleData(sample_data_, &module_data);
 315  E :    ASSERT_EQ(sample_data_->bucket_count, module_data.buckets.size());
 316    :  
 317    :    // If the bucket starts aren't aligned this should fail.
 318  E :    module_data.bucket_start -= 4;
 319    :    EXPECT_FALSE(TestSampleGrinder::IncrementModuleData(
 320  E :        clock_info_.tsc_info.frequency, sample_data_, &module_data));
 321  E :    module_data.bucket_start += 4;
 322    :  
 323    :    // If the bucket lengths aren't consistent this should also fail.
 324  E :    module_data.buckets.resize(sample_data_->bucket_count - 1);
 325    :    EXPECT_FALSE(TestSampleGrinder::IncrementModuleData(
 326  E :        clock_info_.tsc_info.frequency, sample_data_, &module_data));
 327  E :    module_data.buckets.resize(sample_data_->bucket_count);
 328    :  
 329    :    // If the bucket length and start are consistent, then this should pass.
 330    :    EXPECT_TRUE(TestSampleGrinder::IncrementModuleData(
 331  E :        clock_info_.tsc_info.frequency, sample_data_, &module_data));
 332  E :    EXPECT_EQ(8u, module_data.bucket_size);
 333  E :    EXPECT_EQ(5u, module_data.buckets.size());
 334  E :    EXPECT_DOUBLE_EQ(0.3, module_data.buckets[0]);
 335  E :    EXPECT_DOUBLE_EQ(0.1, module_data.buckets[1]);
 336  E :    EXPECT_DOUBLE_EQ(0.1, module_data.buckets[2]);
 337  E :    EXPECT_DOUBLE_EQ(0.5, BucketSum(module_data));
 338    :  
 339    :    // Adding more of the same should work.
 340    :    EXPECT_TRUE(TestSampleGrinder::IncrementModuleData(
 341  E :        clock_info_.tsc_info.frequency, sample_data_, &module_data));
 342  E :    EXPECT_EQ(8u, module_data.bucket_size);
 343  E :    EXPECT_EQ(5u, module_data.buckets.size());
 344  E :    EXPECT_DOUBLE_EQ(0.6, module_data.buckets[0]);
 345  E :    EXPECT_DOUBLE_EQ(0.2, module_data.buckets[1]);
 346  E :    EXPECT_DOUBLE_EQ(0.2, module_data.buckets[2]);
 347  E :    EXPECT_DOUBLE_EQ(1.0, BucketSum(module_data));
 348    :  
 349    :    // Adding larger buckets should see the values split across the finer
 350    :    // resolution aggregated buckets.
 351  E :    sample_data_->bucket_count = 3;
 352  E :    sample_data_->bucket_size = 16;
 353  E :    sample_data_->buckets[0] = 2;
 354  E :    sample_data_->buckets[1] = 0;
 355  E :    sample_data_->buckets[2] = 0;
 356    :    EXPECT_TRUE(TestSampleGrinder::IncrementModuleData(
 357  E :        clock_info_.tsc_info.frequency, sample_data_, &module_data));
 358  E :    EXPECT_EQ(8u, module_data.bucket_size);
 359  E :    EXPECT_EQ(5u, module_data.buckets.size());
 360  E :    EXPECT_DOUBLE_EQ(0.7, module_data.buckets[0]);
 361  E :    EXPECT_DOUBLE_EQ(0.3, module_data.buckets[1]);
 362  E :    EXPECT_DOUBLE_EQ(0.2, module_data.buckets[2]);
 363  E :    EXPECT_DOUBLE_EQ(1.2, BucketSum(module_data));
 364  E :  }
 365    :  
 366  E :  TEST_F(SampleGrinderTest, IncrementHeatMapFromModuleData) {
 367    :    // Make 9 buckets, each with 1 second of samples in them.
 368  E :    SampleGrinder::ModuleData module_data;
 369  E :    module_data.bucket_size = 4;
 370  E :    module_data.buckets.resize(9, 1.0);
 371    :  
 372    :    // RVA    : 0     4     8     12    16    20    24    28    32    36
 373    :    // Buckets: |--0--|--1--|--2--|--3--|--4--|--5--|--6--|--7--|--8--|
 374    :    // Ranges : |--A--|B|       |C| |D| |E |F |  |--G--|  |H| |I|
 375    :    // A perfectly spans a bucket.
 376    :    // B aligns with the left edge of a bucket, but claims all of it.
 377    :    // C aligns with the right edge of a bucket, but claims all of it.
 378    :    // D is in the middle of a bucket and claims all of it.
 379    :    // E and F share a bucket, covering all of it.
 380    :    // G spans 2 buckets.
 381    :    // H and I share a bucket, but don't cover it entirely.
 382    :  
 383    :    typedef SampleGrinder::BasicBlockData BasicBlockData;
 384    :    typedef SampleGrinder::HeatMap HeatMap;
 385    :    typedef SampleGrinder::HeatMap::AddressSpace::Range Range;
 386    :    typedef SampleGrinder::HeatMap::AddressSpace::Range::Address RVA;
 387    :  
 388  E :    HeatMap heat_map;
 389  E :    const BasicBlockData kData = {};
 390  E :    ASSERT_TRUE(heat_map.Insert(Range(RVA(0), 4), kData));  // A.
 391  E :    ASSERT_TRUE(heat_map.Insert(Range(RVA(4), 2), kData));  // B.
 392  E :    ASSERT_TRUE(heat_map.Insert(Range(RVA(10), 2), kData));  // C.
 393  E :    ASSERT_TRUE(heat_map.Insert(Range(RVA(13), 2), kData));  // D.
 394  E :    ASSERT_TRUE(heat_map.Insert(Range(RVA(16), 2), kData));  // E.
 395  E :    ASSERT_TRUE(heat_map.Insert(Range(RVA(18), 2), kData));  // F.
 396  E :    ASSERT_TRUE(heat_map.Insert(Range(RVA(22), 4), kData));  // G.
 397  E :    ASSERT_TRUE(heat_map.Insert(Range(RVA(28), 1), kData));  // H.
 398  E :    ASSERT_TRUE(heat_map.Insert(Range(RVA(31), 1), kData));  // I.
 399    :  
 400  E :    double total_samples = 0;
 401    :    double orphaned_samples = TestSampleGrinder::IncrementHeatMapFromModuleData(
 402  E :        module_data, &heat_map, &total_samples);
 403  E :    EXPECT_DOUBLE_EQ(1.0, orphaned_samples);
 404  E :    EXPECT_DOUBLE_EQ(9.0, total_samples);
 405    :  
 406    :    // We expect the heat to have been distributed to the ranges in the following
 407    :    // quantities.
 408  E :    const double kHeat[] = { /* A */ 1.0, /* B */ 1.0, /* C */ 1.0,
 409  E :                             /* D */ 1.0, /* E */ 0.5, /* F */ 0.5,
 410  E :                             /* G */ 2.0, /* H */ 0.5, /* I */ 0.5 };
 411  E :    ASSERT_EQ(arraysize(kHeat), heat_map.size());
 412  E :    HeatMap::const_iterator it = heat_map.begin();
 413  E :    for (size_t i = 0; it != heat_map.end(); ++it, ++i)
 414  E :      EXPECT_DOUBLE_EQ(kHeat[i], it->second.heat);
 415  E :  }
 416    :  
 417  E :  TEST_F(SampleGrinderTest, RollUpByName) {
 418  E :    const std::string kFoo = "foo";
 419  E :    const std::string kBar = "bar";
 420    :  
 421    :    typedef TestSampleGrinder::HeatMap::AddressSpace::Range Range;
 422    :    typedef TestSampleGrinder::HeatMap::AddressSpace::Range::Address RVA;
 423    :  
 424    :    // Create a very simple heat map.
 425  E :    TestSampleGrinder::HeatMap heat_map;
 426  E :    TestSampleGrinder::BasicBlockData bbd0 = { &kFoo, &kBar, 1.0 };
 427  E :    TestSampleGrinder::BasicBlockData bbd1 = { &kBar, &kFoo, 2.0 };
 428  E :    ASSERT_TRUE(heat_map.Insert(Range(RVA(0), 4), bbd0));
 429  E :    ASSERT_TRUE(heat_map.Insert(Range(RVA(4), 4), bbd1));
 430    :  
 431  E :    TestSampleGrinder::NameHeatMap nhm;
 432  E :    TestSampleGrinder::NameHeatMap expected_nhm;
 433    :  
 434  E :    expected_nhm[&kFoo] = 2.0;
 435  E :    expected_nhm[&kBar] = 1.0;
 436  E :    TestSampleGrinder::RollUpByName(SampleGrinder::kFunction, heat_map, &nhm);
 437  E :    EXPECT_THAT(nhm, testing::ContainerEq(expected_nhm));
 438    :  
 439  E :    nhm.clear();
 440  E :    expected_nhm[&kFoo] = 1.0;
 441  E :    expected_nhm[&kBar] = 2.0;
 442  E :    TestSampleGrinder::RollUpByName(SampleGrinder::kCompiland, heat_map, &nhm);
 443  E :    EXPECT_THAT(nhm, testing::ContainerEq(expected_nhm));
 444  E :  }
 445    :  
 446  E :  TEST_F(SampleGrinderTest, ParseEmptyCommandLineFails) {
 447  E :    TestSampleGrinder g;
 448  E :    EXPECT_FALSE(g.ParseCommandLine(&cmd_line_));
 449  E :  }
 450    :  
 451  E :  TEST_F(SampleGrinderTest, ParseMinimalCommandLineSucceeds) {
 452  E :    TestSampleGrinder g;
 453  E :    cmd_line_.AppendSwitchPath(SampleGrinder::kImage, test_dll_path_);
 454  E :    EXPECT_TRUE(g.ParseCommandLine(&cmd_line_));
 455  E :    EXPECT_EQ(test_dll_path_, g.image_path_);
 456  E :    EXPECT_EQ(SampleGrinder::kBasicBlock, g.aggregation_level_);
 457  E :  }
 458    :  
 459  E :  TEST_F(SampleGrinderTest, ParseCommandLineAggregationLevel) {
 460    :    // Test command line without specifying '--image'.
 461    :  
 462  E :    cmd_line_.AppendSwitchASCII(SampleGrinder::kAggregationLevel, "basic-block");
 463    :    {
 464  E :      TestSampleGrinder g;
 465  E :      EXPECT_FALSE(g.ParseCommandLine(&cmd_line_));
 466  E :    }
 467    :  
 468  E :    cmd_line_.Init(0, NULL);
 469  E :    cmd_line_.AppendSwitchASCII(SampleGrinder::kAggregationLevel, "function");
 470    :    {
 471  E :      TestSampleGrinder g;
 472  E :      EXPECT_TRUE(g.ParseCommandLine(&cmd_line_));
 473  E :      EXPECT_TRUE(g.image_path_.empty());
 474  E :      EXPECT_EQ(SampleGrinder::kFunction, g.aggregation_level_);
 475  E :    }
 476    :  
 477  E :    cmd_line_.Init(0, NULL);
 478  E :    cmd_line_.AppendSwitchASCII(SampleGrinder::kAggregationLevel, "compiland");
 479    :    {
 480  E :      TestSampleGrinder g;
 481  E :      EXPECT_TRUE(g.ParseCommandLine(&cmd_line_));
 482  E :      EXPECT_TRUE(g.image_path_.empty());
 483  E :      EXPECT_EQ(SampleGrinder::kCompiland, g.aggregation_level_);
 484  E :    }
 485    :  
 486  E :    cmd_line_.Init(0, NULL);
 487  E :    cmd_line_.AppendSwitchASCII(SampleGrinder::kAggregationLevel, "line");
 488    :    {
 489  E :      TestSampleGrinder g;
 490  E :      EXPECT_TRUE(g.ParseCommandLine(&cmd_line_));
 491  E :      EXPECT_TRUE(g.image_path_.empty());
 492  E :      EXPECT_EQ(SampleGrinder::kLine, g.aggregation_level_);
 493  E :    }
 494    :  
 495  E :    cmd_line_.Init(0, NULL);
 496  E :    cmd_line_.AppendSwitchASCII(SampleGrinder::kAggregationLevel, "foobar");
 497    :    {
 498  E :      TestSampleGrinder g;
 499  E :      EXPECT_FALSE(g.ParseCommandLine(&cmd_line_));
 500  E :    }
 501    :  
 502    :    // Test command line when specifying '--image'.
 503    :  
 504  E :    cmd_line_.Init(0, NULL);
 505  E :    cmd_line_.AppendSwitchPath(SampleGrinder::kImage, test_dll_path_);
 506  E :    cmd_line_.AppendSwitchASCII(SampleGrinder::kAggregationLevel, "basic-block");
 507    :    {
 508  E :      TestSampleGrinder g;
 509  E :      EXPECT_TRUE(g.ParseCommandLine(&cmd_line_));
 510  E :      EXPECT_EQ(test_dll_path_, g.image_path_);
 511  E :      EXPECT_EQ(SampleGrinder::kBasicBlock, g.aggregation_level_);
 512  E :    }
 513    :  
 514  E :    cmd_line_.Init(0, NULL);
 515  E :    cmd_line_.AppendSwitchPath(SampleGrinder::kImage, test_dll_path_);
 516  E :    cmd_line_.AppendSwitchASCII(SampleGrinder::kAggregationLevel, "function");
 517    :    {
 518  E :      TestSampleGrinder g;
 519  E :      EXPECT_TRUE(g.ParseCommandLine(&cmd_line_));
 520  E :      EXPECT_EQ(test_dll_path_, g.image_path_);
 521  E :      EXPECT_EQ(SampleGrinder::kFunction, g.aggregation_level_);
 522  E :    }
 523    :  
 524  E :    cmd_line_.Init(0, NULL);
 525  E :    cmd_line_.AppendSwitchPath(SampleGrinder::kImage, test_dll_path_);
 526  E :    cmd_line_.AppendSwitchASCII(SampleGrinder::kAggregationLevel, "line");
 527    :    {
 528  E :      TestSampleGrinder g;
 529  E :      EXPECT_TRUE(g.ParseCommandLine(&cmd_line_));
 530  E :      EXPECT_EQ(test_dll_path_, g.image_path_);
 531  E :      EXPECT_EQ(SampleGrinder::kLine, g.aggregation_level_);
 532  E :    }
 533    :  
 534  E :    cmd_line_.Init(0, NULL);
 535  E :    cmd_line_.AppendSwitchPath(SampleGrinder::kImage, test_dll_path_);
 536  E :    cmd_line_.AppendSwitchASCII(SampleGrinder::kAggregationLevel, "compiland");
 537    :    {
 538  E :      TestSampleGrinder g;
 539  E :      EXPECT_TRUE(g.ParseCommandLine(&cmd_line_));
 540  E :      EXPECT_EQ(test_dll_path_, g.image_path_);
 541  E :      EXPECT_EQ(SampleGrinder::kCompiland, g.aggregation_level_);
 542  E :    }
 543    :  
 544  E :    cmd_line_.Init(0, NULL);
 545  E :    cmd_line_.AppendSwitchPath(SampleGrinder::kImage, test_dll_path_);
 546  E :    cmd_line_.AppendSwitchASCII(SampleGrinder::kAggregationLevel, "foobar");
 547    :    {
 548  E :      TestSampleGrinder g;
 549  E :      EXPECT_FALSE(g.ParseCommandLine(&cmd_line_));
 550  E :    }
 551  E :  }
 552    :  
 553  E :  TEST_F(SampleGrinderTest, SetParserSucceeds) {
 554  E :    TestSampleGrinder g;
 555  E :    EXPECT_TRUE(g.parser_ == NULL);
 556    :  
 557  E :    g.SetParser(&parser_);
 558  E :    EXPECT_EQ(&parser_, g.parser_);
 559  E :  }
 560    :  
 561  E :  TEST_F(SampleGrinderTest, GrindBasicBlock) {
 562  E :    TestSampleGrinder g;
 563  E :    ASSERT_NO_FATAL_FAILURE(GrindSucceeds(SampleGrinder::kBasicBlock, true));
 564  E :  }
 565    :  
 566  E :  TEST_F(SampleGrinderTest, GrindFunction) {
 567  E :    TestSampleGrinder g;
 568  E :    ASSERT_NO_FATAL_FAILURE(GrindSucceeds(SampleGrinder::kFunction, true));
 569  E :  }
 570    :  
 571  E :  TEST_F(SampleGrinderTest, GrindFunctionNoImageSpecified) {
 572  E :    TestSampleGrinder g;
 573  E :    ASSERT_NO_FATAL_FAILURE(GrindSucceeds(SampleGrinder::kFunction, false));
 574  E :  }
 575    :  
 576  E :  TEST_F(SampleGrinderTest, GrindCompiland) {
 577  E :    TestSampleGrinder g;
 578  E :    ASSERT_NO_FATAL_FAILURE(GrindSucceeds(SampleGrinder::kCompiland, true));
 579  E :  }
 580    :  
 581  E :  TEST_F(SampleGrinderTest, GrindCompilandNoImageSpecified) {
 582  E :    TestSampleGrinder g;
 583  E :    ASSERT_NO_FATAL_FAILURE(GrindSucceeds(SampleGrinder::kCompiland, false));
 584  E :  }
 585    :  
 586  E :  TEST_F(SampleGrinderTest, GrindLine) {
 587  E :    TestSampleGrinder g;
 588  E :    ASSERT_NO_FATAL_FAILURE(GrindSucceeds(SampleGrinder::kLine, true));
 589  E :  }
 590    :  
 591  E :  TEST_F(SampleGrinderTest, GrindLineNoImageSpecified) {
 592  E :    TestSampleGrinder g;
 593  E :    ASSERT_NO_FATAL_FAILURE(GrindSucceeds(SampleGrinder::kLine, false));
 594  E :  }
 595    :  
 596    :  }  // namespace grinders
 597    :  }  // namespace grinder

Coverage information generated Wed Dec 11 11:34:16 2013.