1 // Copyright (C) 2019 The Android Open Source Project
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 "repr/protobuf/ir_diff_dumper.h"
16
17 #include "repr/ir_diff_representation.h"
18 #include "repr/protobuf/api.h"
19 #include "repr/protobuf/converter.h"
20
21 #include <fstream>
22 #include <memory>
23 #include <string>
24
25 #include <llvm/Support/raw_ostream.h>
26
27 #include <google/protobuf/io/zero_copy_stream_impl.h>
28 #include <google/protobuf/text_format.h>
29
30
31 namespace header_checker {
32 namespace repr {
33
34
AddLibNameIR(const std::string & name)35 void ProtobufIRDiffDumper::AddLibNameIR(const std::string &name) {
36 diff_tu_->set_lib_name(name);
37 }
38
AddArchIR(const std::string & arch)39 void ProtobufIRDiffDumper::AddArchIR(const std::string &arch) {
40 diff_tu_->set_arch(arch);
41 }
42
GetCompatibilityStatusIR()43 CompatibilityStatusIR ProtobufIRDiffDumper::GetCompatibilityStatusIR() {
44 if (diff_tu_->functions_removed().size() != 0 ||
45 diff_tu_->global_vars_removed().size() != 0 ||
46 diff_tu_->function_diffs().size() != 0 ||
47 diff_tu_->global_var_diffs().size() != 0 ||
48 diff_tu_->enum_type_diffs().size() != 0 ||
49 diff_tu_->record_type_diffs().size() != 0) {
50 return CompatibilityStatusIR::Incompatible;
51 }
52
53 CompatibilityStatusIR combined_status = CompatibilityStatusIR::Compatible;
54
55 if (diff_tu_->enum_type_extension_diffs().size() != 0 ||
56 diff_tu_->functions_added().size() != 0 ||
57 diff_tu_->global_vars_added().size() != 0 ||
58 diff_tu_->record_type_extension_diffs().size() != 0 ||
59 diff_tu_->function_extension_diffs().size() != 0) {
60 combined_status = combined_status | CompatibilityStatusIR::Extension;
61 }
62
63 if (diff_tu_->unreferenced_enum_type_diffs().size() != 0 ||
64 diff_tu_->unreferenced_enum_type_extension_diffs().size() != 0 ||
65 diff_tu_->unreferenced_enum_types_added().size() != 0 ||
66 diff_tu_->unreferenced_enum_types_removed().size() != 0 ||
67 diff_tu_->unreferenced_record_type_diffs().size() != 0 ||
68 diff_tu_->unreferenced_record_type_extension_diffs().size() != 0 ||
69 diff_tu_->unreferenced_record_types_added().size() != 0 ||
70 diff_tu_->unreferenced_record_types_removed().size() != 0) {
71 combined_status =
72 combined_status | CompatibilityStatusIR::UnreferencedChanges;
73 }
74
75 if (diff_tu_->removed_elf_functions().size() != 0 ||
76 diff_tu_->removed_elf_objects().size() != 0) {
77 combined_status = combined_status | CompatibilityStatusIR::ElfIncompatible;
78 }
79
80 return combined_status;
81 }
82
AddCompatibilityStatusIR(CompatibilityStatusIR status)83 void ProtobufIRDiffDumper::AddCompatibilityStatusIR(
84 CompatibilityStatusIR status) {
85 diff_tu_->set_compatibility_status(CompatibilityStatusIRToProtobuf(status));
86 }
87
AddDiffMessageIR(const DiffMessageIR * message,const std::string & type_stack,DiffKind diff_kind)88 bool ProtobufIRDiffDumper::AddDiffMessageIR(const DiffMessageIR *message,
89 const std::string &type_stack,
90 DiffKind diff_kind) {
91 switch (message->Kind()) {
92 case RecordTypeKind:
93 return AddRecordTypeDiffIR(
94 static_cast<const RecordTypeDiffIR *>(message), type_stack,
95 diff_kind);
96 case EnumTypeKind:
97 return AddEnumTypeDiffIR(
98 static_cast<const EnumTypeDiffIR *>(message), type_stack, diff_kind);
99 case GlobalVarKind:
100 return AddGlobalVarDiffIR(
101 static_cast<const GlobalVarDiffIR*>(message), type_stack, diff_kind);
102 case FunctionKind:
103 return AddFunctionDiffIR(
104 static_cast<const FunctionDiffIR*>(message), type_stack, diff_kind);
105 default:
106 break;
107 }
108 llvm::errs() << "Dump Diff attempted on something not a user defined type / "
109 << "function / global variable\n";
110 return false;
111 }
112
AddLinkableMessageIR(const LinkableMessageIR * message,DiffKind diff_kind)113 bool ProtobufIRDiffDumper::AddLinkableMessageIR(
114 const LinkableMessageIR *message, DiffKind diff_kind) {
115 switch (message->GetKind()) {
116 case RecordTypeKind:
117 return AddLoneRecordTypeDiffIR(
118 static_cast<const RecordTypeIR *>(message), diff_kind);
119 case EnumTypeKind:
120 return AddLoneEnumTypeDiffIR(
121 static_cast<const EnumTypeIR *>(message), diff_kind);
122 case GlobalVarKind:
123 return AddLoneGlobalVarDiffIR(
124 static_cast<const GlobalVarIR*>(message), diff_kind);
125 case FunctionKind:
126 return AddLoneFunctionDiffIR(
127 static_cast<const FunctionIR*>(message), diff_kind);
128 default:
129 break;
130 }
131 llvm::errs() << "Dump Diff attempted on something not a user defined type / "
132 << "function / global variable\n";
133 return false;
134 }
135
AddElfSymbolMessageIR(const ElfSymbolIR * elf_symbol,DiffKind diff_kind)136 bool ProtobufIRDiffDumper::AddElfSymbolMessageIR(const ElfSymbolIR *elf_symbol,
137 DiffKind diff_kind) {
138 switch (elf_symbol->GetKind()) {
139 case ElfSymbolIR::ElfFunctionKind:
140 return AddElfFunctionIR(static_cast<const ElfFunctionIR *>(elf_symbol),
141 diff_kind);
142 break;
143 case ElfSymbolIR::ElfObjectKind:
144 return AddElfObjectIR(static_cast<const ElfObjectIR *>(elf_symbol),
145 diff_kind);
146 break;
147 }
148 // Any other kind is invalid
149 return false;
150 }
151
AddElfFunctionIR(const ElfFunctionIR * elf_function_ir,DiffKind diff_kind)152 bool ProtobufIRDiffDumper::AddElfFunctionIR(
153 const ElfFunctionIR *elf_function_ir, DiffKind diff_kind) {
154 abi_dump::ElfFunction *added_elf_function = nullptr;
155 switch (diff_kind) {
156 case DiffKind::Removed:
157 added_elf_function = diff_tu_->add_removed_elf_functions();
158 break;
159 case DiffKind::Added:
160 added_elf_function = diff_tu_->add_added_elf_functions();
161 break;
162 default:
163 llvm::errs() << "Invalid call to AddElfFunctionIR\n";
164 return false;
165 }
166 if (added_elf_function == nullptr) {
167 return false;
168 }
169 *added_elf_function =
170 IRToProtobufConverter::ConvertElfFunctionIR(elf_function_ir);
171 return true;
172 }
173
AddElfObjectIR(const ElfObjectIR * elf_object_ir,DiffKind diff_kind)174 bool ProtobufIRDiffDumper::AddElfObjectIR(
175 const ElfObjectIR *elf_object_ir, DiffKind diff_kind) {
176 abi_dump::ElfObject *added_elf_object = nullptr;
177 switch (diff_kind) {
178 case DiffKind::Removed:
179 added_elf_object = diff_tu_->add_removed_elf_objects();
180 break;
181 case DiffKind::Added:
182 added_elf_object = diff_tu_->add_added_elf_objects();
183 break;
184 default:
185 llvm::errs() << "Invalid call to AddElfObjectIR\n";
186 return false;
187 }
188 if (added_elf_object == nullptr) {
189 return false;
190 }
191 *added_elf_object =
192 IRToProtobufConverter::ConvertElfObjectIR(elf_object_ir);
193 return true;
194 }
195
AddLoneRecordTypeDiffIR(const RecordTypeIR * record_type_ir,DiffKind diff_kind)196 bool ProtobufIRDiffDumper::AddLoneRecordTypeDiffIR(
197 const RecordTypeIR *record_type_ir, DiffKind diff_kind) {
198 abi_dump::RecordType *added_record_type = nullptr;
199 switch (diff_kind) {
200 case DiffKind::Removed:
201 // Referenced record types do not get reported as added / removed,
202 // the diff shows up in the parent type / function/ global variable
203 // referencing the record.
204 added_record_type = diff_tu_->add_unreferenced_record_types_removed();
205 break;
206 case DiffKind::Added:
207 added_record_type = diff_tu_->add_unreferenced_record_types_added();
208 break;
209 default:
210 llvm::errs() << "Invalid call to AddLoneRecordTypeDiffIR\n";
211 return false;
212 }
213 if (added_record_type == nullptr) {
214 return false;
215 }
216 *added_record_type =
217 IRToProtobufConverter::ConvertRecordTypeIR(record_type_ir);
218 return true;
219 }
220
AddLoneFunctionDiffIR(const FunctionIR * function_ir,DiffKind diff_kind)221 bool ProtobufIRDiffDumper::AddLoneFunctionDiffIR(
222 const FunctionIR *function_ir, DiffKind diff_kind) {
223 abi_dump::FunctionDecl *added_function = nullptr;
224 switch (diff_kind) {
225 case DiffKind::Removed:
226 added_function = diff_tu_->add_functions_removed();
227 break;
228 case DiffKind::Added:
229 added_function = diff_tu_->add_functions_added();
230 break;
231 default:
232 llvm::errs() << "Invalid call to AddLoneFunctionDiffIR\n";
233 return false;
234 }
235 *added_function = IRToProtobufConverter::ConvertFunctionIR(function_ir);
236 return true;
237 }
238
AddLoneEnumTypeDiffIR(const EnumTypeIR * enum_type_ir,DiffKind diff_kind)239 bool ProtobufIRDiffDumper::AddLoneEnumTypeDiffIR(
240 const EnumTypeIR *enum_type_ir, DiffKind diff_kind) {
241 abi_dump::EnumType *added_enum_type = nullptr;
242 switch (diff_kind) {
243 case DiffKind::Removed:
244 // Referenced enum types do not get reported as added / removed,
245 // the diff shows up in the parent type / function/ global variable
246 // referencing the enum.
247 added_enum_type = diff_tu_->add_unreferenced_enum_types_removed();
248 break;
249 case DiffKind::Added:
250 added_enum_type = diff_tu_->add_unreferenced_enum_types_added();
251 break;
252 default:
253 llvm::errs() << "Invalid call to AddLoneRecordTypeDiffIR\n";
254 return false;
255 }
256 if (added_enum_type == nullptr) {
257 return false;
258 }
259 *added_enum_type = IRToProtobufConverter::ConvertEnumTypeIR(enum_type_ir);
260 return true;
261 }
262
AddLoneGlobalVarDiffIR(const GlobalVarIR * global_var_ir,DiffKind diff_kind)263 bool ProtobufIRDiffDumper::AddLoneGlobalVarDiffIR(
264 const GlobalVarIR *global_var_ir, DiffKind diff_kind) {
265 abi_dump::GlobalVarDecl *added_global_var = nullptr;
266 switch (diff_kind) {
267 case DiffKind::Removed:
268 added_global_var = diff_tu_->add_global_vars_removed();
269 break;
270 case DiffKind::Added:
271 added_global_var = diff_tu_->add_global_vars_added();
272 break;
273 default:
274 llvm::errs() << "Invalid call to AddLoneFunctionDiffIR\n";
275 return false;
276 }
277 *added_global_var = IRToProtobufConverter::ConvertGlobalVarIR(global_var_ir);
278 return true;
279 }
280
AddRecordTypeDiffIR(const RecordTypeDiffIR * record_diff_ir,const std::string & type_stack,DiffKind diff_kind)281 bool ProtobufIRDiffDumper::AddRecordTypeDiffIR(
282 const RecordTypeDiffIR *record_diff_ir, const std::string &type_stack,
283 DiffKind diff_kind) {
284 abi_diff::RecordTypeDiff *added_record_type_diff = nullptr;
285 bool is_extended = record_diff_ir->IsExtended();
286 switch (diff_kind) {
287 case DiffKind::Unreferenced:
288 if (is_extended) {
289 added_record_type_diff =
290 diff_tu_->add_unreferenced_record_type_extension_diffs();
291 } else {
292 added_record_type_diff = diff_tu_->add_unreferenced_record_type_diffs();
293 }
294 break;
295 case DiffKind::Referenced:
296 if (is_extended) {
297 added_record_type_diff = diff_tu_->add_record_type_extension_diffs();
298 } else {
299 added_record_type_diff = diff_tu_->add_record_type_diffs();
300 }
301 break;
302 default:
303 break;
304 }
305 if (!added_record_type_diff) {
306 return false;
307 }
308
309 *added_record_type_diff =
310 IRDiffToProtobufConverter::ConvertRecordTypeDiffIR(record_diff_ir);
311 added_record_type_diff->set_type_stack(type_stack);
312 return true;
313 }
314
AddFunctionDiffIR(const FunctionDiffIR * function_diff_ir,const std::string & type_stack,DiffKind diff_kind)315 bool ProtobufIRDiffDumper::AddFunctionDiffIR(
316 const FunctionDiffIR *function_diff_ir, const std::string &type_stack,
317 DiffKind diff_kind) {
318 abi_diff::FunctionDeclDiff *added_function_diff =
319 function_diff_ir->IsExtended() ? diff_tu_->add_function_extension_diffs()
320 : diff_tu_->add_function_diffs();
321 if (!added_function_diff) {
322 return false;
323 }
324 *added_function_diff =
325 IRDiffToProtobufConverter::ConvertFunctionDiffIR(function_diff_ir);
326 return true;
327 }
328
AddEnumTypeDiffIR(const EnumTypeDiffIR * enum_diff_ir,const std::string & type_stack,DiffKind diff_kind)329 bool ProtobufIRDiffDumper::AddEnumTypeDiffIR(const EnumTypeDiffIR *enum_diff_ir,
330 const std::string &type_stack,
331 DiffKind diff_kind) {
332 abi_diff::EnumTypeDiff *added_enum_type_diff = nullptr;
333 switch (diff_kind) {
334 case DiffKind::Unreferenced:
335 if (enum_diff_ir->IsExtended()) {
336 added_enum_type_diff =
337 diff_tu_->add_unreferenced_enum_type_extension_diffs();
338 } else {
339 added_enum_type_diff =
340 diff_tu_->add_unreferenced_enum_type_diffs();
341 }
342 break;
343 case DiffKind::Referenced:
344 if (enum_diff_ir->IsExtended()) {
345 added_enum_type_diff =
346 diff_tu_->add_enum_type_extension_diffs();
347 } else {
348 added_enum_type_diff =
349 diff_tu_->add_enum_type_diffs();
350 }
351 break;
352 default:
353 break;
354 }
355 if (!added_enum_type_diff) {
356 return false;
357 }
358 *added_enum_type_diff =
359 IRDiffToProtobufConverter::ConvertEnumTypeDiffIR(enum_diff_ir);
360 added_enum_type_diff->set_type_stack(type_stack);
361 return true;
362 }
363
AddGlobalVarDiffIR(const GlobalVarDiffIR * global_var_diff_ir,const std::string & type_stack,DiffKind diff_kind)364 bool ProtobufIRDiffDumper::AddGlobalVarDiffIR(
365 const GlobalVarDiffIR *global_var_diff_ir, const std::string &type_stack,
366 DiffKind diff_kind) {
367 abi_diff::GlobalVarDeclDiff *added_global_var_diff =
368 diff_tu_->add_global_var_diffs();
369 if (!added_global_var_diff) {
370 return false;
371 }
372 *added_global_var_diff =
373 IRDiffToProtobufConverter::ConvertGlobalVarDiffIR(global_var_diff_ir);
374 return true;
375 }
376
Dump()377 bool ProtobufIRDiffDumper::Dump() {
378 GOOGLE_PROTOBUF_VERIFY_VERSION;
379 assert(diff_tu_.get() != nullptr);
380 std::ofstream text_output(dump_path_);
381 google::protobuf::io::OstreamOutputStream text_os(&text_output);
382 return google::protobuf::TextFormat::Print(*diff_tu_.get(), &text_os);
383 }
384
CreateProtobufIRDiffDumper(const std::string & dump_path)385 std::unique_ptr<IRDiffDumper> CreateProtobufIRDiffDumper(
386 const std::string &dump_path) {
387 return std::make_unique<ProtobufIRDiffDumper>(dump_path);
388 }
389
390
391 } // namespace repr
392 } // namespace header_checker
393