Commit dea72424 authored by Lee Howes's avatar Lee Howes Committed by Facebook Github Bot

Add heap-allocating version of getStackTrace

Summary: In stack-constrained environments, primarily fibers, the context state allocated by getStackTrace can be problematic. getStackTraceHeap gives the opposite guarantee from getStackTraceSafe - there is no significant stack allocation and we explicitly heap allocate instead.

Reviewed By: terrelln

Differential Revision: D19575634

fbshipit-source-id: 2ec86eccec55d1044bd2532324c648c791df5a07
parent 72c71129
...@@ -24,6 +24,9 @@ ...@@ -24,6 +24,9 @@
#include <execinfo.h> #include <execinfo.h>
#endif #endif
#include <folly/Portability.h>
#include <memory>
namespace folly { namespace folly {
namespace symbolizer { namespace symbolizer {
...@@ -57,17 +60,19 @@ inline bool getFrameInfo(unw_cursor_t* cursor, uintptr_t& ip) { ...@@ -57,17 +60,19 @@ inline bool getFrameInfo(unw_cursor_t* cursor, uintptr_t& ip) {
ip = uip - (r == 0); ip = uip - (r == 0);
return true; return true;
} }
} // namespace
ssize_t getStackTraceSafe(uintptr_t* addresses, size_t maxAddresses) { FOLLY_ALWAYS_INLINE
ssize_t getStackTraceInPlace(
unw_context_t& context,
unw_cursor_t& cursor,
uintptr_t* addresses,
size_t maxAddresses) {
if (maxAddresses == 0) { if (maxAddresses == 0) {
return 0; return 0;
} }
unw_context_t context;
if (unw_getcontext(&context) < 0) { if (unw_getcontext(&context) < 0) {
return -1; return -1;
} }
unw_cursor_t cursor;
if (unw_init_local(&cursor, &context) < 0) { if (unw_init_local(&cursor, &context) < 0) {
return -1; return -1;
} }
...@@ -90,5 +95,25 @@ ssize_t getStackTraceSafe(uintptr_t* addresses, size_t maxAddresses) { ...@@ -90,5 +95,25 @@ ssize_t getStackTraceSafe(uintptr_t* addresses, size_t maxAddresses) {
} }
return count; return count;
} }
} // namespace
ssize_t getStackTraceSafe(uintptr_t* addresses, size_t maxAddresses) {
unw_context_t context;
unw_cursor_t cursor;
return getStackTraceInPlace(context, cursor, addresses, maxAddresses);
}
ssize_t getStackTraceHeap(uintptr_t* addresses, size_t maxAddresses) {
struct Ctx {
unw_context_t context;
unw_cursor_t cursor;
};
auto ctx_ptr = std::make_unique<Ctx>();
if (!ctx_ptr) {
return -1;
}
return getStackTraceInPlace(
ctx_ptr->context, ctx_ptr->cursor, addresses, maxAddresses);
}
} // namespace symbolizer } // namespace symbolizer
} // namespace folly } // namespace folly
...@@ -44,5 +44,17 @@ ssize_t getStackTrace(uintptr_t* addresses, size_t maxAddresses); ...@@ -44,5 +44,17 @@ ssize_t getStackTrace(uintptr_t* addresses, size_t maxAddresses);
* Async-signal-safe, but likely slower. * Async-signal-safe, but likely slower.
*/ */
ssize_t getStackTraceSafe(uintptr_t* addresses, size_t maxAddresses); ssize_t getStackTraceSafe(uintptr_t* addresses, size_t maxAddresses);
/**
* Get the current stack trace into addresses, which has room for at least
* maxAddresses frames.
*
* Returns the number of frames written in the array.
* Returns -1 on failure.
*
* Heap allocates its context. Likely slower than getStackTrace but
* avoids large stack allocations.
*/
ssize_t getStackTraceHeap(uintptr_t* addresses, size_t maxAddresses);
} // namespace symbolizer } // namespace symbolizer
} // namespace folly } // namespace folly
...@@ -77,6 +77,14 @@ inline bool getStackTraceSafe(FrameArray<N>& fa) { ...@@ -77,6 +77,14 @@ inline bool getStackTraceSafe(FrameArray<N>& fa) {
return detail::fixFrameArray(fa, getStackTraceSafe(fa.addresses, N)); return detail::fixFrameArray(fa, getStackTraceSafe(fa.addresses, N));
} }
template <size_t N>
FOLLY_ALWAYS_INLINE bool getStackTraceHeap(FrameArray<N>& fa);
template <size_t N>
inline bool getStackTraceHeap(FrameArray<N>& fa) {
return detail::fixFrameArray(fa, getStackTraceHeap(fa.addresses, N));
}
class Symbolizer { class Symbolizer {
public: public:
static constexpr auto kDefaultLocationInfoMode = LocationInfoMode::FAST; static constexpr auto kDefaultLocationInfoMode = LocationInfoMode::FAST;
......
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/experimental/symbolizer/StackTrace.h>
#include <folly/experimental/symbolizer/Symbolizer.h>
#include <folly/fibers/FiberManager.h>
#include <folly/fibers/SimpleLoopController.h>
#include <folly/init/Init.h>
#include <folly/portability/GTest.h>
using namespace folly::fibers;
using namespace folly::symbolizer;
constexpr size_t kMaxAddresses = 1000;
void fBaseline(FrameArray<kMaxAddresses>& /* unused */) {
auto ffa = std::make_unique<FrameArray<kMaxAddresses>>();
(void)ffa;
}
void fStack(FrameArray<kMaxAddresses>& fa) {
auto ffa = std::make_unique<FrameArray<kMaxAddresses>>();
(void)ffa;
getStackTrace(fa);
}
void fHeap(FrameArray<kMaxAddresses>& fa) {
auto ffa = std::make_unique<FrameArray<kMaxAddresses>>();
(void)ffa;
getStackTraceHeap(fa);
}
// Check that the requires stacks for capturing a stack trace do not
// exceed reasonable levels surprisingly.
TEST(StackTraceSizeLimitTest, FiberLimit) {
FiberManager::Options opts;
opts.recordStackEvery = 1;
auto t = [&](folly::Function<void(FrameArray<kMaxAddresses>&)> f,
size_t highWaterMark) {
FiberManager manager(std::make_unique<SimpleLoopController>(), opts);
auto& loopController =
dynamic_cast<SimpleLoopController&>(manager.loopController());
bool checkRan = false;
FrameArray<kMaxAddresses> fa;
manager.addTask([&] {
checkRan = true;
f(fa);
});
EXPECT_EQ(manager.stackHighWatermark(), 0);
loopController.loop([&]() { loopController.stop(); });
EXPECT_LE(manager.stackHighWatermark(), highWaterMark);
EXPECT_TRUE(checkRan);
};
// Initial run
t(fBaseline, 10000);
#ifdef NDEBUG
// Baseline
t(fBaseline, 1600);
// Standard version
t(fStack, 6800);
// Heap version
t(fHeap, 2200);
#else
// Values for opt builds
#ifndef FOLLY_SANITIZE_ADDRESS
// Baseline
t(fBaseline, 3700);
// Standard version
t(fStack, 8000);
// Heap version
t(fHeap, 3700);
#else
// Baseline
t(fBaseline, 0);
// Standard version
t(fStack, 0);
// Heap version
t(fHeap, 0);
#endif
#endif
}
...@@ -38,7 +38,11 @@ void verifyStackTraces() { ...@@ -38,7 +38,11 @@ void verifyStackTraces() {
FrameArray<kMaxAddresses> faSafe; FrameArray<kMaxAddresses> faSafe;
CHECK(getStackTraceSafe(faSafe)); CHECK(getStackTraceSafe(faSafe));
FrameArray<kMaxAddresses> faHeap;
CHECK(getStackTraceHeap(faHeap));
CHECK_EQ(fa.frameCount, faSafe.frameCount); CHECK_EQ(fa.frameCount, faSafe.frameCount);
CHECK_EQ(fa.frameCount, faHeap.frameCount);
if (VLOG_IS_ON(1)) { if (VLOG_IS_ON(1)) {
Symbolizer symbolizer; Symbolizer symbolizer;
...@@ -51,14 +55,19 @@ void verifyStackTraces() { ...@@ -51,14 +55,19 @@ void verifyStackTraces() {
symbolizer.symbolize(faSafe); symbolizer.symbolize(faSafe);
VLOG(1) << "getStackTraceSafe\n"; VLOG(1) << "getStackTraceSafe\n";
printer.println(faSafe); printer.println(faSafe);
symbolizer.symbolize(faHeap);
VLOG(1) << "getStackTraceHeap\n";
printer.println(faHeap);
} }
// Other than the top 2 frames (this one and getStackTrace / // Other than the top 2 frames (this one and getStackTrace /
// getStackTraceSafe), the stack traces should be identical // getStackTraceSafe), the stack traces should be identical
for (size_t i = 2; i < fa.frameCount; ++i) { for (size_t i = 2; i < fa.frameCount; ++i) {
LOG(INFO) << "i=" << i << " " << std::hex << "0x" << fa.addresses[i] LOG(INFO) << "i=" << i << " " << std::hex << "0x" << fa.addresses[i]
<< " 0x" << faSafe.addresses[i]; << " 0x" << faSafe.addresses[i] << " 0x" << faHeap.addresses[i];
EXPECT_EQ(fa.addresses[i], faSafe.addresses[i]); EXPECT_EQ(fa.addresses[i], faSafe.addresses[i]);
EXPECT_EQ(fa.addresses[i], faHeap.addresses[i]);
} }
} }
......
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