[Android Unwinder] Implement address table lookup
This CL implements address table lookup for android unwinder.
The lookup of address table provides mapping from pc to
`FunctionTableIndex`.
Design Doc:
https://docs.google.com/document/d/1IYTmGCJZoiQ242xPUZX1fATD6ivsjU1TAt_fPv74ocs/edit?usp=sharing
Bug: 1240698
Change-Id: Ic6f64721d24d1bcd1a247669b656dd499c43b124
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3114830
Commit-Queue: Charlie Hu <chenleihu@google.com>
Reviewed-by: Mike Wittman <wittman@chromium.org>
Cr-Commit-Position: refs/heads/main@{#920915}
diff --git a/base/profiler/chrome_unwind_table_android_unittest.cc b/base/profiler/chrome_unwind_table_android_unittest.cc
index 4ce0b1e8..6d9dc02b 100644
--- a/base/profiler/chrome_unwind_table_android_unittest.cc
+++ b/base/profiler/chrome_unwind_table_android_unittest.cc
@@ -663,10 +663,10 @@
};
EXPECT_EQ(unwind_instruction_table + 3,
- GetFirstUnwindInstructionFromInstructionOffset(
+ GetFirstUnwindInstructionFromFunctionOffsetTableIndex(
unwind_instruction_table, function_offset_table,
- /* function_offset_table_byte_index */ 0x0,
- /* instruction_offset_from_function_start */ 128));
+ {/* instruction_offset_from_function_start */ 128,
+ /* function_offset_table_byte_index */ 0x0}));
}
TEST(ChromeUnwindTableAndroidTest,
@@ -691,10 +691,10 @@
};
EXPECT_EQ(unwind_instruction_table + 3,
- GetFirstUnwindInstructionFromInstructionOffset(
+ GetFirstUnwindInstructionFromFunctionOffsetTableIndex(
unwind_instruction_table, function_offset_table,
- /* function_offset_table_byte_index */ 0x0,
- /* instruction_offset_from_function_start */ 129));
+ {/* instruction_offset_from_function_start */ 129,
+ /* function_offset_table_byte_index */ 0x0}));
}
TEST(ChromeUnwindTableAndroidTest, TestFunctionOffsetTableLookupZeroOffset) {
@@ -718,10 +718,228 @@
};
EXPECT_EQ(unwind_instruction_table + 4,
- GetFirstUnwindInstructionFromInstructionOffset(
+ GetFirstUnwindInstructionFromFunctionOffsetTableIndex(
unwind_instruction_table, function_offset_table,
- /* function_offset_table_byte_index */ 0x0,
- /* instruction_offset_from_function_start */ 0));
+ {/* instruction_offset_from_function_start */ 0,
+ /* function_offset_table_byte_index */ 0x0}));
}
+TEST(ChromeUnwindTableAndroidTest, TestAddressTableLookupEntryInPage) {
+ const uint32_t page_start_instructions[] = {0, 2};
+ const FunctionTableEntry function_offset_table_indices[] = {
+ // Page 0
+ {
+ /* function_start_address_page_instruction_offset */ 0,
+ /* function_offset_table_byte_index */ 20,
+ },
+ {
+ /* function_start_address_page_instruction_offset */ 4,
+ /* function_offset_table_byte_index */ 40,
+ },
+ // Page 1
+ {
+ /* function_start_address_page_instruction_offset */ 6,
+ /* function_offset_table_byte_index */ 70,
+ },
+ };
+
+ {
+ const uint32_t page_number = 0;
+ const uint32_t page_instruction_offset = 4;
+ const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
+ page_start_instructions, function_offset_table_indices,
+ /* instruction_offset */ (page_instruction_offset << 1) +
+ (page_number << 17));
+ ASSERT_NE(absl::nullopt, entry_found);
+ EXPECT_EQ(0, entry_found->instruction_offset_from_function_start);
+ EXPECT_EQ(40ul, entry_found->function_offset_table_byte_index);
+ }
+
+ {
+ const uint32_t page_number = 0;
+ const uint32_t page_instruction_offset = 50;
+ const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
+ page_start_instructions, function_offset_table_indices,
+ /* instruction_offset */ (page_instruction_offset << 1) +
+ (page_number << 17));
+ ASSERT_NE(absl::nullopt, entry_found);
+ EXPECT_EQ(46, entry_found->instruction_offset_from_function_start);
+ EXPECT_EQ(40ul, entry_found->function_offset_table_byte_index);
+ }
+
+ // Lookup last instruction in last function.
+ {
+ const uint32_t page_number = 1;
+ const uint32_t page_instruction_offset = 0xffff;
+ const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
+ page_start_instructions, function_offset_table_indices,
+ /* instruction_offset */ (page_instruction_offset << 1) +
+ (page_number << 17));
+ ASSERT_NE(absl::nullopt, entry_found);
+ // 0xffff - 6 = 0xfff9.
+ EXPECT_EQ(0xfff9, entry_found->instruction_offset_from_function_start);
+ EXPECT_EQ(70ul, entry_found->function_offset_table_byte_index);
+ }
+}
+
+TEST(ChromeUnwindTableAndroidTest, TestAddressTableLookupEmptyPage) {
+ const uint32_t page_start_instructions[] = {0, 1, 1};
+ const FunctionTableEntry function_offset_table_indices[] = {
+ // Page 0
+ {
+ /* function_start_address_page_instruction_offset */ 0,
+ /* function_offset_table_byte_index */ 20,
+ },
+ // Page 1 is empty
+ // Page 2
+ {
+ /* function_start_address_page_instruction_offset */ 6,
+ /* function_offset_table_byte_index */ 70,
+ },
+ };
+
+ const uint32_t page_number = 1;
+ const uint32_t page_instruction_offset = 4;
+ const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
+ page_start_instructions, function_offset_table_indices,
+ /* instruction_offset */ (page_instruction_offset << 1) +
+ (page_number << 17));
+ ASSERT_NE(absl::nullopt, entry_found);
+ EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
+ EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
+}
+
+TEST(ChromeUnwindTableAndroidTest,
+ TestAddressTableLookupInvalidIntructionOffset) {
+ const uint32_t page_start_instructions[] = {0, 1};
+ const FunctionTableEntry function_offset_table_indices[] = {
+ // Page 0
+ // This function spans from page 0 offset 0 to page 1 offset 5.
+ {
+ /* function_start_address_page_instruction_offset */ 0,
+ /* function_offset_table_byte_index */ 20,
+ },
+ // Page 1
+ {
+ /* function_start_address_page_instruction_offset */ 6,
+ /* function_offset_table_byte_index */ 70,
+ },
+ };
+
+ // Instruction offset lies after last page on page table.
+ {
+ const uint32_t page_number = 50;
+ const uint32_t page_instruction_offset = 6;
+ const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
+ page_start_instructions, function_offset_table_indices,
+ /* instruction_offset */ (page_instruction_offset << 1) +
+ (page_number << 17));
+ ASSERT_EQ(absl::nullopt, entry_found);
+ }
+ {
+ const uint32_t page_number = 2;
+ const uint32_t page_instruction_offset = 0;
+ const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
+ page_start_instructions, function_offset_table_indices,
+ /* instruction_offset */ (page_instruction_offset << 1) +
+ (page_number << 17));
+ ASSERT_EQ(absl::nullopt, entry_found);
+ }
+}
+
+TEST(ChromeUnwindTableAndroidTest,
+ TestAddressTableLookupOnSecondPageOfFunctionSpanningPageBoundary) {
+ const uint32_t page_start_instructions[] = {0, 1, 2};
+ const FunctionTableEntry function_offset_table_indices[] = {
+ // Page 0
+ {
+ /* function_start_address_page_instruction_offset */ 0,
+ /* function_offset_table_byte_index */ 20,
+ },
+ // Page 1
+ {
+ /* function_start_address_page_instruction_offset */ 6,
+ /* function_offset_table_byte_index */ 70,
+ },
+ // Page 2
+ {
+ /* function_start_address_page_instruction_offset */ 10,
+ /* function_offset_table_byte_index */ 80,
+ }};
+
+ const uint32_t page_number = 1;
+ const uint32_t page_instruction_offset = 4;
+ const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
+ page_start_instructions, function_offset_table_indices,
+ /* instruction_offset */ (page_instruction_offset << 1) +
+ (page_number << 17));
+ ASSERT_NE(absl::nullopt, entry_found);
+ EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
+ EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
+}
+
+TEST(ChromeUnwindTableAndroidTest,
+ TestAddressTableLookupWithinFunctionSpanningMultiplePages) {
+ const uint32_t page_start_instructions[] = {0, 1, 1, 1};
+ const FunctionTableEntry function_offset_table_indices[] = {
+ // Page 0
+ // This function spans from page 0 offset 0 to page 3 offset 5.
+ {
+ /* function_start_address_page_instruction_offset */ 0,
+ /* function_offset_table_byte_index */ 20,
+ },
+ // Page 1 is empty
+ // Page 2 is empty
+ // Page 3
+ {
+ /* function_start_address_page_instruction_offset */ 6,
+ /* function_offset_table_byte_index */ 70,
+ },
+ };
+
+ {
+ const uint32_t page_number = 0;
+ const uint32_t page_instruction_offset = 4;
+ const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
+ page_start_instructions, function_offset_table_indices,
+ /* instruction_offset */ (page_instruction_offset << 1) +
+ (page_number << 17));
+ ASSERT_NE(absl::nullopt, entry_found);
+ EXPECT_EQ(0x4, entry_found->instruction_offset_from_function_start);
+ EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
+ }
+ {
+ const uint32_t page_number = 1;
+ const uint32_t page_instruction_offset = 4;
+ const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
+ page_start_instructions, function_offset_table_indices,
+ /* instruction_offset */ (page_instruction_offset << 1) +
+ (page_number << 17));
+ ASSERT_NE(absl::nullopt, entry_found);
+ EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
+ EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
+ }
+ {
+ const uint32_t page_number = 2;
+ const uint32_t page_instruction_offset = 4;
+ const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
+ page_start_instructions, function_offset_table_indices,
+ /* instruction_offset */ (page_instruction_offset << 1) +
+ (page_number << 17));
+ ASSERT_NE(absl::nullopt, entry_found);
+ EXPECT_EQ(0x20004, entry_found->instruction_offset_from_function_start);
+ EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
+ }
+ {
+ const uint32_t page_number = 3;
+ const uint32_t page_instruction_offset = 4;
+ const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
+ page_start_instructions, function_offset_table_indices,
+ /* instruction_offset */ (page_instruction_offset << 1) +
+ (page_number << 17));
+ ASSERT_NE(absl::nullopt, entry_found);
+ EXPECT_EQ(0x30004, entry_found->instruction_offset_from_function_start);
+ EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
+ }
+}
} // namespace base