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