1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
// 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;
unwinder.Initialize(&module_cache);
std::vector<base::Frame> stack;
EXPECT_EQ(base::UnwindResult::kCompleted,
unwinder.TryUnwind(®ister_context, stack_top, &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;
unwinder.Initialize(&module_cache);
std::vector<base::Frame> stack;
EXPECT_EQ(base::UnwindResult::kCompleted,
unwinder.TryUnwind(®ister_context, stack_top, &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;
unwinder.Initialize(&module_cache);
std::vector<base::Frame> stack;
EXPECT_EQ(base::UnwindResult::kCompleted,
unwinder.TryUnwind(®ister_context, stack_top, &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
|