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