xref: /aosp_15_r20/external/cronet/base/profiler/libunwindstack_unwinder_android_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/profiler/libunwindstack_unwinder_android.h"
6 
7 #include <sys/mman.h>
8 
9 #include <inttypes.h>
10 #include <string.h>
11 #include <vector>
12 
13 #include "base/android/build_info.h"
14 #include "base/functional/bind.h"
15 #include "base/native_library.h"
16 #include "base/path_service.h"
17 #include "base/profiler/register_context.h"
18 #include "base/profiler/stack_buffer.h"
19 #include "base/profiler/stack_copier_signal.h"
20 #include "base/profiler/stack_sampler.h"
21 #include "base/profiler/stack_sampling_profiler_java_test_util.h"
22 #include "base/profiler/stack_sampling_profiler_test_util.h"
23 #include "base/profiler/thread_delegate_posix.h"
24 #include "base/test/bind.h"
25 #include "build/build_config.h"
26 #include "stack_sampling_profiler_test_util.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 
29 namespace base {
30 
31 namespace {
32 class TestStackCopierDelegate : public StackCopier::Delegate {
33  public:
OnStackCopy()34   void OnStackCopy() override {}
35 };
36 
CaptureScenario(UnwindScenario * scenario,ModuleCache * module_cache,OnceCallback<void (RegisterContext *,uintptr_t,std::vector<Frame> *)> unwind_callback)37 std::vector<Frame> CaptureScenario(
38     UnwindScenario* scenario,
39     ModuleCache* module_cache,
40     OnceCallback<void(RegisterContext*, uintptr_t, std::vector<Frame>*)>
41         unwind_callback) {
42   std::vector<Frame> sample;
43   WithTargetThread(
44       scenario,
45       BindLambdaForTesting(
46           [&](SamplingProfilerThreadToken target_thread_token) {
47             auto thread_delegate =
48                 ThreadDelegatePosix::Create(target_thread_token);
49             ASSERT_TRUE(thread_delegate);
50             auto stack_copier =
51                 std::make_unique<StackCopierSignal>(std::move(thread_delegate));
52             std::unique_ptr<StackBuffer> stack_buffer =
53                 StackSampler::CreateStackBuffer();
54 
55             RegisterContext thread_context;
56             uintptr_t stack_top;
57             TimeTicks timestamp;
58             TestStackCopierDelegate delegate;
59             bool success =
60                 stack_copier->CopyStack(stack_buffer.get(), &stack_top,
61                                         &timestamp, &thread_context, &delegate);
62             ASSERT_TRUE(success);
63 
64             sample.emplace_back(
65                 RegisterContextInstructionPointer(&thread_context),
66                 module_cache->GetModuleForAddress(
67                     RegisterContextInstructionPointer(&thread_context)));
68 
69             std::move(unwind_callback).Run(&thread_context, stack_top, &sample);
70           }));
71 
72   return sample;
73 }
74 }  // namespace
75 
76 // Checks that the expected information is present in sampled frames.
TEST(LibunwindstackUnwinderAndroidTest,PlainFunction)77 TEST(LibunwindstackUnwinderAndroidTest, PlainFunction) {
78   UnwindScenario scenario(BindRepeating(&CallWithPlainFunction));
79 
80   ModuleCache module_cache;
81   auto unwinder = std::make_unique<LibunwindstackUnwinderAndroid>();
82 
83   unwinder->Initialize(&module_cache);
84   std::vector<Frame> sample =
85       CaptureScenario(&scenario, &module_cache,
86                       BindLambdaForTesting([&](RegisterContext* thread_context,
87                                                uintptr_t stack_top,
88                                                std::vector<Frame>* sample) {
89                         ASSERT_TRUE(unwinder->CanUnwindFrom(sample->back()));
90                         UnwindResult result = unwinder->TryUnwind(
91                             thread_context, stack_top, sample);
92                         EXPECT_EQ(UnwindResult::kCompleted, result);
93                       }));
94 
95   // Check that all the modules are valid.
96   for (const auto& frame : sample)
97     EXPECT_NE(nullptr, frame.module);
98 
99   // The stack should contain a full unwind.
100   ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
101                                scenario.GetSetupFunctionAddressRange(),
102                                scenario.GetOuterFunctionAddressRange()});
103 }
104 
105 // Checks that the unwinder handles stacks containing dynamically-allocated
106 // stack memory.
TEST(LibunwindstackUnwinderAndroidTest,Alloca)107 TEST(LibunwindstackUnwinderAndroidTest, Alloca) {
108   UnwindScenario scenario(BindRepeating(&CallWithAlloca));
109 
110   ModuleCache module_cache;
111   auto unwinder = std::make_unique<LibunwindstackUnwinderAndroid>();
112 
113   unwinder->Initialize(&module_cache);
114   std::vector<Frame> sample =
115       CaptureScenario(&scenario, &module_cache,
116                       BindLambdaForTesting([&](RegisterContext* thread_context,
117                                                uintptr_t stack_top,
118                                                std::vector<Frame>* sample) {
119                         ASSERT_TRUE(unwinder->CanUnwindFrom(sample->back()));
120                         UnwindResult result = unwinder->TryUnwind(
121                             thread_context, stack_top, sample);
122                         EXPECT_EQ(UnwindResult::kCompleted, result);
123                       }));
124 
125   // Check that all the modules are valid.
126   for (const auto& frame : sample)
127     EXPECT_NE(nullptr, frame.module);
128 
129   // The stack should contain a full unwind.
130   ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
131                                scenario.GetSetupFunctionAddressRange(),
132                                scenario.GetOuterFunctionAddressRange()});
133 }
134 
135 // Checks that a stack that runs through another library produces a stack with
136 // the expected functions.
TEST(LibunwindstackUnwinderAndroidTest,OtherLibrary)137 TEST(LibunwindstackUnwinderAndroidTest, OtherLibrary) {
138   NativeLibrary other_library = LoadOtherLibrary();
139   UnwindScenario scenario(
140       BindRepeating(&CallThroughOtherLibrary, Unretained(other_library)));
141 
142   ModuleCache module_cache;
143   auto unwinder = std::make_unique<LibunwindstackUnwinderAndroid>();
144 
145   unwinder->Initialize(&module_cache);
146   std::vector<Frame> sample =
147       CaptureScenario(&scenario, &module_cache,
148                       BindLambdaForTesting([&](RegisterContext* thread_context,
149                                                uintptr_t stack_top,
150                                                std::vector<Frame>* sample) {
151                         ASSERT_TRUE(unwinder->CanUnwindFrom(sample->back()));
152                         UnwindResult result = unwinder->TryUnwind(
153                             thread_context, stack_top, sample);
154                         EXPECT_EQ(UnwindResult::kCompleted, result);
155                       }));
156 
157   // The stack should contain a full unwind.
158   ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
159                                scenario.GetSetupFunctionAddressRange(),
160                                scenario.GetOuterFunctionAddressRange()});
161 }
162 
163 // Checks that java frames can be unwound through and have function names.
TEST(LibunwindstackUnwinderAndroidTest,JavaFunction)164 TEST(LibunwindstackUnwinderAndroidTest, JavaFunction) {
165   auto* build_info = base::android::BuildInfo::GetInstance();
166   // Due to varying availability of compiled/JITed java unwind tables, unwinding
167   // is only expected to reliably succeed on Android P+
168   // https://android.googlesource.com/platform/system/unwinding/+/refs/heads/master/libunwindstack/AndroidVersions.md#android-9-pie_api-level-28
169   // The libunwindstack doc mentions in Android 9 it got the support for
170   // unwinding through JIT'd frames.
171   bool can_unwind = build_info->sdk_int() >= base::android::SDK_VERSION_P;
172   if (!can_unwind) {
173     GTEST_SKIP() << "Unwind info is not available on older version of Android";
174   }
175 
176   UnwindScenario scenario(base::BindRepeating(callWithJavaFunction));
177 
178   auto unwinder = std::make_unique<LibunwindstackUnwinderAndroid>();
179 
180   ModuleCache module_cache;
181   unwinder->Initialize(&module_cache);
182   const std::vector<Frame> sample =
183       CaptureScenario(&scenario, &module_cache,
184                       BindLambdaForTesting([&](RegisterContext* thread_context,
185                                                uintptr_t stack_top,
186                                                std::vector<Frame>* sample) {
187                         ASSERT_TRUE(unwinder->CanUnwindFrom(sample->back()));
188                         UnwindResult result = unwinder->TryUnwind(
189                             thread_context, stack_top, sample);
190                         EXPECT_EQ(UnwindResult::kCompleted, result);
191                       }));
192 
193   // Check that all the modules are valid.
194   for (const auto& frame : sample) {
195     EXPECT_NE(frame.module, nullptr);
196   }
197 
198   // The stack should contain a full unwind.
199   ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
200                                scenario.GetSetupFunctionAddressRange(),
201                                scenario.GetOuterFunctionAddressRange()});
202   ExpectStackContainsNames(sample, {"org.chromium.base.profiler.TestSupport."
203                                     "callWithJavaFunction"});
204 }
205 
206 // TODO(b/269239545): Delete or re-enable (and enable updatable maps) this test.
TEST(LibunwindstackUnwinderAndroidTest,DISABLED_ReparsesMapsOnNewDynamicLibraryLoad)207 TEST(LibunwindstackUnwinderAndroidTest,
208      DISABLED_ReparsesMapsOnNewDynamicLibraryLoad) {
209   // The current version of /proc/self/maps is used to create
210   // memory_regions_map_ object.
211   auto unwinder = std::make_unique<LibunwindstackUnwinderAndroid>();
212   ModuleCache module_cache;
213   unwinder->Initialize(&module_cache);
214 
215   // Dynamically loading a library should update maps and a reparse is required
216   // to actually unwind through functions involving this library.
217   NativeLibrary dynamic_library =
218       LoadTestLibrary("base_profiler_reparsing_test_support_library");
219   UnwindScenario scenario(
220       BindRepeating(&CallThroughOtherLibrary, Unretained(dynamic_library)));
221 
222   auto sample =
223       CaptureScenario(&scenario, &module_cache,
224                       BindLambdaForTesting([&](RegisterContext* thread_context,
225                                                uintptr_t stack_top,
226                                                std::vector<Frame>* sample) {
227                         ASSERT_TRUE(unwinder->CanUnwindFrom(sample->back()));
228                         UnwindResult result = unwinder->TryUnwind(
229                             thread_context, stack_top, sample);
230                         EXPECT_EQ(UnwindResult::kCompleted, result);
231                       }));
232 
233   ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
234                                scenario.GetSetupFunctionAddressRange(),
235                                scenario.GetOuterFunctionAddressRange()});
236 }
237 
238 }  // namespace base
239