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/coverage_grinder.h"
16 :
17 : #include "base/files/file_path.h"
18 : #include "base/strings/string_util.h"
19 : #include "syzygy/common/indexed_frequency_data.h"
20 : #include "syzygy/grinder/cache_grind_writer.h"
21 : #include "syzygy/grinder/lcov_writer.h"
22 : #include "syzygy/pdb/pdb_reader.h"
23 : #include "syzygy/pdb/pdb_util.h"
24 : #include "syzygy/pe/find.h"
25 :
26 : namespace grinder {
27 : namespace grinders {
28 :
29 : namespace {
30 :
31 : using basic_block_util::ModuleInformation;
32 : using basic_block_util::RelativeAddressRange;
33 : using basic_block_util::GetFrequency;
34 : using basic_block_util::LoadPdbInfo;
35 : using basic_block_util::IsValidFrequencySize;
36 : using basic_block_util::PdbInfo;
37 : using basic_block_util::PdbInfoMap;
38 : using trace::parser::AbsoluteAddress64;
39 :
40 : } // namespace
41 :
42 : CoverageGrinder::CoverageGrinder()
43 : : parser_(NULL),
44 : event_handler_errored_(false),
45 E : output_format_(kLcovFormat) {
46 E : }
47 :
48 E : CoverageGrinder::~CoverageGrinder() {
49 E : }
50 :
51 E : bool CoverageGrinder::ParseCommandLine(const base::CommandLine* command_line) {
52 E : DCHECK(command_line != NULL);
53 :
54 : // If the switch isn't present we have nothing to do!
55 E : const char kOutputFormat[] = "output-format";
56 E : if (!command_line->HasSwitch(kOutputFormat))
57 E : return true;
58 :
59 E : std::string format = command_line->GetSwitchValueASCII(kOutputFormat);
60 E : if (base::LowerCaseEqualsASCII(format, "lcov")) {
61 E : output_format_ = kLcovFormat;
62 E : } else if (base::LowerCaseEqualsASCII(format, "cachegrind")) {
63 E : output_format_ = kCacheGrindFormat;
64 E : } else {
65 E : LOG(ERROR) << "Unknown output format: " << format << ".";
66 E : return false;
67 : }
68 E : return true;
69 E : }
70 :
71 E : void CoverageGrinder::SetParser(Parser* parser) {
72 E : DCHECK(parser != NULL);
73 E : parser_ = parser;
74 E : }
75 :
76 E : bool CoverageGrinder::Grind() {
77 E : if (event_handler_errored_) {
78 i : LOG(WARNING) << "Failed to handle all basic block frequency data events, "
79 : << "coverage results will be partial.";
80 : }
81 :
82 E : if (pdb_info_cache_.empty()) {
83 E : LOG(ERROR) << "No coverage data was encountered.";
84 E : return false;
85 : }
86 :
87 E : PdbInfoMap::const_iterator it = pdb_info_cache_.begin();
88 E : for (; it != pdb_info_cache_.end(); ++it) {
89 E : if (!coverage_data_.Add(it->second.line_info)) {
90 i : LOG(ERROR) << "Failed to aggregate line information from PDB: "
91 : << it->first.path;
92 i : return false;
93 : }
94 E : }
95 E : DCHECK(!coverage_data_.source_file_coverage_data_map().empty());
96 :
97 E : return true;
98 E : }
99 :
100 E : bool CoverageGrinder::OutputData(FILE* file) {
101 E : DCHECK(file != NULL);
102 E : DCHECK(!coverage_data_.source_file_coverage_data_map().empty());
103 :
104 : // These functions log verbosely for us.
105 E : switch (output_format_) {
106 : case kLcovFormat: {
107 E : if (!WriteLcovCoverageFile(coverage_data_, file))
108 i : return false;
109 E : break;
110 : }
111 :
112 : case kCacheGrindFormat: {
113 E : if (!WriteCacheGrindCoverageFile(coverage_data_, file))
114 i : return false;
115 E : break;
116 : }
117 :
118 i : default: NOTREACHED() << "Unknown OutputFormat.";
119 : }
120 :
121 E : return true;
122 E : }
123 :
124 : void CoverageGrinder::OnIndexedFrequency(
125 : base::Time time,
126 : DWORD process_id,
127 : DWORD thread_id,
128 E : const TraceIndexedFrequencyData* data) {
129 E : DCHECK(data != NULL);
130 E : DCHECK(parser_ != NULL);
131 :
132 : if (data->data_type != common::IndexedFrequencyData::COVERAGE &&
133 E : data->data_type != common::IndexedFrequencyData::BASIC_BLOCK_ENTRY) {
134 i : return;
135 : }
136 :
137 E : if (data->num_entries == 0) {
138 i : LOG(INFO) << "Skipping empty basic block frequency data.";
139 i : return;
140 : }
141 :
142 E : if (!IsValidFrequencySize(data->frequency_size)) {
143 i : LOG(ERROR) << "Basic block frequency data has invalid frequency_size ("
144 : << data->frequency_size << ").";
145 i : event_handler_errored_ = true;
146 i : return;
147 : }
148 :
149 : // Get the module information for which this BB frequency data belongs.
150 : const ModuleInformation* module_info = parser_->GetModuleInformation(
151 E : process_id, AbsoluteAddress64(data->module_base_addr));
152 E : if (module_info == NULL) {
153 i : LOG(ERROR) << "Failed to find module information for basic block frequency"
154 : << " data.";
155 i : event_handler_errored_ = true;
156 i : return;
157 : }
158 :
159 : // TODO(chrisha): Validate that the PE file itself is instrumented as
160 : // expected? This isn't strictly necessary but would add another level of
161 : // safety checking.
162 :
163 : // Get the PDB info. This loads the line information and the basic-block
164 : // ranges if not already done, otherwise it returns the cached version.
165 E : PdbInfo* pdb_info = NULL;
166 E : if (!LoadPdbInfo(&pdb_info_cache_, *module_info, &pdb_info)) {
167 i : event_handler_errored_ = true;
168 i : return;
169 : }
170 :
171 E : DCHECK(pdb_info != NULL);
172 :
173 : // Sanity check the contents.
174 E : if (data->num_entries != pdb_info->bb_ranges.size()) {
175 i : LOG(ERROR) << "Mismatch between trace data BB count and PDB BB count.";
176 i : event_handler_errored_ = true;
177 i : return;
178 : }
179 :
180 : // Run over the BB frequency data and mark non-zero frequency BBs as having
181 : // been visited.
182 E : for (size_t bb_index = 0; bb_index < data->num_entries; ++bb_index) {
183 E : uint32 bb_freq = GetFrequency(data, bb_index, 0);
184 :
185 E : if (bb_freq == 0)
186 E : continue;
187 :
188 : // Mark this basic-block as visited.
189 E : const RelativeAddressRange& bb_range = pdb_info->bb_ranges[bb_index];
190 : if (!pdb_info->line_info.Visit(bb_range.start(),
191 : bb_range.size(),
192 E : bb_freq)) {
193 i : LOG(ERROR) << "Failed to visit BB at " << bb_range << ".";
194 i : event_handler_errored_ = true;
195 i : return;
196 : }
197 E : }
198 E : }
199 :
200 : } // namespace grinders
201 : } // namespace grinder
|