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
|