1 : // Copyright 2012 Google Inc.
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/coverage_grinder.h"
16 :
17 : #include "base/file_path.h"
18 : #include "syzygy/common/basic_block_frequency_data.h"
19 : #include "syzygy/pdb/pdb_reader.h"
20 : #include "syzygy/pdb/pdb_util.h"
21 : #include "syzygy/pe/find.h"
22 :
23 : namespace grinder {
24 :
25 : namespace {
26 :
27 : using sym_util::ModuleInformation;
28 : using trace::parser::AbsoluteAddress64;
29 :
30 : bool GetBasicBlockRanges(
31 : const FilePath& pdb_path,
32 E : CoverageGrinder::RelativeAddressRangeVector* bb_ranges) {
33 E : DCHECK(!pdb_path.empty());
34 E : DCHECK(bb_ranges != NULL);
35 :
36 : // Read the PDB file.
37 E : pdb::PdbReader pdb_reader;
38 E : pdb::PdbFile pdb_file;
39 E : if (!pdb_reader.Read(pdb_path, &pdb_file)) {
40 i : LOG(ERROR) << "Failed to read PDB: " << pdb_path.value();
41 i : return false;
42 : }
43 :
44 : // Get the name-stream map from the PDB.
45 E : pdb::PdbInfoHeader70 pdb_header = {};
46 E : pdb::NameStreamMap name_stream_map;
47 E : if (!pdb::ReadHeaderInfoStream(pdb_file, &pdb_header, &name_stream_map)) {
48 i : LOG(ERROR) << "Failed to read PDB header info stream: " << pdb_path.value();
49 i : return false;
50 : }
51 :
52 : // Get the basic block addresses from the PDB file.
53 : pdb::NameStreamMap::const_iterator name_it = name_stream_map.find(
54 E : common::kBasicBlockRangesStreamName);
55 E : if (name_it == name_stream_map.end()) {
56 i : LOG(ERROR) << "PDB does not contain basic block ranges stream: "
57 : << pdb_path.value();
58 i : return false;
59 : }
60 E : scoped_refptr<pdb::PdbStream> bb_ranges_stream;
61 E : bb_ranges_stream = pdb_file.GetStream(name_it->second);
62 E : if (bb_ranges_stream.get() == NULL) {
63 i : LOG(ERROR) << "PDB basic block ranges stream has invalid index: "
64 : << name_it->second;
65 i : return false;
66 : }
67 :
68 : // Read the basic block range stream.
69 : if (!bb_ranges_stream->Seek(0) ||
70 E : !bb_ranges_stream->Read(bb_ranges)) {
71 i : LOG(ERROR) << "Failed to read basic block range stream from PDB: "
72 : << pdb_path.value();
73 i : return false;
74 : }
75 :
76 E : return true;
77 E : }
78 :
79 E : uint32 GetFrequency(const uint8* data, size_t size, size_t bb_index) {
80 E : DCHECK(data != NULL);
81 E : DCHECK(size == 1 || size == 2 || size == 4);
82 :
83 E : switch (size) {
84 E : case 1: return data[bb_index];
85 i : case 2: return reinterpret_cast<const uint16*>(data)[bb_index];
86 i : case 4: return reinterpret_cast<const uint32*>(data)[bb_index];
87 : }
88 :
89 i : NOTREACHED();
90 i : return 0;
91 E : }
92 :
93 : } // namespace
94 :
95 : CoverageGrinder::CoverageGrinder()
96 E : : parser_(NULL), event_handler_errored_(false) {
97 E : }
98 :
99 E : CoverageGrinder::~CoverageGrinder() {
100 E : }
101 :
102 E : bool CoverageGrinder::ParseCommandLine(const CommandLine* command_line) {
103 : // We don't do any additional parsing.
104 E : return true;
105 E : }
106 :
107 E : void CoverageGrinder::SetParser(Parser* parser) {
108 E : parser_ = parser;
109 E : }
110 :
111 E : bool CoverageGrinder::Grind() {
112 E : if (event_handler_errored_) {
113 i : LOG(WARNING) << "Failed to handle all basic block frequency data events, "
114 : << "coverage results will be partial.";
115 : }
116 :
117 E : if (pdb_info_map_.empty()) {
118 E : LOG(ERROR) << "No coverage data was encountered.";
119 E : return false;
120 : }
121 :
122 E : PdbInfoMap::const_iterator it = pdb_info_map_.begin();
123 E : for (; it != pdb_info_map_.end(); ++it) {
124 E : if (!lcov_writer_.Add(it->second.line_info)) {
125 i : LOG(ERROR) << "Failed to aggregate line information from PDB: "
126 : << it->first;
127 i : return false;
128 : }
129 E : }
130 E : DCHECK(!lcov_writer_.source_file_coverage_info_map().empty());
131 :
132 E : return true;
133 E : }
134 :
135 E : bool CoverageGrinder::OutputData(FILE* file) {
136 E : DCHECK(file != NULL);
137 E : DCHECK(!lcov_writer_.source_file_coverage_info_map().empty());
138 :
139 E : if (!lcov_writer_.Write(file)) {
140 i : LOG(ERROR) << "Failed to write LCOV file.";
141 i : return false;
142 : }
143 :
144 E : return true;
145 E : }
146 :
147 : void CoverageGrinder::OnBasicBlockFrequency(
148 : base::Time time,
149 : DWORD process_id,
150 : DWORD thread_id,
151 E : const TraceBasicBlockFrequencyData* data) {
152 E : DCHECK(parser_ != NULL);
153 :
154 E : if (data->num_basic_blocks == 0) {
155 i : LOG(INFO) << "Skipping empty basic block frequency data.";
156 i : return;
157 : }
158 :
159 : if (data->frequency_size != 1 && data->frequency_size != 2 &&
160 E : data->frequency_size != 4) {
161 i : LOG(ERROR) << "Basic block frequency data has invalid frequency_size ("
162 : << data->frequency_size << ").";
163 i : event_handler_errored_ = true;
164 i : return;
165 : }
166 :
167 : // Get the module information for which this BB frequency data belongs.
168 : const ModuleInformation* module_info = parser_->GetModuleInformation(
169 E : process_id, AbsoluteAddress64(data->module_base_addr));
170 E : if (module_info == NULL) {
171 i : LOG(ERROR) << "Failed to find module information for basic block frequency "
172 : << "data.";
173 i : event_handler_errored_ = true;
174 i : return;
175 : }
176 :
177 : // TODO(chrisha): Validate that the PE file itself is instrumented as
178 : // expected? This isn't strictly necessary but would add another level of
179 : // safety checking.
180 :
181 : // Find the PDB for the module.
182 E : FilePath module_path(module_info->image_file_name);
183 E : FilePath pdb_path;
184 E : if (!pe::FindPdbForModule(module_path, &pdb_path) || pdb_path.empty()) {
185 i : LOG(ERROR) << "Failed to find PDB for module: " << module_path.value();
186 i : event_handler_errored_ = true;
187 i : return;
188 : }
189 :
190 : // Get the PDB info. This loads the line information and the basic-block
191 : // ranges if not already done, otherwise it returns the cached version.
192 E : PdbInfo* pdb_info = NULL;
193 E : if (!GetPdbInfo(pdb_path, &pdb_info)) {
194 i : event_handler_errored_ = true;
195 i : return;
196 : }
197 E : DCHECK(pdb_info != NULL);
198 :
199 : // Sanity check the contents.
200 E : if (data->num_basic_blocks != pdb_info->bb_ranges.size()) {
201 i : LOG(ERROR) << "Mismatch between trace data BB count and PDB BB count.";
202 i : event_handler_errored_ = true;
203 i : return;
204 : }
205 :
206 : // Run over the BB frequency data and mark non-zero frequency BBs as having
207 : // been visited.
208 E : for (size_t bb_index = 0; bb_index < data->num_basic_blocks; ++bb_index) {
209 : uint32 bb_freq = GetFrequency(data->frequency_data,
210 : data->frequency_size,
211 E : bb_index);
212 :
213 E : if (bb_freq == 0)
214 E : continue;
215 :
216 : // Mark this basic-block as visited.
217 E : const RelativeAddressRange& bb_range = pdb_info->bb_ranges[bb_index];
218 E : if (!pdb_info->line_info.Visit(bb_range.start(), bb_range.size())) {
219 i : LOG(ERROR) << "Failed to visit BB at " << bb_range << ".";
220 i : event_handler_errored_ = true;
221 i : return;
222 : }
223 E : }
224 E : }
225 :
226 E : bool CoverageGrinder::GetPdbInfo(const FilePath& pdb_path, PdbInfo** pdb_info) {
227 E : DCHECK(pdb_info != NULL);
228 :
229 E : *pdb_info = NULL;
230 :
231 : // Look for a cached entry first.
232 E : PdbInfoMap::iterator it = pdb_info_map_.find(pdb_path.value());
233 E : if (it != pdb_info_map_.end()) {
234 i : *pdb_info = &(it->second);
235 i : return true;
236 : }
237 :
238 : // Load the line information from the PDB.
239 E : PdbInfo& pdb_info_ref = pdb_info_map_[pdb_path.value()];
240 E : if (!pdb_info_ref.line_info.Init(pdb_path)) {
241 i : LOG(ERROR) << "Failed to extract line information from PDB file: "
242 : << pdb_path.value();
243 i : return false;
244 : }
245 :
246 : // This logs verbosely for us.
247 E : if (!GetBasicBlockRanges(pdb_path, &pdb_info_ref.bb_ranges))
248 i : return false;
249 :
250 E : *pdb_info = &pdb_info_ref;
251 :
252 E : return true;
253 E : }
254 :
255 : } // namespace grinder
|