1 : // Copyright 2011 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/pe/dia_util.h"
16 :
17 : #include <map>
18 : #include <set>
19 : #include <vector>
20 :
21 : #include "base/bind.h"
22 : #include "base/files/file_path.h"
23 : #include "base/win/scoped_bstr.h"
24 : #include "base/win/scoped_comptr.h"
25 : #include "gmock/gmock.h"
26 : #include "syzygy/core/file_util.h"
27 : #include "syzygy/core/unittest_util.h"
28 : #include "syzygy/pdb/pdb_data.h"
29 : #include "syzygy/pe/unittest_util.h"
30 :
31 : namespace pe {
32 :
33 : namespace {
34 :
35 : using base::win::ScopedBstr;
36 : using base::win::ScopedComPtr;
37 :
38 : static const wchar_t kNonsenseStreamName[] =
39 : L"ThisStreamNameCertainlyDoesNotExist";
40 :
41 : static const wchar_t kTestDllObjPath[] = L"obj\\test_dll\\test_dll.obj";
42 :
43 : struct FilePathLess {
44 E : bool operator()(const std::wstring& lhs, const std::wstring& rhs) {
45 E : return base::FilePath::CompareLessIgnoreCase(lhs, rhs);
46 E : }
47 : };
48 :
49 : class DiaUtilTest : public testing::PELibUnitTest {
50 : };
51 :
52 E : MATCHER_P(IsSameFile, value, "") {
53 E : base::FilePath path1(arg);
54 E : base::FilePath path2(value);
55 E : core::FilePathCompareResult result = core::CompareFilePaths(path1, path2);
56 E : return result == core::kEquivalentFilePaths;
57 E : }
58 :
59 : } // namespace
60 :
61 E : TEST_F(DiaUtilTest, CreateDiaSource) {
62 E : ScopedComPtr<IDiaDataSource> dia_source;
63 E : EXPECT_TRUE(CreateDiaSource(dia_source.Receive()));
64 E : }
65 :
66 E : TEST_F(DiaUtilTest, CreateDiaSesssionDll) {
67 E : ScopedComPtr<IDiaDataSource> dia_source;
68 E : ASSERT_TRUE(CreateDiaSource(dia_source.Receive()));
69 :
70 E : ScopedComPtr<IDiaSession> dia_session;
71 : EXPECT_TRUE(CreateDiaSession(
72 : testing::GetExeRelativePath(testing::kTestDllName),
73 : dia_source.get(),
74 E : dia_session.Receive()));
75 E : }
76 :
77 E : TEST_F(DiaUtilTest, CreateDiaSesssionPdb) {
78 E : ScopedComPtr<IDiaDataSource> dia_source;
79 E : ASSERT_TRUE(CreateDiaSource(dia_source.Receive()));
80 :
81 E : ScopedComPtr<IDiaSession> dia_session;
82 : EXPECT_TRUE(CreateDiaSession(
83 : testing::GetExeRelativePath(testing::kTestDllPdbName),
84 : dia_source.get(),
85 E : dia_session.Receive()));
86 E : }
87 :
88 E : TEST_F(DiaUtilTest, FindDiaTableByIid) {
89 E : ScopedComPtr<IDiaDataSource> dia_source;
90 E : ASSERT_TRUE(CreateDiaSource(dia_source.Receive()));
91 :
92 E : ScopedComPtr<IDiaSession> dia_session;
93 : ASSERT_TRUE(CreateDiaSession(
94 : testing::GetExeRelativePath(testing::kTestDllPdbName),
95 : dia_source.get(),
96 E : dia_session.Receive()));
97 :
98 E : ScopedComPtr<IDiaEnumSectionContribs> section_contribs;
99 : EXPECT_EQ(kSearchSucceeded,
100 : FindDiaTable(section_contribs.iid(),
101 : dia_session.get(),
102 E : reinterpret_cast<void**>(section_contribs.Receive())));
103 E : }
104 :
105 E : TEST_F(DiaUtilTest, FindDiaTableByType) {
106 E : ScopedComPtr<IDiaDataSource> dia_source;
107 E : ASSERT_TRUE(CreateDiaSource(dia_source.Receive()));
108 :
109 E : ScopedComPtr<IDiaSession> dia_session;
110 : ASSERT_TRUE(CreateDiaSession(
111 : testing::GetExeRelativePath(testing::kTestDllPdbName),
112 : dia_source.get(),
113 E : dia_session.Receive()));
114 :
115 E : ScopedComPtr<IDiaEnumSectionContribs> section_contribs;
116 : EXPECT_EQ(kSearchSucceeded,
117 E : FindDiaTable(dia_session.get(), section_contribs.Receive()));
118 E : }
119 :
120 E : TEST_F(DiaUtilTest, FindDiaDebugStream) {
121 E : ScopedComPtr<IDiaDataSource> dia_source;
122 E : ASSERT_TRUE(CreateDiaSource(dia_source.Receive()));
123 :
124 E : ScopedComPtr<IDiaSession> dia_session;
125 : ASSERT_TRUE(CreateDiaSession(
126 : testing::GetExeRelativePath(testing::kTestDllPdbName),
127 : dia_source.get(),
128 E : dia_session.Receive()));
129 :
130 E : ScopedComPtr<IDiaEnumDebugStreamData> debug_stream;
131 :
132 : EXPECT_EQ(kSearchFailed,
133 : FindDiaDebugStream(kNonsenseStreamName,
134 : dia_session.get(),
135 E : debug_stream.Receive()));
136 :
137 : EXPECT_EQ(kSearchSucceeded,
138 : FindDiaDebugStream(kFixupDiaDebugStreamName,
139 : dia_session.get(),
140 E : debug_stream.Receive()));
141 E : }
142 :
143 E : TEST_F(DiaUtilTest, LoadDiaDebugStream) {
144 E : ScopedComPtr<IDiaDataSource> dia_source;
145 E : ASSERT_TRUE(CreateDiaSource(dia_source.Receive()));
146 :
147 E : ScopedComPtr<IDiaSession> dia_session;
148 : ASSERT_TRUE(CreateDiaSession(
149 : testing::GetExeRelativePath(testing::kTestDllPdbName),
150 : dia_source.get(),
151 E : dia_session.Receive()));
152 :
153 E : ScopedComPtr<IDiaEnumDebugStreamData> debug_stream;
154 : ASSERT_EQ(kSearchSucceeded,
155 : FindDiaDebugStream(kFixupDiaDebugStreamName,
156 : dia_session.get(),
157 E : debug_stream.Receive()));
158 :
159 E : std::vector<pdb::PdbFixup> fixups;
160 E : EXPECT_TRUE(LoadDiaDebugStream(debug_stream.get(), &fixups));
161 E : EXPECT_FALSE(fixups.empty());
162 E : }
163 :
164 E : TEST_F(DiaUtilTest, FindAndLoadDiaDebugStreamByName) {
165 E : ScopedComPtr<IDiaDataSource> dia_source;
166 E : ASSERT_TRUE(CreateDiaSource(dia_source.Receive()));
167 :
168 E : ScopedComPtr<IDiaSession> dia_session;
169 : ASSERT_TRUE(CreateDiaSession(
170 : testing::GetExeRelativePath(testing::kTestDllPdbName),
171 : dia_source.get(),
172 E : dia_session.Receive()));
173 :
174 E : std::vector<pdb::PdbFixup> fixups;
175 :
176 : EXPECT_EQ(kSearchFailed,
177 : FindAndLoadDiaDebugStreamByName(kNonsenseStreamName,
178 : dia_session.get(),
179 E : &fixups));
180 E : EXPECT_TRUE(fixups.empty());
181 :
182 : EXPECT_EQ(kSearchSucceeded,
183 : FindAndLoadDiaDebugStreamByName(kFixupDiaDebugStreamName,
184 : dia_session.get(),
185 E : &fixups));
186 E : EXPECT_FALSE(fixups.empty());
187 E : }
188 :
189 : class DiaUtilVisitorTest : public DiaUtilTest {
190 : public:
191 E : virtual void SetUp() OVERRIDE {
192 E : ASSERT_TRUE(CreateDiaSource(dia_source_.Receive()));
193 E : ASSERT_TRUE(CreateDiaSession(
194 : testing::GetExeRelativePath(testing::kTestDllName),
195 : dia_source_.get(),
196 : dia_session_.Receive()));
197 E : ASSERT_EQ(S_OK, dia_session_->get_globalScope(dia_globals_.Receive()));
198 E : }
199 :
200 : typedef std::vector<std::wstring> StringVector;
201 E : bool OnFunction(StringVector* names, IDiaSymbol* function) {
202 E : EXPECT_TRUE(IsSymTag(function, SymTagFunction));
203 E : ScopedBstr name;
204 E : EXPECT_EQ(S_OK, function->get_name(name.Receive()));
205 E : names->push_back(com::ToString(name));
206 E : return true;
207 E : }
208 :
209 E : bool OnCompiland(StringVector* names, IDiaSymbol* compiland) {
210 E : EXPECT_TRUE(IsSymTag(compiland, SymTagCompiland));
211 E : ScopedBstr name;
212 E : EXPECT_EQ(S_OK, compiland->get_name(name.Receive()));
213 E : names->push_back(com::ToString(name));
214 E : return true;
215 E : }
216 :
217 : bool OnCompilandFind(const std::wstring& compiland_path,
218 : ScopedComPtr<IDiaSymbol>* compiland_out,
219 E : IDiaSymbol* compiland) {
220 E : EXPECT_TRUE(IsSymTag(compiland, SymTagCompiland));
221 E : ScopedBstr name;
222 E : EXPECT_EQ(S_OK, compiland->get_name(name.Receive()));
223 E : if (testing::Value(com::ToString(name), IsSameFile(compiland_path))) {
224 E : *compiland_out = compiland;
225 E : return false;
226 : }
227 E : return true;
228 E : }
229 :
230 : typedef std::set<std::pair<DWORD, DWORD>> LineSet;
231 : typedef std::map<std::wstring, LineSet, FilePathLess> LineMap;
232 E : bool OnLine(LineMap* line_map, IDiaLineNumber* line) {
233 E : ScopedComPtr<IDiaSourceFile> source_file;
234 E : EXPECT_HRESULT_SUCCEEDED(line->get_sourceFile(source_file.Receive()));
235 :
236 E : ScopedBstr source_name;
237 E : EXPECT_HRESULT_SUCCEEDED(source_file->get_fileName(source_name.Receive()));
238 :
239 E : ScopedComPtr<IDiaSymbol> compiland;
240 E : EXPECT_HRESULT_SUCCEEDED(line->get_compiland(compiland.Receive()));
241 :
242 E : DWORD line_number = 0;
243 E : EXPECT_HRESULT_SUCCEEDED(line->get_lineNumber(&line_number));
244 E : DWORD line_number_end = 0;
245 E : EXPECT_HRESULT_SUCCEEDED(line->get_lineNumberEnd(&line_number_end));
246 :
247 : // This doesn't necessarily have to hold, but so far it seems to do so.
248 E : EXPECT_EQ(line_number, line_number_end);
249 :
250 : (*line_map)[com::ToString(source_name)].insert(
251 E : std::make_pair(line_number, line_number_end));
252 :
253 E : return true;
254 E : }
255 :
256 : ScopedComPtr<IDiaDataSource> dia_source_;
257 : ScopedComPtr<IDiaSession> dia_session_;
258 : ScopedComPtr<IDiaSymbol> dia_globals_;
259 : };
260 :
261 E : TEST_F(DiaUtilVisitorTest, ChildVisitorTest) {
262 E : ChildVisitor visitor(dia_globals_, SymTagFunction);
263 :
264 E : StringVector function_names;
265 : ASSERT_TRUE(visitor.VisitChildren(
266 : base::Bind(&DiaUtilVisitorTest::OnFunction,
267 : base::Unretained(this),
268 E : &function_names)));
269 :
270 : // Expect that we found a bunch of functions.
271 E : ASSERT_LT(1U, function_names.size());
272 : // One of them should be "DllMain".
273 E : ASSERT_THAT(function_names, testing::Contains(L"DllMain"));
274 E : }
275 :
276 E : TEST_F(DiaUtilVisitorTest, CompilandVisitorTest) {
277 E : CompilandVisitor visitor(dia_session_);
278 :
279 E : StringVector compiland_names;
280 : ASSERT_TRUE(visitor.VisitAllCompilands(
281 : base::Bind(&DiaUtilVisitorTest::OnCompiland,
282 : base::Unretained(this),
283 E : &compiland_names)));
284 :
285 : // We expect to have seen some compiland_names.
286 E : ASSERT_LT(0U, compiland_names.size());
287 :
288 : // One of the compiland_names should be the test_dll.obj file.
289 E : base::FilePath test_dll_obj = testing::GetOutputRelativePath(kTestDllObjPath);
290 : ASSERT_THAT(compiland_names,
291 E : testing::Contains(IsSameFile(test_dll_obj.value())));
292 E : }
293 :
294 E : TEST_F(DiaUtilVisitorTest, LineVisitorTest) {
295 E : CompilandVisitor compiland_visitor(dia_session_);
296 :
297 : // Start by finding the test dll compiland.
298 E : ScopedComPtr<IDiaSymbol> compiland;
299 E : base::FilePath test_dll_obj = testing::GetOutputRelativePath(kTestDllObjPath);
300 : ASSERT_FALSE(compiland_visitor.VisitAllCompilands(
301 : base::Bind(&DiaUtilVisitorTest::OnCompilandFind,
302 : base::Unretained(this),
303 : test_dll_obj.value(),
304 E : &compiland)));
305 :
306 E : ASSERT_TRUE(compiland != NULL);
307 :
308 : // Now enumerate all line entries in that compiland.
309 E : LineVisitor line_visitor(dia_session_, compiland);
310 :
311 E : LineMap line_map;
312 : ASSERT_TRUE(line_visitor.VisitLines(
313 : base::Bind(&DiaUtilVisitorTest::OnLine,
314 : base::Unretained(this),
315 E : &line_map)));
316 :
317 : // We expect to have at least one file.
318 E : ASSERT_LE(1U, line_map.size());
319 :
320 : base::FilePath test_dll_cc =
321 E : testing::GetSrcRelativePath(L"syzygy\\pe\\test_dll.cc");
322 E : ASSERT_TRUE(line_map.find(test_dll_cc.value()) != line_map.end());
323 :
324 E : ASSERT_LT(1U, line_map[test_dll_cc.value()].size());
325 E : }
326 :
327 : } // namespace pe
|