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