Revert "Reland "Android: Add code to parse the CFI info added to the apk""
This reverts commit839ddb373b
. Reason for revert: <INSERT REASONING HERE> Original change's description: > Reland "Android: Add code to parse the CFI info added to the apk" > > This reverts commit88328b6800
. > > Reason for revert: seems to break "generate_build_files" step on several of our build bots, see e.g.: > > https://build.chromium.org/deprecated/chromium.webrtc/builders/Linux%20Builder/builds/126662 > > Original change's description: > > Revert "Android: Add code to parse the CFI info added to the apk" > > > > This reverts commit1d8b0c0096
. > > > > Reason for revert: https://crbug.com/826718 > > > > CL causes failures on Android CFI builder. > > > > Original change's description: > > > Android: Add code to parse the CFI info added to the apk > > > > > > The previous CL added support for adding unwind tables in apk: > > > https://chromium-review.googlesource.com/c/chromium/src/+/956971 > > > This CL parses and adds test for unwinding on base_unittests_apk. > > > The non-official builds do not contain debug info since all bots set > > > strip_debug_info=true. So, make sure base_unittests do not take this > > > flag. > > > > > > BUG=819888 > > > > > > Change-Id: Ib69909e14f9f8623ac07154ef60be248558b08a4 > > > Reviewed-on: https://chromium-review.googlesource.com/958326 > > > Commit-Queue: Siddhartha S <ssid@chromium.org> > > > Reviewed-by: Daniel Cheng <dcheng@chromium.org> > > > Reviewed-by: Primiano Tucci <primiano@chromium.org> > > > Reviewed-by: Dirk Pranke <dpranke@chromium.org> > > > Reviewed-by: agrieve <agrieve@chromium.org> > > > Cr-Commit-Position: refs/heads/master@{#546343} > > > > TBR=dcheng@chromium.org,primiano@chromium.org,dpranke@chromium.org,ssid@chromium.org,agrieve@chromium.org,dskiba@chromium.org > > > > Change-Id: Ib9bcb81ffa30861ea91107c1a1f3ccb8f29a7f7d > > No-Presubmit: true > > No-Tree-Checks: true > > No-Try: true > > Bug: 819888 > > Reviewed-on: https://chromium-review.googlesource.com/983972 > > Reviewed-by: François Doray <fdoray@chromium.org> > > Commit-Queue: François Doray <fdoray@chromium.org> > > Cr-Commit-Position: refs/heads/master@{#546472} > > TBR=dcheng@chromium.org,primiano@chromium.org,fdoray@chromium.org,dpranke@chromium.org,ssid@chromium.org,agrieve@chromium.org,dskiba@chromium.org > > Change-Id: Ib39e4563c82264389d7a740a877d27edef343101 > No-Presubmit: true > No-Tree-Checks: true > No-Try: true > Bug: 819888 > Reviewed-on: https://chromium-review.googlesource.com/984092 > Reviewed-by: Henrik Andreasson <henrika@chromium.org> > Commit-Queue: Henrik Andreasson <henrika@chromium.org> > Cr-Commit-Position: refs/heads/master@{#546479} TBR=dcheng@chromium.org,primiano@chromium.org,fdoray@chromium.org,dpranke@chromium.org,ssid@chromium.org,agrieve@chromium.org,dskiba@chromium.org,henrika@chromium.org Change-Id: I1bbb46629fed0b9467482d66f88a548e4d949a24 No-Presubmit: true No-Tree-Checks: true No-Try: true Bug: 819888 Reviewed-on: https://chromium-review.googlesource.com/984013 Reviewed-by: Peter Mayo <petermayo@chromium.org> Commit-Queue: Peter Mayo <petermayo@chromium.org> Cr-Commit-Position: refs/heads/master@{#546481}
This commit is contained in:
base
build/config/compiler
@ -1274,13 +1274,6 @@ jumbo_component("base") {
|
||||
# Needs to be a public config so that dependent targets link against it as
|
||||
# well when doing a component build.
|
||||
public_configs = [ ":android_system_libs" ]
|
||||
|
||||
if (can_unwind_with_cfi_table) {
|
||||
sources += [
|
||||
"trace_event/cfi_backtrace_android.cc",
|
||||
"trace_event/cfi_backtrace_android.h",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# Chromeos.
|
||||
@ -2469,20 +2462,6 @@ test("base_unittests") {
|
||||
}
|
||||
|
||||
if (is_android) {
|
||||
# Add unwind tables in base_unittests_apk test apk. The unwind tables are
|
||||
# generated from debug info in the binary. Removing "default_symbols" and
|
||||
# adding symbols config removes the "strip_debug" config that strips the
|
||||
# debug info, on base unittests apk.
|
||||
if (can_unwind_with_cfi_table) {
|
||||
configs -= [ "//build/config/compiler:default_symbols" ]
|
||||
if (symbol_level == 2) {
|
||||
configs += [ "//build/config/compiler:symbols" ]
|
||||
} else {
|
||||
configs += [ "//build/config/compiler:minimal_symbols" ]
|
||||
}
|
||||
add_unwind_tables_in_apk = true
|
||||
sources += [ "trace_event/cfi_backtrace_android_unittest.cc" ]
|
||||
}
|
||||
sources -= [
|
||||
"process/process_unittest.cc",
|
||||
"process/process_util_unittest.cc",
|
||||
|
@ -1,277 +0,0 @@
|
||||
// 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 "base/trace_event/cfi_backtrace_android.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "base/android/apk_assets.h"
|
||||
#include "base/debug/stack_trace.h"
|
||||
|
||||
#if !defined(ARCH_CPU_ARMEL)
|
||||
#error This file should not be built for this architecture.
|
||||
#endif
|
||||
|
||||
/*
|
||||
Basics of unwinding:
|
||||
For each instruction in a function we need to know what is the offset of SP
|
||||
(Stack Pointer) to reach the previous function's stack frame. To know which
|
||||
function is being invoked, we need the return address of the next function. The
|
||||
CFI information for an instruction is made up of 2 offsets, CFA (Call Frame
|
||||
Address) offset and RA (Return Address) offset. The CFA offset is the change in
|
||||
SP made by the function till the current instruction. This depends on amount of
|
||||
memory allocated on stack by the function plus some registers that the function
|
||||
stores that needs to be restored at the end of function. So, at each instruction
|
||||
the CFA offset tells the offset from original SP before the function call. The
|
||||
RA offset tells us the offset from the previous SP into the current function
|
||||
where the return address is stored.
|
||||
|
||||
The unwind table contains rows of 64 bits each.
|
||||
We have 2 types of rows, FUNCTION and CFI.
|
||||
Each function with CFI info has a single FUNCTION row, followed by one or more
|
||||
CFI rows. All the addresses of the CFI rows will be within the function.
|
||||
1. FUNCTION. Bits in order of high to low represent:
|
||||
31 bits: specifies function address, without the last bit (always 0).
|
||||
1 bit : always 1. Last bit of the address, specifies the row type is
|
||||
FUNCTION.
|
||||
32 bits: length of the current function.
|
||||
|
||||
2. CFI. Bits in order of high to low represent:
|
||||
31 bits: instruction address in the current function.
|
||||
1 bit : always 0. Last bit of the address, specifies the row type is CFI.
|
||||
30 bits: CFA offset / 4.
|
||||
2 bits: RA offset / 4.
|
||||
If the RA offset of a row is 0, then use the offset of the previous rows in the
|
||||
same function.
|
||||
TODO(ssid): Make sure RA offset is always present.
|
||||
|
||||
See extract_unwind_tables.py for details about how this data is extracted from
|
||||
breakpad symbol files.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
extern char __executable_start;
|
||||
extern char _etext;
|
||||
}
|
||||
|
||||
namespace base {
|
||||
namespace trace_event {
|
||||
|
||||
namespace {
|
||||
|
||||
// The bit of the address that is used to specify the type of the row is
|
||||
// FUNCTION or CFI type.
|
||||
constexpr uint32_t kFunctionTypeMask = 0x1;
|
||||
|
||||
// The mask on the CFI row data that is used to get the high 30 bits and
|
||||
// multiply it by 4 to get CFA offset. Since the last 2 bits are masked out, a
|
||||
// shift is not necessary.
|
||||
constexpr uint32_t kCFAMask = 0xfffffffc;
|
||||
|
||||
// The mask on the CFI row data that is used to get the low 2 bits and multiply
|
||||
// it by 4 to get the RA offset.
|
||||
constexpr uint32_t kRAMask = 0x3;
|
||||
constexpr uint32_t kRAShift = 2;
|
||||
|
||||
// The code in this file assumes we are running in 32-bit builds since all the
|
||||
// addresses in the unwind table are specified in 32 bits.
|
||||
static_assert(sizeof(uintptr_t) == 4,
|
||||
"The unwind table format is only valid for 32 bit builds.");
|
||||
|
||||
// The struct that corresponds to each row in the unwind table. The row can be
|
||||
// of any type, CFI or FUNCTION. The first 4 bytes in the row represents the
|
||||
// address and the next 4 bytes have data. The members of this struct is in
|
||||
// order of the input format. We cast the memory map of the unwind table as an
|
||||
// array of CFIUnwindInfo and use it to read data and search. So, the size of
|
||||
// this struct should be 8 bytes and the order of the members is fixed according
|
||||
// to the given format.
|
||||
struct CFIUnwindInfo {
|
||||
// The address is either the start address of the function or the instruction
|
||||
// address where the CFI information changes in a function. If the last bit of
|
||||
// the address is 1 then it specifies that the row is of type FUNCTION and if
|
||||
// the last bit is 0 then it specifies the row is of type CFI.
|
||||
uintptr_t addr;
|
||||
|
||||
// If the row type is function, |data| is |function_length|. If the row type
|
||||
// is CFI, |data| is |cfi_data|.
|
||||
union {
|
||||
// Represents the total length of the function that start with the |addr|.
|
||||
uintptr_t function_length;
|
||||
// Represents the CFA and RA offsets to get information about next stack
|
||||
// frame.
|
||||
uintptr_t cfi_data;
|
||||
} data;
|
||||
|
||||
bool is_function_type() const { return !!(addr & kFunctionTypeMask); }
|
||||
|
||||
// Returns the address of the current row, CFI or FUNCTION type.
|
||||
uintptr_t address() const {
|
||||
return is_function_type() ? (addr & ~kFunctionTypeMask) : addr;
|
||||
}
|
||||
|
||||
// Return the RA offset when the current row is CFI type.
|
||||
uintptr_t ra_offset() const {
|
||||
DCHECK(!is_function_type());
|
||||
return (data.cfi_data & kRAMask) << kRAShift;
|
||||
}
|
||||
|
||||
// Returns the CFA offset if the current row is CFI type.
|
||||
uintptr_t cfa_offset() const {
|
||||
DCHECK(!is_function_type());
|
||||
return data.cfi_data & kCFAMask;
|
||||
}
|
||||
|
||||
// Returns true if the instruction is within the function address range, given
|
||||
// that the current row is FUNCTION type and the |instruction_addr| is offset
|
||||
// address of instruction from the start of the binary.
|
||||
bool is_instruction_in_function(uintptr_t instruction_addr) const {
|
||||
DCHECK(is_function_type());
|
||||
return (instruction_addr >= address()) &&
|
||||
(instruction_addr <= address() + data.function_length);
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(
|
||||
sizeof(CFIUnwindInfo) == 8,
|
||||
"The CFIUnwindInfo struct must be exactly 8 bytes for searching.");
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
CFIBacktraceAndroid* CFIBacktraceAndroid::GetInstance() {
|
||||
static CFIBacktraceAndroid* instance = new CFIBacktraceAndroid();
|
||||
return instance;
|
||||
}
|
||||
|
||||
CFIBacktraceAndroid::CFIBacktraceAndroid() {
|
||||
Initialize();
|
||||
}
|
||||
|
||||
CFIBacktraceAndroid::~CFIBacktraceAndroid() {}
|
||||
|
||||
void CFIBacktraceAndroid::Initialize() {
|
||||
// The address |_etext| gives the end of the .text section in the binary. This
|
||||
// value is more accurate than parsing the memory map since the mapped
|
||||
// regions are usualy larger than the .text section.
|
||||
executable_end_addr_ = reinterpret_cast<uintptr_t>(&_etext);
|
||||
// The address of |__executable_start| gives the start address of the
|
||||
// executable. This value is used to find the offset address of the
|
||||
// instruction in binary from PC.
|
||||
executable_start_addr_ = reinterpret_cast<uintptr_t>(&__executable_start);
|
||||
|
||||
// This file name is defined by extract_unwind_tables.gni.
|
||||
static constexpr char kCfiFileName[] = "assets/unwind_cfi";
|
||||
MemoryMappedFile::Region cfi_region;
|
||||
int fd = base::android::OpenApkAsset(kCfiFileName, &cfi_region);
|
||||
if (fd < 0)
|
||||
return;
|
||||
cfi_mmap_ = std::make_unique<MemoryMappedFile>();
|
||||
// The CFI region starts at |cfi_region.offset|.
|
||||
if (!cfi_mmap_->Initialize(base::File(fd), cfi_region))
|
||||
return;
|
||||
// The CFI file should contain rows of 8 bytes each.
|
||||
DCHECK_EQ(0u, cfi_region.size % sizeof(CFIUnwindInfo));
|
||||
unwind_table_row_count_ = cfi_region.size / sizeof(CFIUnwindInfo);
|
||||
can_unwind_stack_frames_ = true;
|
||||
}
|
||||
|
||||
size_t CFIBacktraceAndroid::Unwind(const void** out_trace,
|
||||
size_t max_depth) const {
|
||||
// This function walks the stack using the call frame information to find the
|
||||
// return addresses of all the functions that belong to current binary in call
|
||||
// stack. For each function the CFI table defines the offset of the previous
|
||||
// call frame and offset where the return address is stored.
|
||||
if (!can_unwind_stack_frames())
|
||||
return 0;
|
||||
|
||||
// Get the current register state. This register state can be taken at any
|
||||
// point in the function and the unwind information would be for this point.
|
||||
// Define local variables before trying to get the current PC and SP to make
|
||||
// sure the register state obtained is consistent with each other.
|
||||
uintptr_t pc = 0, sp = 0;
|
||||
asm volatile("mov %0, pc" : "=r"(pc));
|
||||
asm volatile("mov %0, sp" : "=r"(sp));
|
||||
|
||||
// We can only unwind as long as the pc is within the chrome.so.
|
||||
size_t depth = 0;
|
||||
while (pc > executable_start_addr_ && pc <= executable_end_addr_ &&
|
||||
depth < max_depth) {
|
||||
out_trace[depth++] = reinterpret_cast<void*>(pc);
|
||||
// The offset of function from the start of the chrome.so binary:
|
||||
uintptr_t func_addr = pc - executable_start_addr_;
|
||||
CFIRow cfi{};
|
||||
if (!FindCFIRowForPC(func_addr, &cfi))
|
||||
break;
|
||||
|
||||
// The rules for unwinding using the CFI information are:
|
||||
// SP_prev = SP_cur + cfa_offset and
|
||||
// PC_prev = * (SP_prev - ra_offset).
|
||||
sp = sp + cfi.cfa_offset;
|
||||
memcpy(&pc, reinterpret_cast<uintptr_t*>(sp - cfi.ra_offset),
|
||||
sizeof(uintptr_t));
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
bool CFIBacktraceAndroid::FindCFIRowForPC(
|
||||
uintptr_t func_addr,
|
||||
CFIBacktraceAndroid::CFIRow* cfi) const {
|
||||
// Consider the CFI mapped region as an array of CFIUnwindInfo since each row
|
||||
// is 8 bytes long and it contains |cfi_region_size_| / 8 rows. We define
|
||||
// start and end iterator on this array and use std::lower_bound() to binary
|
||||
// search on this array. std::lower_bound() returns the row that corresponds
|
||||
// to the first row that has address greater than the current value, since
|
||||
// address is used in compartor.
|
||||
const CFIUnwindInfo* start =
|
||||
reinterpret_cast<const CFIUnwindInfo*>(cfi_mmap_->data());
|
||||
const CFIUnwindInfo* end = start + unwind_table_row_count_;
|
||||
const CFIUnwindInfo to_find = {func_addr, {0}};
|
||||
const CFIUnwindInfo* found = std::lower_bound(
|
||||
start, end, to_find,
|
||||
[](const auto& a, const auto& b) { return a.addr < b.addr; });
|
||||
*cfi = {0};
|
||||
|
||||
// The given address is less than the start address in the CFI table if
|
||||
// lower_bound() returns start.
|
||||
if (found == start)
|
||||
return false;
|
||||
// If the given address is equal to the found address, then use the found row.
|
||||
// Otherwise the required row is always one less than the value returned by
|
||||
// std::lower_bound().
|
||||
if (found == end || found->address() != func_addr)
|
||||
found--;
|
||||
|
||||
DCHECK_LE(found->address(), func_addr);
|
||||
DCHECK(!found->is_function_type())
|
||||
<< "Current PC cannot be start of a function";
|
||||
|
||||
// The CFIUnwindInfo::data field hold the CFI information since the row
|
||||
// found should not correspond to function start address. So, interpret the
|
||||
// data in the found row as CFI data which contains the CFA and RA offsets.
|
||||
*cfi = {found->cfa_offset(), found->ra_offset()};
|
||||
DCHECK(cfi->cfa_offset);
|
||||
|
||||
// Find the function data for the current row by iterating till we reach the a
|
||||
// row of type FUNCTION, to check if the unwind information is valid. Also
|
||||
// find the RA offset if we do not have it in the CFI row found.
|
||||
const CFIUnwindInfo* it = found;
|
||||
for (; !it->is_function_type() && it >= start; it--) {
|
||||
// If ra offset of the last specified row should be used, if unspecified.
|
||||
// TODO(ssid): This should be fixed in the format and we should always
|
||||
// output ra offset.
|
||||
if (!cfi->ra_offset)
|
||||
cfi->ra_offset = it->ra_offset();
|
||||
}
|
||||
// If the given function adddress does not belong to the function found, then
|
||||
// the unwind info is invalid.
|
||||
if (!it->is_instruction_in_function(func_addr))
|
||||
return false;
|
||||
|
||||
DCHECK(cfi->ra_offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace trace_event
|
||||
} // namespace base
|
@ -1,103 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#ifndef BASE_TRACE_EVENT_CFI_BACKTRACE_ANDROID_H_
|
||||
#define BASE_TRACE_EVENT_CFI_BACKTRACE_ANDROID_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/debug/debugging_buildflags.h"
|
||||
#include "base/files/memory_mapped_file.h"
|
||||
#include "base/gtest_prod_util.h"
|
||||
|
||||
namespace base {
|
||||
namespace trace_event {
|
||||
|
||||
// This class is used to unwind stack frames in the current thread. The unwind
|
||||
// information (dwarf debug info) is stripped from the chrome binary and we do
|
||||
// not build with exception tables (ARM EHABI) in release builds. So, we use a
|
||||
// custom unwind table which is generated and added to specific android builds,
|
||||
// when add_unwind_tables_in_apk build option is specified. This unwind table
|
||||
// contains information for unwinding stack frames when the functions calls are
|
||||
// from lib[mono]chrome.so. The file is added as an asset to the apk and the
|
||||
// table is used to unwind stack frames for profiling. This class implements
|
||||
// methods to read and parse the unwind table and unwind stack frames using this
|
||||
// data.
|
||||
class BASE_EXPORT CFIBacktraceAndroid {
|
||||
public:
|
||||
// Creates and initializes the unwinder on first call.
|
||||
static CFIBacktraceAndroid* GetInstance();
|
||||
|
||||
// Returns true if stack unwinding is possible using CFI unwind tables in apk.
|
||||
// There is no need to check this before each unwind call. Will always return
|
||||
// the same value based on CFI tables being present in the binary.
|
||||
bool can_unwind_stack_frames() const { return can_unwind_stack_frames_; }
|
||||
|
||||
// Returns the program counters by unwinding stack in the current thread in
|
||||
// order of latest call frame first. Unwinding works only if
|
||||
// can_unwind_stack_frames() returns true. This function does not allocate
|
||||
// memory from heap. For each stack frame, this method searches through the
|
||||
// unwind table mapped in memory to find the unwind information for function
|
||||
// and walks the stack to find all the return address. This only works until
|
||||
// the last function call from the chrome.so. We do not have unwind
|
||||
// information to unwind beyond any frame outside of chrome.so. Calls to
|
||||
// Unwind() are thread safe and lock free, once Initialize() returns success.
|
||||
size_t Unwind(const void** out_trace, size_t max_depth) const;
|
||||
|
||||
private:
|
||||
FRIEND_TEST_ALL_PREFIXES(CFIBacktraceAndroidTest, TestFindCFIRow);
|
||||
FRIEND_TEST_ALL_PREFIXES(CFIBacktraceAndroidTest, TestUnwinding);
|
||||
|
||||
// The CFI information that correspond to an instruction.
|
||||
struct CFIRow {
|
||||
bool operator==(const CFIBacktraceAndroid::CFIRow& o) const {
|
||||
return cfa_offset == o.cfa_offset && ra_offset == o.ra_offset;
|
||||
}
|
||||
|
||||
// The offset of the call frame address of previous function from the
|
||||
// current stack pointer. Rule for unwinding SP: SP_prev = SP_cur +
|
||||
// cfa_offset.
|
||||
size_t cfa_offset = 0;
|
||||
// The offset of location of return address from the previous call frame
|
||||
// address. Rule for unwinding PC: PC_prev = * (SP_prev - ra_offset).
|
||||
size_t ra_offset = 0;
|
||||
};
|
||||
|
||||
CFIBacktraceAndroid();
|
||||
~CFIBacktraceAndroid();
|
||||
|
||||
// Initializes unwind tables using the CFI asset file in the apk if present.
|
||||
// Also stores the limits of mapped region of the lib[mono]chrome.so binary,
|
||||
// since the unwind is only feasible for addresses within the .so file. Once
|
||||
// initialized, the memory map of the unwind table is never cleared since we
|
||||
// cannot guarantee that all the threads are done using the memory map when
|
||||
// heap profiling is turned off. But since we keep the memory map is clean,
|
||||
// the system can choose to evict the unused pages when needed. This would
|
||||
// still reduce the total amount of address space available in process.
|
||||
void Initialize();
|
||||
|
||||
// Finds the CFI row for the given |func_addr| in terms of offset from
|
||||
// the start of the current binary.
|
||||
bool FindCFIRowForPC(uintptr_t func_addr, CFIRow* out) const;
|
||||
|
||||
// Details about the memory mapped region which contains the libchrome.so
|
||||
// library file.
|
||||
uintptr_t executable_start_addr_ = 0;
|
||||
uintptr_t executable_end_addr_ = 0;
|
||||
|
||||
// The start address of the memory mapped unwind table asset file. Unique ptr
|
||||
// because it is replaced in tests.
|
||||
std::unique_ptr<MemoryMappedFile> cfi_mmap_;
|
||||
size_t unwind_table_row_count_ = 0;
|
||||
bool can_unwind_stack_frames_ = false;
|
||||
};
|
||||
|
||||
} // namespace trace_event
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_TRACE_EVENT_CFI_BACKTRACE_ANDROID_H_
|
@ -1,117 +0,0 @@
|
||||
// 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 "base/trace_event/cfi_backtrace_android.h"
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace base {
|
||||
namespace trace_event {
|
||||
|
||||
namespace {
|
||||
|
||||
void* GetPC() {
|
||||
return __builtin_return_address(0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(CFIBacktraceAndroidTest, TestUnwinding) {
|
||||
auto* unwinder = CFIBacktraceAndroid::GetInstance();
|
||||
EXPECT_TRUE(unwinder->can_unwind_stack_frames());
|
||||
EXPECT_GT(unwinder->executable_start_addr_, 0u);
|
||||
EXPECT_GT(unwinder->executable_end_addr_, unwinder->executable_start_addr_);
|
||||
EXPECT_GT(unwinder->cfi_mmap_->length(), 0u);
|
||||
|
||||
const size_t kMaxFrames = 100;
|
||||
const void* frames[kMaxFrames];
|
||||
size_t unwind_count = unwinder->Unwind(frames, kMaxFrames);
|
||||
// Expect at least 2 frames in the result.
|
||||
ASSERT_GT(unwind_count, 2u);
|
||||
EXPECT_LE(unwind_count, kMaxFrames);
|
||||
|
||||
const size_t kMaxCurrentFuncCodeSize = 50;
|
||||
const uintptr_t current_pc = reinterpret_cast<uintptr_t>(GetPC());
|
||||
const uintptr_t actual_frame = reinterpret_cast<uintptr_t>(frames[2]);
|
||||
EXPECT_NEAR(current_pc, actual_frame, kMaxCurrentFuncCodeSize);
|
||||
|
||||
for (size_t i = 0; i < unwind_count; ++i) {
|
||||
EXPECT_GT(reinterpret_cast<uintptr_t>(frames[i]),
|
||||
unwinder->executable_start_addr_);
|
||||
EXPECT_LT(reinterpret_cast<uintptr_t>(frames[i]),
|
||||
unwinder->executable_end_addr_);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CFIBacktraceAndroidTest, TestFindCFIRow) {
|
||||
auto* unwinder = CFIBacktraceAndroid::GetInstance();
|
||||
size_t input[] = {// Function 1 - 0x1000
|
||||
0x1001, 0x500,
|
||||
0x1002, 0x111,
|
||||
0x1008, 0x220,
|
||||
0x1040, 0x330,
|
||||
0x1050, 0x332,
|
||||
0x1080, 0x330,
|
||||
|
||||
// Function 2 - 0x2000
|
||||
0x2001, 0x22,
|
||||
0x2004, 0x13,
|
||||
0x2008, 0x23,
|
||||
|
||||
// Function 3 - 0x2024
|
||||
0x2025, 0x100,
|
||||
0x2030, 0x33,
|
||||
0x2100, 0x40,
|
||||
|
||||
// Function 4 - 0x2200
|
||||
0x2201, 0x10,
|
||||
0x2204, 0x2e};
|
||||
FilePath temp_path;
|
||||
CreateTemporaryFile(&temp_path);
|
||||
EXPECT_EQ(
|
||||
static_cast<int>(sizeof(input)),
|
||||
WriteFile(temp_path, reinterpret_cast<char*>(input), sizeof(input)));
|
||||
|
||||
unwinder->cfi_mmap_.reset(new MemoryMappedFile());
|
||||
ASSERT_TRUE(unwinder->cfi_mmap_->Initialize(temp_path));
|
||||
unwinder->unwind_table_row_count_ = sizeof(input) / (2 * sizeof(size_t));
|
||||
|
||||
CFIBacktraceAndroid::CFIRow cfi_row = {0};
|
||||
EXPECT_FALSE(unwinder->FindCFIRowForPC(0x00, &cfi_row));
|
||||
EXPECT_FALSE(unwinder->FindCFIRowForPC(0x100, &cfi_row));
|
||||
EXPECT_FALSE(unwinder->FindCFIRowForPC(0x1501, &cfi_row));
|
||||
EXPECT_FALSE(unwinder->FindCFIRowForPC(0x3000, &cfi_row));
|
||||
EXPECT_FALSE(unwinder->FindCFIRowForPC(0x2023, &cfi_row));
|
||||
EXPECT_FALSE(unwinder->FindCFIRowForPC(0x2215, &cfi_row));
|
||||
|
||||
const CFIBacktraceAndroid::CFIRow kRow1 = {0x110, 0x4};
|
||||
const CFIBacktraceAndroid::CFIRow kRow2 = {0x220, 0x4};
|
||||
const CFIBacktraceAndroid::CFIRow kRow3 = {0x330, 0x8};
|
||||
const CFIBacktraceAndroid::CFIRow kRow4 = {0x30, 0xc};
|
||||
const CFIBacktraceAndroid::CFIRow kRow5 = {0x2c, 0x8};
|
||||
EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1002, &cfi_row));
|
||||
EXPECT_EQ(kRow1, cfi_row);
|
||||
EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1003, &cfi_row));
|
||||
EXPECT_EQ(kRow1, cfi_row);
|
||||
EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1008, &cfi_row));
|
||||
EXPECT_EQ(kRow2, cfi_row);
|
||||
EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1009, &cfi_row));
|
||||
EXPECT_EQ(kRow2, cfi_row);
|
||||
EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1039, &cfi_row));
|
||||
EXPECT_EQ(kRow2, cfi_row);
|
||||
EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1080, &cfi_row));
|
||||
EXPECT_EQ(kRow3, cfi_row);
|
||||
EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1100, &cfi_row));
|
||||
EXPECT_EQ(kRow3, cfi_row);
|
||||
EXPECT_TRUE(unwinder->FindCFIRowForPC(0x2050, &cfi_row));
|
||||
EXPECT_EQ(kRow4, cfi_row);
|
||||
EXPECT_TRUE(unwinder->FindCFIRowForPC(0x2208, &cfi_row));
|
||||
EXPECT_EQ(kRow5, cfi_row);
|
||||
EXPECT_TRUE(unwinder->FindCFIRowForPC(0x2210, &cfi_row));
|
||||
EXPECT_EQ(kRow5, cfi_row);
|
||||
}
|
||||
|
||||
} // namespace trace_event
|
||||
} // namespace base
|
@ -2096,6 +2096,9 @@ config("minimal_symbols") {
|
||||
# TODO(thakis): Consider making clang emit DW_AT_linkage_name in -g1 mode;
|
||||
# failing that consider doing this on non-Android too.
|
||||
cflags += [ "-fdebug-info-for-profiling" ]
|
||||
if (strip_debug_info) {
|
||||
ldflags += [ "-Wl,--strip-debug" ]
|
||||
}
|
||||
}
|
||||
|
||||
# Note: -gsplit-dwarf implicitly turns on -g2 with clang, so don't pass it.
|
||||
@ -2122,18 +2125,6 @@ config("default_symbols") {
|
||||
} else {
|
||||
assert(false)
|
||||
}
|
||||
|
||||
# This config is removed by base unittests apk.
|
||||
if (is_android && is_clang && strip_debug_info) {
|
||||
configs += [ ":strip_debug" ]
|
||||
}
|
||||
}
|
||||
|
||||
config("strip_debug") {
|
||||
if (!defined(ldflags)) {
|
||||
ldflags = []
|
||||
}
|
||||
ldflags += [ "-Wl,--strip-debug" ]
|
||||
}
|
||||
|
||||
if (is_ios || is_mac) {
|
||||
|
@ -141,12 +141,6 @@ if (current_cpu == "arm" && arm_use_thumb) {
|
||||
|
||||
assert(!can_unwind_with_frame_pointers || enable_frame_pointers)
|
||||
|
||||
# Unwinding with CFI table is only possible on static library builds and
|
||||
# requried only when frame pointers are not enabled.
|
||||
can_unwind_with_cfi_table =
|
||||
is_android && !is_component_build && !can_unwind_with_frame_pointers &&
|
||||
current_cpu == "arm"
|
||||
|
||||
declare_args() {
|
||||
# Whether or not the official builds should be built with full WPO. Enabled by
|
||||
# default for the PGO and the x64 builds.
|
||||
|
Reference in New Issue
Block a user