blob: 76eaeed73129477ba92873ef5f0e8a6b760bf60c [file] [log] [blame]
// 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/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 "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,
&timestamp, &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.
// TODO(https://crbug.com/1147315): Fix, re-enable on all ASAN bots.
// TODO(https://crbug.com/1368981): After fix, re-enable on all bots except
// if defined(ADDRESS_SANITIZER).
TEST(LibunwindstackUnwinderAndroidTest, DISABLED_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.
// TODO(https://crbug.com/1147315): Fix, re-enable on all ASAN bots.
// TODO(https://crbug.com/1368981): After fix, re-enable on all bots except
// if defined(ADDRESS_SANITIZER).
TEST(LibunwindstackUnwinderAndroidTest, DISABLED_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.
// TODO(https://crbug.com/1147315): Fix, re-enable on all ASAN bots.
// TODO(https://crbug.com/1368981): After fix, re-enable on all bots except
// if defined(ADDRESS_SANITIZER).
TEST(LibunwindstackUnwinderAndroidTest, DISABLED_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.
// TODO(crbug/1370137): Figure out more accurate bound to where we will
// always succeed to unwind.
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"});
}
} // namespace base