• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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