Cache DWARF location rules for a given pc.

Decoding the DWARF opcodes is expensive so make sure we cache it.

This speeds unwinding in simpleperf by over a factor of 3x.

Add unit tests for this new behavior.

Bug: 77258731

Test: libbacktrace/libunwindstack unit tests on host and target.
Test: Ran debuggerd -b on various processes on target.
Change-Id: Ia516c0fa5d3e5f76746190bb4b6fdf49fd1c9388
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index 3fcd2b6..071d2df 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -165,4 +165,72 @@
   ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
 }
 
+static bool MockGetCfaLocationInfo(::testing::Unused, const DwarfFde* fde,
+                                   dwarf_loc_regs_t* loc_regs) {
+  loc_regs->pc_start = fde->pc_start;
+  loc_regs->pc_end = fde->pc_end;
+  return true;
+}
+
+TEST_F(DwarfSectionTest, Step_cache) {
+  MockDwarfSection mock_section(&memory_);
+
+  DwarfCie cie{};
+  DwarfFde fde{};
+  fde.pc_start = 0x500;
+  fde.pc_end = 0x2000;
+  fde.cie = &cie;
+
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+      .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+  MemoryFake process;
+  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+      .WillRepeatedly(::testing::Return(true));
+
+  bool finished;
+  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(mock_section.Step(0x1500, nullptr, &process, &finished));
+}
+
+TEST_F(DwarfSectionTest, Step_cache_not_in_pc) {
+  MockDwarfSection mock_section(&memory_);
+
+  DwarfCie cie{};
+  DwarfFde fde0{};
+  fde0.pc_start = 0x1000;
+  fde0.pc_end = 0x2000;
+  fde0.cie = &cie;
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde0));
+  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde0, ::testing::_))
+      .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+  MemoryFake process;
+  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+      .WillRepeatedly(::testing::Return(true));
+
+  bool finished;
+  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+
+  DwarfFde fde1{};
+  fde1.pc_start = 0x500;
+  fde1.pc_end = 0x800;
+  fde1.cie = &cie;
+  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x600, ::testing::_))
+      .WillOnce(::testing::Return(true));
+  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde1));
+  EXPECT_CALL(mock_section, GetCfaLocationInfo(0x600, &fde1, ::testing::_))
+      .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+  ASSERT_TRUE(mock_section.Step(0x600, nullptr, &process, &finished));
+  ASSERT_TRUE(mock_section.Step(0x700, nullptr, &process, &finished));
+}
+
 }  // namespace unwindstack