1 : // Copyright 2012 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/profile_grinder.h"
16 :
17 : #include "base/win/scoped_com_initializer.h"
18 : #include "gtest/gtest.h"
19 : #include "syzygy/core/unittest_util.h"
20 : #include "syzygy/pe/unittest_util.h"
21 :
22 : namespace grinder {
23 : namespace grinders {
24 :
25 : namespace {
26 :
27 : const wchar_t kProfileTraceFile[] = L"profile_traces/trace-1.bin";
28 :
29 : class TestProfileGrinder : public ProfileGrinder {
30 : public:
31 : // Expose for testing.
32 : using ProfileGrinder::CodeLocation;
33 : using ProfileGrinder::FunctionLocation;
34 : using ProfileGrinder::PartData;
35 : using ProfileGrinder::FindOrCreatePart;
36 :
37 : typedef ProfileGrinder::InvocationNodeMap InvocationNodeMap;
38 :
39 : using ProfileGrinder::parser_;
40 : using ProfileGrinder::parts_;
41 : };
42 :
43 : class ProfileGrinderTest : public testing::PELibUnitTest {
44 : public:
45 : typedef testing::PELibUnitTest Super;
46 :
47 E : ProfileGrinderTest() : cmd_line_(base::FilePath(L"profile_grinder.exe")) {
48 E : }
49 :
50 E : virtual void SetUp() override {
51 E : Super::Test::SetUp();
52 E : cmd_line_.AppendSwitchASCII("mode", "profile");
53 E : }
54 :
55 E : void InitParser(trace::parser::ParseEventHandlerImpl* handler) {
56 E : ASSERT_TRUE(handler != NULL);
57 :
58 E : ASSERT_TRUE(parser_.Init(handler));
59 :
60 : base::FilePath trace_file =
61 E : testing::GetExeTestDataRelativePath(kProfileTraceFile);
62 :
63 E : ASSERT_TRUE(parser_.OpenTraceFile(trace_file));
64 E : }
65 :
66 E : void GrindAndOutputSucceeds() {
67 E : TestProfileGrinder grinder;
68 E : grinder.ParseCommandLine(&cmd_line_);
69 :
70 E : ASSERT_NO_FATAL_FAILURE(InitParser(&grinder));
71 E : grinder.SetParser(&parser_);
72 E : ASSERT_TRUE(parser_.Consume());
73 :
74 E : EXPECT_TRUE(grinder.Grind());
75 :
76 E : testing::ScopedTempFile output_path;
77 E : base::ScopedFILE output_file(base::OpenFile(output_path.path(), "wb"));
78 E : ASSERT_TRUE(output_file.get() != NULL);
79 :
80 E : EXPECT_TRUE(grinder.OutputData(output_file.get()));
81 E : output_file.reset();
82 :
83 E : int64 cache_grind_file_size = 0;
84 E : ASSERT_TRUE(base::GetFileSize(output_path.path(),
85 : &cache_grind_file_size));
86 E : EXPECT_LT(0u, cache_grind_file_size);
87 E : }
88 :
89 E : void IssueSetupEvents(TestProfileGrinder* grinder) {
90 : grinder->OnThreadName(base::Time::Now(),
91 : ::GetCurrentProcessId(),
92 : ::GetCurrentThreadId(),
93 E : "TestThread");
94 :
95 : grinder->OnDynamicSymbol(::GetCurrentProcessId(),
96 : kFunctionSymbolId,
97 E : "Function");
98 :
99 : grinder->OnDynamicSymbol(::GetCurrentProcessId(),
100 : kCallerSymbolId,
101 E : "Caller");
102 E : }
103 :
104 : static const uint32 kFunctionSymbolId = 0x10;
105 : static const uint32 kCallerSymbolId = 0x33;
106 :
107 E : void IssueSymbolInvocationEvent(TestProfileGrinder* grinder) {
108 E : TraceBatchInvocationInfo batch = {};
109 E : batch.invocations[0].function_symbol_id = kFunctionSymbolId;
110 E : batch.invocations[0].caller_symbol_id = kCallerSymbolId;
111 E : batch.invocations[0].caller_offset = 0x30;
112 :
113 E : batch.invocations[0].num_calls = 1000;
114 E : batch.invocations[0].flags = kFunctionIsSymbol | kCallerIsSymbol;
115 E : batch.invocations[0].cycles_min = 10;
116 E : batch.invocations[0].cycles_max = 1000;
117 E : batch.invocations[0].cycles_sum = 1000 * 100;
118 :
119 : grinder->OnInvocationBatch(base::Time::Now(),
120 : ::GetCurrentProcessId(),
121 : ::GetCurrentThreadId(),
122 : 1,
123 E : &batch);
124 E : }
125 :
126 : // Ensures that COM is initialized for tests in this fixture.
127 : base::win::ScopedCOMInitializer com_initializer_;
128 :
129 : base::CommandLine cmd_line_;
130 : trace::parser::Parser parser_;
131 : };
132 :
133 : } // namespace
134 :
135 E : TEST_F(ProfileGrinderTest, CodeLocation) {
136 : typedef TestProfileGrinder::CodeLocation CodeLocation;
137 :
138 E : CodeLocation loc1;
139 E : EXPECT_FALSE(loc1.is_symbol());
140 E : EXPECT_EQ(NULL, loc1.module());
141 E : EXPECT_EQ(0, loc1.rva());
142 :
143 E : EXPECT_TRUE(loc1 == CodeLocation());
144 :
145 : // Change location to a symbol.
146 E : const uint32 kSymbolId = 0x1345;
147 E : const size_t kSymbolOffset = 0x13;
148 E : loc1.Set(::GetCurrentProcessId(), kSymbolId, kSymbolOffset);
149 E : EXPECT_TRUE(loc1.is_symbol());
150 E : EXPECT_EQ(::GetCurrentProcessId(), loc1.process_id());
151 E : EXPECT_EQ(kSymbolId, loc1.symbol_id());
152 E : EXPECT_EQ(kSymbolOffset, loc1.symbol_offset());
153 :
154 E : EXPECT_FALSE(loc1 == CodeLocation());
155 :
156 : // Test copy construction.
157 E : EXPECT_TRUE(loc1 == CodeLocation(loc1));
158 :
159 E : CodeLocation loc2;
160 : // loc2 differs only in offset from loc1.
161 E : loc2.Set(::GetCurrentProcessId(), kSymbolId, 0);
162 E : EXPECT_TRUE(loc2 != loc1);
163 E : EXPECT_TRUE(loc2 < loc1);
164 :
165 E : const pe::ModuleInformation kModuleInfo;
166 E : const RVA kRva = 0x10945;
167 : // Change them both to module/rva, and test for equality.
168 E : loc1.Set(&kModuleInfo, kRva);
169 E : loc2.Set(&kModuleInfo, kRva);
170 :
171 E : EXPECT_TRUE(loc1 == loc2);
172 E : }
173 :
174 E : TEST_F(ProfileGrinderTest, GrindSymbolTestData) {
175 : // Issue a symbol invocation event against a test grinder.
176 E : TestProfileGrinder grinder;
177 E : IssueSetupEvents(&grinder);
178 E : IssueSymbolInvocationEvent(&grinder);
179 :
180 : // Grind the data.
181 E : ASSERT_TRUE(grinder.Grind());
182 :
183 : // Then retrieve and validate the data.
184 E : ASSERT_EQ(1, grinder.parts_.size());
185 : TestProfileGrinder::PartData* part =
186 : grinder.FindOrCreatePart(::GetCurrentProcessId(),
187 E : ::GetCurrentThreadId());
188 :
189 E : ASSERT_TRUE(part != NULL);
190 E : ASSERT_EQ("TestThread", part->thread_name_);
191 :
192 : // We get one node for the (unknown) caller, and one for the function called.
193 E : ASSERT_EQ(2, part->nodes_.size());
194 :
195 E : TestProfileGrinder::InvocationNodeMap::iterator it = part->nodes_.begin();
196 :
197 E : EXPECT_TRUE(it->first.is_symbol());
198 E : EXPECT_EQ(::GetCurrentProcessId(), it->first.process_id());
199 E : EXPECT_EQ(kFunctionSymbolId, it->first.symbol_id());
200 :
201 E : ++it;
202 E : ASSERT_TRUE(it != part->nodes_.end());
203 :
204 E : EXPECT_TRUE(it->first.is_symbol());
205 E : EXPECT_EQ(::GetCurrentProcessId(), it->first.process_id());
206 E : EXPECT_EQ(kCallerSymbolId, it->first.symbol_id());
207 E : }
208 :
209 E : TEST_F(ProfileGrinderTest, ParseEmptyCommandLineSucceeds) {
210 E : TestProfileGrinder grinder;
211 E : EXPECT_TRUE(grinder.ParseCommandLine(&cmd_line_));
212 E : EXPECT_FALSE(grinder.thread_parts());
213 E : }
214 :
215 E : TEST_F(ProfileGrinderTest, ParseThreadPartsSwitchOnCommandLine) {
216 E : TestProfileGrinder grinder;
217 E : cmd_line_.AppendSwitch("thread-parts");
218 E : EXPECT_TRUE(grinder.ParseCommandLine(&cmd_line_));
219 E : }
220 :
221 E : TEST_F(ProfileGrinderTest, SetParserSucceeds) {
222 E : TestProfileGrinder grinder;
223 E : grinder.ParseCommandLine(&cmd_line_);
224 :
225 E : ASSERT_NO_FATAL_FAILURE(InitParser(&grinder));
226 :
227 E : grinder.SetParser(&parser_);
228 E : EXPECT_EQ(&parser_, grinder.parser_);
229 E : }
230 :
231 E : TEST_F(ProfileGrinderTest, GrindAndOutputCacheGrindDataSucceeds) {
232 E : ASSERT_NO_FATAL_FAILURE(GrindAndOutputSucceeds());
233 : // TODO(etienneb): Validate the output is a valid CacheGrind file.
234 E : }
235 :
236 : } // namespace grinders
237 : } // namespace grinder
|