Commit 659b4301 authored by ssid's avatar ssid Committed by Commit Bot

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: default avatarMike Wittman <wittman@chromium.org>
Reviewed-by: default avatarNico Weber <thakis@chromium.org>
Reviewed-by: default avataroysteine <oysteine@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805274}
parent 2f163ad2
......@@ -10,6 +10,7 @@
#include <sstream>
#include "base/check_op.h"
#include "base/optional.h"
#include "base/stl_util.h"
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
......@@ -95,17 +96,9 @@ bool IsStackFrameValid(uintptr_t fp, uintptr_t prev_fp, uintptr_t stack_end) {
// <more frames from Chrome>
// __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
// (or if stack scanning is not supported on the current platform).
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.
constexpr size_t kMaxStackScanArea = 8192;
......@@ -130,7 +123,6 @@ uintptr_t ScanStackForNextFrame(uintptr_t fp, uintptr_t stack_end) {
}
}
}
#endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
return 0;
}
......@@ -250,44 +242,122 @@ std::ostream& operator<<(std::ostream& os, const StackTrace& s) {
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
size_t TraceStackFramePointers(const void** out_trace,
size_t max_depth,
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
// be valid.
uintptr_t fp = reinterpret_cast<uintptr_t>(__builtin_frame_address(0)) -
kStackFrameAdjustment;
struct AddressRange {
uintptr_t start;
uintptr_t end;
};
uintptr_t stack_end = GetStackEnd();
bool IsWithinRange(uintptr_t address, const AddressRange& range) {
return address >= range.start && address <= range.end;
}
size_t TraceStackFramePointersInternal(
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;
while (depth < max_depth) {
if (skip_initial != 0) {
skip_initial--;
} else {
out_trace[depth++] = reinterpret_cast<const void*>(GetStackFramePC(fp));
uintptr_t pc = GetStackFramePC(*fp);
// Case 1: If we are unwinding on a copied stack, then
// |caller_function_range| will not exist.
//
// 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);
if (IsStackFrameValid(next_fp, fp, stack_end)) {
uintptr_t next_fp = GetNextStackFrame(*fp);
if (IsStackFrameValid(next_fp, *fp, stack_end)) {
fp = next_fp;
continue;
}
next_fp = ScanStackForNextFrame(fp, stack_end);
if (!enable_scanning)
break;
next_fp = ScanStackForNextFrame(*fp, stack_end);
if (next_fp) {
fp = next_fp;
continue;
} else {
break;
}
// Failed to find next frame.
break;
}
return depth;
}
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;
}
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)
: fp_(fp),
parent_fp_(parent_fp),
......
......@@ -144,6 +144,20 @@ BASE_EXPORT std::ostream& operator<<(std::ostream& os, const StackTrace& s);
BASE_EXPORT size_t CollectStackTrace(void** trace, size_t count);
#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
// reliable than StackTrace. It should work for debug and profiling builds,
// but not for release builds (although there are some exceptions).
......@@ -151,10 +165,25 @@ BASE_EXPORT size_t CollectStackTrace(void** trace, size_t count);
// Writes at most |max_depth| frames (instruction pointers) into |out_trace|
// 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.
// Returns number of frames written.
BASE_EXPORT size_t TraceStackFramePointers(const void** out_trace,
size_t max_depth,
size_t skip_initial);
// Returns number of frames written. |enable_scanning| enables scanning on
// platforms that do not enable scanning by default.
BASE_EXPORT size_t
TraceStackFramePointers(const void** out_trace,
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
// TraceStackFramePointers() visits |parent_fp| after visiting |fp|.
......
......@@ -13,6 +13,8 @@
#include "base/logging.h"
#include "base/process/kill.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 "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -274,30 +276,63 @@ TEST_F(StackTraceTest, itoa_r) {
#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>
void NOINLINE ExpectStackFramePointers(const void** frames,
size_t max_depth) {
size_t max_depth,
bool copy_stack) {
code_start:
// Calling __builtin_frame_address() forces compiler to emit
// frame pointers, even if they are not enabled.
EXPECT_NE(nullptr, __builtin_frame_address(0));
ExpectStackFramePointers<Depth - 1>(frames, max_depth);
ExpectStackFramePointers<Depth - 1>(frames, max_depth, copy_stack);
constexpr size_t frame_index = Depth - 1;
const void* frame = frames[frame_index];
EXPECT_GE(frame, &&code_start) << "For frame at index " << frame_index;
EXPECT_LE(frame, &&code_end) << "For frame at index " << frame_index;
code_end: return;
}
}
template <>
void NOINLINE ExpectStackFramePointers<1>(const void** frames,
size_t max_depth) {
template <>
void NOINLINE ExpectStackFramePointers<1>(const void** frames,
size_t max_depth,
bool copy_stack) {
code_start:
// Calling __builtin_frame_address() forces compiler to emit
// frame pointers, even if they are not enabled.
EXPECT_NE(nullptr, __builtin_frame_address(0));
size_t count = TraceStackFramePointers(frames, max_depth, 0);
size_t count = 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);
const void* frame = frames[0];
......@@ -318,7 +353,24 @@ void NOINLINE ExpectStackFramePointers<1>(const void** frames,
TEST_F(StackTraceTest, MAYBE_TraceStackFramePointers) {
constexpr size_t kDepth = 5;
const void* frames[kDepth];
ExpectStackFramePointers<kDepth>(frames, kDepth);
ExpectStackFramePointers<kDepth>(frames, kDepth, /*copy_stack=*/false);
}
#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)
......
......@@ -21,9 +21,6 @@ std::unique_ptr<StackSampler> StackSampler::Create(
std::vector<std::unique_ptr<Unwinder>> core_unwinders,
RepeatingClosure record_sample_callback,
StackSamplerTestDelegate* test_delegate) {
// |core_unwinders| must contain NativeUnwinderAndroid and
// ChromeUnwinderAndroid, respectively.
DCHECK_EQ(2U, core_unwinders.size());
return std::make_unique<StackSamplerImpl>(
std::make_unique<StackCopierSignal>(
std::make_unique<ThreadDelegatePosix>(thread_token)),
......
......@@ -92,6 +92,11 @@ source_set("tests") {
"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) {
sources += [ "tracing_service_unittest.cc" ]
}
......
......@@ -119,6 +119,13 @@ target(tracing_lib_type, "cpp") {
"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) {
sources += [
"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,8 +7,10 @@
#include <limits>
#include <set>
#include "base/android/library_loader/anchor_functions.h"
#include "base/bind_helpers.h"
#include "base/debug/leak_annotations.h"
#include "base/debug/stack_trace.h"
#include "base/hash/hash.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
......@@ -31,18 +33,22 @@
#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"
#if defined(OS_ANDROID)
#if ANDROID_STACK_UNWINDING_SUPPORTED
#include <dlfcn.h>
#include "base/android/reached_code_profiler.h"
#endif
#if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
defined(OFFICIAL_BUILD)
#include <dlfcn.h>
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
#include "services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android.h"
#elif BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && defined(OFFICIAL_BUILD)
#include "base/trace_event/cfi_backtrace_android.h"
#include "services/tracing/public/cpp/stack_sampling/stack_sampler_android.h"
#endif
#endif // ANDROID_STACK_UNWINDING_SUPPORTED
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
#include "services/tracing/public/cpp/stack_sampling/loader_lock_sampler_win.h"
#endif
......@@ -54,6 +60,25 @@ namespace tracing {
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.
TracingSamplerProfiler* g_main_thread_instance = nullptr;
......@@ -360,18 +385,15 @@ TracingSamplerProfiler::TracingProfileBuilder::GetCallstackIDAndMaybeEmit(
std::string module_id;
uintptr_t rel_pc = 0;
#if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
defined(OFFICIAL_BUILD)
#if ANDROID_STACK_UNWINDING_SUPPORTED
Dl_info info = {};
// For chrome address we do not have symbols on the binary. So, just write
// the offset address. For addresses on framework libraries, symbolize
// and write the function name.
if (frame.instruction_pointer == 0) {
frame_name = "Scanned";
} else if (base::trace_event::CFIBacktraceAndroid::is_chrome_address(
frame.instruction_pointer)) {
rel_pc = frame.instruction_pointer -
base::trace_event::CFIBacktraceAndroid::executable_start_addr();
} else if (is_chrome_address(frame.instruction_pointer)) {
rel_pc = frame.instruction_pointer - executable_start_addr();
} else if (dladdr(reinterpret_cast<void*>(frame.instruction_pointer),
&info) != 0) {
// TODO(ssid): Add offset and module debug id if symbol was not resolved
......@@ -390,13 +412,11 @@ TracingSamplerProfiler::TracingProfileBuilder::GetCallstackIDAndMaybeEmit(
// If no module is available, then name it unknown. Adding PC would be
// useless anyway.
if (module_name.empty()) {
DCHECK(!base::trace_event::CFIBacktraceAndroid::is_chrome_address(
frame.instruction_pointer));
if (module_name.empty() && !is_chrome_address(frame.instruction_pointer)) {
frame_name = "Unknown";
rel_pc = 0;
}
#else
#else // ANDROID_STACK_UNWINDING_SUPPORTED
if (frame.module) {
module_name = frame.module->GetDebugBasename().MaybeAsASCII();
module_id = frame.module->GetId();
......@@ -405,7 +425,7 @@ TracingSamplerProfiler::TracingProfileBuilder::GetCallstackIDAndMaybeEmit(
module_name = module_id = "";
frame_name = "Unknown";
}
#endif
#endif // !ANDROID_STACK_UNWINDING_SUPPORTED
MangleModuleIDIfNeeded(&module_id);
......@@ -654,15 +674,19 @@ void TracingSamplerProfiler::StartTracing(
return;
}
#if defined(OS_ANDROID)
#if ANDROID_STACK_UNWINDING_SUPPORTED
// 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.
if (base::android::IsReachedCodeProfilerEnabled())
return;
#endif
#else // ANDROID_STACK_UNWINDING_SUPPORTED
// 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())
return;
#endif // !ANDROID_STACK_UNWINDING_SUPPORTED
base::StackSamplingProfiler::SamplingParams params;
params.samples_per_profile = std::numeric_limits<int>::max();
......@@ -679,14 +703,22 @@ void TracingSamplerProfiler::StartTracing(
profile_builder_ = profile_builder.get();
// Create and start the stack sampling profiler.
#if defined(OS_ANDROID)
#if BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && defined(OFFICIAL_BUILD)
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
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();
profiler_ = std::make_unique<base::StackSamplingProfiler>(
params, std::move(profile_builder),
std::make_unique<StackSamplerAndroid>(sampled_thread_token_,
module_cache));
profiler_->Start();
#endif // BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && defined(OFFICIAL_BUILD)
#endif
#else // defined(OS_ANDROID)
profiler_ = std::make_unique<base::StackSamplingProfiler>(
sampled_thread_token_, params, std::move(profile_builder));
......
......@@ -25,6 +25,14 @@
#include "services/tracing/public/cpp/perfetto/interning_index.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 {
class PerfettoProducer;
......@@ -167,9 +175,8 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler {
// Returns whether of not the sampler profiling is able to unwind the stack
// on this platform.
constexpr static bool IsStackUnwindingSupported() {
#if defined(OS_MAC) || defined(OS_WIN) && defined(_WIN64) || \
(defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
defined(OFFICIAL_BUILD))
#if defined(OS_MAC) || defined(OS_WIN) && defined(_WIN64) || \
ANDROID_STACK_UNWINDING_SUPPORTED
return true;
#else
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