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/grinder/indexed_frequency_data_serializer.h"
25 : #include "syzygy/pdb/pdb_reader.h"
26 : #include "syzygy/pdb/pdb_util.h"
27 : #include "syzygy/pe/find.h"
28 :
29 : namespace grinder {
30 : namespace basic_block_util {
31 :
32 : bool ModuleIdentityComparator::operator()(
33 E : const ModuleInformation& lhs, const ModuleInformation& rhs) {
34 E : if (lhs.module_size < rhs.module_size)
35 E : return true;
36 E : if (lhs.module_size > rhs.module_size)
37 E : return false;
38 :
39 E : if (lhs.module_time_date_stamp < rhs.module_time_date_stamp)
40 E : return true;
41 E : if (lhs.module_time_date_stamp > rhs.module_time_date_stamp)
42 E : return false;
43 :
44 E : return lhs.path < rhs.path;
45 E : }
46 :
47 : bool operator==(const IndexedFrequencyInformation& lhs,
48 E : const IndexedFrequencyInformation& rhs) {
49 : return lhs.num_entries == rhs.num_entries &&
50 : lhs.num_columns == rhs.num_columns &&
51 : lhs.data_type == rhs.data_type &&
52 E : lhs.frequency_map == rhs.frequency_map;
53 E : }
54 :
55 : bool FindIndexedFrequencyInfo(
56 : const pe::PEFile::Signature& signature,
57 : const ModuleIndexedFrequencyMap& module_entry_map,
58 E : const IndexedFrequencyInformation** information) {
59 E : DCHECK(information != NULL);
60 E : *information = NULL;
61 :
62 : // Find exactly one consistent entry count vector in the map.
63 E : const IndexedFrequencyInformation* result = NULL;
64 E : ModuleIndexedFrequencyMap::const_iterator it = module_entry_map.begin();
65 E : for (; it != module_entry_map.end(); ++it) {
66 E : const pe::PEFile::Signature candidate(it->first);
67 E : if (candidate.IsConsistent(signature)) {
68 E : if (result != NULL) {
69 E : LOG(ERROR) << "Found multiple module instances in the "
70 : << "indexed frequency map.";
71 E : return false;
72 : }
73 E : result = &it->second;
74 : }
75 E : }
76 :
77 : // Handle the case where there is no consistent module found.
78 E : if (result == NULL) {
79 E : LOG(ERROR) << "Did not find module in the entry count map.";
80 E : return false;
81 : }
82 :
83 : // Return the entry counts that were found.
84 E : *information = result;
85 E : return true;
86 E : }
87 :
88 : bool LoadBranchStatisticsFromFile(const base::FilePath& file,
89 : const pe::PEFile::Signature& signature,
90 E : IndexedFrequencyMap* frequencies) {
91 E : DCHECK(!file.empty());
92 E : DCHECK_NE(reinterpret_cast<IndexedFrequencyMap*>(NULL), frequencies);
93 :
94 : // Load profile information from JSON file.
95 E : ModuleIndexedFrequencyMap module_entry_count_map;
96 E : IndexedFrequencyDataSerializer serializer;
97 E : if (!serializer.LoadFromJson(file, &module_entry_count_map)) {
98 E : LOG(ERROR) << "Failed to load profile information.";
99 E : return false;
100 : }
101 :
102 : // Retrieve the module with the right signature.
103 E : const IndexedFrequencyInformation* branch_statistics = NULL;
104 : if (!FindIndexedFrequencyInfo(signature,
105 : module_entry_count_map,
106 E : &branch_statistics)) {
107 E : LOG(ERROR) << "Failed to find module for '"
108 : << signature.path << "'.";
109 E : return false;
110 : }
111 : DCHECK_NE(reinterpret_cast<const IndexedFrequencyInformation*>(NULL),
112 E : branch_statistics);
113 :
114 : // Validate that the data is in the expected format.
115 E : DCHECK_EQ(3U, branch_statistics->num_columns);
116 E : DCHECK_EQ(common::IndexedFrequencyData::BRANCH, branch_statistics->data_type);
117 E : DCHECK_EQ(4U, branch_statistics->frequency_size);
118 :
119 E : *frequencies = branch_statistics->frequency_map;
120 E : return true;
121 E : }
122 :
123 : bool LoadBasicBlockRanges(const base::FilePath& pdb_path,
124 E : RelativeAddressRangeVector* bb_ranges) {
125 E : DCHECK(!pdb_path.empty());
126 E : DCHECK(bb_ranges != NULL);
127 :
128 : // Read the PDB file.
129 E : pdb::PdbReader pdb_reader;
130 E : pdb::PdbFile pdb_file;
131 E : if (!pdb_reader.Read(pdb_path, &pdb_file)) {
132 E : LOG(ERROR) << "Failed to read PDB: " << pdb_path.value();
133 E : return false;
134 : }
135 :
136 : // Get the name-stream map from the PDB.
137 E : pdb::PdbInfoHeader70 pdb_header = {};
138 E : pdb::NameStreamMap name_stream_map;
139 E : if (!pdb::ReadHeaderInfoStream(pdb_file, &pdb_header, &name_stream_map)) {
140 i : LOG(ERROR) << "Failed to read PDB header info stream: " << pdb_path.value();
141 i : return false;
142 : }
143 :
144 : // Get the basic block addresses from the PDB file.
145 : pdb::NameStreamMap::const_iterator name_it = name_stream_map.find(
146 E : common::kBasicBlockRangesStreamName);
147 E : if (name_it == name_stream_map.end()) {
148 E : LOG(ERROR) << "PDB does not contain basic block ranges stream: "
149 : << pdb_path.value();
150 E : return false;
151 : }
152 E : scoped_refptr<pdb::PdbStream> bb_ranges_stream;
153 E : bb_ranges_stream = pdb_file.GetStream(name_it->second);
154 E : if (bb_ranges_stream.get() == NULL) {
155 i : LOG(ERROR) << "PDB basic block ranges stream has invalid index: "
156 : << name_it->second;
157 i : return false;
158 : }
159 :
160 : // Read the basic block range stream.
161 : if (!bb_ranges_stream->Seek(0) ||
162 E : !bb_ranges_stream->Read(bb_ranges)) {
163 i : LOG(ERROR) << "Failed to read basic block range stream from PDB: "
164 : << pdb_path.value();
165 i : return false;
166 : }
167 :
168 E : return true;
169 E : }
170 :
171 : bool LoadPdbInfo(PdbInfoMap* pdb_info_cache,
172 : const ModuleInformation& module_info,
173 E : PdbInfo** pdb_info) {
174 E : DCHECK(pdb_info_cache != NULL);
175 E : DCHECK(pdb_info != NULL);
176 :
177 E : *pdb_info = NULL;
178 :
179 : // Look for a cached entry first. If the cached entry is found but has no
180 : // pdb_path then it is a cached failure.
181 E : PdbInfoMap::iterator it = pdb_info_cache->find(module_info);
182 E : if (it != pdb_info_cache->end()) {
183 E : *pdb_info = &(it->second);
184 E : bool is_valid = !it->second.pdb_path.empty();
185 E : return is_valid;
186 : }
187 :
188 : // Insert a new (empty) PdbInfo for module_info and keep a reference to it.
189 : // If any of the operations below fail, the pdb_path in the PdbInfo structure
190 : // will not have been populated.
191 E : PdbInfo& pdb_info_ref = (*pdb_info_cache)[module_info];
192 :
193 : // Find the PDB file for the module.
194 E : base::FilePath pdb_path;
195 E : base::FilePath module_path(module_info.path);
196 E : if (!pe::FindPdbForModule(module_path, &pdb_path) || pdb_path.empty()) {
197 i : LOG(ERROR) << "Failed to find PDB for module: " << module_path.value();
198 i : return false;
199 : }
200 :
201 : // Load the line information from the PDB.
202 E : if (!pdb_info_ref.line_info.Init(pdb_path)) {
203 i : LOG(ERROR) << "Failed to extract line information from PDB file: "
204 : << pdb_path.value();
205 i : return false;
206 : }
207 :
208 : // This logs verbosely for us.
209 E : if (!LoadBasicBlockRanges(pdb_path, &pdb_info_ref.bb_ranges)) {
210 i : return false;
211 : }
212 :
213 : // Populate the pdb_path field of pdb_info_ref, which marks the cached
214 : // entry as valid.
215 E : pdb_info_ref.pdb_path = pdb_path;
216 :
217 : // Return a pointer to the pdb_info entry.
218 E : *pdb_info = &pdb_info_ref;
219 :
220 E : return true;
221 E : }
222 :
223 E : bool IsValidFrequencySize(size_t size) {
224 E : return size == 1 || size == 2 || size == 4;
225 E : }
226 :
227 : // TODO(sebmarchand): Move this to indexed_frequency_util.
228 : uint32 GetFrequency(const TraceIndexedFrequencyData* data,
229 : size_t bb_id,
230 E : size_t column) {
231 E : DCHECK(data != NULL);
232 E : DCHECK(IsValidFrequencySize(data->frequency_size));
233 E : DCHECK_LT(bb_id, data->num_entries);
234 E : DCHECK_LT(column, data->num_columns);
235 :
236 : DCHECK(data->data_type == common::IndexedFrequencyData::BASIC_BLOCK_ENTRY ||
237 : data->data_type == common::IndexedFrequencyData::BRANCH ||
238 E : data->data_type == common::IndexedFrequencyData::COVERAGE);
239 :
240 E : size_t offset = bb_id * data->num_columns + column;
241 E : switch (data->frequency_size) {
242 : case 1:
243 E : return data->frequency_data[offset];
244 : case 2:
245 E : return reinterpret_cast<const uint16*>(data->frequency_data)[offset];
246 : case 4:
247 E : return reinterpret_cast<const uint32*>(data->frequency_data)[offset];
248 : }
249 :
250 i : NOTREACHED();
251 i : return 0;
252 E : }
253 :
254 : } // namespace basic_block_util
255 : } // namespace grinder
|