xref: /aosp_15_r20/system/extras/simpleperf/cmd_merge.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1*288bf522SAndroid Build Coastguard Worker /*
2*288bf522SAndroid Build Coastguard Worker  * Copyright (C) 2020 The Android Open Source Project
3*288bf522SAndroid Build Coastguard Worker  *
4*288bf522SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*288bf522SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*288bf522SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*288bf522SAndroid Build Coastguard Worker  *
8*288bf522SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*288bf522SAndroid Build Coastguard Worker  *
10*288bf522SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*288bf522SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*288bf522SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*288bf522SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*288bf522SAndroid Build Coastguard Worker  * limitations under the License.
15*288bf522SAndroid Build Coastguard Worker  */
16*288bf522SAndroid Build Coastguard Worker 
17*288bf522SAndroid Build Coastguard Worker #include <stdio.h>
18*288bf522SAndroid Build Coastguard Worker 
19*288bf522SAndroid Build Coastguard Worker #include <memory>
20*288bf522SAndroid Build Coastguard Worker #include <regex>
21*288bf522SAndroid Build Coastguard Worker #include <string>
22*288bf522SAndroid Build Coastguard Worker 
23*288bf522SAndroid Build Coastguard Worker #include <android-base/macros.h>
24*288bf522SAndroid Build Coastguard Worker #include <android-base/strings.h>
25*288bf522SAndroid Build Coastguard Worker 
26*288bf522SAndroid Build Coastguard Worker #include "command.h"
27*288bf522SAndroid Build Coastguard Worker #include "event_attr.h"
28*288bf522SAndroid Build Coastguard Worker #include "record_file.h"
29*288bf522SAndroid Build Coastguard Worker #include "thread_tree.h"
30*288bf522SAndroid Build Coastguard Worker #include "utils.h"
31*288bf522SAndroid Build Coastguard Worker 
32*288bf522SAndroid Build Coastguard Worker namespace simpleperf {
33*288bf522SAndroid Build Coastguard Worker namespace {
34*288bf522SAndroid Build Coastguard Worker 
35*288bf522SAndroid Build Coastguard Worker class MergedFileFeature {
36*288bf522SAndroid Build Coastguard Worker  public:
MergedFileFeature(FileFeature & file)37*288bf522SAndroid Build Coastguard Worker   MergedFileFeature(FileFeature& file)
38*288bf522SAndroid Build Coastguard Worker       : path_(file.path),
39*288bf522SAndroid Build Coastguard Worker         type_(file.type),
40*288bf522SAndroid Build Coastguard Worker         min_vaddr_(file.min_vaddr),
41*288bf522SAndroid Build Coastguard Worker         file_offset_of_min_vaddr_(file.file_offset_of_min_vaddr),
42*288bf522SAndroid Build Coastguard Worker         dex_file_offsets_(std::move(file.dex_file_offsets)) {
43*288bf522SAndroid Build Coastguard Worker     for (auto& symbol : file.symbols) {
44*288bf522SAndroid Build Coastguard Worker       symbol_map_.emplace(symbol.addr, std::move(symbol));
45*288bf522SAndroid Build Coastguard Worker     }
46*288bf522SAndroid Build Coastguard Worker   }
47*288bf522SAndroid Build Coastguard Worker 
Merge(FileFeature & file)48*288bf522SAndroid Build Coastguard Worker   bool Merge(FileFeature& file) {
49*288bf522SAndroid Build Coastguard Worker     if (file.type != type_ || file.min_vaddr != min_vaddr_ ||
50*288bf522SAndroid Build Coastguard Worker         file.file_offset_of_min_vaddr != file_offset_of_min_vaddr_ ||
51*288bf522SAndroid Build Coastguard Worker         file.dex_file_offsets != dex_file_offsets_) {
52*288bf522SAndroid Build Coastguard Worker       return false;
53*288bf522SAndroid Build Coastguard Worker     }
54*288bf522SAndroid Build Coastguard Worker     for (auto& symbol : file.symbols) {
55*288bf522SAndroid Build Coastguard Worker       auto it = symbol_map_.lower_bound(symbol.addr);
56*288bf522SAndroid Build Coastguard Worker       if (it != symbol_map_.end()) {
57*288bf522SAndroid Build Coastguard Worker         const auto& found = it->second;
58*288bf522SAndroid Build Coastguard Worker         if (found.addr == symbol.addr && found.len == symbol.len &&
59*288bf522SAndroid Build Coastguard Worker             strcmp(found.Name(), symbol.Name()) == 0) {
60*288bf522SAndroid Build Coastguard Worker           // The symbol already exists in symbol_map.
61*288bf522SAndroid Build Coastguard Worker           continue;
62*288bf522SAndroid Build Coastguard Worker         }
63*288bf522SAndroid Build Coastguard Worker         if (symbol.addr + symbol.len > found.addr) {
64*288bf522SAndroid Build Coastguard Worker           // an address conflict with the next symbol
65*288bf522SAndroid Build Coastguard Worker           return false;
66*288bf522SAndroid Build Coastguard Worker         }
67*288bf522SAndroid Build Coastguard Worker       }
68*288bf522SAndroid Build Coastguard Worker       if (it != symbol_map_.begin()) {
69*288bf522SAndroid Build Coastguard Worker         --it;
70*288bf522SAndroid Build Coastguard Worker         if (it->second.addr + it->second.len > symbol.addr) {
71*288bf522SAndroid Build Coastguard Worker           // an address conflict with the previous symbol
72*288bf522SAndroid Build Coastguard Worker           return false;
73*288bf522SAndroid Build Coastguard Worker         }
74*288bf522SAndroid Build Coastguard Worker       }
75*288bf522SAndroid Build Coastguard Worker       symbol_map_.emplace(symbol.addr, std::move(symbol));
76*288bf522SAndroid Build Coastguard Worker     }
77*288bf522SAndroid Build Coastguard Worker     return true;
78*288bf522SAndroid Build Coastguard Worker   }
79*288bf522SAndroid Build Coastguard Worker 
ToFileFeature(FileFeature * file) const80*288bf522SAndroid Build Coastguard Worker   void ToFileFeature(FileFeature* file) const {
81*288bf522SAndroid Build Coastguard Worker     file->path = path_;
82*288bf522SAndroid Build Coastguard Worker     file->type = type_;
83*288bf522SAndroid Build Coastguard Worker     file->min_vaddr = min_vaddr_;
84*288bf522SAndroid Build Coastguard Worker     file->file_offset_of_min_vaddr = file_offset_of_min_vaddr_;
85*288bf522SAndroid Build Coastguard Worker     file->symbol_ptrs.clear();
86*288bf522SAndroid Build Coastguard Worker     for (const auto& [_, symbol] : symbol_map_) {
87*288bf522SAndroid Build Coastguard Worker       file->symbol_ptrs.emplace_back(&symbol);
88*288bf522SAndroid Build Coastguard Worker     }
89*288bf522SAndroid Build Coastguard Worker     file->dex_file_offsets = dex_file_offsets_;
90*288bf522SAndroid Build Coastguard Worker   }
91*288bf522SAndroid Build Coastguard Worker 
92*288bf522SAndroid Build Coastguard Worker  private:
93*288bf522SAndroid Build Coastguard Worker   std::string path_;
94*288bf522SAndroid Build Coastguard Worker   DsoType type_;
95*288bf522SAndroid Build Coastguard Worker   uint64_t min_vaddr_;
96*288bf522SAndroid Build Coastguard Worker   uint64_t file_offset_of_min_vaddr_;
97*288bf522SAndroid Build Coastguard Worker   std::map<uint64_t, Symbol> symbol_map_;
98*288bf522SAndroid Build Coastguard Worker   std::vector<uint64_t> dex_file_offsets_;
99*288bf522SAndroid Build Coastguard Worker 
100*288bf522SAndroid Build Coastguard Worker   DISALLOW_COPY_AND_ASSIGN(MergedFileFeature);
101*288bf522SAndroid Build Coastguard Worker };
102*288bf522SAndroid Build Coastguard Worker 
103*288bf522SAndroid Build Coastguard Worker class MergeCommand : public Command {
104*288bf522SAndroid Build Coastguard Worker  public:
MergeCommand()105*288bf522SAndroid Build Coastguard Worker   MergeCommand()
106*288bf522SAndroid Build Coastguard Worker       : Command("merge", "merge multiple perf.data into one",
107*288bf522SAndroid Build Coastguard Worker                 // clang-format off
108*288bf522SAndroid Build Coastguard Worker "Usage: simpleperf merge [options]\n"
109*288bf522SAndroid Build Coastguard Worker "       Merge multiple perf.data into one. The input files should be recorded on the same\n"
110*288bf522SAndroid Build Coastguard Worker "       device using the same event types.\n"
111*288bf522SAndroid Build Coastguard Worker "-i <file1>,<file2>,...       Input recording files separated by comma\n"
112*288bf522SAndroid Build Coastguard Worker "-o <file>                    output recording file\n"
113*288bf522SAndroid Build Coastguard Worker "\n"
114*288bf522SAndroid Build Coastguard Worker "Examples:\n"
115*288bf522SAndroid Build Coastguard Worker "$ simpleperf merge -i perf1.data,perf2.data -o perf.data\n"
116*288bf522SAndroid Build Coastguard Worker                 // clang-format on
117*288bf522SAndroid Build Coastguard Worker         ) {}
118*288bf522SAndroid Build Coastguard Worker 
Run(const std::vector<std::string> & args)119*288bf522SAndroid Build Coastguard Worker   bool Run(const std::vector<std::string>& args) override {
120*288bf522SAndroid Build Coastguard Worker     // 1. Parse options.
121*288bf522SAndroid Build Coastguard Worker     if (!ParseOptions(args)) {
122*288bf522SAndroid Build Coastguard Worker       return false;
123*288bf522SAndroid Build Coastguard Worker     }
124*288bf522SAndroid Build Coastguard Worker 
125*288bf522SAndroid Build Coastguard Worker     // 2. Open input files and check if they are mergeable.
126*288bf522SAndroid Build Coastguard Worker     for (const auto& file : input_files_) {
127*288bf522SAndroid Build Coastguard Worker       readers_.emplace_back(RecordFileReader::CreateInstance(file));
128*288bf522SAndroid Build Coastguard Worker       if (!readers_.back()) {
129*288bf522SAndroid Build Coastguard Worker         return false;
130*288bf522SAndroid Build Coastguard Worker       }
131*288bf522SAndroid Build Coastguard Worker     }
132*288bf522SAndroid Build Coastguard Worker     if (!IsMergeable()) {
133*288bf522SAndroid Build Coastguard Worker       return false;
134*288bf522SAndroid Build Coastguard Worker     }
135*288bf522SAndroid Build Coastguard Worker 
136*288bf522SAndroid Build Coastguard Worker     // 3. Merge files.
137*288bf522SAndroid Build Coastguard Worker     writer_ = RecordFileWriter::CreateInstance(output_file_);
138*288bf522SAndroid Build Coastguard Worker     if (!writer_) {
139*288bf522SAndroid Build Coastguard Worker       return false;
140*288bf522SAndroid Build Coastguard Worker     }
141*288bf522SAndroid Build Coastguard Worker     if (!MergeAttrSection() || !MergeDataSection() || !MergeFeatureSection()) {
142*288bf522SAndroid Build Coastguard Worker       return false;
143*288bf522SAndroid Build Coastguard Worker     }
144*288bf522SAndroid Build Coastguard Worker     return writer_->Close();
145*288bf522SAndroid Build Coastguard Worker   }
146*288bf522SAndroid Build Coastguard Worker 
147*288bf522SAndroid Build Coastguard Worker  private:
ParseOptions(const std::vector<std::string> & args)148*288bf522SAndroid Build Coastguard Worker   bool ParseOptions(const std::vector<std::string>& args) {
149*288bf522SAndroid Build Coastguard Worker     const OptionFormatMap option_formats = {
150*288bf522SAndroid Build Coastguard Worker         {"-i", {OptionValueType::STRING, OptionType::MULTIPLE}},
151*288bf522SAndroid Build Coastguard Worker         {"-o", {OptionValueType::STRING, OptionType::SINGLE}},
152*288bf522SAndroid Build Coastguard Worker     };
153*288bf522SAndroid Build Coastguard Worker     OptionValueMap options;
154*288bf522SAndroid Build Coastguard Worker     std::vector<std::pair<OptionName, OptionValue>> ordered_options;
155*288bf522SAndroid Build Coastguard Worker     if (!PreprocessOptions(args, option_formats, &options, &ordered_options, nullptr)) {
156*288bf522SAndroid Build Coastguard Worker       return false;
157*288bf522SAndroid Build Coastguard Worker     }
158*288bf522SAndroid Build Coastguard Worker     for (const OptionValue& value : options.PullValues("-i")) {
159*288bf522SAndroid Build Coastguard Worker       auto files = android::base::Split(value.str_value, ",");
160*288bf522SAndroid Build Coastguard Worker       input_files_.insert(input_files_.end(), files.begin(), files.end());
161*288bf522SAndroid Build Coastguard Worker     }
162*288bf522SAndroid Build Coastguard Worker     options.PullStringValue("-o", &output_file_);
163*288bf522SAndroid Build Coastguard Worker 
164*288bf522SAndroid Build Coastguard Worker     CHECK(options.values.empty());
165*288bf522SAndroid Build Coastguard Worker 
166*288bf522SAndroid Build Coastguard Worker     if (input_files_.empty()) {
167*288bf522SAndroid Build Coastguard Worker       LOG(ERROR) << "missing input files";
168*288bf522SAndroid Build Coastguard Worker       return false;
169*288bf522SAndroid Build Coastguard Worker     }
170*288bf522SAndroid Build Coastguard Worker     if (output_file_.empty()) {
171*288bf522SAndroid Build Coastguard Worker       LOG(ERROR) << "missing output file";
172*288bf522SAndroid Build Coastguard Worker       return false;
173*288bf522SAndroid Build Coastguard Worker     }
174*288bf522SAndroid Build Coastguard Worker     return true;
175*288bf522SAndroid Build Coastguard Worker   }
176*288bf522SAndroid Build Coastguard Worker 
IsMergeable()177*288bf522SAndroid Build Coastguard Worker   bool IsMergeable() { return CheckFeatureSection() && CheckAttrSection(); }
178*288bf522SAndroid Build Coastguard Worker 
179*288bf522SAndroid Build Coastguard Worker   // Check feature sections to know if the recording environments are the same.
CheckFeatureSection()180*288bf522SAndroid Build Coastguard Worker   bool CheckFeatureSection() {
181*288bf522SAndroid Build Coastguard Worker     auto get_arch = [](std::unique_ptr<RecordFileReader>& reader) {
182*288bf522SAndroid Build Coastguard Worker       return reader->ReadFeatureString(PerfFileFormat::FEAT_ARCH);
183*288bf522SAndroid Build Coastguard Worker     };
184*288bf522SAndroid Build Coastguard Worker     auto get_kernel_version = [](std::unique_ptr<RecordFileReader>& reader) {
185*288bf522SAndroid Build Coastguard Worker       return reader->ReadFeatureString(PerfFileFormat::FEAT_OSRELEASE);
186*288bf522SAndroid Build Coastguard Worker     };
187*288bf522SAndroid Build Coastguard Worker     auto get_meta_info = [](std::unique_ptr<RecordFileReader>& reader, const char* key) {
188*288bf522SAndroid Build Coastguard Worker       auto it = reader->GetMetaInfoFeature().find(key);
189*288bf522SAndroid Build Coastguard Worker       return it == reader->GetMetaInfoFeature().end() ? "" : it->second;
190*288bf522SAndroid Build Coastguard Worker     };
191*288bf522SAndroid Build Coastguard Worker     auto get_simpleperf_version = [&](std::unique_ptr<RecordFileReader>& reader) {
192*288bf522SAndroid Build Coastguard Worker       return get_meta_info(reader, "simpleperf_version");
193*288bf522SAndroid Build Coastguard Worker     };
194*288bf522SAndroid Build Coastguard Worker     auto get_trace_offcpu = [&](std::unique_ptr<RecordFileReader>& reader) {
195*288bf522SAndroid Build Coastguard Worker       return get_meta_info(reader, "trace_offcpu");
196*288bf522SAndroid Build Coastguard Worker     };
197*288bf522SAndroid Build Coastguard Worker     auto get_event_types = [&](std::unique_ptr<RecordFileReader>& reader) {
198*288bf522SAndroid Build Coastguard Worker       std::string s = get_meta_info(reader, "event_type_info");
199*288bf522SAndroid Build Coastguard Worker       std::vector<std::string> v = android::base::Split(s, "\n");
200*288bf522SAndroid Build Coastguard Worker       std::sort(v.begin(), v.end());
201*288bf522SAndroid Build Coastguard Worker       return android::base::Join(v, ";");
202*288bf522SAndroid Build Coastguard Worker     };
203*288bf522SAndroid Build Coastguard Worker     auto get_android_device = [&](std::unique_ptr<RecordFileReader>& reader) {
204*288bf522SAndroid Build Coastguard Worker       return get_meta_info(reader, "product_props");
205*288bf522SAndroid Build Coastguard Worker     };
206*288bf522SAndroid Build Coastguard Worker     auto get_android_version = [&](std::unique_ptr<RecordFileReader>& reader) {
207*288bf522SAndroid Build Coastguard Worker       return get_meta_info(reader, "android_version");
208*288bf522SAndroid Build Coastguard Worker     };
209*288bf522SAndroid Build Coastguard Worker     auto get_app_package_name = [&](std::unique_ptr<RecordFileReader>& reader) {
210*288bf522SAndroid Build Coastguard Worker       return get_meta_info(reader, "app_package_name");
211*288bf522SAndroid Build Coastguard Worker     };
212*288bf522SAndroid Build Coastguard Worker     auto get_clockid = [&](std::unique_ptr<RecordFileReader>& reader) {
213*288bf522SAndroid Build Coastguard Worker       return get_meta_info(reader, "clockid");
214*288bf522SAndroid Build Coastguard Worker     };
215*288bf522SAndroid Build Coastguard Worker     auto get_used_features = [](std::unique_ptr<RecordFileReader>& reader) {
216*288bf522SAndroid Build Coastguard Worker       std::string s;
217*288bf522SAndroid Build Coastguard Worker       for (const auto& [key, _] : reader->FeatureSectionDescriptors()) {
218*288bf522SAndroid Build Coastguard Worker         s += std::to_string(key) + ",";
219*288bf522SAndroid Build Coastguard Worker       }
220*288bf522SAndroid Build Coastguard Worker       return s;
221*288bf522SAndroid Build Coastguard Worker     };
222*288bf522SAndroid Build Coastguard Worker 
223*288bf522SAndroid Build Coastguard Worker     using value_func_t = std::function<std::string(std::unique_ptr<RecordFileReader>&)>;
224*288bf522SAndroid Build Coastguard Worker     std::vector<std::pair<std::string, value_func_t>> check_entries = {
225*288bf522SAndroid Build Coastguard Worker         std::make_pair("arch", get_arch),
226*288bf522SAndroid Build Coastguard Worker         std::make_pair("kernel_version", get_kernel_version),
227*288bf522SAndroid Build Coastguard Worker         std::make_pair("simpleperf_version", get_simpleperf_version),
228*288bf522SAndroid Build Coastguard Worker         std::make_pair("trace_offcpu", get_trace_offcpu),
229*288bf522SAndroid Build Coastguard Worker         std::make_pair("event_types", get_event_types),
230*288bf522SAndroid Build Coastguard Worker         std::make_pair("android_device", get_android_device),
231*288bf522SAndroid Build Coastguard Worker         std::make_pair("android_version", get_android_version),
232*288bf522SAndroid Build Coastguard Worker         std::make_pair("app_package_name", get_app_package_name),
233*288bf522SAndroid Build Coastguard Worker         std::make_pair("clockid", get_clockid),
234*288bf522SAndroid Build Coastguard Worker         std::make_pair("used_features", get_used_features),
235*288bf522SAndroid Build Coastguard Worker     };
236*288bf522SAndroid Build Coastguard Worker 
237*288bf522SAndroid Build Coastguard Worker     for (const auto& [name, get_value] : check_entries) {
238*288bf522SAndroid Build Coastguard Worker       std::string value0 = get_value(readers_[0]);
239*288bf522SAndroid Build Coastguard Worker       for (size_t i = 1; i < readers_.size(); i++) {
240*288bf522SAndroid Build Coastguard Worker         std::string value = get_value(readers_[i]);
241*288bf522SAndroid Build Coastguard Worker         if (value != value0) {
242*288bf522SAndroid Build Coastguard Worker           LOG(ERROR) << input_files_[0] << " and " << input_files_[i] << " are not mergeable for "
243*288bf522SAndroid Build Coastguard Worker                      << name << " difference: " << value0 << " vs " << value;
244*288bf522SAndroid Build Coastguard Worker           return false;
245*288bf522SAndroid Build Coastguard Worker         }
246*288bf522SAndroid Build Coastguard Worker       }
247*288bf522SAndroid Build Coastguard Worker     }
248*288bf522SAndroid Build Coastguard Worker 
249*288bf522SAndroid Build Coastguard Worker     if (readers_[0]->HasFeature(PerfFileFormat::FEAT_AUXTRACE)) {
250*288bf522SAndroid Build Coastguard Worker       LOG(ERROR) << "merging of recording files with auxtrace feature isn't supported";
251*288bf522SAndroid Build Coastguard Worker       return false;
252*288bf522SAndroid Build Coastguard Worker     }
253*288bf522SAndroid Build Coastguard Worker     return true;
254*288bf522SAndroid Build Coastguard Worker   }
255*288bf522SAndroid Build Coastguard Worker 
256*288bf522SAndroid Build Coastguard Worker   // Check attr sections to know if recorded event types are the same.
CheckAttrSection()257*288bf522SAndroid Build Coastguard Worker   bool CheckAttrSection() {
258*288bf522SAndroid Build Coastguard Worker     const EventAttrIds& attrs0 = readers_[0]->AttrSection();
259*288bf522SAndroid Build Coastguard Worker     for (size_t i = 1; i < readers_.size(); i++) {
260*288bf522SAndroid Build Coastguard Worker       const EventAttrIds& attrs = readers_[i]->AttrSection();
261*288bf522SAndroid Build Coastguard Worker       if (attrs.size() != attrs0.size()) {
262*288bf522SAndroid Build Coastguard Worker         LOG(ERROR) << input_files_[0] << " and " << input_files_[i]
263*288bf522SAndroid Build Coastguard Worker                    << " are not mergeable for recording different event types";
264*288bf522SAndroid Build Coastguard Worker         return false;
265*288bf522SAndroid Build Coastguard Worker       }
266*288bf522SAndroid Build Coastguard Worker       for (size_t attr_id = 0; attr_id < attrs.size(); attr_id++) {
267*288bf522SAndroid Build Coastguard Worker         if (attrs[attr_id].attr != attrs0[attr_id].attr) {
268*288bf522SAndroid Build Coastguard Worker           LOG(ERROR) << input_files_[0] << " and " << input_files_[i]
269*288bf522SAndroid Build Coastguard Worker                      << " are not mergeable for recording different event types";
270*288bf522SAndroid Build Coastguard Worker           return false;
271*288bf522SAndroid Build Coastguard Worker         }
272*288bf522SAndroid Build Coastguard Worker       }
273*288bf522SAndroid Build Coastguard Worker     }
274*288bf522SAndroid Build Coastguard Worker     return true;
275*288bf522SAndroid Build Coastguard Worker   }
276*288bf522SAndroid Build Coastguard Worker 
MergeAttrSection()277*288bf522SAndroid Build Coastguard Worker   bool MergeAttrSection() { return writer_->WriteAttrSection(readers_[0]->AttrSection()); }
278*288bf522SAndroid Build Coastguard Worker 
MergeDataSection()279*288bf522SAndroid Build Coastguard Worker   bool MergeDataSection() {
280*288bf522SAndroid Build Coastguard Worker     for (size_t i = 0; i < readers_.size(); i++) {
281*288bf522SAndroid Build Coastguard Worker       if (i != 0) {
282*288bf522SAndroid Build Coastguard Worker         if (!WriteGapInDataSection(i - 1, i)) {
283*288bf522SAndroid Build Coastguard Worker           return false;
284*288bf522SAndroid Build Coastguard Worker         }
285*288bf522SAndroid Build Coastguard Worker       }
286*288bf522SAndroid Build Coastguard Worker       auto callback = [this](std::unique_ptr<Record> record) {
287*288bf522SAndroid Build Coastguard Worker         return ProcessRecord(record.get());
288*288bf522SAndroid Build Coastguard Worker       };
289*288bf522SAndroid Build Coastguard Worker       if (!readers_[i]->ReadDataSection(callback)) {
290*288bf522SAndroid Build Coastguard Worker         return false;
291*288bf522SAndroid Build Coastguard Worker       }
292*288bf522SAndroid Build Coastguard Worker     }
293*288bf522SAndroid Build Coastguard Worker     return true;
294*288bf522SAndroid Build Coastguard Worker   }
295*288bf522SAndroid Build Coastguard Worker 
ProcessRecord(Record * record)296*288bf522SAndroid Build Coastguard Worker   bool ProcessRecord(Record* record) { return writer_->WriteRecord(*record); }
297*288bf522SAndroid Build Coastguard Worker 
WriteGapInDataSection(size_t prev_reader_id,size_t next_reader_id)298*288bf522SAndroid Build Coastguard Worker   bool WriteGapInDataSection(size_t prev_reader_id, size_t next_reader_id) {
299*288bf522SAndroid Build Coastguard Worker     // MergeAttrSection() only maps event_ids in readers_[0] to event attrs. So we need to
300*288bf522SAndroid Build Coastguard Worker     // map event_ids in readers_[next_read_id] to event attrs. The map info is put into an
301*288bf522SAndroid Build Coastguard Worker     // EventIdRecord.
302*288bf522SAndroid Build Coastguard Worker     const std::unordered_map<uint64_t, size_t>& cur_map = readers_[prev_reader_id]->EventIdMap();
303*288bf522SAndroid Build Coastguard Worker     const EventAttrIds& attrs = readers_[next_reader_id]->AttrSection();
304*288bf522SAndroid Build Coastguard Worker     std::vector<uint64_t> event_id_data;
305*288bf522SAndroid Build Coastguard Worker     for (size_t attr_id = 0; attr_id < attrs.size(); attr_id++) {
306*288bf522SAndroid Build Coastguard Worker       for (size_t event_id : attrs[attr_id].ids) {
307*288bf522SAndroid Build Coastguard Worker         if (auto it = cur_map.find(event_id); it == cur_map.end() || it->second != attr_id) {
308*288bf522SAndroid Build Coastguard Worker           event_id_data.push_back(attr_id);
309*288bf522SAndroid Build Coastguard Worker           event_id_data.push_back(event_id);
310*288bf522SAndroid Build Coastguard Worker         }
311*288bf522SAndroid Build Coastguard Worker       }
312*288bf522SAndroid Build Coastguard Worker     }
313*288bf522SAndroid Build Coastguard Worker     if (!event_id_data.empty()) {
314*288bf522SAndroid Build Coastguard Worker       EventIdRecord record(event_id_data);
315*288bf522SAndroid Build Coastguard Worker       if (!ProcessRecord(&record)) {
316*288bf522SAndroid Build Coastguard Worker         return false;
317*288bf522SAndroid Build Coastguard Worker       }
318*288bf522SAndroid Build Coastguard Worker     }
319*288bf522SAndroid Build Coastguard Worker     return true;
320*288bf522SAndroid Build Coastguard Worker   }
321*288bf522SAndroid Build Coastguard Worker 
MergeFeatureSection()322*288bf522SAndroid Build Coastguard Worker   bool MergeFeatureSection() {
323*288bf522SAndroid Build Coastguard Worker     std::vector<int> features;
324*288bf522SAndroid Build Coastguard Worker     for (const auto& [key, _] : readers_[0]->FeatureSectionDescriptors()) {
325*288bf522SAndroid Build Coastguard Worker       features.push_back(key);
326*288bf522SAndroid Build Coastguard Worker     }
327*288bf522SAndroid Build Coastguard Worker     if (!writer_->BeginWriteFeatures(features.size())) {
328*288bf522SAndroid Build Coastguard Worker       return false;
329*288bf522SAndroid Build Coastguard Worker     }
330*288bf522SAndroid Build Coastguard Worker     for (int feature : features) {
331*288bf522SAndroid Build Coastguard Worker       if (feature == PerfFileFormat::FEAT_OSRELEASE || feature == PerfFileFormat::FEAT_ARCH ||
332*288bf522SAndroid Build Coastguard Worker           feature == PerfFileFormat::FEAT_BRANCH_STACK ||
333*288bf522SAndroid Build Coastguard Worker           feature == PerfFileFormat::FEAT_META_INFO || feature == PerfFileFormat::FEAT_CMDLINE) {
334*288bf522SAndroid Build Coastguard Worker         std::vector<char> data;
335*288bf522SAndroid Build Coastguard Worker         if (!readers_[0]->ReadFeatureSection(feature, &data) ||
336*288bf522SAndroid Build Coastguard Worker             !writer_->WriteFeature(feature, data.data(), data.size())) {
337*288bf522SAndroid Build Coastguard Worker           return false;
338*288bf522SAndroid Build Coastguard Worker         }
339*288bf522SAndroid Build Coastguard Worker       } else if (feature == PerfFileFormat::FEAT_BUILD_ID) {
340*288bf522SAndroid Build Coastguard Worker         WriteBuildIdFeature();
341*288bf522SAndroid Build Coastguard Worker       } else if (feature == PerfFileFormat::FEAT_FILE || feature == PerfFileFormat::FEAT_FILE2) {
342*288bf522SAndroid Build Coastguard Worker         WriteFileFeature();
343*288bf522SAndroid Build Coastguard Worker       } else {
344*288bf522SAndroid Build Coastguard Worker         LOG(WARNING) << "Drop feature " << feature << ", which isn't supported in the merge cmd.";
345*288bf522SAndroid Build Coastguard Worker       }
346*288bf522SAndroid Build Coastguard Worker     }
347*288bf522SAndroid Build Coastguard Worker     return writer_->EndWriteFeatures();
348*288bf522SAndroid Build Coastguard Worker   }
349*288bf522SAndroid Build Coastguard Worker 
WriteBuildIdFeature()350*288bf522SAndroid Build Coastguard Worker   bool WriteBuildIdFeature() {
351*288bf522SAndroid Build Coastguard Worker     std::map<std::string, BuildIdRecord> build_ids;
352*288bf522SAndroid Build Coastguard Worker     std::unordered_set<std::string> files_to_drop;
353*288bf522SAndroid Build Coastguard Worker     for (auto& reader : readers_) {
354*288bf522SAndroid Build Coastguard Worker       for (auto& record : reader->ReadBuildIdFeature()) {
355*288bf522SAndroid Build Coastguard Worker         auto it = build_ids.find(record.filename);
356*288bf522SAndroid Build Coastguard Worker         if (it == build_ids.end()) {
357*288bf522SAndroid Build Coastguard Worker           build_ids.emplace(record.filename, std::move(record));
358*288bf522SAndroid Build Coastguard Worker         } else if (it->second.build_id != record.build_id) {
359*288bf522SAndroid Build Coastguard Worker           if (files_to_drop.count(record.filename) == 0) {
360*288bf522SAndroid Build Coastguard Worker             files_to_drop.emplace(record.filename);
361*288bf522SAndroid Build Coastguard Worker             LOG(WARNING)
362*288bf522SAndroid Build Coastguard Worker                 << record.filename
363*288bf522SAndroid Build Coastguard Worker                 << " has different build ids in different record files. So drop its build ids.";
364*288bf522SAndroid Build Coastguard Worker           }
365*288bf522SAndroid Build Coastguard Worker         }
366*288bf522SAndroid Build Coastguard Worker       }
367*288bf522SAndroid Build Coastguard Worker     }
368*288bf522SAndroid Build Coastguard Worker     std::vector<BuildIdRecord> records;
369*288bf522SAndroid Build Coastguard Worker     for (auto& [filename, record] : build_ids) {
370*288bf522SAndroid Build Coastguard Worker       if (files_to_drop.count(filename) == 0) {
371*288bf522SAndroid Build Coastguard Worker         records.emplace_back(std::move(record));
372*288bf522SAndroid Build Coastguard Worker       }
373*288bf522SAndroid Build Coastguard Worker     }
374*288bf522SAndroid Build Coastguard Worker     return writer_->WriteBuildIdFeature(records);
375*288bf522SAndroid Build Coastguard Worker   }
376*288bf522SAndroid Build Coastguard Worker 
WriteFileFeature()377*288bf522SAndroid Build Coastguard Worker   bool WriteFileFeature() {
378*288bf522SAndroid Build Coastguard Worker     std::map<std::string, MergedFileFeature> file_map;
379*288bf522SAndroid Build Coastguard Worker     std::unordered_set<std::string> files_to_drop;
380*288bf522SAndroid Build Coastguard Worker 
381*288bf522SAndroid Build Coastguard Worker     // Read file features.
382*288bf522SAndroid Build Coastguard Worker     for (auto& reader : readers_) {
383*288bf522SAndroid Build Coastguard Worker       FileFeature file;
384*288bf522SAndroid Build Coastguard Worker       uint64_t read_pos = 0;
385*288bf522SAndroid Build Coastguard Worker       bool error = false;
386*288bf522SAndroid Build Coastguard Worker       while (reader->ReadFileFeature(read_pos, file, error)) {
387*288bf522SAndroid Build Coastguard Worker         if (files_to_drop.count(file.path) != 0) {
388*288bf522SAndroid Build Coastguard Worker           continue;
389*288bf522SAndroid Build Coastguard Worker         }
390*288bf522SAndroid Build Coastguard Worker         if (auto it = file_map.find(file.path); it == file_map.end()) {
391*288bf522SAndroid Build Coastguard Worker           file_map.emplace(file.path, file);
392*288bf522SAndroid Build Coastguard Worker         } else if (!it->second.Merge(file)) {
393*288bf522SAndroid Build Coastguard Worker           LOG(WARNING)
394*288bf522SAndroid Build Coastguard Worker               << file.path
395*288bf522SAndroid Build Coastguard Worker               << " has address-conflict symbols in different record files. So drop its symbols.";
396*288bf522SAndroid Build Coastguard Worker           files_to_drop.emplace(file.path);
397*288bf522SAndroid Build Coastguard Worker         }
398*288bf522SAndroid Build Coastguard Worker       }
399*288bf522SAndroid Build Coastguard Worker       if (error) {
400*288bf522SAndroid Build Coastguard Worker         return false;
401*288bf522SAndroid Build Coastguard Worker       }
402*288bf522SAndroid Build Coastguard Worker     }
403*288bf522SAndroid Build Coastguard Worker     // Write file features.
404*288bf522SAndroid Build Coastguard Worker     for (const auto& [file_path, file] : file_map) {
405*288bf522SAndroid Build Coastguard Worker       if (files_to_drop.count(file_path) != 0) {
406*288bf522SAndroid Build Coastguard Worker         continue;
407*288bf522SAndroid Build Coastguard Worker       }
408*288bf522SAndroid Build Coastguard Worker       FileFeature file_feature;
409*288bf522SAndroid Build Coastguard Worker       file.ToFileFeature(&file_feature);
410*288bf522SAndroid Build Coastguard Worker       if (!writer_->WriteFileFeature(file_feature)) {
411*288bf522SAndroid Build Coastguard Worker         return false;
412*288bf522SAndroid Build Coastguard Worker       }
413*288bf522SAndroid Build Coastguard Worker     }
414*288bf522SAndroid Build Coastguard Worker     return true;
415*288bf522SAndroid Build Coastguard Worker   }
416*288bf522SAndroid Build Coastguard Worker 
417*288bf522SAndroid Build Coastguard Worker   std::vector<std::string> input_files_;
418*288bf522SAndroid Build Coastguard Worker   std::vector<std::unique_ptr<RecordFileReader>> readers_;
419*288bf522SAndroid Build Coastguard Worker   std::string output_file_;
420*288bf522SAndroid Build Coastguard Worker   std::unique_ptr<RecordFileWriter> writer_;
421*288bf522SAndroid Build Coastguard Worker };
422*288bf522SAndroid Build Coastguard Worker 
423*288bf522SAndroid Build Coastguard Worker }  // namespace
424*288bf522SAndroid Build Coastguard Worker 
RegisterMergeCommand()425*288bf522SAndroid Build Coastguard Worker void RegisterMergeCommand() {
426*288bf522SAndroid Build Coastguard Worker   return RegisterCommand("merge", [] { return std::unique_ptr<Command>(new MergeCommand); });
427*288bf522SAndroid Build Coastguard Worker }
428*288bf522SAndroid Build Coastguard Worker 
429*288bf522SAndroid Build Coastguard Worker }  // namespace simpleperf
430