proxygen
StackTraceTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2013-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <cstring>
18 
22 
23 #include <glog/logging.h>
24 
26 
27 using namespace folly;
28 using namespace folly::symbolizer;
29 
30 FOLLY_NOINLINE void foo1();
31 FOLLY_NOINLINE void foo2();
32 
34  constexpr size_t kMaxAddresses = 100;
36  CHECK(getStackTrace(fa));
37 
39  CHECK(getStackTraceSafe(faSafe));
40 
41  CHECK_EQ(fa.frameCount, faSafe.frameCount);
42 
43  if (VLOG_IS_ON(1)) {
44  Symbolizer symbolizer;
46 
47  symbolizer.symbolize(fa);
48  VLOG(1) << "getStackTrace\n";
49  printer.println(fa);
50 
51  symbolizer.symbolize(faSafe);
52  VLOG(1) << "getStackTraceSafe\n";
53  printer.println(faSafe);
54  }
55 
56  // Other than the top 2 frames (this one and getStackTrace /
57  // getStackTraceSafe), the stack traces should be identical
58  for (size_t i = 2; i < fa.frameCount; ++i) {
59  LOG(INFO) << "i=" << i << " " << std::hex << "0x" << fa.addresses[i]
60  << " 0x" << faSafe.addresses[i];
61  EXPECT_EQ(fa.addresses[i], faSafe.addresses[i]);
62  }
63 }
64 
65 void foo1() {
66  foo2();
67 }
68 
69 void foo2() {
71 }
72 
73 volatile bool handled = false;
74 void handler(int /* num */, siginfo_t* /* info */, void* /* ctx */) {
75  // Yes, getStackTrace and VLOG aren't async-signal-safe, but signals
76  // raised with raise() aren't "async" signals.
77  foo1();
78  handled = true;
79 }
80 
81 TEST(StackTraceTest, Simple) {
82  foo1();
83 }
84 
85 TEST(StackTraceTest, Signal) {
86  struct sigaction sa;
87  memset(&sa, 0, sizeof(sa));
88  sa.sa_sigaction = handler;
89  sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
90  CHECK_ERR(sigaction(SIGUSR1, &sa, nullptr));
91  raise(SIGUSR1);
93 }
94 
95 ssize_t read_all(int fd, uint8_t* buffer, size_t size) {
96  uint8_t* pos = buffer;
97  ssize_t bytes_read;
98  do {
99  bytes_read = read(fd, pos, size);
100  if (bytes_read < 0) {
101  if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
102  continue;
103  }
104  return bytes_read;
105  }
106 
107  pos += bytes_read;
108  size -= bytes_read;
109  } while (bytes_read > 0 && size > 0);
110 
111  return pos - buffer;
112 }
113 
114 // Returns the position in the file after done reading.
115 off_t get_stack_trace(int fd, size_t file_pos, uint8_t* buffer, size_t count) {
116  off_t rv = lseek(fd, file_pos, SEEK_SET);
117  CHECK_EQ(rv, (off_t)file_pos);
118 
119  // Subtract 1 from size of buffer to hold nullptr.
120  ssize_t bytes_read = read_all(fd, buffer, count - 1);
121  CHECK_GT(bytes_read, 0);
122  buffer[bytes_read] = '\0';
123  return lseek(fd, 0, SEEK_CUR);
124 }
125 
126 template <class StackTracePrinter>
127 void testStackTracePrinter(StackTracePrinter& printer, int fd) {
128  ASSERT_GT(fd, 0);
129 
130  printer.printStackTrace(true);
131  printer.flush();
132 
133  std::array<uint8_t, 4000> first;
134  off_t pos = get_stack_trace(fd, 0, first.data(), first.size());
135  ASSERT_GT(pos, 0);
136 
137  printer.printStackTrace(true);
138  printer.flush();
139 
140  std::array<uint8_t, 4000> second;
141  get_stack_trace(fd, pos, second.data(), second.size());
142 
143  // The first two lines refer to this stack frame, which is different in the
144  // two cases, so strip those off. The rest should be equal.
145  ASSERT_STREQ(
146  strchr(strchr((const char*)first.data(), '\n') + 1, '\n') + 1,
147  strchr(strchr((const char*)second.data(), '\n') + 1, '\n') + 1);
148 }
149 
150 TEST(StackTraceTest, SafeStackTracePrinter) {
151  test::TemporaryFile file;
152 
153  SafeStackTracePrinter printer{10, file.fd()};
154 
155  testStackTracePrinter<SafeStackTracePrinter>(printer, file.fd());
156 }
157 
158 TEST(StackTraceTest, FastStackTracePrinter) {
159  test::TemporaryFile file;
160 
161  FastStackTracePrinter printer{
162  std::make_unique<FDSymbolizePrinter>(file.fd())};
163 
164  testStackTracePrinter<FastStackTracePrinter>(printer, file.fd());
165 }
#define ASSERT_GT(val1, val2)
Definition: gtest.h:1976
std::vector< uint8_t > buffer(kBufferSize+16)
#define ASSERT_STREQ(s1, s2)
Definition: gtest.h:2004
ssize_t getStackTraceSafe(uintptr_t *addresses, size_t maxAddresses)
Definition: StackTrace.cpp:53
volatile bool handled
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
FOLLY_NOINLINE void foo2()
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
void println(uintptr_t address, const SymbolizedFrame &frame)
Definition: Symbolizer.cpp:284
void handler(int, siginfo_t *, void *)
#define FOLLY_NOINLINE
Definition: CPortability.h:142
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
FOLLY_NOINLINE void foo1()
ssize_t getStackTrace(uintptr_t *addresses, size_t maxAddresses)
Definition: StackTrace.cpp:25
size_t read(T &out, folly::io::Cursor &cursor)
Definition: Types-inl.h:258
void verifyStackTraces()
int * count
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
off_t get_stack_trace(int fd, size_t file_pos, uint8_t *buffer, size_t count)
ssize_t read_all(int fd, uint8_t *buffer, size_t size)
void symbolize(const uintptr_t *addresses, SymbolizedFrame *frames, size_t frameCount)
Definition: Symbolizer.cpp:94
TEST(SequencedExecutor, CPUThreadPoolExecutor)
void testStackTracePrinter(StackTracePrinter &printer, int fd)
constexpr detail::First first
Definition: Base-inl.h:2553