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