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 : // Implements utility functions useful to grinders that process basic-block
16 : // frequency data.
17 :
18 : #include "syzygy/grinder/basic_block_util.h"
19 :
20 : #include <algorithm>
21 : #include <functional>
22 :
23 : #include "syzygy/common/indexed_frequency_data.h"
24 : #include "syzygy/pdb/pdb_reader.h"
25 : #include "syzygy/pdb/pdb_util.h"
26 : #include "syzygy/pe/find.h"
27 :
28 : namespace grinder {
29 : namespace basic_block_util {
30 :
31 : bool ModuleIdentityComparator::operator()(
32 E : const ModuleInformation& lhs, const ModuleInformation& rhs) {
33 E : if (lhs.module_size < rhs.module_size)
34 E : return true;
35 E : if (lhs.module_size > rhs.module_size)
36 E : return false;
37 :
38 E : if (lhs.time_date_stamp < rhs.time_date_stamp)
39 E : return true;
40 E : if (lhs.time_date_stamp > rhs.time_date_stamp)
41 E : return false;
42 :
43 E : return lhs.image_file_name < rhs.image_file_name;
44 E : }
45 :
46 : void InitModuleInfo(const pe::PEFile::Signature& signature,
47 E : ModuleInformation* module_info) {
48 E : DCHECK(module_info != NULL);
49 E : module_info->base_address = signature.base_address.value();
50 E : module_info->image_checksum = signature.module_checksum;
51 E : module_info->image_file_name = signature.path;
52 E : module_info->module_size = signature.module_size;
53 E : module_info->time_date_stamp = signature.module_time_date_stamp;
54 E : }
55 :
56 : bool FindEntryCountMap(const pe::PEFile::Signature& signature,
57 : const ModuleEntryCountMap& module_entry_count_map,
58 E : const EntryCountMap** entry_count_map) {
59 E : DCHECK(entry_count_map != NULL);
60 E : *entry_count_map = NULL;
61 :
62 : // Find exactly one consistent entry count vector in the map.
63 E : const EntryCountMap* tmp_entry_count_map = NULL;
64 E : ModuleEntryCountMap::const_iterator it = module_entry_count_map.begin();
65 E : for (; it != module_entry_count_map.end(); ++it) {
66 E : const pe::PEFile::Signature candidate(it->first);
67 E : if (candidate.IsConsistent(signature)) {
68 E : if (tmp_entry_count_map != NULL) {
69 E : LOG(ERROR) << "Found multiple module instances in the entry count map.";
70 E : return false;
71 : }
72 E : tmp_entry_count_map = &it->second;
73 : }
74 E : }
75 :
76 : // Handle the case where there is no consistent module found.
77 E : if (tmp_entry_count_map == NULL) {
78 E : LOG(ERROR) << "Did not find module in the entry count map.";
79 E : return false;
80 : }
81 :
82 : // Return the entry counts that were found.
83 E : *entry_count_map = tmp_entry_count_map;
84 E : return true;
85 E : }
86 :
87 : bool LoadBasicBlockRanges(const base::FilePath& pdb_path,
88 E : RelativeAddressRangeVector* bb_ranges) {
89 E : DCHECK(!pdb_path.empty());
90 E : DCHECK(bb_ranges != NULL);
91 :
92 : // Read the PDB file.
93 E : pdb::PdbReader pdb_reader;
94 E : pdb::PdbFile pdb_file;
95 E : if (!pdb_reader.Read(pdb_path, &pdb_file)) {
96 E : LOG(ERROR) << "Failed to read PDB: " << pdb_path.value();
97 E : return false;
98 : }
99 :
100 : // Get the name-stream map from the PDB.
101 E : pdb::PdbInfoHeader70 pdb_header = {};
102 E : pdb::NameStreamMap name_stream_map;
103 E : if (!pdb::ReadHeaderInfoStream(pdb_file, &pdb_header, &name_stream_map)) {
104 i : LOG(ERROR) << "Failed to read PDB header info stream: " << pdb_path.value();
105 i : return false;
106 : }
107 :
108 : // Get the basic block addresses from the PDB file.
109 : pdb::NameStreamMap::const_iterator name_it = name_stream_map.find(
110 E : common::kBasicBlockRangesStreamName);
111 E : if (name_it == name_stream_map.end()) {
112 E : LOG(ERROR) << "PDB does not contain basic block ranges stream: "
113 : << pdb_path.value();
114 E : return false;
115 : }
116 E : scoped_refptr<pdb::PdbStream> bb_ranges_stream;
117 E : bb_ranges_stream = pdb_file.GetStream(name_it->second);
118 E : if (bb_ranges_stream.get() == NULL) {
119 i : LOG(ERROR) << "PDB basic block ranges stream has invalid index: "
120 : << name_it->second;
121 i : return false;
122 : }
123 :
124 : // Read the basic block range stream.
125 : if (!bb_ranges_stream->Seek(0) ||
126 E : !bb_ranges_stream->Read(bb_ranges)) {
127 i : LOG(ERROR) << "Failed to read basic block range stream from PDB: "
128 : << pdb_path.value();
129 i : return false;
130 : }
131 :
132 E : return true;
133 E : }
134 :
135 : bool LoadPdbInfo(PdbInfoMap* pdb_info_cache,
136 : const ModuleInformation& module_info,
137 E : PdbInfo** pdb_info) {
138 E : DCHECK(pdb_info_cache != NULL);
139 E : DCHECK(pdb_info != NULL);
140 :
141 E : *pdb_info = NULL;
142 :
143 : // Look for a cached entry first. If the cached entry is found but has no
144 : // pdb_path then it is a cached failure.
145 E : PdbInfoMap::iterator it = pdb_info_cache->find(module_info);
146 E : if (it != pdb_info_cache->end()) {
147 E : *pdb_info = &(it->second);
148 E : bool is_valid = !it->second.pdb_path.empty();
149 E : return is_valid;
150 : }
151 :
152 : // Insert a new (empty) PdbInfo for module_info and keep a reference to it.
153 : // If any of the operations below fail, the pdb_path in the PdbInfo structure
154 : // will not have been populated.
155 E : PdbInfo& pdb_info_ref = (*pdb_info_cache)[module_info];
156 :
157 : // Find the PDB file for the module.
158 E : base::FilePath pdb_path;
159 E : base::FilePath module_path(module_info.image_file_name);
160 E : if (!pe::FindPdbForModule(module_path, &pdb_path) || pdb_path.empty()) {
161 i : LOG(ERROR) << "Failed to find PDB for module: " << module_path.value();
162 i : return false;
163 : }
164 :
165 : // Load the line information from the PDB.
166 E : if (!pdb_info_ref.line_info.Init(pdb_path)) {
167 i : LOG(ERROR) << "Failed to extract line information from PDB file: "
168 : << pdb_path.value();
169 i : return false;
170 : }
171 :
172 : // This logs verbosely for us.
173 E : if (!LoadBasicBlockRanges(pdb_path, &pdb_info_ref.bb_ranges)) {
174 i : return false;
175 : }
176 :
177 : // Populate the pdb_path field of pdb_info_ref, which marks the cached
178 : // entry as valid.
179 E : pdb_info_ref.pdb_path = pdb_path;
180 :
181 : // Return a pointer to the pdb_info entry.
182 E : *pdb_info = &pdb_info_ref;
183 :
184 E : return true;
185 E : }
186 :
187 E : bool IsValidFrequencySize(size_t size) {
188 E : return size == 1 || size == 2 || size == 4;
189 E : }
190 :
191 : // TODO(sebmarchand): Move this to indexed_frequency_util.
192 E : uint32 GetFrequency(const TraceIndexedFrequencyData* data, size_t bb_id) {
193 E : DCHECK(data != NULL);
194 E : DCHECK(IsValidFrequencySize(data->frequency_size));
195 E : DCHECK_LT(bb_id, data->num_entries);
196 :
197 E : switch (data->frequency_size) {
198 : case 1:
199 E : return data->frequency_data[bb_id];
200 : case 2:
201 E : return reinterpret_cast<const uint16*>(data->frequency_data)[bb_id];
202 : case 4:
203 E : return reinterpret_cast<const uint32*>(data->frequency_data)[bb_id];
204 : }
205 :
206 i : NOTREACHED();
207 i : return 0;
208 E : }
209 :
210 : } // namespace basic_block_util
211 : } // namespace grinder
|