cpptrace/test/unit/raw_trace.cpp
Vittorio Romeo 0ddbbf43cb
Improve compilation times on Windows (#172)
Thank you for the very useful library!

Few improvements:
- Better header hygiene
- Isolate `windows.h` to `.cpp` whenever possible
- Use `WIN32_LEAN_AND_MEAN`
- Remove unused headers

Tested on Windows with 
```
cmake .. -DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=1 
  -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_FLAGS="-ftime-trace -Wall -Wextra -Wpedantic 
  -Wno-ignored-attributes" -DCMAKE_COLOR_DIAGNOSTICS=1 -DCPPTRACE_BUILD_TESTING=1 
  -DCPPTRACE_BUILD_BENCHMARKING=0
```

There's a lot more that can be improved if you are interested.

---------

Co-authored-by: Jeremy Rifkin <51220084+jeremy-rifkin@users.noreply.github.com>
2024-10-02 10:55:13 -05:00

170 lines
5.0 KiB
C++

#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <gtest/gtest-matchers.h>
#include <gmock/gmock.h>
#include <gmock/gmock-matchers.h>
#include <cpptrace/cpptrace.hpp>
using namespace std::literals;
// Raw trace tests
// This is fickle, however, it's the only way to do it really. I've gotten it reasonably reliable test in practice.
// Sanitizers do interfere.
#ifndef CPPTRACE_SANITIZER_BUILD
// NOTE: MSVC likes creating trampoline-like entries for non-static functions
CPPTRACE_FORCE_NO_INLINE static void raw_trace_basic() {
auto raw_trace = cpptrace::generate_raw_trace();
// look for within 90 bytes of the start of the function
ASSERT_GE(raw_trace.frames.size(), 1);
EXPECT_GE(raw_trace.frames[0], reinterpret_cast<uintptr_t>(raw_trace_basic));
EXPECT_LE(raw_trace.frames[0], reinterpret_cast<uintptr_t>(raw_trace_basic) + 90);
}
#ifndef _MSC_VER
CPPTRACE_FORCE_NO_INLINE void raw_trace_basic_precise() {
a:
auto raw_trace = cpptrace::generate_raw_trace();
b:
// This is stupid, but without it gcc was optimizing both &&a and &&b to point to the start of the function's body
volatile auto x = 0;
if(x) {
goto* &&a;
}
if(x) {
goto* &&b;
}
ASSERT_GE(raw_trace.frames.size(), 1);
EXPECT_GE(raw_trace.frames[0], reinterpret_cast<uintptr_t>(&&a));
EXPECT_LE(raw_trace.frames[0], reinterpret_cast<uintptr_t>(&&b));
}
#endif
TEST(RawTrace, Basic) {
raw_trace_basic();
#ifndef _MSC_VER
raw_trace_basic_precise();
#endif
[[maybe_unused]] volatile int x = 0; // prevent raw_trace_basic_precise() above being a jmp
}
CPPTRACE_FORCE_NO_INLINE static void raw_trace_multi_2(
cpptrace::frame_ptr parent_low_bound,
cpptrace::frame_ptr parent_high_bound
) {
auto raw_trace = cpptrace::generate_raw_trace();
ASSERT_GE(raw_trace.frames.size(), 2);
EXPECT_GE(raw_trace.frames[0], reinterpret_cast<uintptr_t>(raw_trace_multi_2));
EXPECT_LE(raw_trace.frames[0], reinterpret_cast<uintptr_t>(raw_trace_multi_2) + 90);
EXPECT_GE(raw_trace.frames[1], parent_low_bound);
EXPECT_LE(raw_trace.frames[1], parent_high_bound);
}
CPPTRACE_FORCE_NO_INLINE static void raw_trace_multi_1() {
auto raw_trace = cpptrace::generate_raw_trace();
raw_trace_multi_2(reinterpret_cast<uintptr_t>(raw_trace_multi_1), reinterpret_cast<uintptr_t>(raw_trace_multi_1) + 300);
ASSERT_GE(raw_trace.frames.size(), 1);
EXPECT_GE(raw_trace.frames[0], reinterpret_cast<uintptr_t>(raw_trace_multi_1));
EXPECT_LE(raw_trace.frames[0], reinterpret_cast<uintptr_t>(raw_trace_multi_1) + 90);
}
std::vector<std::pair<cpptrace::frame_ptr, cpptrace::frame_ptr>> parents;
CPPTRACE_FORCE_NO_INLINE void record_parent(uintptr_t low_bound, uintptr_t high_bound) {
parents.insert(parents.begin(), {low_bound, high_bound});
}
#ifndef _MSC_VER
CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_3() {
a:
auto raw_trace = cpptrace::generate_raw_trace();
b:
volatile auto x = 0;
if(x) {
goto* &&a;
}
if(x) {
goto* &&b;
}
ASSERT_GE(raw_trace.frames.size(), parents.size() + 1);
EXPECT_GE(raw_trace.frames[0], reinterpret_cast<uintptr_t>(&&a)); // this frame
EXPECT_LE(raw_trace.frames[0], reinterpret_cast<uintptr_t>(&&b));
for(size_t i = 0; i < parents.size(); i++) { // parent frames
EXPECT_GE(raw_trace.frames[i + 1], parents[i].first);
EXPECT_LE(raw_trace.frames[i + 1], parents[i].second);
}
}
CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_2() {
a:
auto raw_trace = cpptrace::generate_raw_trace();
b:
volatile auto x = 0;
if(x) {
goto* &&a;
}
if(x) {
goto* &&b;
}
ASSERT_GE(raw_trace.frames.size(), parents.size() + 1);
EXPECT_GE(raw_trace.frames[0], reinterpret_cast<uintptr_t>(&&a)); // this frame
EXPECT_LE(raw_trace.frames[0], reinterpret_cast<uintptr_t>(&&b));
for(size_t i = 0; i < parents.size(); i++) { // parent frames
EXPECT_GE(raw_trace.frames[i + 1], parents[i].first);
EXPECT_LE(raw_trace.frames[i + 1], parents[i].second);
}
record_parent(reinterpret_cast<uintptr_t>(&&c), reinterpret_cast<uintptr_t>(&&d));
c:
raw_trace_multi_precise_3();
d:
if(x) {
goto* &&c;
}
if(x) {
goto* &&d;
}
}
CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_1() {
a:
auto raw_trace = cpptrace::generate_raw_trace();
b:
volatile auto x = 0;
if(x) {
goto* &&a;
}
if(x) {
goto* &&b;
}
ASSERT_GE(raw_trace.frames.size(), 1);
EXPECT_GE(raw_trace.frames[0], reinterpret_cast<uintptr_t>(&&a));
EXPECT_LE(raw_trace.frames[0], reinterpret_cast<uintptr_t>(&&b));
record_parent(reinterpret_cast<uintptr_t>(&&c), reinterpret_cast<uintptr_t>(&&d));
c:
raw_trace_multi_precise_2();
d:
if(x) {
goto* &&c;
}
if(x) {
goto* &&d;
}
}
#endif
TEST(RawTrace, MultipleCalls) {
parents.clear();
raw_trace_multi_1();
#ifndef _MSC_VER
raw_trace_multi_precise_1();
#endif
}
#endif