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