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/pe/dia_util.h"
16 :
17 : #include <diacreate.h>
18 :
19 : #include "base/logging.h"
20 : #include "base/win/scoped_bstr.h"
21 : #include "base/win/scoped_comptr.h"
22 : #include "syzygy/common/com_utils.h"
23 :
24 : namespace pe {
25 :
26 : using base::win::ScopedBstr;
27 : using base::win::ScopedComPtr;
28 :
29 : const wchar_t kDiaDllName[] = L"msdia120.dll";
30 :
31 : const wchar_t kFixupDiaDebugStreamName[] = L"FIXUP";
32 : const wchar_t kOmapToDiaDebugStreamName[] = L"OMAPTO";
33 : const wchar_t kOmapFromDiaDebugStreamName[] = L"OMAPFROM";
34 :
35 E : bool CreateDiaSource(IDiaDataSource** created_source) {
36 E : DCHECK(created_source != NULL);
37 :
38 E : *created_source = NULL;
39 :
40 E : ScopedComPtr<IDiaDataSource> dia_source;
41 E : HRESULT hr1 = dia_source.CreateInstance(CLSID_DiaSource);
42 E : if (SUCCEEDED(hr1)) {
43 E : *created_source = dia_source.Detach();
44 E : return true;
45 : }
46 :
47 : HRESULT hr2 = NoRegCoCreate(kDiaDllName,
48 : CLSID_DiaSource,
49 : IID_IDiaDataSource,
50 E : reinterpret_cast<void**>(&dia_source));
51 E : if (SUCCEEDED(hr2)) {
52 E : *created_source = dia_source.Detach();
53 E : return true;
54 : }
55 :
56 i : LOG(ERROR) << "Failed to create DiaDataSource.";
57 i : LOG(ERROR) << " CreateInstance failed with: " << common::LogHr(hr1);
58 i : LOG(ERROR) << " NoRegCoCreate failed with: " << common::LogHr(hr2);
59 :
60 i : return false;
61 E : }
62 :
63 : bool CreateDiaSession(const base::FilePath& file,
64 : IDiaDataSource* dia_source,
65 E : IDiaSession** dia_session) {
66 E : DCHECK(dia_source != NULL);
67 E : DCHECK(dia_session != NULL);
68 :
69 E : *dia_session = NULL;
70 :
71 E : HRESULT hr = E_FAIL;
72 :
73 E : if (file.Extension() == L".pdb") {
74 E : hr = dia_source->loadDataFromPdb(file.value().c_str());
75 E : } else {
76 E : hr = dia_source->loadDataForExe(file.value().c_str(), NULL, NULL);
77 : }
78 :
79 E : if (FAILED(hr)) {
80 i : LOG(ERROR) << "Failed to load DIA data for \"" << file.value() << "\": "
81 : << common::LogHr(hr) << ".";
82 i : return false;
83 : }
84 :
85 E : ScopedComPtr<IDiaSession> session;
86 E : hr = dia_source->openSession(session.Receive());
87 E : if (FAILED(hr)) {
88 i : LOG(ERROR) << "Failed to open DIA session for \"" << file.value() << "\" : "
89 : << common::LogHr(hr) << ".";
90 i : return false;
91 : }
92 :
93 E : *dia_session = session.Detach();
94 :
95 E : return true;
96 E : }
97 :
98 : SearchResult FindDiaTable(const IID& iid,
99 : IDiaSession* dia_session,
100 E : void** out_table) {
101 E : DCHECK(dia_session != NULL);
102 E : DCHECK(out_table != NULL);
103 :
104 E : *out_table = NULL;
105 :
106 : // Get the table enumerator.
107 E : base::win::ScopedComPtr<IDiaEnumTables> enum_tables;
108 E : HRESULT hr = dia_session->getEnumTables(enum_tables.Receive());
109 E : if (FAILED(hr)) {
110 i : LOG(ERROR) << "Failed to get DIA table enumerator: "
111 : << common::LogHr(hr) << ".";
112 i : return kSearchErrored;
113 : }
114 :
115 : // Iterate through the tables.
116 E : while (true) {
117 E : base::win::ScopedComPtr<IDiaTable> table;
118 E : ULONG fetched = 0;
119 E : hr = enum_tables->Next(1, table.Receive(), &fetched);
120 E : if (FAILED(hr)) {
121 i : LOG(ERROR) << "Failed to get DIA table: "
122 : << common::LogHr(hr) << ".";
123 i : return kSearchErrored;
124 : }
125 E : if (fetched == 0)
126 i : break;
127 :
128 E : hr = table.QueryInterface(iid, out_table);
129 E : if (SUCCEEDED(hr))
130 E : return kSearchSucceeded;
131 E : }
132 :
133 : // The search completed, even though we didn't find what we were looking for.
134 i : return kSearchFailed;
135 E : }
136 :
137 : SearchResult FindDiaDebugStream(const wchar_t* name,
138 : IDiaSession* dia_session,
139 E : IDiaEnumDebugStreamData** dia_debug_stream) {
140 E : DCHECK(name != NULL);
141 E : DCHECK(dia_session != NULL);
142 E : DCHECK(dia_debug_stream != NULL);
143 :
144 E : *dia_debug_stream = NULL;
145 :
146 E : HRESULT hr = E_FAIL;
147 E : ScopedComPtr<IDiaEnumDebugStreams> debug_streams;
148 E : if (FAILED(hr = dia_session->getEnumDebugStreams(debug_streams.Receive()))) {
149 i : LOG(ERROR) << "Unable to get debug streams: " << common::LogHr(hr) << ".";
150 i : return kSearchErrored;
151 : }
152 :
153 : // Iterate through the debug streams.
154 E : while (true) {
155 E : ScopedComPtr<IDiaEnumDebugStreamData> debug_stream;
156 E : ULONG count = 0;
157 E : HRESULT hr = debug_streams->Next(1, debug_stream.Receive(), &count);
158 E : if (FAILED(hr) || (hr != S_FALSE && count != 1)) {
159 i : LOG(ERROR) << "Unable to load debug stream: "
160 : << common::LogHr(hr) << ".";
161 i : return kSearchErrored;
162 E : } else if (hr == S_FALSE) {
163 : // No more records.
164 E : break;
165 : }
166 :
167 E : ScopedBstr stream_name;
168 E : if (FAILED(hr = debug_stream->get_name(stream_name.Receive()))) {
169 i : LOG(ERROR) << "Unable to get debug stream name: "
170 : << common::LogHr(hr) << ".";
171 i : return kSearchErrored;
172 : }
173 :
174 : // Found the stream?
175 E : if (wcscmp(common::ToString(stream_name), name) == 0) {
176 E : *dia_debug_stream = debug_stream.Detach();
177 E : return kSearchSucceeded;
178 : }
179 E : }
180 :
181 E : return kSearchFailed;
182 E : }
183 :
184 : bool GetSymIndexId(IDiaSymbol* symbol, uint32_t* sym_index_id) {
185 : DCHECK(symbol);
186 : DCHECK(sym_index_id);
187 :
188 : DWORD index_id = 0;
189 : HRESULT hr = symbol->get_symIndexId(&index_id);
190 : if (hr != S_OK) {
191 : LOG(ERROR) << "Error getting symbol's index id. " << common::LogHr(hr);
192 : return false;
193 : }
194 :
195 : *sym_index_id = static_cast<uint32_t>(index_id);
196 : return true;
197 : }
198 :
199 E : bool GetSymTag(IDiaSymbol* symbol, enum SymTagEnum* sym_tag) {
200 E : DCHECK(symbol != NULL);
201 E : DCHECK(sym_tag != NULL);
202 E : DWORD tmp_tag = SymTagNull;
203 E : *sym_tag = SymTagNull;
204 E : HRESULT hr = symbol->get_symTag(&tmp_tag);
205 E : if (hr != S_OK) {
206 i : LOG(ERROR) << "Error getting sym tag: " << common::LogHr(hr) << ".";
207 i : return false;
208 : }
209 E : *sym_tag = static_cast<enum SymTagEnum>(tmp_tag);
210 E : return true;
211 E : }
212 :
213 E : bool IsSymTag(IDiaSymbol* symbol, enum SymTagEnum expected_sym_tag) {
214 E : DCHECK(symbol != NULL);
215 E : DCHECK(expected_sym_tag != SymTagNull);
216 :
217 E : enum SymTagEnum sym_tag = SymTagNull;
218 E : if (!GetSymTag(symbol, &sym_tag))
219 i : return false;
220 :
221 E : return sym_tag == expected_sym_tag;
222 E : }
223 :
224 E : bool GetSymName(IDiaSymbol* symbol, base::string16* name) {
225 E : DCHECK(symbol); DCHECK(name);
226 :
227 E : base::win::ScopedBstr tmp;
228 E : HRESULT hr = symbol->get_name(tmp.Receive());
229 E : if (hr != S_OK) {
230 i : LOG(ERROR) << "Error getting symbol's name: " << common::LogHr(hr) << ".";
231 i : return false;
232 : }
233 :
234 E : *name = common::ToString(tmp);
235 E : return true;
236 E : }
237 :
238 E : bool GetSymUndecoratedName(IDiaSymbol* symbol, base::string16* name) {
239 E : DCHECK(symbol); DCHECK(name);
240 :
241 E : base::win::ScopedBstr tmp;
242 E : HRESULT hr = symbol->get_undecoratedName(tmp.Receive());
243 E : if (hr != S_OK) {
244 i : LOG(ERROR) << "Error getting symbol's undecorated name: "
245 : << common::LogHr(hr) << ".";
246 i : return false;
247 : }
248 :
249 E : *name = common::ToString(tmp);
250 E : return true;
251 E : }
252 :
253 E : bool GetDataKind(IDiaSymbol* symbol, enum DataKind* data_kind) {
254 E : DCHECK(symbol); DCHECK(data_kind);
255 :
256 E : DWORD tmp = 0;
257 E : HRESULT hr = symbol->get_dataKind(&tmp);
258 E : if (hr != S_OK) {
259 i : LOG(ERROR) << "Error getting symbol's data kind: " << common::LogHr(hr)
260 : << ".";
261 i : return false;
262 : }
263 :
264 E : *data_kind = static_cast<DataKind>(tmp);
265 E : return true;
266 E : }
267 :
268 E : bool GetLocationType(IDiaSymbol* symbol, enum LocationType* location_type) {
269 E : DCHECK(symbol); DCHECK(location_type);
270 :
271 E : DWORD tmp = 0;
272 E : HRESULT hr = symbol->get_locationType(&tmp);
273 E : if (hr != S_OK) {
274 i : LOG(ERROR) << "Error getting symbol's location type: " << common::LogHr(hr)
275 : << ".";
276 i : return false;
277 : }
278 :
279 E : *location_type = static_cast<LocationType>(tmp);
280 E : return true;
281 E : }
282 :
283 : bool GetRegisterId(IDiaSymbol* symbol, uint32_t* register_id) {
284 : DCHECK(symbol); DCHECK(register_id);
285 :
286 : DWORD tmp = 0;
287 : HRESULT hr = symbol->get_registerId(&tmp);
288 : if (hr != S_OK) {
289 : LOG(ERROR) << "Error getting symbol's register id: " << common::LogHr(hr)
290 : << ".";
291 : return false;
292 : }
293 :
294 : *register_id = static_cast<uint32_t>(tmp);
295 : return true;
296 : }
297 :
298 E : bool GetSymOffset(IDiaSymbol* symbol, ptrdiff_t* offset) {
299 E : DCHECK(symbol); DCHECK(offset);
300 :
301 E : LONG tmp = 0;
302 E : HRESULT hr = symbol->get_offset(&tmp);
303 E : if (hr != S_OK) {
304 i : LOG(ERROR) << "Error getting symbol's offset: " << common::LogHr(hr) << ".";
305 i : return false;
306 : }
307 :
308 E : *offset = static_cast<ptrdiff_t>(tmp);
309 E : return true;
310 E : }
311 :
312 E : bool GetSymType(IDiaSymbol* symbol, base::win::ScopedComPtr<IDiaSymbol>* type) {
313 E : DCHECK(symbol); DCHECK(type);
314 :
315 E : base::win::ScopedComPtr<IDiaSymbol> tmp;
316 E : HRESULT hr = symbol->get_type(tmp.Receive());
317 E : if (hr != S_OK) {
318 i : LOG(ERROR) << "Error getting symbol's type: " << common::LogHr(hr) << ".";
319 i : return false;
320 : }
321 :
322 E : *type = tmp;
323 E : return true;
324 E : }
325 :
326 E : bool GetSymQualifiers(IDiaSymbol* symbol, bool* is_const, bool* is_volatile) {
327 E : DCHECK(symbol); DCHECK(is_const); DCHECK(is_volatile);
328 :
329 E : BOOL is_const_tmp = FALSE;
330 E : HRESULT hr = symbol->get_constType(&is_const_tmp);
331 E : if (hr != S_OK)
332 i : return false;
333 :
334 E : BOOL is_volatile_tmp = FALSE;
335 E : hr = symbol->get_volatileType(&is_volatile_tmp);
336 E : if (hr != S_OK)
337 i : return false;
338 :
339 E : *is_const = (is_const_tmp == TRUE);
340 E : *is_volatile = (is_volatile_tmp == TRUE);
341 E : return true;
342 E : }
343 :
344 E : bool GetSymCount(IDiaSymbol* symbol, size_t* count) {
345 E : DCHECK(symbol); DCHECK(count);
346 :
347 E : DWORD tmp = 0;
348 E : HRESULT hr = symbol->get_count(&tmp);
349 E : if (hr != S_OK)
350 i : return false;
351 :
352 E : *count = static_cast<size_t>(tmp);
353 E : return true;
354 E : }
355 :
356 : bool GetSymClassParent(IDiaSymbol* symbol,
357 E : base::win::ScopedComPtr<IDiaSymbol>* parent) {
358 E : DCHECK(symbol); DCHECK(parent);
359 E : *parent = nullptr;
360 :
361 E : base::win::ScopedComPtr<IDiaSymbol> tmp;
362 E : HRESULT hr = symbol->get_classParent(tmp.Receive());
363 E : if (hr == S_FALSE) {
364 : // This happens routinely for functions that aren't members.
365 E : return true;
366 : }
367 E : if (hr != S_OK) {
368 i : LOG(ERROR) << "Error getting symbol's class parent: " << common::LogHr(hr)
369 : << ".";
370 i : return false;
371 : }
372 :
373 E : *parent = tmp;
374 E : return true;
375 E : }
376 :
377 : bool GetSymLexicalParent(IDiaSymbol* symbol,
378 : base::win::ScopedComPtr<IDiaSymbol>* parent) {
379 : DCHECK(symbol); DCHECK(parent);
380 :
381 : base::win::ScopedComPtr<IDiaSymbol> tmp;
382 : HRESULT hr = symbol->get_lexicalParent(tmp.Receive());
383 : if (hr != S_OK) {
384 : LOG(ERROR) << "Error getting symbol's lexical parent: " << common::LogHr(hr)
385 : << ".";
386 : return false;
387 : }
388 :
389 : *parent = tmp;
390 : return true;
391 : }
392 :
393 : bool GetFrameBase(IDiaStackFrame* frame, uint64_t* frame_base) {
394 : DCHECK(frame != NULL);
395 : DCHECK(frame_base != NULL);
396 :
397 : ULONGLONG base = 0ULL;
398 : HRESULT hr = frame->get_base(&base);
399 : if (hr != S_OK) {
400 : LOG(ERROR) << "Failed to get stack frame's base address: "
401 : << common::LogHr(hr) << ".";
402 : return false;
403 : }
404 :
405 : *frame_base = static_cast<uint64_t>(base);
406 : return true;
407 : }
408 :
409 : bool GetRegisterValue(IDiaStackFrame* frame,
410 : CV_HREG_e register_index,
411 : uint64_t* register_value) {
412 : DCHECK(frame != NULL);
413 : DCHECK(register_value != NULL);
414 :
415 : ULONGLONG value = 0ULL;
416 : HRESULT hr = frame->get_registerValue(register_index, &value);
417 : if (hr != S_OK) {
418 : // TODO(manzagop): print human readable register names.
419 : LOG(ERROR) << "Failed to get register value for index: "
420 : << register_index << ". Error: " << common::LogHr(hr) << ".";
421 : return false;
422 : }
423 :
424 : *register_value = static_cast<uint64_t>(value);
425 : return true;
426 : }
427 :
428 : bool GetSize(IDiaStackFrame* frame, uint32_t* frame_size) {
429 : DCHECK(frame != NULL);
430 : DCHECK(frame_size != NULL);
431 :
432 : DWORD size = 0U;
433 : HRESULT hr = frame->get_size(&size);
434 : if (hr != S_OK) {
435 : LOG(ERROR) << "Failed to get frame size: " << common::LogHr(hr) << ".";
436 : return false;
437 : }
438 :
439 : *frame_size = static_cast<uint32_t>(size);
440 : return true;
441 : }
442 :
443 : bool GetLocalsBase(IDiaStackFrame* frame, uint64_t* locals_base) {
444 : DCHECK(frame != NULL);
445 : DCHECK(locals_base != NULL);
446 :
447 : ULONGLONG base = 0ULL;
448 : HRESULT hr = frame->get_localsBase(&base);
449 : if (hr != S_OK) {
450 : LOG(ERROR) << "Failed to get base address of locals: " << common::LogHr(hr)
451 : << ".";
452 : return false;
453 : }
454 :
455 : *locals_base = static_cast<uint64_t>(base);
456 : return true;
457 : }
458 :
459 : ChildVisitor::ChildVisitor(IDiaSymbol* parent, enum SymTagEnum type)
460 E : : parent_(parent), type_(type), child_callback_(NULL) {
461 E : DCHECK(parent != NULL);
462 E : }
463 :
464 E : bool ChildVisitor::VisitChildren(const VisitSymbolCallback& child_callback) {
465 E : DCHECK(child_callback_ == NULL);
466 :
467 E : child_callback_ = &child_callback;
468 E : bool ret = VisitChildrenImpl();
469 E : child_callback_ = NULL;
470 :
471 E : return ret;
472 E : }
473 :
474 E : bool ChildVisitor::VisitChildrenImpl() {
475 E : DCHECK(child_callback_ != NULL);
476 :
477 : // Retrieve an enumerator for all children in this PDB.
478 E : base::win::ScopedComPtr<IDiaEnumSymbols> children;
479 : HRESULT hr = parent_->findChildren(type_,
480 : NULL,
481 : nsNone,
482 E : children.Receive());
483 E : if (FAILED(hr)) {
484 i : LOG(ERROR) << "Unable to get children: " << common::LogHr(hr);
485 i : return false;
486 : }
487 :
488 E : if (hr == S_FALSE)
489 i : return true; // No child.
490 :
491 E : return EnumerateChildren(children.get());
492 E : }
493 :
494 E : bool ChildVisitor::EnumerateChildren(IDiaEnumSymbols* children) {
495 E : DCHECK(children!= NULL);
496 :
497 E : while (true) {
498 E : base::win::ScopedComPtr<IDiaSymbol> child;
499 E : ULONG fetched = 0;
500 E : HRESULT hr = children->Next(1, child.Receive(), &fetched);
501 E : if (FAILED(hr)) {
502 i : DCHECK_EQ(0U, fetched);
503 i : DCHECK(child == NULL);
504 i : LOG(ERROR) << "Unable to iterate children: " << common::LogHr(hr);
505 i : return false;
506 : }
507 E : if (hr == S_FALSE)
508 E : break;
509 :
510 E : DCHECK_EQ(1U, fetched);
511 E : DCHECK(child != NULL);
512 :
513 E : if (!VisitChild(child.get()))
514 E : return false;
515 E : }
516 :
517 E : return true;
518 E : }
519 :
520 E : bool ChildVisitor::VisitChild(IDiaSymbol* child) {
521 E : DCHECK(child_callback_ != NULL);
522 :
523 E : return child_callback_->Run(child);
524 E : }
525 :
526 E : CompilandVisitor::CompilandVisitor(IDiaSession* session) : session_(session) {
527 E : DCHECK(session != NULL);
528 E : }
529 :
530 : bool CompilandVisitor::VisitAllCompilands(
531 E : const VisitCompilandCallback& compiland_callback) {
532 E : base::win::ScopedComPtr<IDiaSymbol> global;
533 E : HRESULT hr = session_->get_globalScope(global.Receive());
534 E : if (FAILED(hr)) {
535 i : LOG(ERROR) << "Unable to get global scope: " << common::LogHr(hr);
536 i : return false;
537 : }
538 :
539 E : ChildVisitor visitor(global.get(), SymTagCompiland);
540 :
541 E : return visitor.VisitChildren(compiland_callback);
542 E : }
543 :
544 : LineVisitor::LineVisitor(IDiaSession* session, IDiaSymbol* compiland)
545 E : : session_(session), compiland_(compiland), line_callback_(NULL) {
546 E : DCHECK(session != NULL);
547 E : }
548 :
549 E : bool LineVisitor::VisitLines(const VisitLineCallback& line_callback) {
550 E : DCHECK(line_callback_ == NULL);
551 :
552 E : line_callback_ = &line_callback;
553 E : bool ret = VisitLinesImpl();
554 E : line_callback_ = NULL;
555 :
556 E : return ret;
557 E : }
558 :
559 : bool LineVisitor::EnumerateCompilandSource(IDiaSymbol* compiland,
560 E : IDiaSourceFile* source_file) {
561 E : DCHECK(compiland != NULL);
562 E : DCHECK(source_file != NULL);
563 :
564 E : base::win::ScopedComPtr<IDiaEnumLineNumbers> line_numbers;
565 : HRESULT hr = session_->findLines(compiland,
566 : source_file,
567 E : line_numbers.Receive());
568 E : if (FAILED(hr)) {
569 : // This seems to happen for the occasional header file.
570 i : return true;
571 : }
572 :
573 E : while (true) {
574 E : base::win::ScopedComPtr<IDiaLineNumber> line_number;
575 E : ULONG fetched = 0;
576 E : hr = line_numbers->Next(1, line_number.Receive(), &fetched);
577 E : if (FAILED(hr)) {
578 i : DCHECK_EQ(0U, fetched);
579 i : DCHECK(line_number == NULL);
580 i : LOG(ERROR) << "Unable to iterate line numbers: " << common::LogHr(hr);
581 i : return false;
582 : }
583 E : if (hr == S_FALSE)
584 E : break;
585 :
586 E : DCHECK_EQ(1U, fetched);
587 E : DCHECK(line_number != NULL);
588 :
589 E : if (!VisitSourceLine(line_number.get()))
590 i : return false;
591 E : }
592 :
593 E : return true;
594 E : }
595 :
596 : bool LineVisitor::EnumerateCompilandSources(IDiaSymbol* compiland,
597 E : IDiaEnumSourceFiles* source_files) {
598 E : DCHECK(compiland != NULL);
599 E : DCHECK(source_files != NULL);
600 :
601 E : while (true) {
602 E : base::win::ScopedComPtr<IDiaSourceFile> source_file;
603 E : ULONG fetched = 0;
604 E : HRESULT hr = source_files->Next(1, source_file.Receive(), &fetched);
605 E : if (FAILED(hr)) {
606 i : DCHECK_EQ(0U, fetched);
607 i : DCHECK(source_file == NULL);
608 i : LOG(ERROR) << "Unable to iterate source files: " << common::LogHr(hr);
609 i : return false;
610 : }
611 E : if (hr == S_FALSE)
612 E : break;
613 :
614 E : DCHECK_EQ(1U, fetched);
615 E : DCHECK(compiland != NULL);
616 :
617 E : if (!EnumerateCompilandSource(compiland, source_file.get()))
618 i : return false;
619 E : }
620 :
621 E : return true;
622 E : }
623 :
624 E : bool LineVisitor::VisitLinesImpl() {
625 E : DCHECK(session_ != NULL);
626 E : DCHECK(compiland_ != NULL);
627 E : DCHECK(line_callback_ != NULL);
628 :
629 : // Enumerate all source files referenced by this compiland.
630 E : base::win::ScopedComPtr<IDiaEnumSourceFiles> source_files;
631 : HRESULT hr = session_->findFile(compiland_.get(), NULL, nsNone,
632 E : source_files.Receive());
633 E : if (FAILED(hr)) {
634 i : LOG(ERROR) << "Unable to get source files: " << common::LogHr(hr);
635 i : return false;
636 : }
637 :
638 E : return EnumerateCompilandSources(compiland_.get(), source_files.get());
639 E : }
640 :
641 E : bool LineVisitor::VisitSourceLine(IDiaLineNumber* line_number) {
642 E : DCHECK(line_callback_ != NULL);
643 :
644 E : return line_callback_->Run(line_number);
645 E : }
646 :
647 : } // namespace pe
|