blob: af841a26f7a392e5cc273c38690d17535e84d741 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/tracing/common/stack_unwinder_android.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/post_task.h"
#include "base/test/scoped_task_environment.h"
#include "base/trace_event/cfi_backtrace_android.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace tracing {
namespace {
const size_t kMaxStackFrames = 40;
class StackUnwinderTest : public testing::Test {
public:
StackUnwinderTest() : testing::Test() {}
~StackUnwinderTest() override {}
void SetUp() override {
StackUnwinderAndroid::GetInstance()->Initialize();
base::trace_event::CFIBacktraceAndroid::GetInitializedInstance()
->AllocateCacheForCurrentThread();
}
private:
DISALLOW_COPY_AND_ASSIGN(StackUnwinderTest);
base::test::ScopedTaskEnvironment scoped_task_environment_;
};
uintptr_t GetCurrentPC() {
return reinterpret_cast<uintptr_t>(__builtin_return_address(0));
}
} // namespace
TEST_F(StackUnwinderTest, UnwindCurrentThread) {
const void* frames[kMaxStackFrames];
size_t result =
StackUnwinderAndroid::GetInstance()->TraceStack(frames, kMaxStackFrames);
EXPECT_GT(result, 0u);
// Since we are starting from chrome library function (this), all the unwind
// frames will be chrome frames.
for (size_t i = 0; i < result; ++i) {
EXPECT_TRUE(
base::trace_event::CFIBacktraceAndroid::GetInitializedInstance()
->is_chrome_address(reinterpret_cast<uintptr_t>(frames[i])));
}
}
TEST_F(StackUnwinderTest, UnwindOtherThread) {
base::WaitableEvent unwind_finished_event;
auto task_runner = base::CreateSingleThreadTaskRunnerWithTraits(
{base::MayBlock(), base::TaskPriority::BEST_EFFORT});
auto callback = [](base::PlatformThreadId tid,
base::WaitableEvent* unwind_finished_event,
uintptr_t test_pc) {
const void* frames[kMaxStackFrames];
size_t result = StackUnwinderAndroid::GetInstance()->TraceStack(
tid, frames, kMaxStackFrames);
EXPECT_GT(result, 0u);
for (size_t i = 0; i < result; ++i) {
uintptr_t addr = reinterpret_cast<uintptr_t>(frames[i]);
EXPECT_TRUE(StackUnwinderAndroid::GetInstance()->IsAddressMapped(addr));
}
unwind_finished_event->Signal();
};
// Post task on background thread to unwind the current thread.
task_runner->PostTask(
FROM_HERE, base::BindOnce(callback, base::PlatformThread::CurrentId(),
&unwind_finished_event, GetCurrentPC()));
// While the background thread is trying to unwind make some slow framework
// calls (malloc) so that the current thread can be stopped in framework
// library functions on stack.
// TODO(ssid): Test for reliable unwinding through non-chrome and chrome
// frames.
while (true) {
std::vector<int> temp;
temp.reserve(kMaxStackFrames);
usleep(100);
if (unwind_finished_event.IsSignaled())
break;
}
}
} // namespace tracing