1 : // Copyright 2013 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/grinder/grinders/indexed_frequency_data_grinder.h"
16 :
17 : #include <limits>
18 :
19 : #include "base/files/file_path.h"
20 : #include "base/json/json_reader.h"
21 : #include "syzygy/common/indexed_frequency_data.h"
22 : #include "syzygy/core/json_file_writer.h"
23 : #include "syzygy/pdb/pdb_reader.h"
24 : #include "syzygy/pdb/pdb_util.h"
25 : #include "syzygy/pe/find.h"
26 : #include "syzygy/pe/metadata.h"
27 : #include "syzygy/pe/pe_file.h"
28 : #include "syzygy/version/syzygy_version.h"
29 :
30 : namespace grinder {
31 : namespace grinders {
32 :
33 : IndexedFrequencyDataGrinder::IndexedFrequencyDataGrinder()
34 : : parser_(NULL),
35 E : event_handler_errored_(false) {
36 E : }
37 :
38 : bool IndexedFrequencyDataGrinder::ParseCommandLine(
39 E : const base::CommandLine* command_line) {
40 E : serializer_.set_pretty_print(command_line->HasSwitch("pretty-print"));
41 E : return true;
42 E : }
43 :
44 E : void IndexedFrequencyDataGrinder::SetParser(Parser* parser) {
45 E : DCHECK(parser != NULL);
46 E : parser_ = parser;
47 E : }
48 :
49 E : bool IndexedFrequencyDataGrinder::Grind() {
50 E : if (frequency_data_map_.empty()) {
51 E : LOG(ERROR) << "No basic-block frequency data was encountered.";
52 E : return false;
53 : }
54 :
55 E : return true;
56 E : }
57 :
58 E : bool IndexedFrequencyDataGrinder::OutputData(FILE* file) {
59 E : DCHECK(file != NULL);
60 E : if (!serializer_.SaveAsJson(frequency_data_map_, file))
61 i : return false;
62 E : return true;
63 E : }
64 :
65 : void IndexedFrequencyDataGrinder::OnIndexedFrequency(
66 : base::Time time,
67 : DWORD process_id,
68 : DWORD thread_id,
69 E : const TraceIndexedFrequencyData* data) {
70 E : DCHECK(data != NULL);
71 E : DCHECK(parser_ != NULL);
72 E : DCHECK_NE(0U, data->num_columns);
73 :
74 E : if (data->num_entries == 0) {
75 i : LOG(INFO) << "Skipping empty basic block frequency data.";
76 i : return;
77 : }
78 :
79 E : if (!basic_block_util::IsValidFrequencySize(data->frequency_size)) {
80 i : LOG(ERROR) << "Basic block frequency data has invalid frequency_size ("
81 : << data->frequency_size << ").";
82 i : event_handler_errored_ = true;
83 i : return;
84 : }
85 :
86 : using trace::parser::AbsoluteAddress64;
87 :
88 : // Get the module information for which this BB frequency data belongs.
89 : const ModuleInformation* module_info = parser_->GetModuleInformation(
90 E : process_id, AbsoluteAddress64(data->module_base_addr));
91 E : if (module_info == NULL) {
92 i : LOG(ERROR) << "Failed to find module information.";
93 i : event_handler_errored_ = true;
94 i : return;
95 : }
96 :
97 : const InstrumentedModuleInformation* instrumented_module =
98 E : FindOrCreateInstrumentedModule(module_info);
99 E : if (instrumented_module == NULL) {
100 i : LOG(ERROR) << "Failed to find instrumented module "
101 : << module_info->path;
102 i : event_handler_errored_ = true;
103 i : return;
104 : }
105 :
106 E : if (data->num_entries != instrumented_module->block_ranges.size()) {
107 i : LOG(ERROR) << "Unexpected data size for instrumented module "
108 : << module_info->path;
109 i : event_handler_errored_ = true;
110 i : return;
111 : }
112 :
113 E : UpdateBasicBlockFrequencyData(*instrumented_module, data);
114 E : }
115 :
116 : void IndexedFrequencyDataGrinder::UpdateBasicBlockFrequencyData(
117 : const InstrumentedModuleInformation& instrumented_module,
118 E : const TraceIndexedFrequencyData* data) {
119 : using basic_block_util::BasicBlockOffset;
120 : using basic_block_util::EntryCountType;
121 : using basic_block_util::GetFrequency;
122 : using basic_block_util::IndexedFrequencyInformation;
123 : using basic_block_util::IndexedFrequencyMap;
124 : using basic_block_util::RelativeAddress;
125 :
126 E : DCHECK(data != NULL);
127 E : DCHECK_NE(0U, data->num_entries);
128 E : DCHECK_NE(0U, data->num_columns);
129 :
130 : // Find the entry for this module.
131 : ModuleIndexedFrequencyMap::iterator look =
132 E : frequency_data_map_.find(instrumented_module.original_module);
133 :
134 E : if (look == frequency_data_map_.end()) {
135 E : IndexedFrequencyInformation info = {};
136 E : info.num_entries = data->num_entries;
137 E : info.num_columns = data->num_columns;
138 E : info.frequency_size = data->frequency_size;
139 : info.data_type =
140 E : static_cast<common::IndexedFrequencyData::DataType>(data->data_type);
141 :
142 : look = frequency_data_map_.insert(
143 : std::make_pair(instrumented_module.original_module,
144 E : info)).first;
145 E : }
146 :
147 : // Validate fields are compatible to be grinded together.
148 E : IndexedFrequencyInformation& info = look->second;
149 : if (info.num_entries != data->num_entries ||
150 : info.num_columns != data->num_columns ||
151 : info.frequency_size != data->frequency_size ||
152 E : info.data_type != data->data_type) {
153 i : event_handler_errored_ = true;
154 i : return;
155 : }
156 :
157 : // Run over the BB frequency data and increment values for each basic block
158 : // using saturation arithmetic.
159 E : IndexedFrequencyMap& bb_entries = info.frequency_map;
160 E : for (size_t bb_id = 0; bb_id < data->num_entries; ++bb_id) {
161 E : for (size_t column = 0; column < data->num_columns; ++column) {
162 E : EntryCountType amount = GetFrequency(data, bb_id, column);
163 E : if (amount != 0) {
164 : BasicBlockOffset offs =
165 E : instrumented_module.block_ranges[bb_id].start().value();
166 :
167 : EntryCountType& value = bb_entries[
168 E : std::make_pair(RelativeAddress(offs), column)];
169 E : if (amount < 0) {
170 : // We need to detect uint32 to int32 overflow because JSON file output
171 : // int32 and basic block agent use an uint32 counter.
172 i : value = std::numeric_limits<EntryCountType>::max();
173 i : } else {
174 : value += std::min(
175 E : amount, std::numeric_limits<EntryCountType>::max() - value);
176 : }
177 : }
178 E : }
179 E : }
180 E : }
181 :
182 : const IndexedFrequencyDataGrinder::InstrumentedModuleInformation*
183 : IndexedFrequencyDataGrinder::FindOrCreateInstrumentedModule(
184 E : const ModuleInformation* module_info) {
185 : // See if we already encountered this instrumented module.
186 E : InstrumentedModuleMap::iterator it(instrumented_modules_.find(*module_info));
187 E : if (it != instrumented_modules_.end())
188 i : return &it->second;
189 :
190 : // Get the original file's metadata.
191 E : base::FilePath module_path(module_info->path);
192 E : pe::PEFile instrumented_module;
193 E : if (!instrumented_module.Init(module_path)) {
194 i : LOG(ERROR) << "Unable to locate instrumented module: "
195 : << module_path.value();
196 i : return NULL;
197 : }
198 :
199 E : pe::Metadata metadata;
200 E : if (!metadata.LoadFromPE(instrumented_module)) {
201 i : LOG(ERROR) << "Unable to load metadata from module: "
202 : << module_path.value();
203 i : return NULL;
204 : }
205 :
206 : // Find the PDB file for the module.
207 E : base::FilePath pdb_path;
208 E : if (!pe::FindPdbForModule(module_path, &pdb_path) || pdb_path.empty()) {
209 i : LOG(ERROR) << "Failed to find PDB for module: " << module_path.value();
210 i : return NULL;
211 : }
212 :
213 E : RelativeAddressRangeVector block_ranges;
214 : // This logs verbosely for us.
215 E : if (!basic_block_util::LoadBasicBlockRanges(pdb_path, &block_ranges)) {
216 i : return NULL;
217 : }
218 :
219 : // We've located all the information we need, create and initialize the
220 : // record.
221 E : InstrumentedModuleInformation& info = instrumented_modules_[*module_info];
222 E : info.original_module = metadata.module_signature();
223 E : info.block_ranges.swap(block_ranges);
224 :
225 E : return &info;
226 E : }
227 :
228 : } // namespace grinders
229 : } // namespace grinder
|