blob: de7edbcb501053e14f7fb9e6bc2918559c3d48b5 [file] [log] [blame] [edit]
/*
* Copyright (C) 2022 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "Test.h"
#include <wtf/StackTrace.h>
#include <wtf/StringPrintStream.h>
#include <wtf/text/MakeString.h>
#include <wtf/unicode/CharacterNames.h>
#if PLATFORM(WIN) && defined(NDEBUG)
#define MAYBE(name) DISABLED_##name
#else
#define MAYBE(name) name
#endif
namespace TestWebKitAPI {
TEST(StackTraceTest, MAYBE(StackTraceWorks))
{
static constexpr int framesToShow = 31;
void* samples[framesToShow];
int frames = framesToShow;
WTFGetBacktrace(samples, &frames);
StackTracePrinter stackTrace { { samples, static_cast<size_t>(frames) } };
StringPrintStream out;
out.print(stackTrace);
auto results = out.toString().split(newlineCharacter);
ASSERT_GT(results.size(), 2u);
EXPECT_TRUE(results[0].contains("WTFGetBacktrace"_s));
EXPECT_TRUE(results[1].contains("StackTraceWorks"_s));
}
namespace {
struct Frame {
int number;
void* address;
String name;
};
}
static void expectEqualFrames(std::span<void* const> expectedStack, StackTrace& testedStack, bool skipFirstFrameAddress)
{
Vector<Frame> expected;
StackTraceSymbolResolver { expectedStack }.forEach([&expected](int number, void* address, const char* name) {
expected.append({ number, address, String::fromLatin1(name) });
});
Vector<Frame> tested;
StackTraceSymbolResolver { testedStack }.forEach([&tested](int number, void* address, const char* name) {
tested.append({ number, address, String::fromLatin1(name) });
});
ASSERT_EQ(expected.size(), tested.size());
for (size_t j = 0; j < expected.size(); ++j) {
EXPECT_EQ(expected[j].number, tested[j].number) << " j:" << j;
EXPECT_EQ(expected[j].name, tested[j].name) << " j:" << j << " " << expected[j].name << " != " << tested[j].name;
// Address of tested capture call in vs the verification capture call is different.
if (skipFirstFrameAddress && !j)
continue;
EXPECT_EQ(expected[j].address, tested[j].address) << "j:" << j;
}
}
// Test that captureStackTrace() returns [a, b, c, ...].
// Test that captureStackTrace(limit, skip) is able to respect the requested limit and skip.
TEST(StackTraceTest, MAYBE(CaptureStackTraceLimitAndSkip))
{
// Verify captureStackTrace() results by manually inspecting WTFGetBacktrace().
// WTFGetBacktrace() returns [WTFGetBacktrace, a, b, c, ...].
static constexpr int maxFramesToCapture = 100;
void* stack[maxFramesToCapture];
int frames = maxFramesToCapture;
WTFGetBacktrace(stack, &frames);
// Skip WTFGetBacktrace.
--frames;
void** expectedStack = stack + 1;
// Test odd case where StackTrace::captureStackTrace(0) returns 1 result.
{
std::span<void* const> expected { expectedStack, 1 };
{
SCOPED_TRACE("Zero returns one result case");
auto tested = StackTrace::captureStackTrace(0);
expectEqualFrames(expected, *tested, true);
}
{
SCOPED_TRACE("Zero returns one result case example 1");
// The above is odd because using 0 is the same as using 1.
auto tested = StackTrace::captureStackTrace(1);
expectEqualFrames(expected, *tested, true);
}
}
// Test limiting the frame capture depth to `i = 1..` works.
// Test also 7 more than what's really available.
size_t testedMaxFrames = static_cast<size_t>(frames + 7);
for (size_t maxFrames = 1; maxFrames < testedMaxFrames; ++maxFrames) {
for (size_t skipFrames = 0; skipFrames < testedMaxFrames; ++skipFrames) {
SCOPED_TRACE(makeString("maxFrames: "_s, maxFrames, " skipFrames: "_s, skipFrames));
size_t expectedFrameCount = std::min(maxFrames + skipFrames, static_cast<size_t>(frames));
size_t expectedSkipFrames = std::min(skipFrames, expectedFrameCount);
std::span<void* const> expected { expectedStack + expectedSkipFrames, expectedFrameCount - expectedSkipFrames };
auto tested = StackTrace::captureStackTrace(maxFrames, skipFrames);
bool shouldSkipFirstFrameAddressComparison = !skipFrames;
expectEqualFrames(expected, *tested, shouldSkipFirstFrameAddressComparison);
}
}
}
}
#undef MAYBE