237 lines
9.9 KiB
C++
237 lines
9.9 KiB
C++
// Copyright 2022 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "base/profiler/libunwindstack_unwinder_android.h"
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <inttypes.h>
|
|
#include <string.h>
|
|
#include <vector>
|
|
|
|
#include "base/android/build_info.h"
|
|
#include "base/functional/bind.h"
|
|
#include "base/native_library.h"
|
|
#include "base/path_service.h"
|
|
#include "base/profiler/register_context.h"
|
|
#include "base/profiler/stack_buffer.h"
|
|
#include "base/profiler/stack_copier_signal.h"
|
|
#include "base/profiler/stack_sampler.h"
|
|
#include "base/profiler/stack_sampling_profiler_java_test_util.h"
|
|
#include "base/profiler/stack_sampling_profiler_test_util.h"
|
|
#include "base/profiler/thread_delegate_posix.h"
|
|
#include "base/test/bind.h"
|
|
#include "build/build_config.h"
|
|
#include "stack_sampling_profiler_test_util.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace base {
|
|
|
|
namespace {
|
|
class TestStackCopierDelegate : public StackCopier::Delegate {
|
|
public:
|
|
void OnStackCopy() override {}
|
|
};
|
|
|
|
std::vector<Frame> CaptureScenario(
|
|
UnwindScenario* scenario,
|
|
ModuleCache* module_cache,
|
|
OnceCallback<void(RegisterContext*, uintptr_t, std::vector<Frame>*)>
|
|
unwind_callback) {
|
|
std::vector<Frame> sample;
|
|
WithTargetThread(
|
|
scenario,
|
|
BindLambdaForTesting(
|
|
[&](SamplingProfilerThreadToken target_thread_token) {
|
|
auto thread_delegate =
|
|
ThreadDelegatePosix::Create(target_thread_token);
|
|
ASSERT_TRUE(thread_delegate);
|
|
auto stack_copier =
|
|
std::make_unique<StackCopierSignal>(std::move(thread_delegate));
|
|
std::unique_ptr<StackBuffer> stack_buffer =
|
|
StackSampler::CreateStackBuffer();
|
|
|
|
RegisterContext thread_context;
|
|
uintptr_t stack_top;
|
|
TimeTicks timestamp;
|
|
TestStackCopierDelegate delegate;
|
|
bool success =
|
|
stack_copier->CopyStack(stack_buffer.get(), &stack_top,
|
|
×tamp, &thread_context, &delegate);
|
|
ASSERT_TRUE(success);
|
|
|
|
sample.emplace_back(
|
|
RegisterContextInstructionPointer(&thread_context),
|
|
module_cache->GetModuleForAddress(
|
|
RegisterContextInstructionPointer(&thread_context)));
|
|
|
|
std::move(unwind_callback).Run(&thread_context, stack_top, &sample);
|
|
}));
|
|
|
|
return sample;
|
|
}
|
|
} // namespace
|
|
|
|
// Checks that the expected information is present in sampled frames.
|
|
TEST(LibunwindstackUnwinderAndroidTest, PlainFunction) {
|
|
UnwindScenario scenario(BindRepeating(&CallWithPlainFunction));
|
|
|
|
ModuleCache module_cache;
|
|
auto unwinder = std::make_unique<LibunwindstackUnwinderAndroid>();
|
|
|
|
unwinder->Initialize(&module_cache);
|
|
std::vector<Frame> sample =
|
|
CaptureScenario(&scenario, &module_cache,
|
|
BindLambdaForTesting([&](RegisterContext* thread_context,
|
|
uintptr_t stack_top,
|
|
std::vector<Frame>* sample) {
|
|
ASSERT_TRUE(unwinder->CanUnwindFrom(sample->back()));
|
|
UnwindResult result = unwinder->TryUnwind(
|
|
thread_context, stack_top, sample);
|
|
EXPECT_EQ(UnwindResult::kCompleted, result);
|
|
}));
|
|
|
|
// Check that all the modules are valid.
|
|
for (const auto& frame : sample)
|
|
EXPECT_NE(nullptr, frame.module);
|
|
|
|
// The stack should contain a full unwind.
|
|
ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
|
|
scenario.GetSetupFunctionAddressRange(),
|
|
scenario.GetOuterFunctionAddressRange()});
|
|
}
|
|
|
|
// Checks that the unwinder handles stacks containing dynamically-allocated
|
|
// stack memory.
|
|
TEST(LibunwindstackUnwinderAndroidTest, Alloca) {
|
|
UnwindScenario scenario(BindRepeating(&CallWithAlloca));
|
|
|
|
ModuleCache module_cache;
|
|
auto unwinder = std::make_unique<LibunwindstackUnwinderAndroid>();
|
|
|
|
unwinder->Initialize(&module_cache);
|
|
std::vector<Frame> sample =
|
|
CaptureScenario(&scenario, &module_cache,
|
|
BindLambdaForTesting([&](RegisterContext* thread_context,
|
|
uintptr_t stack_top,
|
|
std::vector<Frame>* sample) {
|
|
ASSERT_TRUE(unwinder->CanUnwindFrom(sample->back()));
|
|
UnwindResult result = unwinder->TryUnwind(
|
|
thread_context, stack_top, sample);
|
|
EXPECT_EQ(UnwindResult::kCompleted, result);
|
|
}));
|
|
|
|
// Check that all the modules are valid.
|
|
for (const auto& frame : sample)
|
|
EXPECT_NE(nullptr, frame.module);
|
|
|
|
// The stack should contain a full unwind.
|
|
ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
|
|
scenario.GetSetupFunctionAddressRange(),
|
|
scenario.GetOuterFunctionAddressRange()});
|
|
}
|
|
|
|
// Checks that a stack that runs through another library produces a stack with
|
|
// the expected functions.
|
|
TEST(LibunwindstackUnwinderAndroidTest, OtherLibrary) {
|
|
NativeLibrary other_library = LoadOtherLibrary();
|
|
UnwindScenario scenario(
|
|
BindRepeating(&CallThroughOtherLibrary, Unretained(other_library)));
|
|
|
|
ModuleCache module_cache;
|
|
auto unwinder = std::make_unique<LibunwindstackUnwinderAndroid>();
|
|
|
|
unwinder->Initialize(&module_cache);
|
|
std::vector<Frame> sample =
|
|
CaptureScenario(&scenario, &module_cache,
|
|
BindLambdaForTesting([&](RegisterContext* thread_context,
|
|
uintptr_t stack_top,
|
|
std::vector<Frame>* sample) {
|
|
ASSERT_TRUE(unwinder->CanUnwindFrom(sample->back()));
|
|
UnwindResult result = unwinder->TryUnwind(
|
|
thread_context, stack_top, sample);
|
|
EXPECT_EQ(UnwindResult::kCompleted, result);
|
|
}));
|
|
|
|
// The stack should contain a full unwind.
|
|
ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
|
|
scenario.GetSetupFunctionAddressRange(),
|
|
scenario.GetOuterFunctionAddressRange()});
|
|
}
|
|
|
|
// Checks that java frames can be unwound through and have function names.
|
|
TEST(LibunwindstackUnwinderAndroidTest, JavaFunction) {
|
|
auto* build_info = base::android::BuildInfo::GetInstance();
|
|
// Due to varying availability of compiled/JITed java unwind tables, unwinding
|
|
// is only expected to reliably succeed on Android P+
|
|
// https://android.googlesource.com/platform/system/unwinding/+/refs/heads/master/libunwindstack/AndroidVersions.md#android-9-pie_api-level-28
|
|
// The libunwindstack doc mentions in Android 9 it got the support for
|
|
// unwinding through JIT'd frames.
|
|
bool can_unwind = build_info->sdk_int() >= base::android::SDK_VERSION_P;
|
|
if (!can_unwind) {
|
|
GTEST_SKIP() << "Unwind info is not available on older version of Android";
|
|
}
|
|
|
|
UnwindScenario scenario(base::BindRepeating(callWithJavaFunction));
|
|
|
|
auto unwinder = std::make_unique<LibunwindstackUnwinderAndroid>();
|
|
|
|
ModuleCache module_cache;
|
|
unwinder->Initialize(&module_cache);
|
|
const std::vector<Frame> sample =
|
|
CaptureScenario(&scenario, &module_cache,
|
|
BindLambdaForTesting([&](RegisterContext* thread_context,
|
|
uintptr_t stack_top,
|
|
std::vector<Frame>* sample) {
|
|
ASSERT_TRUE(unwinder->CanUnwindFrom(sample->back()));
|
|
UnwindResult result = unwinder->TryUnwind(
|
|
thread_context, stack_top, sample);
|
|
EXPECT_EQ(UnwindResult::kCompleted, result);
|
|
}));
|
|
|
|
// Check that all the modules are valid.
|
|
for (const auto& frame : sample) {
|
|
EXPECT_NE(frame.module, nullptr);
|
|
}
|
|
|
|
// The stack should contain a full unwind.
|
|
ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
|
|
scenario.GetSetupFunctionAddressRange(),
|
|
scenario.GetOuterFunctionAddressRange()});
|
|
ExpectStackContainsNames(sample, {"org.chromium.base.profiler.TestSupport."
|
|
"callWithJavaFunction"});
|
|
}
|
|
|
|
TEST(LibunwindstackUnwinderAndroidTest, ReparsesMapsOnNewDynamicLibraryLoad) {
|
|
// The current version of /proc/self/maps is used to create
|
|
// memory_regions_map_ object.
|
|
auto unwinder = std::make_unique<LibunwindstackUnwinderAndroid>();
|
|
ModuleCache module_cache;
|
|
unwinder->Initialize(&module_cache);
|
|
|
|
// Dynamically loading a library should update maps and a reparse is required
|
|
// to actually unwind through functions involving this library.
|
|
NativeLibrary dynamic_library =
|
|
LoadTestLibrary("base_profiler_reparsing_test_support_library");
|
|
UnwindScenario scenario(
|
|
BindRepeating(&CallThroughOtherLibrary, Unretained(dynamic_library)));
|
|
|
|
auto sample =
|
|
CaptureScenario(&scenario, &module_cache,
|
|
BindLambdaForTesting([&](RegisterContext* thread_context,
|
|
uintptr_t stack_top,
|
|
std::vector<Frame>* sample) {
|
|
ASSERT_TRUE(unwinder->CanUnwindFrom(sample->back()));
|
|
UnwindResult result = unwinder->TryUnwind(
|
|
thread_context, stack_top, sample);
|
|
EXPECT_EQ(UnwindResult::kCompleted, result);
|
|
}));
|
|
|
|
ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
|
|
scenario.GetSetupFunctionAddressRange(),
|
|
scenario.GetOuterFunctionAddressRange()});
|
|
}
|
|
|
|
} // namespace base
|