Skip to content

Commit

Permalink
Read proc/self/maps to find main thread stack base
Browse files Browse the repository at this point in the history
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: Mike Wittman <wittman@chromium.org>
Reviewed-by: Tommy Nyquist <nyquist@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810829}
  • Loading branch information
ssiddhartha authored and Commit Bot committed Sep 25, 2020
1 parent 1fdb72e commit 30dea2a
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 23 deletions.
1 change: 1 addition & 0 deletions base/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]
}
}
Expand Down
7 changes: 5 additions & 2 deletions base/profiler/native_unwinder_android_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
22 changes: 15 additions & 7 deletions base/profiler/stack_copier_signal_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 4 additions & 2 deletions base/profiler/stack_sampler_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
63 changes: 52 additions & 11 deletions base/profiler/thread_delegate_posix.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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_;
Expand Down Expand Up @@ -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
7 changes: 6 additions & 1 deletion base/profiler/thread_delegate_posix.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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_;
};
Expand Down
42 changes: 42 additions & 0 deletions base/profiler/thread_delegate_posix_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 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

0 comments on commit 30dea2a

Please sign in to comment.