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 "gtest/gtest.h"
18 : #include "syzygy/core/unittest_util.h"
19 : #include "syzygy/pe/unittest_util.h"
20 :
21 : namespace grinder {
22 : namespace grinders {
23 :
24 : namespace {
25 :
26 : const wchar_t kProfileTraceFile[] = L"profile_traces/trace-1.bin";
27 :
28 : class TestProfileGrinder : public ProfileGrinder {
29 : public:
30 : // Expose for testing.
31 : using ProfileGrinder::CodeLocation;
32 : using ProfileGrinder::FunctionLocation;
33 : using ProfileGrinder::PartData;
34 : using ProfileGrinder::FindOrCreatePart;
35 :
36 : typedef ProfileGrinder::InvocationNodeMap InvocationNodeMap;
37 :
38 : using ProfileGrinder::parser_;
39 : using ProfileGrinder::parts_;
40 : };
41 :
42 : class ProfileGrinderTest : public testing::PELibUnitTest {
43 : public:
44 : typedef testing::PELibUnitTest Super;
45 :
46 E : ProfileGrinderTest() : cmd_line_(base::FilePath(L"profile_grinder.exe")) {
47 E : }
48 :
49 E : virtual void SetUp() OVERRIDE {
50 E : Super::Test::SetUp();
51 E : cmd_line_.AppendSwitchASCII("mode", "profile");
52 E : }
53 :
54 E : void InitParser(trace::parser::ParseEventHandlerImpl* handler) {
55 E : ASSERT_TRUE(handler != NULL);
56 :
57 E : ASSERT_TRUE(parser_.Init(handler));
58 :
59 : base::FilePath trace_file =
60 E : testing::GetExeTestDataRelativePath(kProfileTraceFile);
61 :
62 E : ASSERT_TRUE(parser_.OpenTraceFile(trace_file));
63 E : }
64 :
65 E : void GrindAndOutputSucceeds() {
66 E : TestProfileGrinder grinder;
67 E : grinder.ParseCommandLine(&cmd_line_);
68 :
69 E : ASSERT_NO_FATAL_FAILURE(InitParser(&grinder));
70 E : grinder.SetParser(&parser_);
71 E : ASSERT_TRUE(parser_.Consume());
72 :
73 E : EXPECT_TRUE(grinder.Grind());
74 :
75 E : testing::ScopedTempFile output_path;
76 : file_util::ScopedFILE output_file(
77 E : file_util::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(file_util::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 : CommandLine cmd_line_;
127 : trace::parser::Parser parser_;
128 : };
129 :
130 : } // namespace
131 :
132 E : TEST_F(ProfileGrinderTest, CodeLocation) {
133 : typedef TestProfileGrinder::CodeLocation CodeLocation;
134 :
135 E : CodeLocation loc1;
136 E : EXPECT_FALSE(loc1.is_symbol());
137 E : EXPECT_EQ(NULL, loc1.module());
138 E : EXPECT_EQ(0, loc1.rva());
139 :
140 E : EXPECT_TRUE(loc1 == CodeLocation());
141 :
142 : // Change location to a symbol.
143 E : const uint32 kSymbolId = 0x1345;
144 E : const size_t kSymbolOffset = 0x13;
145 E : loc1.Set(::GetCurrentProcessId(), kSymbolId, kSymbolOffset);
146 E : EXPECT_TRUE(loc1.is_symbol());
147 E : EXPECT_EQ(::GetCurrentProcessId(), loc1.process_id());
148 E : EXPECT_EQ(kSymbolId, loc1.symbol_id());
149 E : EXPECT_EQ(kSymbolOffset, loc1.symbol_offset());
150 :
151 E : EXPECT_FALSE(loc1 == CodeLocation());
152 :
153 : // Test copy construction.
154 E : EXPECT_TRUE(loc1 == CodeLocation(loc1));
155 :
156 E : CodeLocation loc2;
157 : // loc2 differs only in offset from loc1.
158 E : loc2.Set(::GetCurrentProcessId(), kSymbolId, 0);
159 E : EXPECT_TRUE(loc2 != loc1);
160 E : EXPECT_TRUE(loc2 < loc1);
161 :
162 E : const sym_util::ModuleInformation kModuleInfo;
163 E : const RVA kRva = 0x10945;
164 : // Change them both to module/rva, and test for equality.
165 E : loc1.Set(&kModuleInfo, kRva);
166 E : loc2.Set(&kModuleInfo, kRva);
167 :
168 E : EXPECT_TRUE(loc1 == loc2);
169 E : }
170 :
171 E : TEST_F(ProfileGrinderTest, GrindSymbolTestData) {
172 : // Issue a symbol invocation event against a test grinder.
173 E : TestProfileGrinder grinder;
174 E : IssueSetupEvents(&grinder);
175 E : IssueSymbolInvocationEvent(&grinder);
176 :
177 : // Grind the data.
178 E : ASSERT_TRUE(grinder.Grind());
179 :
180 : // Then retrieve and validate the data.
181 E : ASSERT_EQ(1, grinder.parts_.size());
182 : TestProfileGrinder::PartData* part =
183 : grinder.FindOrCreatePart(::GetCurrentProcessId(),
184 E : ::GetCurrentThreadId());
185 :
186 E : ASSERT_TRUE(part != NULL);
187 E : ASSERT_EQ("TestThread", part->thread_name_);
188 :
189 : // We get one node for the (unknown) caller, and one for the function called.
190 E : ASSERT_EQ(2, part->nodes_.size());
191 :
192 E : TestProfileGrinder::InvocationNodeMap::iterator it = part->nodes_.begin();
193 :
194 E : EXPECT_TRUE(it->first.is_symbol());
195 E : EXPECT_EQ(::GetCurrentProcessId(), it->first.process_id());
196 E : EXPECT_EQ(kFunctionSymbolId, it->first.symbol_id());
197 :
198 E : ++it;
199 E : ASSERT_TRUE(it != part->nodes_.end());
200 :
201 E : EXPECT_TRUE(it->first.is_symbol());
202 E : EXPECT_EQ(::GetCurrentProcessId(), it->first.process_id());
203 E : EXPECT_EQ(kCallerSymbolId, it->first.symbol_id());
204 E : }
205 :
206 E : TEST_F(ProfileGrinderTest, ParseEmptyCommandLineSucceeds) {
207 E : TestProfileGrinder grinder;
208 E : EXPECT_TRUE(grinder.ParseCommandLine(&cmd_line_));
209 E : EXPECT_FALSE(grinder.thread_parts());
210 E : }
211 :
212 E : TEST_F(ProfileGrinderTest, ParseThreadPartsSwitchOnCommandLine) {
213 E : TestProfileGrinder grinder;
214 E : cmd_line_.AppendSwitch("thread-parts");
215 E : EXPECT_TRUE(grinder.ParseCommandLine(&cmd_line_));
216 E : }
217 :
218 E : TEST_F(ProfileGrinderTest, SetParserSucceeds) {
219 E : TestProfileGrinder grinder;
220 E : grinder.ParseCommandLine(&cmd_line_);
221 :
222 E : ASSERT_NO_FATAL_FAILURE(InitParser(&grinder));
223 :
224 E : grinder.SetParser(&parser_);
225 E : EXPECT_EQ(&parser_, grinder.parser_);
226 E : }
227 :
228 E : TEST_F(ProfileGrinderTest, GrindAndOutputCacheGrindDataSucceeds) {
229 E : ASSERT_NO_FATAL_FAILURE(GrindAndOutputSucceeds());
230 : // TODO(etienneb): Validate the output is a valid CacheGrind file.
231 E : }
232 :
233 : } // namespace grinders
234 : } // namespace grinder
|