summaryrefslogtreecommitdiff
path: root/chromium/services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android_unittest.cc
blob: 7183ba62315e867bf80bdbc043399f8c7819ec00 (plain)
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(&register_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(&register_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(&register_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