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