Commit 3533f3d0 authored by Haiyang Pan's avatar Haiyang Pan Committed by Commit Bot

Revert "Android: Add support for tracing sampling profiler on arm64"

This reverts commit 659b4301.

Reason for revert: Likely caused compile failure in android x86 builders since:
* https://ci.chromium.org/p/chromium/builders/ci/android-pie-x86-rel/2148
* https://ci.chromium.org/p/chromium/builders/ci/android-marshmallow-x86-rel/1252

Tracking bug: crbug.com/1126519

Original change's description:
> Android: Add support for tracing sampling profiler on arm64
> 
> Support stack unwinding for tracing sampling profiler, in arm64
> builds. The builds always enable frame pointers, so stack unwinding can
> be done using frame pointers.
> 
> 
> Future work: Support module cache for arm 64.
> 
> BUG=1110044
> 
> Change-Id: If98bcc5582edc75b61c5a385cd29535f4a6ee195
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2321230
> Commit-Queue: ssid <ssid@chromium.org>
> Reviewed-by: Mike Wittman <wittman@chromium.org>
> Reviewed-by: Nico Weber <thakis@chromium.org>
> Reviewed-by: oysteine <oysteine@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#805274}

TBR=thakis@chromium.org,wittman@chromium.org,oysteine@chromium.org,ssid@chromium.org

Change-Id: Ib9b2d1563951937e22b4299b44bdeec21e2c014f
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 1110044
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2401799Reviewed-by: default avatarHaiyang Pan <hypan@google.com>
Commit-Queue: Haiyang Pan <hypan@google.com>
Cr-Commit-Position: refs/heads/master@{#805367}
parent 1996d89e
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
#include <sstream> #include <sstream>
#include "base/check_op.h" #include "base/check_op.h"
#include "base/optional.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
...@@ -96,9 +95,17 @@ bool IsStackFrameValid(uintptr_t fp, uintptr_t prev_fp, uintptr_t stack_end) { ...@@ -96,9 +95,17 @@ bool IsStackFrameValid(uintptr_t fp, uintptr_t prev_fp, uintptr_t stack_end) {
// <more frames from Chrome> // <more frames from Chrome>
// __libc_start_main // __libc_start_main
// //
// For stack scanning to be efficient it's very important for the thread to
// be started by Chrome. In that case we naturally terminate unwinding once
// we reach the origin of the stack (i.e. GetStackEnd()). If the thread is
// not started by Chrome (e.g. Android's main thread), then we end up always
// scanning area at the origin of the stack, wasting time and not finding any
// frames (since Android libraries don't have frame pointers).
//
// ScanStackForNextFrame() returns 0 if it couldn't find a valid frame // ScanStackForNextFrame() returns 0 if it couldn't find a valid frame
// (or if stack scanning is not supported on the current platform). // (or if stack scanning is not supported on the current platform).
uintptr_t ScanStackForNextFrame(uintptr_t fp, uintptr_t stack_end) { uintptr_t ScanStackForNextFrame(uintptr_t fp, uintptr_t stack_end) {
#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// Enough to resume almost all prematurely terminated traces. // Enough to resume almost all prematurely terminated traces.
constexpr size_t kMaxStackScanArea = 8192; constexpr size_t kMaxStackScanArea = 8192;
...@@ -123,6 +130,7 @@ uintptr_t ScanStackForNextFrame(uintptr_t fp, uintptr_t stack_end) { ...@@ -123,6 +130,7 @@ uintptr_t ScanStackForNextFrame(uintptr_t fp, uintptr_t stack_end) {
} }
} }
} }
#endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
return 0; return 0;
} }
...@@ -242,122 +250,44 @@ std::ostream& operator<<(std::ostream& os, const StackTrace& s) { ...@@ -242,122 +250,44 @@ std::ostream& operator<<(std::ostream& os, const StackTrace& s) {
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
struct AddressRange { size_t TraceStackFramePointers(const void** out_trace,
uintptr_t start; size_t max_depth,
uintptr_t end; size_t skip_initial) {
}; // Usage of __builtin_frame_address() enables frame pointers in this
// function even if they are not enabled globally. So 'fp' will always
bool IsWithinRange(uintptr_t address, const AddressRange& range) { // be valid.
return address >= range.start && address <= range.end; uintptr_t fp = reinterpret_cast<uintptr_t>(__builtin_frame_address(0)) -
} kStackFrameAdjustment;
size_t TraceStackFramePointersInternal( uintptr_t stack_end = GetStackEnd();
base::Optional<uintptr_t> fp,
uintptr_t stack_end,
size_t max_depth,
size_t skip_initial,
bool enable_scanning,
base::Optional<AddressRange> caller_function_range,
const void** out_trace) {
// If |fp| is not provided then try to unwind the current stack. In this case
// the caller function cannot pass in it's own frame pointer to unwind
// because the frame pointer may not be valid here. The compiler can optimize
// the tail function call from the caller to skip to the previous frame of the
// caller directly, making it's frame pointer invalid when we reach this
// function.
if (!fp) {
// Usage of __builtin_frame_address() enables frame pointers in this
// function even if they are not enabled globally. So 'fp' will always
// be valid.
fp = reinterpret_cast<uintptr_t>(__builtin_frame_address(0)) -
kStackFrameAdjustment;
}
size_t depth = 0; size_t depth = 0;
while (depth < max_depth) { while (depth < max_depth) {
uintptr_t pc = GetStackFramePC(*fp); if (skip_initial != 0) {
// Case 1: If we are unwinding on a copied stack, then skip_initial--;
// |caller_function_range| will not exist. } else {
// out_trace[depth++] = reinterpret_cast<const void*>(GetStackFramePC(fp));
// Case 2: If we are unwinding the current stack from this function's frame,
// the next frame could be either the caller (TraceStackFramePointers()) or
// the function that called TraceStackFramePointers() (say Fn()).
//
// 2a. If the current function (depending on optimization of the build) is
// inlined, or the tail call to this function from TraceStackFramePointers()
// causes the frame pointer to skip directly to Fn(), the stack will look
// like this:
// 1st Frame: TraceStackFramePointersInternal()
// TraceStackFramePointers() has no frame
// 2nd Frame: Fn()
// ...
// In this case we do not want to skip the caller from the output.
//
// 2b. Otherwise the stack will look like this:
// 1st Frame: TraceStackFramePointersInternal()
// 2nd Frame: <stack space of TraceStackFramePointers()> <- Skip
// 3rd Frame: Fn()
// In this case, the next pc will be within the caller function's
// addresses, so skip the frame.
if (!caller_function_range || !IsWithinRange(pc, *caller_function_range)) {
if (skip_initial != 0) {
skip_initial--;
} else {
out_trace[depth++] = reinterpret_cast<const void*>(pc);
}
} }
uintptr_t next_fp = GetNextStackFrame(*fp); uintptr_t next_fp = GetNextStackFrame(fp);
if (IsStackFrameValid(next_fp, *fp, stack_end)) { if (IsStackFrameValid(next_fp, fp, stack_end)) {
fp = next_fp; fp = next_fp;
continue; continue;
} }
if (!enable_scanning) next_fp = ScanStackForNextFrame(fp, stack_end);
break;
next_fp = ScanStackForNextFrame(*fp, stack_end);
if (next_fp) { if (next_fp) {
fp = next_fp; fp = next_fp;
} else { continue;
break;
} }
}
return depth; // Failed to find next frame.
} break;
}
size_t TraceStackFramePointers(const void** out_trace,
size_t max_depth,
size_t skip_initial,
bool enable_scanning) {
// This function's frame can be skipped by the compiler since the callee
// function can jump to caller of this function directly while execution.
// Since there is no way to guarantee that the first frame the trace stack
// function finds will be this function or the previous function, skip the
// current function if it is found.
TraceStackFramePointers_start:
AddressRange current_fn_range = {
reinterpret_cast<uintptr_t>(&&TraceStackFramePointers_start),
reinterpret_cast<uintptr_t>(&&TraceStackFramePointers_end)};
size_t depth = TraceStackFramePointersInternal(
/*fp=*/base::nullopt, GetStackEnd(), max_depth, skip_initial,
enable_scanning, current_fn_range, out_trace);
TraceStackFramePointers_end:
return depth; return depth;
} }
size_t TraceStackFramePointersFromBuffer(uintptr_t fp,
uintptr_t stack_end,
const void** out_trace,
size_t max_depth,
size_t skip_initial,
bool enable_scanning) {
return TraceStackFramePointersInternal(fp, stack_end, max_depth, skip_initial,
enable_scanning, base::nullopt,
out_trace);
}
ScopedStackFrameLinker::ScopedStackFrameLinker(void* fp, void* parent_fp) ScopedStackFrameLinker::ScopedStackFrameLinker(void* fp, void* parent_fp)
: fp_(fp), : fp_(fp),
parent_fp_(parent_fp), parent_fp_(parent_fp),
......
...@@ -144,20 +144,6 @@ BASE_EXPORT std::ostream& operator<<(std::ostream& os, const StackTrace& s); ...@@ -144,20 +144,6 @@ BASE_EXPORT std::ostream& operator<<(std::ostream& os, const StackTrace& s);
BASE_EXPORT size_t CollectStackTrace(void** trace, size_t count); BASE_EXPORT size_t CollectStackTrace(void** trace, size_t count);
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
// For stack scanning to be efficient it's very important for the thread to
// be started by Chrome. In that case we naturally terminate unwinding once
// we reach the origin of the stack (i.e. GetStackEnd()). If the thread is
// not started by Chrome (e.g. Android's main thread), then we end up always
// scanning area at the origin of the stack, wasting time and not finding any
// frames (since Android libraries don't have frame pointers). Scanning is not
// enabled on other posix platforms due to legacy reasons.
#if defined(OS_LINUX)
constexpr bool kEnableScanningByDefault = true;
#else
constexpr bool kEnableScanningByDefault = false;
#endif
// Traces the stack by using frame pointers. This function is faster but less // Traces the stack by using frame pointers. This function is faster but less
// reliable than StackTrace. It should work for debug and profiling builds, // reliable than StackTrace. It should work for debug and profiling builds,
// but not for release builds (although there are some exceptions). // but not for release builds (although there are some exceptions).
...@@ -165,25 +151,10 @@ constexpr bool kEnableScanningByDefault = false; ...@@ -165,25 +151,10 @@ constexpr bool kEnableScanningByDefault = false;
// Writes at most |max_depth| frames (instruction pointers) into |out_trace| // Writes at most |max_depth| frames (instruction pointers) into |out_trace|
// after skipping |skip_initial| frames. Note that the function itself is not // after skipping |skip_initial| frames. Note that the function itself is not
// added to the trace so |skip_initial| should be 0 in most cases. // added to the trace so |skip_initial| should be 0 in most cases.
// Returns number of frames written. |enable_scanning| enables scanning on // Returns number of frames written.
// platforms that do not enable scanning by default. BASE_EXPORT size_t TraceStackFramePointers(const void** out_trace,
BASE_EXPORT size_t size_t max_depth,
TraceStackFramePointers(const void** out_trace, size_t skip_initial);
size_t max_depth,
size_t skip_initial,
bool enable_scanning = kEnableScanningByDefault);
// Same as above function, but allows to pass in frame pointer and stack end
// address for unwinding. This is useful when unwinding based on a copied stack
// segment. Note that the client has to take care of rewriting all the pointers
// in the stack pointing within the stack to point to the copied addresses.
BASE_EXPORT size_t TraceStackFramePointersFromBuffer(
uintptr_t fp,
uintptr_t stack_end,
const void** out_trace,
size_t max_depth,
size_t skip_initial,
bool enable_scanning = kEnableScanningByDefault);
// Links stack frame |fp| to |parent_fp|, so that during stack unwinding // Links stack frame |fp| to |parent_fp|, so that during stack unwinding
// TraceStackFramePointers() visits |parent_fp| after visiting |fp|. // TraceStackFramePointers() visits |parent_fp| after visiting |fp|.
......
...@@ -13,8 +13,6 @@ ...@@ -13,8 +13,6 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/process/kill.h" #include "base/process/kill.h"
#include "base/process/process_handle.h" #include "base/process/process_handle.h"
#include "base/profiler/stack_buffer.h"
#include "base/profiler/stack_copier.h"
#include "base/test/test_timeouts.h" #include "base/test/test_timeouts.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -276,63 +274,30 @@ TEST_F(StackTraceTest, itoa_r) { ...@@ -276,63 +274,30 @@ TEST_F(StackTraceTest, itoa_r) {
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
class CopyFunction : public StackCopier {
public:
using StackCopier::CopyStackContentsAndRewritePointers;
};
// Copies the current stack segment, starting from the frame pointer of the
// caller frame. Also fills in |stack_end| for the copied stack.
static std::unique_ptr<StackBuffer> NOINLINE
CopyCurrentStackAndRewritePointers(uintptr_t* out_fp, uintptr_t* stack_end) {
const uint8_t* fp =
reinterpret_cast<const uint8_t*>(__builtin_frame_address(0));
uintptr_t original_stack_end = GetStackEnd();
size_t stack_size = original_stack_end - reinterpret_cast<uintptr_t>(fp);
auto buffer = std::make_unique<StackBuffer>(stack_size);
*out_fp = reinterpret_cast<uintptr_t>(
CopyFunction::CopyStackContentsAndRewritePointers(
fp, reinterpret_cast<const uintptr_t*>(original_stack_end),
StackBuffer::kPlatformStackAlignment, buffer->buffer()));
*stack_end = *out_fp + stack_size;
return buffer;
}
template <size_t Depth> template <size_t Depth>
void NOINLINE ExpectStackFramePointers(const void** frames, void NOINLINE ExpectStackFramePointers(const void** frames,
size_t max_depth, size_t max_depth) {
bool copy_stack) {
code_start: code_start:
// Calling __builtin_frame_address() forces compiler to emit // Calling __builtin_frame_address() forces compiler to emit
// frame pointers, even if they are not enabled. // frame pointers, even if they are not enabled.
EXPECT_NE(nullptr, __builtin_frame_address(0)); EXPECT_NE(nullptr, __builtin_frame_address(0));
ExpectStackFramePointers<Depth - 1>(frames, max_depth, copy_stack); ExpectStackFramePointers<Depth - 1>(frames, max_depth);
constexpr size_t frame_index = Depth - 1; constexpr size_t frame_index = Depth - 1;
const void* frame = frames[frame_index]; const void* frame = frames[frame_index];
EXPECT_GE(frame, &&code_start) << "For frame at index " << frame_index; EXPECT_GE(frame, &&code_start) << "For frame at index " << frame_index;
EXPECT_LE(frame, &&code_end) << "For frame at index " << frame_index; EXPECT_LE(frame, &&code_end) << "For frame at index " << frame_index;
code_end: return; code_end: return;
} }
template <> template <>
void NOINLINE ExpectStackFramePointers<1>(const void** frames, void NOINLINE ExpectStackFramePointers<1>(const void** frames,
size_t max_depth, size_t max_depth) {
bool copy_stack) {
code_start: code_start:
// Calling __builtin_frame_address() forces compiler to emit // Calling __builtin_frame_address() forces compiler to emit
// frame pointers, even if they are not enabled. // frame pointers, even if they are not enabled.
EXPECT_NE(nullptr, __builtin_frame_address(0)); EXPECT_NE(nullptr, __builtin_frame_address(0));
size_t count = 0; size_t count = TraceStackFramePointers(frames, max_depth, 0);
if (copy_stack) {
uintptr_t stack_end = 0, fp = 0;
std::unique_ptr<StackBuffer> copy =
CopyCurrentStackAndRewritePointers(&fp, &stack_end);
count =
TraceStackFramePointersFromBuffer(fp, stack_end, frames, max_depth, 0);
} else {
count = TraceStackFramePointers(frames, max_depth, 0);
}
ASSERT_EQ(max_depth, count); ASSERT_EQ(max_depth, count);
const void* frame = frames[0]; const void* frame = frames[0];
...@@ -353,24 +318,7 @@ void NOINLINE ExpectStackFramePointers(const void** frames, ...@@ -353,24 +318,7 @@ void NOINLINE ExpectStackFramePointers(const void** frames,
TEST_F(StackTraceTest, MAYBE_TraceStackFramePointers) { TEST_F(StackTraceTest, MAYBE_TraceStackFramePointers) {
constexpr size_t kDepth = 5; constexpr size_t kDepth = 5;
const void* frames[kDepth]; const void* frames[kDepth];
ExpectStackFramePointers<kDepth>(frames, kDepth, /*copy_stack=*/false); ExpectStackFramePointers<kDepth>(frames, kDepth);
}
#if defined(MEMORY_SANITIZER)
// The test triggers use-of-uninitialized-value errors on MSan bots.
// This is expected because we're walking and reading the stack, and
// sometimes we read fp / pc from the place that previously held
// uninitialized value.
#define MAYBE_TraceStackFramePointersFromBuffer \
DISABLED_TraceStackFramePointersFromBuffer
#else
#define MAYBE_TraceStackFramePointersFromBuffer \
TraceStackFramePointersFromBuffer
#endif
TEST_F(StackTraceTest, MAYBE_TraceStackFramePointersFromBuffer) {
constexpr size_t kDepth = 5;
const void* frames[kDepth];
ExpectStackFramePointers<kDepth>(frames, kDepth, /*copy_stack=*/true);
} }
#if defined(OS_ANDROID) || defined(OS_APPLE) #if defined(OS_ANDROID) || defined(OS_APPLE)
......
...@@ -21,6 +21,9 @@ std::unique_ptr<StackSampler> StackSampler::Create( ...@@ -21,6 +21,9 @@ std::unique_ptr<StackSampler> StackSampler::Create(
std::vector<std::unique_ptr<Unwinder>> core_unwinders, std::vector<std::unique_ptr<Unwinder>> core_unwinders,
RepeatingClosure record_sample_callback, RepeatingClosure record_sample_callback,
StackSamplerTestDelegate* test_delegate) { StackSamplerTestDelegate* test_delegate) {
// |core_unwinders| must contain NativeUnwinderAndroid and
// ChromeUnwinderAndroid, respectively.
DCHECK_EQ(2U, core_unwinders.size());
return std::make_unique<StackSamplerImpl>( return std::make_unique<StackSamplerImpl>(
std::make_unique<StackCopierSignal>( std::make_unique<StackCopierSignal>(
std::make_unique<ThreadDelegatePosix>(thread_token)), std::make_unique<ThreadDelegatePosix>(thread_token)),
......
...@@ -92,11 +92,6 @@ source_set("tests") { ...@@ -92,11 +92,6 @@ source_set("tests") {
"public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc", "public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc",
] ]
if (is_android && current_cpu == "arm64") {
sources +=
[ "public/cpp/stack_sampling/stack_unwinder_arm64_android_unittest.cc" ]
}
if (!is_android) { if (!is_android) {
sources += [ "tracing_service_unittest.cc" ] sources += [ "tracing_service_unittest.cc" ]
} }
......
...@@ -119,13 +119,6 @@ target(tracing_lib_type, "cpp") { ...@@ -119,13 +119,6 @@ target(tracing_lib_type, "cpp") {
"tracing_features.h", "tracing_features.h",
] ]
if (is_android && current_cpu == "arm64") {
sources += [
"stack_sampling/stack_unwinder_arm64_android.cc",
"stack_sampling/stack_unwinder_arm64_android.h",
]
}
if (enable_loader_lock_sampling) { if (enable_loader_lock_sampling) {
sources += [ sources += [
"stack_sampling/loader_lock_sampler_win.cc", "stack_sampling/loader_lock_sampler_win.cc",
......
// 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 "services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android.h"
#include "base/debug/stack_trace.h"
namespace tracing {
bool UnwinderArm64::CanUnwindFrom(const base::Frame& current_frame) const {
return true;
}
base::UnwindResult UnwinderArm64::TryUnwind(
base::RegisterContext* thread_context,
uintptr_t stack_top,
base::ModuleCache* module_cache,
std::vector<base::Frame>* stack) const {
uintptr_t fp = thread_context->regs[29];
constexpr size_t kMaxDepth = 40;
const void* out_trace[kMaxDepth] = {};
// If the fp is not valid, then pass the stack pointer as fp. The unwind
// function scans the stack to find the next frame.
if (fp < thread_context->sp || fp >= stack_top) {
fp = thread_context->sp;
}
size_t depth = base::debug::TraceStackFramePointersFromBuffer(
fp, stack_top, out_trace, kMaxDepth, 0, /*enable_scanning=*/true);
for (size_t i = 0; i < depth; ++i) {
uintptr_t pc = reinterpret_cast<uintptr_t>(out_trace[i]);
stack->push_back(base::Frame(pc, module_cache->GetModuleForAddress(pc)));
}
return base::UnwindResult::COMPLETED;
}
} // namespace tracing
// 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.
#ifndef SERVICES_TRACING_PUBLIC_CPP_STACK_SAMPLING_STACK_UNWINDER_ARM64_ANDROID_H_
#define SERVICES_TRACING_PUBLIC_CPP_STACK_SAMPLING_STACK_UNWINDER_ARM64_ANDROID_H_
#include "base/component_export.h"
#include "base/profiler/unwinder.h"
namespace tracing {
// Unwinder implementation for arm64 builds with frame pointer enabled.
class COMPONENT_EXPORT(TRACING_CPP) UnwinderArm64 : public base::Unwinder {
public:
bool CanUnwindFrom(const base::Frame& current_frame) const override;
base::UnwindResult TryUnwind(base::RegisterContext* thread_context,
uintptr_t stack_top,
base::ModuleCache* module_cache,
std::vector<base::Frame>* stack) const override;
};
} // namespace tracing
#endif // SERVICES_TRACING_PUBLIC_CPP_STACK_SAMPLING_STACK_UNWINDER_ARM64_ANDROID_H_
// 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 "services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android.h"
#include "base/profiler/register_context.h"
#include "base/profiler/stack_buffer.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace tracing {
namespace {
constexpr size_t kMaxFrameCount = 5;
constexpr size_t kFrameSize = 40;
constexpr size_t kStackSize = 1024;
constexpr uintptr_t kFirstSamplePc = 0x12345678;
void SetValue(uintptr_t address, uintptr_t value) {
uintptr_t* ptr = reinterpret_cast<uintptr_t*>(address);
*ptr = value;
}
void FillFrames(uintptr_t fp, size_t frame_count) {
for (size_t i = 0; i < frame_count; ++i) {
SetValue(fp, fp + kFrameSize);
SetValue(fp + sizeof(uintptr_t), kFirstSamplePc + i);
fp = fp + kFrameSize;
}
}
} // namespace
TEST(UnwinderArm64Test, UnwindValidStack) {
base::RegisterContext register_context{};
base::ModuleCache module_cache;
base::StackBuffer buffer(kStackSize);
memset(buffer.buffer(), 0, kStackSize);
const uintptr_t fp = reinterpret_cast<uintptr_t>(buffer.buffer());
const size_t stack_top = fp + kStackSize;
register_context.regs[29] = fp;
register_context.sp = fp;
FillFrames(fp, kMaxFrameCount);
UnwinderArm64 unwinder;
std::vector<base::Frame> stack;
EXPECT_EQ(
base::UnwindResult::COMPLETED,
unwinder.TryUnwind(&register_context, stack_top, &module_cache, &stack));
ASSERT_EQ(kMaxFrameCount, stack.size());
for (size_t i = 0; i < kMaxFrameCount; ++i) {
EXPECT_EQ(kFirstSamplePc + i, stack[i].instruction_pointer);
EXPECT_EQ(nullptr, stack[i].module);
}
}
TEST(UnwinderArm64Test, UnwindInvalidFirstFrame) {
base::RegisterContext register_context{};
base::ModuleCache module_cache;
base::StackBuffer buffer(kStackSize);
memset(buffer.buffer(), 0, kStackSize);
uintptr_t fp = reinterpret_cast<uintptr_t>(buffer.buffer());
const size_t stack_top = fp + kStackSize;
register_context.regs[29] = fp;
register_context.sp = fp;
// FP from register context points bad frame within stack.
fp += 80;
FillFrames(fp, kMaxFrameCount);
UnwinderArm64 unwinder;
std::vector<base::Frame> stack;
EXPECT_EQ(
base::UnwindResult::COMPLETED,
unwinder.TryUnwind(&register_context, stack_top, &module_cache, &stack));
// One extra frame is added when scanning starts.
ASSERT_EQ(kMaxFrameCount + 1, stack.size());
for (size_t i = 0; i < kMaxFrameCount; ++i) {
EXPECT_EQ(kFirstSamplePc + i, stack[i + 1].instruction_pointer);
EXPECT_EQ(nullptr, stack[i + 1].module);
}
}
TEST(UnwinderArm64Test, UnwindInvalidFp) {
base::RegisterContext register_context{};
base::ModuleCache module_cache;
base::StackBuffer buffer(kStackSize);
memset(buffer.buffer(), 0, kStackSize);
uintptr_t fp = reinterpret_cast<uintptr_t>(buffer.buffer());
const size_t stack_top = fp + kStackSize;
// FP points to a bad value. SP will be used to start unwinding. SP points to
// some point in end of stack, but the first frame starts after 20 bytes.
register_context.regs[29] = 50;
register_context.sp = fp;
fp += 80;
FillFrames(fp, kMaxFrameCount);
UnwinderArm64 unwinder;
std::vector<base::Frame> stack;
EXPECT_EQ(
base::UnwindResult::COMPLETED,
unwinder.TryUnwind(&register_context, stack_top, &module_cache, &stack));
// One extra frame is added when scanning starts.
ASSERT_EQ(kMaxFrameCount + 1, stack.size());
for (size_t i = 0; i < kMaxFrameCount; ++i) {
EXPECT_EQ(kFirstSamplePc + i, stack[i + 1].instruction_pointer);
EXPECT_EQ(nullptr, stack[i + 1].module);
}
}
} // namespace tracing
...@@ -7,10 +7,8 @@ ...@@ -7,10 +7,8 @@
#include <limits> #include <limits>
#include <set> #include <set>
#include "base/android/library_loader/anchor_functions.h"
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "base/debug/leak_annotations.h" #include "base/debug/leak_annotations.h"
#include "base/debug/stack_trace.h"
#include "base/hash/hash.h" #include "base/hash/hash.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/no_destructor.h" #include "base/no_destructor.h"
...@@ -33,22 +31,18 @@ ...@@ -33,22 +31,18 @@
#include "third_party/perfetto/protos/perfetto/trace/track_event/process_descriptor.pbzero.h" #include "third_party/perfetto/protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/thread_descriptor.pbzero.h" #include "third_party/perfetto/protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
#if ANDROID_STACK_UNWINDING_SUPPORTED #if defined(OS_ANDROID)
#include <dlfcn.h>
#include "base/android/reached_code_profiler.h" #include "base/android/reached_code_profiler.h"
#endif
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) #if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
#include "services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android.h" defined(OFFICIAL_BUILD)
#include <dlfcn.h>
#elif BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && defined(OFFICIAL_BUILD)
#include "base/trace_event/cfi_backtrace_android.h" #include "base/trace_event/cfi_backtrace_android.h"
#include "services/tracing/public/cpp/stack_sampling/stack_sampler_android.h" #include "services/tracing/public/cpp/stack_sampling/stack_sampler_android.h"
#endif #endif
#endif // ANDROID_STACK_UNWINDING_SUPPORTED
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING) #if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
#include "services/tracing/public/cpp/stack_sampling/loader_lock_sampler_win.h" #include "services/tracing/public/cpp/stack_sampling/loader_lock_sampler_win.h"
#endif #endif
...@@ -60,25 +54,6 @@ namespace tracing { ...@@ -60,25 +54,6 @@ namespace tracing {
namespace { namespace {
#if ANDROID_STACK_UNWINDING_SUPPORTED
extern "C" {
// The address of |__executable_start| gives the start address of the
// executable or shared library. This value is used to find the offset address
// of the instruction in binary from PC.
extern char __executable_start;
} // extern "C"
bool is_chrome_address(uintptr_t pc) {
return pc >= base::android::kStartOfText && pc < base::android::kEndOfText;
}
uintptr_t executable_start_addr() {
return reinterpret_cast<uintptr_t>(&__executable_start);
}
#endif // ANDROID_STACK_UNWINDING_SUPPORTED
// Pointer to the main thread instance, if any. // Pointer to the main thread instance, if any.
TracingSamplerProfiler* g_main_thread_instance = nullptr; TracingSamplerProfiler* g_main_thread_instance = nullptr;
...@@ -385,15 +360,18 @@ TracingSamplerProfiler::TracingProfileBuilder::GetCallstackIDAndMaybeEmit( ...@@ -385,15 +360,18 @@ TracingSamplerProfiler::TracingProfileBuilder::GetCallstackIDAndMaybeEmit(
std::string module_id; std::string module_id;
uintptr_t rel_pc = 0; uintptr_t rel_pc = 0;
#if ANDROID_STACK_UNWINDING_SUPPORTED #if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
defined(OFFICIAL_BUILD)
Dl_info info = {}; Dl_info info = {};
// For chrome address we do not have symbols on the binary. So, just write // For chrome address we do not have symbols on the binary. So, just write
// the offset address. For addresses on framework libraries, symbolize // the offset address. For addresses on framework libraries, symbolize
// and write the function name. // and write the function name.
if (frame.instruction_pointer == 0) { if (frame.instruction_pointer == 0) {
frame_name = "Scanned"; frame_name = "Scanned";
} else if (is_chrome_address(frame.instruction_pointer)) { } else if (base::trace_event::CFIBacktraceAndroid::is_chrome_address(
rel_pc = frame.instruction_pointer - executable_start_addr(); frame.instruction_pointer)) {
rel_pc = frame.instruction_pointer -
base::trace_event::CFIBacktraceAndroid::executable_start_addr();
} else if (dladdr(reinterpret_cast<void*>(frame.instruction_pointer), } else if (dladdr(reinterpret_cast<void*>(frame.instruction_pointer),
&info) != 0) { &info) != 0) {
// TODO(ssid): Add offset and module debug id if symbol was not resolved // TODO(ssid): Add offset and module debug id if symbol was not resolved
...@@ -412,11 +390,13 @@ TracingSamplerProfiler::TracingProfileBuilder::GetCallstackIDAndMaybeEmit( ...@@ -412,11 +390,13 @@ TracingSamplerProfiler::TracingProfileBuilder::GetCallstackIDAndMaybeEmit(
// If no module is available, then name it unknown. Adding PC would be // If no module is available, then name it unknown. Adding PC would be
// useless anyway. // useless anyway.
if (module_name.empty() && !is_chrome_address(frame.instruction_pointer)) { if (module_name.empty()) {
DCHECK(!base::trace_event::CFIBacktraceAndroid::is_chrome_address(
frame.instruction_pointer));
frame_name = "Unknown"; frame_name = "Unknown";
rel_pc = 0; rel_pc = 0;
} }
#else // ANDROID_STACK_UNWINDING_SUPPORTED #else
if (frame.module) { if (frame.module) {
module_name = frame.module->GetDebugBasename().MaybeAsASCII(); module_name = frame.module->GetDebugBasename().MaybeAsASCII();
module_id = frame.module->GetId(); module_id = frame.module->GetId();
...@@ -425,7 +405,7 @@ TracingSamplerProfiler::TracingProfileBuilder::GetCallstackIDAndMaybeEmit( ...@@ -425,7 +405,7 @@ TracingSamplerProfiler::TracingProfileBuilder::GetCallstackIDAndMaybeEmit(
module_name = module_id = ""; module_name = module_id = "";
frame_name = "Unknown"; frame_name = "Unknown";
} }
#endif // !ANDROID_STACK_UNWINDING_SUPPORTED #endif
MangleModuleIDIfNeeded(&module_id); MangleModuleIDIfNeeded(&module_id);
...@@ -674,19 +654,15 @@ void TracingSamplerProfiler::StartTracing( ...@@ -674,19 +654,15 @@ void TracingSamplerProfiler::StartTracing(
return; return;
} }
#if ANDROID_STACK_UNWINDING_SUPPORTED #if defined(OS_ANDROID)
// The sampler profiler would conflict with the reached code profiler if they // The sampler profiler would conflict with the reached code profiler if they
// run at the same time because they use the same signal to suspend threads. // run at the same time because they use the same signal to suspend threads.
if (base::android::IsReachedCodeProfilerEnabled()) if (base::android::IsReachedCodeProfilerEnabled())
return; return;
#else // ANDROID_STACK_UNWINDING_SUPPORTED #endif
// On Android the sampling profiler is implemented by tracing service and is
// not yet supported by base::StackSamplingProfiler. So, only check this if
// service does not support unwinding in current platform.
if (!base::StackSamplingProfiler::IsSupported()) if (!base::StackSamplingProfiler::IsSupported())
return; return;
#endif // !ANDROID_STACK_UNWINDING_SUPPORTED
base::StackSamplingProfiler::SamplingParams params; base::StackSamplingProfiler::SamplingParams params;
params.samples_per_profile = std::numeric_limits<int>::max(); params.samples_per_profile = std::numeric_limits<int>::max();
...@@ -703,22 +679,14 @@ void TracingSamplerProfiler::StartTracing( ...@@ -703,22 +679,14 @@ void TracingSamplerProfiler::StartTracing(
profile_builder_ = profile_builder.get(); profile_builder_ = profile_builder.get();
// Create and start the stack sampling profiler. // Create and start the stack sampling profiler.
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) #if BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && defined(OFFICIAL_BUILD)
std::vector<std::unique_ptr<base::Unwinder>> unwinder;
unwinder.push_back(std::make_unique<UnwinderArm64>());
profiler_ = std::make_unique<base::StackSamplingProfiler>(
sampled_thread_token_, params, std::move(profile_builder),
std::move(unwinder));
profiler_->Start();
#elif BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && defined(OFFICIAL_BUILD)
auto* module_cache = profile_builder->GetModuleCache(); auto* module_cache = profile_builder->GetModuleCache();
profiler_ = std::make_unique<base::StackSamplingProfiler>( profiler_ = std::make_unique<base::StackSamplingProfiler>(
params, std::move(profile_builder), params, std::move(profile_builder),
std::make_unique<StackSamplerAndroid>(sampled_thread_token_, std::make_unique<StackSamplerAndroid>(sampled_thread_token_,
module_cache)); module_cache));
profiler_->Start(); profiler_->Start();
#endif #endif // BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && defined(OFFICIAL_BUILD)
#else // defined(OS_ANDROID) #else // defined(OS_ANDROID)
profiler_ = std::make_unique<base::StackSamplingProfiler>( profiler_ = std::make_unique<base::StackSamplingProfiler>(
sampled_thread_token_, params, std::move(profile_builder)); sampled_thread_token_, params, std::move(profile_builder));
......
...@@ -25,14 +25,6 @@ ...@@ -25,14 +25,6 @@
#include "services/tracing/public/cpp/perfetto/interning_index.h" #include "services/tracing/public/cpp/perfetto/interning_index.h"
#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h" #include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h"
#if defined(OS_ANDROID) && \
(BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) || \
(BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && defined(OFFICIAL_BUILD)))
#define ANDROID_STACK_UNWINDING_SUPPORTED 1
#else
#define ANDROID_STACK_UNWINDING_SUPPORTED 0
#endif
namespace tracing { namespace tracing {
class PerfettoProducer; class PerfettoProducer;
...@@ -175,8 +167,9 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler { ...@@ -175,8 +167,9 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler {
// Returns whether of not the sampler profiling is able to unwind the stack // Returns whether of not the sampler profiling is able to unwind the stack
// on this platform. // on this platform.
constexpr static bool IsStackUnwindingSupported() { constexpr static bool IsStackUnwindingSupported() {
#if defined(OS_MAC) || defined(OS_WIN) && defined(_WIN64) || \ #if defined(OS_MAC) || defined(OS_WIN) && defined(_WIN64) || \
ANDROID_STACK_UNWINDING_SUPPORTED (defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
defined(OFFICIAL_BUILD))
return true; return true;
#else #else
return false; return false;
......
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