-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathframe_ut.cpp
More file actions
189 lines (161 loc) · 7.42 KB
/
Copy pathframe_ut.cpp
File metadata and controls
189 lines (161 loc) · 7.42 KB
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/*
* Copyright 2026, Datadog, Inc
*/
#include <gtest/gtest.h>
#include <climits>
#include "../../main/cpp/frame.h"
#include "../../main/cpp/gtest_crash_handler.h"
// Test-only friend accessor for VM internals. It exists solely so these unit
// tests can exercise the VM-specific raw-pointer path in FrameType and must
// never be reused from production code.
class VMTestAccessor {
public:
static bool getHotspot() { return VM::_hotspot; }
static void setHotspot(bool v) { VM::_hotspot = v; }
};
class VMHotspotGuard {
private:
bool _saved;
public:
explicit VMHotspotGuard(bool hotspot) : _saved(VMTestAccessor::getHotspot()) {
VMTestAccessor::setHotspot(hotspot);
}
~VMHotspotGuard() {
VMTestAccessor::setHotspot(_saved);
}
};
static constexpr char FRAME_TEST_NAME[] = "FrameTest";
class GlobalSetup {
public:
GlobalSetup() {
installGtestCrashHandler<FRAME_TEST_NAME>();
}
~GlobalSetup() {
restoreDefaultSignalHandlers();
}
};
static GlobalSetup global_setup;
// ---- encode ----------------------------------------------------------------
TEST(FrameTypeEncodeTest, EncodedMarkerBitIsSet) {
// Encoded values must have bit 20 set so decode can distinguish them from raw BCIs
int encoded = FrameType::encode(FRAME_JIT_COMPILED, 0);
EXPECT_NE(encoded & (1 << 20), 0) << "encode() must set the encoded-marker bit (bit 20)";
}
TEST(FrameTypeEncodeTest, EncodeStoresTypeInUpperBits) {
// Type occupies bits [23:21], extracted by >> 21 & 7
for (int t = FRAME_INTERPRETED; t <= FRAME_TYPE_MAX; ++t) {
int encoded = FrameType::encode(t, 0);
int extracted = (encoded >> 21) & 0x7;
EXPECT_EQ(extracted, t) << "encode() must store type " << t << " in bits [23:21]";
}
}
TEST(FrameTypeEncodeTest, EncodeBciStoredInLower16Bits) {
int bci = 0x1234;
int encoded = FrameType::encode(FRAME_INTERPRETED, bci);
EXPECT_EQ(encoded & 0xffff, bci) << "encode() must store BCI in the lower 16 bits";
}
TEST(FrameTypeEncodeTest, EncodeBciMaskedTo16Bits) {
// BCI is masked with 0xffff; overflow bits are silently dropped
int encoded = FrameType::encode(FRAME_INTERPRETED, 0x1ffff);
EXPECT_EQ(encoded & 0xffff, 0xffff)
<< "encode() must mask BCI to 16 bits (0xffff & 0x1ffff == 0xffff)";
}
TEST(FrameTypeEncodeTest, RawPointerBitNotSetByDefault) {
int encoded = FrameType::encode(FRAME_JIT_COMPILED, 0);
EXPECT_EQ(encoded & (1 << 30), 0) << "rawPointer flag (bit 30) must not be set by default";
}
// ---- decode ----------------------------------------------------------------
TEST(FrameTypeDecodeTest, DecodeZeroReturnsJitCompiled) {
EXPECT_EQ(FrameType::decode(0), FRAME_JIT_COMPILED)
<< "decode(0) must return FRAME_JIT_COMPILED (unencoded sentinel)";
}
TEST(FrameTypeDecodeTest, DecodeNegativeReturnsJitCompiled) {
EXPECT_EQ(FrameType::decode(-1), FRAME_JIT_COMPILED);
EXPECT_EQ(FrameType::decode(INT_MIN), FRAME_JIT_COMPILED);
EXPECT_EQ(FrameType::decode(-100), FRAME_JIT_COMPILED);
}
TEST(FrameTypeDecodeTest, DecodeUnencodedPositiveReturnsJitCompiled) {
// Values without the encoded-marker bit should be treated as raw BCIs
EXPECT_EQ(FrameType::decode(1), FRAME_JIT_COMPILED);
EXPECT_EQ(FrameType::decode(42), FRAME_JIT_COMPILED);
int bci = (int)(unsigned short)(0xffff);
EXPECT_EQ(FrameType::decode(bci), FRAME_JIT_COMPILED);
}
TEST(FrameTypeDecodeTest, RoundTripAllTypesZeroBci) {
// encode then decode must recover the original type for every frame type.
// BCI=0 exercises the common case (first bytecode instruction in a method).
for (int t = FRAME_INTERPRETED; t <= FRAME_TYPE_MAX; ++t) {
int encoded = FrameType::encode(t, 0);
FrameTypeId decoded = FrameType::decode(encoded);
EXPECT_EQ(decoded, static_cast<FrameTypeId>(t))
<< "Round-trip failed for type " << t << " with BCI=0"
<< " (encoded=0x" << std::hex << encoded << ")";
}
}
TEST(FrameTypeDecodeTest, RoundTripAllTypesNonZeroBci) {
// Spot-check several BCI values across the full [0, 0xffff] range
const int bcis[] = {1, 4, 7, 16, 20, 100, 1000, 0x8000, 0xffff};
for (int bci : bcis) {
for (int t = FRAME_INTERPRETED; t <= FRAME_TYPE_MAX; ++t) {
int encoded = FrameType::encode(t, bci);
FrameTypeId decoded = FrameType::decode(encoded);
EXPECT_EQ(decoded, static_cast<FrameTypeId>(t))
<< "Round-trip failed for type " << t << " BCI=" << bci
<< " (encoded=0x" << std::hex << encoded << ")";
}
}
}
// ---- isRawPointer ----------------------------------------------------------
TEST(FrameTypeIsRawPointerTest, FalseForZero) {
EXPECT_FALSE(FrameType::isRawPointer(0));
}
TEST(FrameTypeIsRawPointerTest, FalseForNegative) {
EXPECT_FALSE(FrameType::isRawPointer(-1));
EXPECT_FALSE(FrameType::isRawPointer(INT_MIN));
}
TEST(FrameTypeIsRawPointerTest, FalseForEncodedWithoutFlag) {
// A normally-encoded value must not appear as a raw pointer
for (int t = FRAME_INTERPRETED; t <= FRAME_TYPE_MAX; ++t) {
int encoded = FrameType::encode(t, 0);
EXPECT_FALSE(FrameType::isRawPointer(encoded))
<< "isRawPointer() must be false for encode(" << t << ", 0)";
}
}
TEST(FrameTypeIsRawPointerTest, TrueWhenBit30IsSetOnHotspot) {
// Manually set the raw-pointer flag (bit 30) on an encoded value.
// Raw pointers only exist on HotSpot, so isRawPointer() must be gated on it.
VMHotspotGuard hotspot(true);
int base = FrameType::encode(FRAME_JIT_COMPILED, 0);
int withFlag = base | (1 << 30);
EXPECT_TRUE(FrameType::isRawPointer(withFlag))
<< "isRawPointer() must be true when bit 30 is set, value is positive, and VM is HotSpot";
}
TEST(FrameTypeIsRawPointerTest, FalseWhenBit30IsSetOnNonHotspot) {
// Non-HotSpot VMs can coincidentally produce an unencoded bci with bit 30
// set (e.g. OpenJ9's raw AsyncGetCallTrace output); that must not be
// mistaken for HotSpot's raw-pointer encoding.
VMHotspotGuard hotspot(false);
int base = FrameType::encode(FRAME_JIT_COMPILED, 0);
int withFlag = base | (1 << 30);
EXPECT_FALSE(FrameType::isRawPointer(withFlag))
<< "isRawPointer() must be false when VM is not HotSpot, even with bit 30 set";
}
TEST(FrameTypeIsRawPointerTest, FalseWithOnlyBit30SetOnNegative) {
// The function requires bci > 0, so a value with bit 30 set that is
// interpreted as negative (bit 31 also set) must return false
int negativeWithBit30 = static_cast<int>(0xC0000000u); // bits 31 and 30 set → negative
EXPECT_LT(negativeWithBit30, 0);
EXPECT_FALSE(FrameType::isRawPointer(negativeWithBit30));
}
TEST(FrameTypeIsRawPointerTest, FalseForRawAsgctBciWithBit30SetButNoEncodedMarker) {
// Regression test: a raw, unencoded ASGCT BCI (bit 20 / ENCODED_MASK not set)
// that incidentally has bit 30 set must NOT be reported as a raw pointer.
// Such values occur on non-HotSpot VMs (e.g. J9), which never call encode()
// and therefore never set ENCODED_MASK; only encode(..., rawPointer=true)
// (HotSpot-only, per its own assert) is allowed to set RAW_POINTER_MASK.
int rawAsgctBciWithBit30 = 1 << 30;
VMHotspotGuard hotspot(true);
EXPECT_FALSE(FrameType::isRawPointer(rawAsgctBciWithBit30))
<< "isRawPointer() must require ENCODED_MASK (bit 20) before trusting bit 30, "
<< "otherwise raw ASGCT BCIs that never went through encode() can false-positive";
}