1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <stdio.h>
18 
19 #include <memory>
20 #include <string>
21 
22 #include "ETMDecoder.h"
23 #include "command.h"
24 #include "record_file.h"
25 #include "thread_tree.h"
26 #include "utils.h"
27 
28 using namespace simpleperf;
29 
30 namespace {
31 
32 using AddrPair = std::pair<uint64_t, uint64_t>;
33 
34 struct AddrPairHash {
operator ()__anonba09125c0111::AddrPairHash35   size_t operator()(const AddrPair& ap) const noexcept {
36     size_t seed = 0;
37     HashCombine(seed, ap.first);
38     HashCombine(seed, ap.second);
39     return seed;
40   }
41 };
42 
43 struct BinaryInfo {
44   std::unordered_map<AddrPair, uint64_t, AddrPairHash> range_count_map;
45   std::unordered_map<AddrPair, uint64_t, AddrPairHash> branch_count_map;
46 };
47 
48 class InjectCommand : public Command {
49  public:
InjectCommand()50   InjectCommand()
51       : Command("inject", "convert etm instruction tracing data into instr ranges",
52                 // clang-format off
53 "Usage: simpleperf inject [options]\n"
54 "--binary binary_name         Generate data only for binaries containing binary_name.\n"
55 "-i <file>                    input perf.data, generated by recording cs-etm event type.\n"
56 "                             Default is perf.data.\n"
57 "-o <file>                    output file. Default is perf_inject.data.\n"
58 "                             The output is in text format accepted by AutoFDO.\n"
59 "--dump-etm type1,type2,...   Dump etm data. A type is one of raw, packet and element.\n"
60 "--symdir <dir>               Look for binaries in a directory recursively.\n"
61                 // clang-format on
62                 ),
63         output_fp_(nullptr, fclose) {}
64 
Run(const std::vector<std::string> & args)65   bool Run(const std::vector<std::string>& args) override {
66     if (!ParseOptions(args)) {
67       return false;
68     }
69     record_file_reader_ = RecordFileReader::CreateInstance(input_filename_);
70     if (!record_file_reader_) {
71       return false;
72     }
73     record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_);
74     output_fp_.reset(fopen(output_filename_.c_str(), "w"));
75     if (!output_fp_) {
76       PLOG(ERROR) << "failed to write to " << output_filename_;
77       return false;
78     }
79     if (!record_file_reader_->ReadDataSection([this](auto r) { return ProcessRecord(r.get()); })) {
80       return false;
81     }
82     PostProcess();
83     output_fp_.reset(nullptr);
84     return true;
85   }
86 
87  private:
ParseOptions(const std::vector<std::string> & args)88   bool ParseOptions(const std::vector<std::string>& args) {
89     for (size_t i = 0; i < args.size(); i++) {
90       if (args[i] == "--binary") {
91         if (!NextArgumentOrError(args, &i)) {
92           return false;
93         }
94         binary_name_filter_ = args[i];
95       } else if (args[i] == "-i") {
96         if (!NextArgumentOrError(args, &i)) {
97           return false;
98         }
99         input_filename_ = args[i];
100       } else if (args[i] == "-o") {
101         if (!NextArgumentOrError(args, &i)) {
102           return false;
103         }
104         output_filename_ = args[i];
105       } else if (args[i] == "--dump-etm") {
106         if (!NextArgumentOrError(args, &i) || !ParseEtmDumpOption(args[i], &etm_dump_option_)) {
107           return false;
108         }
109       } else if (args[i] == "--symdir") {
110         if (!NextArgumentOrError(args, &i) || !Dso::AddSymbolDir(args[i])) {
111           return false;
112         }
113       } else {
114         ReportUnknownOption(args, i);
115         return false;
116       }
117     }
118     return true;
119   }
120 
ProcessRecord(Record * r)121   bool ProcessRecord(Record* r) {
122     thread_tree_.Update(*r);
123     if (r->type() == PERF_RECORD_AUXTRACE_INFO) {
124       auto instr_range_callback = [this](auto& range) { ProcessInstrRange(range); };
125       etm_decoder_ = ETMDecoder::Create(*static_cast<AuxTraceInfoRecord*>(r), thread_tree_);
126       if (!etm_decoder_) {
127         return false;
128       }
129       etm_decoder_->EnableDump(etm_dump_option_);
130       etm_decoder_->RegisterCallback(instr_range_callback);
131     } else if (r->type() == PERF_RECORD_AUX) {
132       AuxRecord* aux = static_cast<AuxRecord*>(r);
133       uint64_t aux_size = aux->data->aux_size;
134       if (aux_size > 0) {
135         if (aux_data_buffer_.size() < aux_size) {
136           aux_data_buffer_.resize(aux_size);
137         }
138         if (!record_file_reader_->ReadAuxData(aux->Cpu(), aux->data->aux_offset,
139                                               aux_data_buffer_.data(), aux_size)) {
140           LOG(ERROR) << "failed to read aux data";
141           return false;
142         }
143         return etm_decoder_->ProcessData(aux_data_buffer_.data(), aux_size);
144       }
145     }
146     return true;
147   }
148 
ProcessInstrRange(const ETMInstrRange & instr_range)149   void ProcessInstrRange(const ETMInstrRange& instr_range) {
150     if (instr_range.dso->GetDebugFilePath().find(binary_name_filter_) == std::string::npos) {
151       return;
152     }
153     auto& binary = binary_map_[instr_range.dso->GetDebugFilePath()];
154     binary.range_count_map[AddrPair(instr_range.start_addr, instr_range.end_addr)] +=
155         instr_range.branch_taken_count + instr_range.branch_not_taken_count;
156     if (instr_range.branch_taken_count > 0) {
157       binary.branch_count_map[AddrPair(instr_range.end_addr, instr_range.branch_to_addr)] +=
158           instr_range.branch_taken_count;
159     }
160   }
161 
PostProcess()162   void PostProcess() {
163     for (const auto& pair : binary_map_) {
164       const std::string& binary_path = pair.first;
165       const BinaryInfo& binary = pair.second;
166 
167       // Write range_count_map.
168       fprintf(output_fp_.get(), "%zu\n", binary.range_count_map.size());
169       for (const auto& pair2 : binary.range_count_map) {
170         const AddrPair& addr_range = pair2.first;
171         uint64_t count = pair2.second;
172 
173         fprintf(output_fp_.get(), "%" PRIx64 "-%" PRIx64 ":%" PRIu64 "\n", addr_range.first,
174                 addr_range.second, count);
175       }
176 
177       // Write addr_count_map.
178       fprintf(output_fp_.get(), "0\n");
179 
180       // Write branch_count_map.
181       fprintf(output_fp_.get(), "%zu\n", binary.branch_count_map.size());
182       for (const auto& pair2 : binary.branch_count_map) {
183         const AddrPair& branch = pair2.first;
184         uint64_t count = pair2.second;
185 
186         fprintf(output_fp_.get(), "%" PRIx64 "->%" PRIx64 ":%" PRIu64 "\n", branch.first,
187                 branch.second, count);
188       }
189 
190       // Write the binary path in comment.
191       fprintf(output_fp_.get(), "// %s\n\n", binary_path.c_str());
192     }
193   }
194 
195   std::string binary_name_filter_;
196   std::string input_filename_ = "perf.data";
197   std::string output_filename_ = "perf_inject.data";
198   ThreadTree thread_tree_;
199   std::unique_ptr<RecordFileReader> record_file_reader_;
200   ETMDumpOption etm_dump_option_;
201   std::unique_ptr<ETMDecoder> etm_decoder_;
202   std::vector<uint8_t> aux_data_buffer_;
203   std::unique_ptr<FILE, decltype(&fclose)> output_fp_;
204 
205   // Store results for AutoFDO.
206   std::unordered_map<std::string, BinaryInfo> binary_map_;
207 };
208 
209 }  // namespace
210 
RegisterInjectCommand()211 void RegisterInjectCommand() {
212   return RegisterCommand("inject", [] { return std::unique_ptr<Command>(new InjectCommand); });
213 }
214