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 : #include "syzygy/pe/dia_browser.h"
15 :
16 : #include <diacreate.h>
17 :
18 : #include "base/bind.h"
19 : #include "base/path_service.h"
20 : #include "base/files/file_path.h"
21 : #include "base/memory/scoped_ptr.h"
22 : #include "base/win/scoped_comptr.h"
23 : #include "gmock/gmock.h"
24 : #include "gtest/gtest.h"
25 : #include "syzygy/core/unittest_util.h"
26 : #include "syzygy/pe/dia_util.h"
27 :
28 : using base::win::ScopedComPtr;
29 : using testing::_;
30 : using testing::Return;
31 :
32 : namespace pe {
33 :
34 : using builder::Callback;
35 : using builder::Not;
36 : using builder::Opt;
37 : using builder::Or;
38 : using builder::Plus;
39 : using builder::Seq;
40 : using builder::Star;
41 : using builder::Tag;
42 : using builder::Tags;
43 :
44 : class PatternTest: public testing::Test {
45 : public:
46 E : PatternTest() {
47 E : }
48 :
49 : MOCK_METHOD3(OnMatch, DiaBrowser::BrowserDirective(
50 : const DiaBrowser&,
51 : const DiaBrowser::SymTagVector&,
52 i : const DiaBrowser::SymbolPtrVector&));
53 :
54 E : virtual void SetUp() {
55 E : on_match_ = base::Bind(&PatternTest::OnMatch, base::Unretained(this));
56 E : }
57 :
58 : protected:
59 : DiaBrowser::MatchCallback on_match_;
60 : };
61 :
62 : const wchar_t kPdbName[] = L"syzygy\\pe\\test_data\\test_dll.pdb";
63 :
64 : class DiaBrowserTest: public testing::Test {
65 : public:
66 E : DiaBrowserTest() {
67 E : }
68 :
69 : DiaBrowser::BrowserDirective OnPartialMatchTerminate(
70 : const DiaBrowser& dia_browser,
71 : const DiaBrowser::SymTagVector& tag_lineage,
72 E : const DiaBrowser::SymbolPtrVector& symbol_lineage) {
73 : // Call the 'OnPartialMatch' for bookkeeping reasons.
74 E : OnPartialMatch(dia_browser, tag_lineage, symbol_lineage);
75 E : if (!tag_lineage.empty() && tag_lineage.back() == SymTagUDT)
76 E : return DiaBrowser::kBrowserTerminatePath;
77 E : return DiaBrowser::kBrowserContinue;
78 E : }
79 :
80 : MOCK_METHOD3(OnPartialMatch, DiaBrowser::BrowserDirective(
81 : const DiaBrowser& dia_browser,
82 : const DiaBrowser::SymTagVector& tag_lineage,
83 E : const DiaBrowser::SymbolPtrVector& symbol_lineage));
84 :
85 : MOCK_METHOD3(OnFullMatch, DiaBrowser::BrowserDirective(
86 : const DiaBrowser& dia_browser,
87 : const DiaBrowser::SymTagVector& tag_lineage,
88 E : const DiaBrowser::SymbolPtrVector& symbol_lineage));
89 :
90 E : virtual void SetUp() {
91 : on_partial_match_term_ =
92 : base::Bind(&DiaBrowserTest::OnPartialMatchTerminate,
93 E : base::Unretained(this));
94 : on_partial_match_ = base::Bind(&DiaBrowserTest::OnPartialMatch,
95 E : base::Unretained(this));
96 : on_full_match_ = base::Bind(&DiaBrowserTest::OnFullMatch,
97 E : base::Unretained(this));
98 :
99 E : if (!SUCCEEDED(dia_source_.CreateInstance(CLSID_DiaSource)))
100 E : ASSERT_HRESULT_SUCCEEDED(NoRegCoCreate(
101 : pe::kDiaDllName, CLSID_DiaSource, IID_IDiaDataSource,
102 : reinterpret_cast<void**>(&dia_source_)));
103 :
104 : HRESULT hr = dia_source_->loadDataFromPdb(
105 E : testing::GetSrcRelativePath(kPdbName).value().c_str());
106 E : ASSERT_HRESULT_SUCCEEDED(hr);
107 :
108 E : hr = dia_source_->openSession(dia_session_.Receive());
109 E : ASSERT_HRESULT_SUCCEEDED(hr);
110 :
111 E : hr = dia_session_->get_globalScope(global_.Receive());
112 E : ASSERT_HRESULT_SUCCEEDED(hr);
113 E : }
114 :
115 E : virtual void TearDown() {
116 E : global_.Release();
117 E : dia_session_.Release();
118 E : dia_source_.Release();
119 E : }
120 :
121 : protected:
122 : DiaBrowser::MatchCallback on_partial_match_term_;
123 : DiaBrowser::MatchCallback on_partial_match_;
124 : DiaBrowser::MatchCallback on_full_match_;
125 : ScopedComPtr<IDiaDataSource> dia_source_;
126 : ScopedComPtr<IDiaSession> dia_session_;
127 : ScopedComPtr<IDiaSymbol> global_;
128 : };
129 :
130 : } // namespace pe
131 :
132 : namespace pe {
133 :
134 : class TestDiaBrowser : public DiaBrowser {
135 : public:
136 : using DiaBrowser::TestMatch;
137 : };
138 :
139 E : TEST_F(PatternTest, NullMatchingPatternIsInvalid) {
140 E : TestDiaBrowser dia_browser;
141 :
142 : // The pattern 'Compiland?' would match everything with a null match,
143 : // so it should be rejected.
144 E : EXPECT_FALSE(dia_browser.AddPattern(Opt(SymTagCompiland), on_match_));
145 E : }
146 :
147 E : TEST_F(PatternTest, Wildcard) {
148 E : TestDiaBrowser dia_browser;
149 :
150 E : ASSERT_TRUE(dia_browser.AddPattern(Tag(SymTagNull), on_match_));
151 :
152 : // The wild-card pattern should match every sym tag.
153 E : for (size_t i = kSymTagBegin; i < kSymTagEnd; ++i) {
154 E : SymTag sym_tag = static_cast<SymTag>(i);
155 E : std::vector<SymTag> sym_tags(1, sym_tag);
156 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
157 E : }
158 E : }
159 :
160 E : TEST_F(PatternTest, Seq) {
161 E : TestDiaBrowser dia_browser;
162 :
163 : // Set up pattern 'Compiland.Function.Block.Data'.
164 : ASSERT_TRUE(dia_browser.AddPattern(
165 : Seq(SymTagCompiland, SymTagFunction, SymTagBlock, SymTagData),
166 E : on_match_));
167 :
168 : // This exact pattern should match, but not any prefix of it, nor any
169 : // longer pattern containing it as a prefix.
170 E : std::vector<SymTag> sym_tags;
171 E : sym_tags.push_back(SymTagCompiland);
172 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
173 E : sym_tags.push_back(SymTagFunction);
174 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
175 E : sym_tags.push_back(SymTagBlock);
176 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
177 E : sym_tags.push_back(SymTagData);
178 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
179 E : sym_tags.push_back(SymTagData);
180 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
181 E : }
182 :
183 E : TEST_F(PatternTest, EmptySymTagBitSetRejected) {
184 E : TestDiaBrowser dia_browser;
185 :
186 : // A pattern with an element that can't match anything should be rejected.
187 E : EXPECT_FALSE(dia_browser.AddPattern(Not(SymTagNull), on_match_));
188 E : EXPECT_FALSE(dia_browser.AddPattern(Tags(SymTagBitSet()), on_match_));
189 E : }
190 :
191 E : TEST_F(PatternTest, Not) {
192 E : TestDiaBrowser dia_browser;
193 :
194 : // Set up pattern '[^Compiland]'
195 E : ASSERT_TRUE(dia_browser.AddPattern(Not(SymTagCompiland), on_match_));
196 :
197 : // This should match every SymTag *except* Compiland.
198 E : for (size_t i = kSymTagBegin; i < kSymTagEnd; ++i) {
199 E : SymTag sym_tag = static_cast<SymTag>(i);
200 E : std::vector<SymTag> sym_tags(1, sym_tag);
201 E : if (i == SymTagCompiland)
202 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
203 E : else
204 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
205 E : }
206 E : }
207 :
208 E : TEST_F(PatternTest, MultiArgNot) {
209 E : TestDiaBrowser dia_browser;
210 :
211 : // The multi-arg versions accept up to 8 inputs currently. Test
212 : // with a full set of inputs. The sym tags used here are consecutive
213 : // from kSymTagBegin.
214 : ASSERT_TRUE(dia_browser.AddPattern(Not(SymTagExe,
215 : SymTagCompiland,
216 : SymTagCompilandDetails,
217 : SymTagCompilandEnv,
218 : SymTagFunction,
219 : SymTagBlock,
220 : SymTagData,
221 : SymTagAnnotation),
222 E : on_match_));
223 :
224 : // Ensure the pattern doesn't match the first 8 symtags, but matches all
225 : // the rest.
226 E : for (size_t i = kSymTagBegin; i < kSymTagEnd; ++i) {
227 E : SymTag sym_tag = static_cast<SymTag>(i);
228 E : std::vector<SymTag> sym_tags(1, sym_tag);
229 E : if (i <= SymTagAnnotation)
230 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
231 E : else
232 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
233 E : }
234 E : }
235 :
236 E : TEST_F(PatternTest, MultiArgTags) {
237 E : TestDiaBrowser dia_browser;
238 :
239 : // The multi-arg versions accept up to 8 inputs currently. Test
240 : // with a full set of inputs. The sym tags used here are consecutive
241 : // from kSymTagBegin.
242 : ASSERT_TRUE(dia_browser.AddPattern(Tags(SymTagExe,
243 : SymTagCompiland,
244 : SymTagCompilandDetails,
245 : SymTagCompilandEnv,
246 : SymTagFunction,
247 : SymTagBlock,
248 : SymTagData,
249 : SymTagAnnotation),
250 E : on_match_));
251 :
252 : // Ensure the pattern matches the first 8 symtags, but does not match all
253 : // the rest.
254 E : for (size_t i = kSymTagBegin; i < kSymTagEnd; ++i) {
255 E : SymTag sym_tag = static_cast<SymTag>(i);
256 E : std::vector<SymTag> sym_tags(1, sym_tag);
257 E : if (i <= SymTagAnnotation)
258 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
259 E : else
260 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
261 E : }
262 E : }
263 :
264 E : TEST_F(PatternTest, Opt) {
265 E : TestDiaBrowser dia_browser;
266 :
267 : // Set up pattern 'Compiland?.Function'.
268 : ASSERT_TRUE(dia_browser.AddPattern(
269 E : Seq(Opt(SymTagCompiland), SymTagFunction), on_match_));
270 :
271 E : std::vector<SymTag> sym_tags;
272 :
273 : // 'Compiland' should not match.
274 E : sym_tags.push_back(SymTagCompiland);
275 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
276 :
277 : // 'Compiland.Function' should match.
278 E : sym_tags.push_back(SymTagFunction);
279 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
280 :
281 : // 'Function' should match.
282 E : sym_tags.clear();
283 E : sym_tags.push_back(SymTagFunction);
284 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
285 E : }
286 :
287 E : TEST_F(PatternTest, Star) {
288 E : TestDiaBrowser dia_browser;
289 :
290 : // Set up pattern 'Compiland.Block*.Data'.
291 : ASSERT_TRUE(dia_browser.AddPattern(
292 E : Seq(SymTagCompiland, Star(SymTagBlock), SymTagData), on_match_));
293 :
294 E : std::vector<SymTag> sym_tags;
295 :
296 : // 'Compiland' should not match.
297 E : sym_tags.push_back(SymTagCompiland);
298 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
299 :
300 : // 'Compiland.Data' should match.
301 E : sym_tags.push_back(SymTagData);
302 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
303 :
304 : // 'Compiland.Block.Data' should match, with Block repeated
305 : // arbitrarily many times. We can only check a finite number, obviously.
306 E : for (size_t i = 0; i < 10; ++i) {
307 E : sym_tags.pop_back();
308 E : sym_tags.push_back(SymTagBlock);
309 E : sym_tags.push_back(SymTagData);
310 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
311 E : }
312 E : }
313 :
314 E : TEST_F(PatternTest, Plus) {
315 E : TestDiaBrowser dia_browser;
316 :
317 : // Set up pattern 'Compiland.Block+.Data'.
318 : ASSERT_TRUE(dia_browser.AddPattern(
319 E : Seq(SymTagCompiland, Plus(SymTagBlock), SymTagData), on_match_));
320 :
321 E : std::vector<SymTag> sym_tags;
322 :
323 : // 'Compiland' should not match'.
324 E : sym_tags.push_back(SymTagCompiland);
325 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
326 :
327 : // 'Compiland.Data' should not match.
328 E : sym_tags.push_back(SymTagData);
329 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
330 :
331 : // 'Compiland.Block.Data' should match, with Block repeated
332 : // arbitrarily many times. We can only check a finite number, obviously.
333 E : for (size_t i = 0; i < 10; ++i) {
334 E : sym_tags.pop_back();
335 E : sym_tags.push_back(SymTagBlock);
336 E : sym_tags.push_back(SymTagData);
337 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
338 E : }
339 E : }
340 :
341 E : TEST_F(PatternTest, Or) {
342 E : TestDiaBrowser dia_browser;
343 :
344 : // Add an 'or' pattern that uses all 8 arguments.
345 : ASSERT_TRUE(dia_browser.AddPattern(
346 : Or(SymTagCompiland,
347 : Seq(SymTagData, SymTagCompiland, SymTagExe),
348 : Seq(SymTagExe, SymTagCompiland),
349 : Seq(SymTagExe, SymTagData),
350 : Seq(SymTagExe, SymTagExe),
351 : Seq(SymTagLabel, SymTagCompiland),
352 : Seq(SymTagLabel, SymTagLabel, SymTagLabel),
353 : SymTagVTable),
354 E : on_match_));
355 :
356 E : std::vector<SymTag> sym_tags;
357 E : sym_tags.push_back(SymTagCompiland);
358 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
359 :
360 E : sym_tags.clear();
361 E : sym_tags.push_back(SymTagData);
362 E : sym_tags.push_back(SymTagCompiland);
363 E : sym_tags.push_back(SymTagExe);
364 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
365 :
366 E : sym_tags.clear();
367 E : sym_tags.push_back(SymTagExe);
368 E : sym_tags.push_back(SymTagCompiland);
369 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
370 :
371 E : sym_tags.back() = SymTagData;
372 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
373 :
374 E : sym_tags.back() = SymTagExe;
375 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
376 :
377 E : sym_tags.clear();
378 E : sym_tags.push_back(SymTagLabel);
379 E : sym_tags.push_back(SymTagCompiland);
380 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
381 :
382 E : sym_tags.back() = SymTagLabel;
383 E : sym_tags.push_back(SymTagLabel);
384 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
385 :
386 E : sym_tags.clear();
387 E : sym_tags.push_back(SymTagVTable);
388 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
389 E : }
390 :
391 E : TEST_F(DiaBrowserTest, AllCompilandSymbolsExplored) {
392 E : TestDiaBrowser dia_browser;
393 :
394 E : dia_browser.AddPattern(Tag(SymTagCompiland), on_full_match_);
395 :
396 : EXPECT_CALL(*this, OnFullMatch(_, _, _)).Times(154).
397 E : WillRepeatedly(Return(DiaBrowser::kBrowserContinue));
398 E : dia_browser.Browse(global_.get());
399 E : }
400 :
401 E : TEST_F(DiaBrowserTest, AllDataSymbolsExplored) {
402 E : TestDiaBrowser dia_browser;
403 :
404 : // Search for (Wildcard)*.Data
405 : dia_browser.AddPattern(Seq(Star(SymTagNull), SymTagData),
406 E : on_full_match_);
407 :
408 : EXPECT_CALL(*this, OnFullMatch(_, _, _)).Times(2896).
409 E : WillRepeatedly(Return(DiaBrowser::kBrowserContinue));
410 E : dia_browser.Browse(global_.get());
411 E : }
412 :
413 E : TEST_F(DiaBrowserTest, AllDataSymbolsExploredWithPopCallbacks) {
414 E : TestDiaBrowser dia_browser;
415 :
416 : // Search for (Wildcard)*.Data
417 : dia_browser.AddPattern(Seq(Star(SymTagNull), SymTagData),
418 E : on_full_match_, on_full_match_);
419 :
420 : EXPECT_CALL(*this, OnFullMatch(_, _, _)).Times(2 * 2896).
421 E : WillRepeatedly(Return(DiaBrowser::kBrowserContinue));
422 E : dia_browser.Browse(global_.get());
423 E : }
424 :
425 E : TEST_F(DiaBrowserTest, SomePathsTerminated) {
426 E : TestDiaBrowser dia_browser;
427 :
428 : // Search for UDT.Data and Enum.Data. This will find:
429 : // 428 Enum.Data
430 : // 1077 UDT.Data
431 : // However, we terminate partial matches when they get to
432 : // UDT.
433 : dia_browser.AddPattern(Seq(Callback(Or(SymTagEnum, SymTagUDT),
434 : on_partial_match_term_),
435 : SymTagData),
436 E : on_full_match_);
437 :
438 : // With VC++ 2010, OnPartialMatch hits 174 nodes.
439 : static const size_t kNumPartialMatches = 174;
440 :
441 : // Only the 428 Enum.Data full matches should be hit.
442 : static const size_t kNumFullMatches = 428;
443 :
444 : EXPECT_CALL(*this, OnPartialMatch(_, _, _)).Times(kNumPartialMatches).
445 E : WillRepeatedly(Return(DiaBrowser::kBrowserContinue));
446 : EXPECT_CALL(*this, OnFullMatch(_, _, _)).Times(kNumFullMatches).
447 E : WillRepeatedly(Return(DiaBrowser::kBrowserContinue));
448 E : dia_browser.Browse(global_.get());
449 E : }
450 :
451 : } // namespace pe
|