Dump bit width from classes

The ABI tool dumps bit width for bit fields in classes. It reports any
bit width change as ABI difference. It can detect the case where a bit
field is resized without affecting the other fields.

Test: ANDROID_BUILD_TOP=`realpath .` \
      PATH=$PATH:`realpath out/soong/dist/bin` \
      development/vndk/tools/header-checker/tests/test.py
Bug: 323447559
Change-Id: I669155a98a870b87a1ca8ea44f3f848358b304a8
diff --git a/vndk/tools/header-checker/src/dumper/abi_wrappers.cpp b/vndk/tools/header-checker/src/dumper/abi_wrappers.cpp
index 4c81580..24749e7 100644
--- a/vndk/tools/header-checker/src/dumper/abi_wrappers.cpp
+++ b/vndk/tools/header-checker/src/dumper/abi_wrappers.cpp
@@ -605,9 +605,11 @@
     }
     std::string field_name(field->getName());
     uint64_t field_offset = record_layout.getFieldOffset(field_index);
+    uint64_t bit_width =
+        field->isBitField() ? field->getBitWidthValue(*ast_contextp_) : 0;
     recordp->AddRecordField(repr::RecordFieldIR(
         field_name, GetTypeUniqueId(field_type), field_offset,
-        AccessClangToIR(field->getAccess())));
+        AccessClangToIR(field->getAccess()), field->isBitField(), bit_width));
     field++;
     field_index++;
   }
diff --git a/vndk/tools/header-checker/src/repr/abi_diff_helpers.cpp b/vndk/tools/header-checker/src/repr/abi_diff_helpers.cpp
index f99aa61..9c4f20c 100644
--- a/vndk/tools/header-checker/src/repr/abi_diff_helpers.cpp
+++ b/vndk/tools/header-checker/src/repr/abi_diff_helpers.cpp
@@ -366,7 +366,9 @@
   // CompareAndDumpTypeDiff should not return kDirectExt.
   // In case it happens, report an incompatible diff for review.
   if (field_diff_status.IsExtension() ||
-      old_field->GetOffset() != new_field->GetOffset()) {
+      old_field->GetOffset() != new_field->GetOffset() ||
+      old_field->IsBitField() != new_field->IsBitField() ||
+      old_field->GetBitWidth() != new_field->GetBitWidth()) {
     field_diff_status.CombineWith(DiffStatus::kDirectDiff);
   }
   field_diff_status.CombineWith(
@@ -376,8 +378,8 @@
 
 // This function filters out the pairs of old and new fields that meet the
 // following conditions:
-//   The old field's (offset, type) is unique in old_fields.
-//   The new field's (offset, type) is unique in new_fields.
+//   The old field's (offset, bit width, type) is unique in old_fields.
+//   The new field's (offset, bit width, type) is unique in new_fields.
 //   The two fields have compatible attributes except the name.
 //
 // This function returns either kNoDiff or kIndirectDiff. It is the status of
@@ -393,6 +395,12 @@
     if (first->GetOffset() != second->GetOffset()) {
       return first->GetOffset() < second->GetOffset();
     }
+    if (first->IsBitField() != second->IsBitField()) {
+      return first->IsBitField() < second->IsBitField();
+    }
+    if (first->GetBitWidth() != second->GetBitWidth()) {
+      return first->GetBitWidth() < second->GetBitWidth();
+    }
     return first->GetReferencedType() < second->GetReferencedType();
   };
   std::sort(old_fields.begin(), old_end, is_less);
@@ -452,14 +460,17 @@
   // Map names to RecordFieldIR.
   AbiElementMap<const RecordFieldIR *> old_fields_map;
   AbiElementMap<const RecordFieldIR *> new_fields_map;
-  utils::AddToMap(
-      &old_fields_map, old_fields,
-      [](const RecordFieldIR *f) {return f->GetName();},
-      [](const RecordFieldIR *f) {return f;});
-  utils::AddToMap(
-      &new_fields_map, new_fields,
-      [](const RecordFieldIR *f) {return f->GetName();},
-      [](const RecordFieldIR *f) {return f;});
+
+  auto get_field_name = [](const RecordFieldIR *f) -> std::string {
+    return !f->GetName().empty()
+               ? f->GetName()
+               : std::to_string(f->GetOffset()) + "#" + f->GetReferencedType();
+  };
+
+  utils::AddToMap(&old_fields_map, old_fields, get_field_name,
+                  [](const RecordFieldIR *f) { return f; });
+  utils::AddToMap(&new_fields_map, new_fields, get_field_name,
+                  [](const RecordFieldIR *f) { return f; });
   // Compare the fields whose names are not present in both records.
   result.removed_fields =
       utils::FindRemovedElements(old_fields_map, new_fields_map);
diff --git a/vndk/tools/header-checker/src/repr/ir_representation.h b/vndk/tools/header-checker/src/repr/ir_representation.h
index 8f76f65..731af27 100644
--- a/vndk/tools/header-checker/src/repr/ir_representation.h
+++ b/vndk/tools/header-checker/src/repr/ir_representation.h
@@ -330,9 +330,14 @@
 class RecordFieldIR : public ReferencesOtherType {
  public:
   RecordFieldIR(const std::string &name, const std::string &type,
-                uint64_t offset, AccessSpecifierIR access)
-      : ReferencesOtherType(type), name_(name), offset_(offset),
-        access_(access) {}
+                uint64_t offset, AccessSpecifierIR access, bool is_bit_field,
+                uint64_t bit_width)
+      : ReferencesOtherType(type),
+        name_(name),
+        offset_(offset),
+        access_(access),
+        is_bit_field_(is_bit_field),
+        bit_width_(bit_width) {}
 
   RecordFieldIR() {}
 
@@ -348,10 +353,16 @@
     return access_;
   }
 
+  bool IsBitField() const { return is_bit_field_; }
+
+  uint64_t GetBitWidth() const { return bit_width_; }
+
  protected:
   std::string name_;
   uint64_t offset_ = 0;
   AccessSpecifierIR access_ = AccessSpecifierIR::PublicAccess;
+  bool is_bit_field_ = false;
+  uint64_t bit_width_ = 0;
 };
 
 class RecordTypeIR : public TypeIR, public TemplatedArtifactIR {
diff --git a/vndk/tools/header-checker/src/repr/json/ir_dumper.cpp b/vndk/tools/header-checker/src/repr/json/ir_dumper.cpp
index bf9e38e..faf49d6 100644
--- a/vndk/tools/header-checker/src/repr/json/ir_dumper.cpp
+++ b/vndk/tools/header-checker/src/repr/json/ir_dumper.cpp
@@ -106,6 +106,8 @@
   record_field.Set("referenced_type", record_field_ir->GetReferencedType());
   AddAccess(record_field, record_field_ir->GetAccess());
   record_field.Set("field_offset", (uint64_t)record_field_ir->GetOffset());
+  record_field.Set("is_bit_field", record_field_ir->IsBitField());
+  record_field.Set("bit_width", (uint64_t)record_field_ir->GetBitWidth());
   return record_field;
 }
 
diff --git a/vndk/tools/header-checker/src/repr/json/ir_reader.cpp b/vndk/tools/header-checker/src/repr/json/ir_reader.cpp
index 51e1087..9c41d45 100644
--- a/vndk/tools/header-checker/src/repr/json/ir_reader.cpp
+++ b/vndk/tools/header-checker/src/repr/json/ir_reader.cpp
@@ -232,7 +232,8 @@
   for (auto &&field : record_type.GetObjects("fields")) {
     RecordFieldIR record_field_ir(
         field.GetString("field_name"), field.GetString("referenced_type"),
-        field.GetUint("field_offset"), GetAccess(field));
+        field.GetUint("field_offset"), GetAccess(field),
+        field.GetBool("is_bit_field"), field.GetUint("bit_width"));
     record_ir->AddRecordField(std::move(record_field_ir));
   }
 }
diff --git a/vndk/tools/header-checker/src/repr/protobuf/converter.h b/vndk/tools/header-checker/src/repr/protobuf/converter.h
index 585001e..191ac0f 100644
--- a/vndk/tools/header-checker/src/repr/protobuf/converter.h
+++ b/vndk/tools/header-checker/src/repr/protobuf/converter.h
@@ -307,6 +307,10 @@
   record_field_protobuf->set_access(
       AccessIRToProtobuf(record_field_ir->GetAccess()));
   record_field_protobuf->set_field_offset(record_field_ir->GetOffset());
+  if (record_field_ir->IsBitField()) {
+    record_field_protobuf->set_is_bit_field(true);
+    record_field_protobuf->set_bit_width(record_field_ir->GetBitWidth());
+  }
 }
 
 inline bool SetIRToProtobufBaseSpecifier(
diff --git a/vndk/tools/header-checker/src/repr/protobuf/ir_reader.cpp b/vndk/tools/header-checker/src/repr/protobuf/ir_reader.cpp
index 3b08c66..7735714 100644
--- a/vndk/tools/header-checker/src/repr/protobuf/ir_reader.cpp
+++ b/vndk/tools/header-checker/src/repr/protobuf/ir_reader.cpp
@@ -133,9 +133,10 @@
     const google::protobuf::RepeatedPtrField<abi_dump::RecordFieldDecl> &rfp) {
   std::vector<RecordFieldIR> record_type_fields_ir;
   for (auto &&field : rfp) {
-    RecordFieldIR record_field_ir(field.field_name(), field.referenced_type(),
-                                  field.field_offset(),
-                                  AccessProtobufToIR(field.access()));
+    RecordFieldIR record_field_ir(
+        field.field_name(), field.referenced_type(), field.field_offset(),
+        AccessProtobufToIR(field.access()), field.is_bit_field(),
+        field.bit_width());
     record_type_fields_ir.emplace_back(std::move(record_field_ir));
   }
   return record_type_fields_ir;
diff --git a/vndk/tools/header-checker/src/repr/protobuf/proto/abi_dump.proto b/vndk/tools/header-checker/src/repr/protobuf/proto/abi_dump.proto
index 1ce604e..110699c 100644
--- a/vndk/tools/header-checker/src/repr/protobuf/proto/abi_dump.proto
+++ b/vndk/tools/header-checker/src/repr/protobuf/proto/abi_dump.proto
@@ -79,11 +79,12 @@
 }
 
 message RecordFieldDecl {
-  // For future additions.
   optional string referenced_type = 1;
   optional uint64 field_offset = 2;
   optional string field_name = 3;
   optional AccessSpecifier access = 4 [default = public_access];
+  optional bool is_bit_field = 5;
+  optional uint64 bit_width = 6;
 }
 
 message EnumFieldDecl {
diff --git a/vndk/tools/header-checker/tests/integration/bit_field/include/base.h b/vndk/tools/header-checker/tests/integration/bit_field/include/base.h
new file mode 100644
index 0000000..08a6e96
--- /dev/null
+++ b/vndk/tools/header-checker/tests/integration/bit_field/include/base.h
@@ -0,0 +1,13 @@
+struct Struct {
+  struct {
+    char unnamed_struct;
+  };
+  char : 2;
+  char a : 1;
+  char : 0;
+  char b : 1;
+};
+
+extern "C" {
+void function(Struct*);
+}
diff --git a/vndk/tools/header-checker/tests/integration/bit_field/include/diff.h b/vndk/tools/header-checker/tests/integration/bit_field/include/diff.h
new file mode 100644
index 0000000..9907bf1
--- /dev/null
+++ b/vndk/tools/header-checker/tests/integration/bit_field/include/diff.h
@@ -0,0 +1,13 @@
+struct Struct {
+  struct {
+    char unnamed_struct;
+  };
+  char : 2;
+  char a : 1;
+  char : 0;
+  char b : 2;
+};
+
+extern "C" {
+void function(Struct*);
+}
diff --git a/vndk/tools/header-checker/tests/integration/bit_field/map.txt b/vndk/tools/header-checker/tests/integration/bit_field/map.txt
new file mode 100644
index 0000000..0398ef9
--- /dev/null
+++ b/vndk/tools/header-checker/tests/integration/bit_field/map.txt
@@ -0,0 +1,4 @@
+libbit_field {
+  global:
+    function;
+};
diff --git a/vndk/tools/header-checker/tests/module.py b/vndk/tools/header-checker/tests/module.py
index 151c961..0a97635 100755
--- a/vndk/tools/header-checker/tests/module.py
+++ b/vndk/tools/header-checker/tests/module.py
@@ -821,6 +821,25 @@
         linker_flags=['-input-format', 'Json', '-output-format', 'Json'],
         has_reference_dump=True,
     ),
+    LsdumpModule(
+        name='libbit_field',
+        arch='arm64',
+        srcs=['integration/bit_field/include/base.h'],
+        version_script='integration/bit_field/map.txt',
+        export_include_dirs=['integration/bit_field/include'],
+        dumper_flags=['-output-format', 'Json'],
+        linker_flags=['-input-format', 'Json', '-output-format', 'Json'],
+        has_reference_dump=True,
+    ),
+    LsdumpModule(
+        name='libbit_field_diff',
+        arch='arm64',
+        srcs=['integration/bit_field/include/diff.h'],
+        version_script='integration/bit_field/map.txt',
+        export_include_dirs=['integration/bit_field/include'],
+        dumper_flags=['-output-format', 'Json'],
+        linker_flags=['-input-format', 'Json', '-output-format', 'Json'],
+    ),
 ]
 
 TEST_MODULES = {m.name: m for m in TEST_MODULES}
diff --git a/vndk/tools/header-checker/tests/reference_dumps/arm64/libbit_field.so.lsdump b/vndk/tools/header-checker/tests/reference_dumps/arm64/libbit_field.so.lsdump
new file mode 100644
index 0000000..07acf77
--- /dev/null
+++ b/vndk/tools/header-checker/tests/reference_dumps/arm64/libbit_field.so.lsdump
@@ -0,0 +1,113 @@
+{
+ "array_types" : [],
+ "builtin_types" :
+ [
+  {
+   "alignment" : 1,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIc",
+   "name" : "char",
+   "size" : 1
+  },
+  {
+   "linker_set_key" : "_ZTIv",
+   "name" : "void"
+  }
+ ],
+ "elf_functions" :
+ [
+  {
+   "name" : "function"
+  }
+ ],
+ "elf_objects" : [],
+ "enum_types" : [],
+ "function_types" : [],
+ "functions" :
+ [
+  {
+   "function_name" : "function",
+   "linker_set_key" : "function",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP6Struct"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "development/vndk/tools/header-checker/tests/integration/bit_field/include/base.h"
+  }
+ ],
+ "global_vars" : [],
+ "lvalue_reference_types" : [],
+ "pointer_types" :
+ [
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIP6Struct",
+   "name" : "Struct *",
+   "referenced_type" : "_ZTI6Struct",
+   "size" : 8,
+   "source_file" : "development/vndk/tools/header-checker/tests/integration/bit_field/include/base.h"
+  }
+ ],
+ "qualified_types" : [],
+ "record_types" :
+ [
+  {
+   "alignment" : 1,
+   "fields" :
+   [
+    {
+     "referenced_type" : "_ZTIN6StructUt_E"
+    },
+    {
+     "bit_width" : 2,
+     "field_offset" : 8,
+     "is_bit_field" : true,
+     "referenced_type" : "_ZTIc"
+    },
+    {
+     "bit_width" : 1,
+     "field_name" : "a",
+     "field_offset" : 10,
+     "is_bit_field" : true,
+     "referenced_type" : "_ZTIc"
+    },
+    {
+     "field_offset" : 16,
+     "is_bit_field" : true,
+     "referenced_type" : "_ZTIc"
+    },
+    {
+     "bit_width" : 1,
+     "field_name" : "b",
+     "field_offset" : 16,
+     "is_bit_field" : true,
+     "referenced_type" : "_ZTIc"
+    }
+   ],
+   "linker_set_key" : "_ZTI6Struct",
+   "name" : "Struct",
+   "size" : 3,
+   "source_file" : "development/vndk/tools/header-checker/tests/integration/bit_field/include/base.h"
+  },
+  {
+   "alignment" : 1,
+   "fields" :
+   [
+    {
+     "field_name" : "unnamed_struct",
+     "referenced_type" : "_ZTIc"
+    }
+   ],
+   "is_anonymous" : true,
+   "linker_set_key" : "_ZTIN6StructUt_E",
+   "name" : "Struct::(anonymous)",
+   "size" : 1,
+   "source_file" : "development/vndk/tools/header-checker/tests/integration/bit_field/include/base.h"
+  }
+ ],
+ "rvalue_reference_types" : []
+}
diff --git a/vndk/tools/header-checker/tests/test.py b/vndk/tools/header-checker/tests/test.py
index 0ca37e0..39934a9 100755
--- a/vndk/tools/header-checker/tests/test.py
+++ b/vndk/tools/header-checker/tests/test.py
@@ -539,6 +539,14 @@
                                    'x86_64', 'libtest', diff_flags)
         self.assertEqual(return_code, 1)
 
+    def test_bit_field_diff(self):
+        self.prepare_and_absolute_diff_all_archs(
+            "libbit_field", "libbit_field")
+        self.prepare_and_run_abi_diff_all_archs(
+            "libbit_field", "libbit_field_diff", 8,
+            flags=["-input-format-new", "Json", "-input-format-old", "Json"],
+            create_old=False, create_new=True)
+
 
 if __name__ == '__main__':
     unittest.main()