Commit 30dea2af authored by ssid's avatar ssid Committed by Commit Bot

Read proc/self/maps to find main thread stack base

pthread_getattr_np implementation in bionic throws sigabrt on failure to
read proc/ file. Implement this function to avoid crashes.

BUG=1129941

Change-Id: I54b94a609201a998924aaa8c8e77051f85acf377
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2425744
Commit-Queue: ssid <ssid@chromium.org>
Reviewed-by: default avatarMike Wittman <wittman@chromium.org>
Reviewed-by: default avatarTommy Nyquist <nyquist@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810829}
parent 1fdb72e8
......@@ -3096,6 +3096,7 @@ test("base_unittests") {
sources += [
"cpu_affinity_posix_unittest.cc",
"profiler/stack_copier_signal_unittest.cc",
"profiler/thread_delegate_posix_unittest.cc",
]
}
}
......
......@@ -73,8 +73,11 @@ std::vector<Frame> CaptureScenario(
scenario,
BindLambdaForTesting(
[&](SamplingProfilerThreadToken target_thread_token) {
auto stack_copier = std::make_unique<StackCopierSignal>(
std::make_unique<ThreadDelegatePosix>(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();
......
......@@ -95,8 +95,10 @@ TEST(StackCopierSignalTest, MAYBE_CopyStack) {
RegisterContext context;
TestStackCopierDelegate stack_copier_delegate;
StackCopierSignal copier(std::make_unique<ThreadDelegatePosix>(
GetSamplingProfilerCurrentThreadToken()));
auto thread_delegate =
ThreadDelegatePosix::Create(GetSamplingProfilerCurrentThreadToken());
ASSERT_TRUE(thread_delegate);
StackCopierSignal copier(std::move(thread_delegate));
// Copy the sentinel values onto the stack. Volatile to defeat compiler
// optimizations.
......@@ -132,8 +134,10 @@ TEST(StackCopierSignalTest, MAYBE_CopyStackTimestamp) {
RegisterContext context;
TestStackCopierDelegate stack_copier_delegate;
StackCopierSignal copier(std::make_unique<ThreadDelegatePosix>(
GetSamplingProfilerCurrentThreadToken()));
auto thread_delegate =
ThreadDelegatePosix::Create(GetSamplingProfilerCurrentThreadToken());
ASSERT_TRUE(thread_delegate);
StackCopierSignal copier(std::move(thread_delegate));
TimeTicks before = TimeTicks::Now();
bool result = copier.CopyStack(&stack_buffer, &stack_top, &timestamp,
......@@ -159,8 +163,10 @@ TEST(StackCopierSignalTest, MAYBE_CopyStackDelegateInvoked) {
RegisterContext context;
TestStackCopierDelegate stack_copier_delegate;
StackCopierSignal copier(std::make_unique<ThreadDelegatePosix>(
GetSamplingProfilerCurrentThreadToken()));
auto thread_delegate =
ThreadDelegatePosix::Create(GetSamplingProfilerCurrentThreadToken());
ASSERT_TRUE(thread_delegate);
StackCopierSignal copier(std::move(thread_delegate));
bool result = copier.CopyStack(&stack_buffer, &stack_top, &timestamp,
&context, &stack_copier_delegate);
......@@ -190,7 +196,9 @@ TEST(StackCopierSignalTest, MAYBE_CopyStackFromOtherThread) {
const SamplingProfilerThreadToken thread_token =
target_thread.GetThreadToken();
StackCopierSignal copier(std::make_unique<ThreadDelegatePosix>(thread_token));
auto thread_delegate = ThreadDelegatePosix::Create(thread_token);
ASSERT_TRUE(thread_delegate);
StackCopierSignal copier(std::move(thread_delegate));
bool result = copier.CopyStack(&stack_buffer, &stack_top, &timestamp,
&context, &stack_copier_delegate);
......
......@@ -21,9 +21,11 @@ std::unique_ptr<StackSampler> StackSampler::Create(
UnwindersFactory core_unwinders_factory,
RepeatingClosure record_sample_callback,
StackSamplerTestDelegate* test_delegate) {
auto thread_delegate = ThreadDelegatePosix::Create(thread_token);
if (!thread_delegate)
return nullptr;
return std::make_unique<StackSamplerImpl>(
std::make_unique<StackCopierSignal>(
std::make_unique<ThreadDelegatePosix>(thread_token)),
std::make_unique<StackCopierSignal>(std::move(thread_delegate)),
std::move(core_unwinders_factory), module_cache,
std::move(record_sample_callback), test_delegate);
}
......
......@@ -2,18 +2,44 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <inttypes.h>
#include <pthread.h>
#include <stdio.h>
#include "base/memory/ptr_util.h"
#include "base/optional.h"
#include "base/process/process_handle.h"
#include "base/profiler/thread_delegate_posix.h"
#include "base/stl_util.h"
#include "build/build_config.h"
#if defined(OS_ANDROID)
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#endif
namespace base {
namespace {
#if defined(OS_ANDROID)
base::Optional<uintptr_t> GetAndroidMainThreadStackBaseAddressImpl() {
char line[1024];
base::ScopedFILE fp(base::OpenFile(base::FilePath("/proc/self/maps"), "r"));
uintptr_t stack_addr = reinterpret_cast<uintptr_t>(line);
if (!fp)
return base::nullopt;
while (fgets(line, sizeof(line), fp.get()) != nullptr) {
uintptr_t start, end;
if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR, &start, &end) == 2) {
if (start <= stack_addr && stack_addr < end)
return end;
}
}
return base::nullopt;
}
#endif
uintptr_t GetThreadStackBaseAddressImpl(
SamplingProfilerThreadToken thread_token) {
pthread_attr_t attr;
......@@ -27,15 +53,18 @@ uintptr_t GetThreadStackBaseAddressImpl(
return base_address;
}
uintptr_t GetThreadStackBaseAddress(SamplingProfilerThreadToken thread_token) {
base::Optional<uintptr_t> GetThreadStackBaseAddress(
SamplingProfilerThreadToken thread_token) {
#if defined(OS_ANDROID)
// Caches the main thread base address on Android since Bionic has to read
// /proc/$PID/maps to obtain it. Other thread base addresses are sourced from
// pthread state so are cheap to get.
// The implementation of pthread_getattr_np() in Bionic reads proc/self/maps
// to find the main thread base address, and throws SIGABRT when it fails to
// read or parse the file. So, try to read the maps to get the main thread
// stack base and cache the result. Other thread base addresses are sourced
// from pthread state so are cheap to get.
const bool is_main_thread = thread_token.id == GetCurrentProcId();
if (is_main_thread) {
static const uintptr_t main_thread_base_address =
GetThreadStackBaseAddressImpl(thread_token);
static const base::Optional<uintptr_t> main_thread_base_address =
GetAndroidMainThreadStackBaseAddressImpl();
return main_thread_base_address;
}
#endif
......@@ -44,10 +73,18 @@ uintptr_t GetThreadStackBaseAddress(SamplingProfilerThreadToken thread_token) {
} // namespace
ThreadDelegatePosix::ThreadDelegatePosix(
SamplingProfilerThreadToken thread_token)
: thread_id_(thread_token.id),
thread_stack_base_address_(GetThreadStackBaseAddress(thread_token)) {}
// static
std::unique_ptr<ThreadDelegatePosix> ThreadDelegatePosix::Create(
SamplingProfilerThreadToken thread_token) {
base::Optional<uintptr_t> base_address =
GetThreadStackBaseAddress(thread_token);
if (!base_address)
return nullptr;
return base::WrapUnique(
new ThreadDelegatePosix(thread_token.id, *base_address));
}
ThreadDelegatePosix::~ThreadDelegatePosix() = default;
PlatformThreadId ThreadDelegatePosix::GetThreadId() const {
return thread_id_;
......@@ -117,4 +154,8 @@ std::vector<uintptr_t*> ThreadDelegatePosix::GetRegistersToRewrite(
#endif
}
ThreadDelegatePosix::ThreadDelegatePosix(PlatformThreadId id,
uintptr_t base_address)
: thread_id_(id), thread_stack_base_address_(base_address) {}
} // namespace base
......@@ -16,7 +16,10 @@ namespace base {
// POSIX.
class BASE_EXPORT ThreadDelegatePosix : public ThreadDelegate {
public:
ThreadDelegatePosix(SamplingProfilerThreadToken thread_token);
static std::unique_ptr<ThreadDelegatePosix> Create(
SamplingProfilerThreadToken thread_token);
~ThreadDelegatePosix() override;
ThreadDelegatePosix(const ThreadDelegatePosix&) = delete;
ThreadDelegatePosix& operator=(const ThreadDelegatePosix&) = delete;
......@@ -28,6 +31,8 @@ class BASE_EXPORT ThreadDelegatePosix : public ThreadDelegate {
RegisterContext* thread_context) override;
private:
ThreadDelegatePosix(PlatformThreadId id, uintptr_t base_address);
const PlatformThreadId thread_id_;
const uintptr_t thread_stack_base_address_;
};
......
// Copyright 2020 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 "base/profiler/thread_delegate_posix.h"
#include "base/process/process_handle.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
// ASAN moves local variables outside of the stack extents.
#if defined(ADDRESS_SANITIZER)
#define MAYBE_CurrentThreadBase DISABLED_CurrentThreadBase
#else
#define MAYBE_CurrentThreadBase CurrentThreadBase
#endif
TEST(ThreadDelegatePosixTest, MAYBE_CurrentThreadBase) {
auto delegate =
ThreadDelegatePosix::Create(GetSamplingProfilerCurrentThreadToken());
ASSERT_TRUE(delegate);
uintptr_t base = delegate->GetStackBaseAddress();
EXPECT_GT(base, 0u);
uintptr_t stack_addr = reinterpret_cast<uintptr_t>(&base);
// Check that end of stack is within 4MiB of a current stack address.
EXPECT_LE(base, stack_addr + 4 * 1024 * 1024);
}
#if defined(OS_ANDROID)
TEST(ThreadDelegatePosixTest, AndroidMainThreadStackBase) {
// The delegate does not use pthread id for main thread.
auto delegate = ThreadDelegatePosix::Create(
SamplingProfilerThreadToken{GetCurrentProcId(), pthread_t()});
ASSERT_TRUE(delegate);
uintptr_t base = delegate->GetStackBaseAddress();
EXPECT_GT(base, 0u);
}
#endif // defined(OS_ANDROID)
} // namespace base
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment