forked from google/centipede
-
Notifications
You must be signed in to change notification settings - Fork 5
/
runner_cmp_trace.h
140 lines (120 loc) · 4.49 KB
/
runner_cmp_trace.h
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
// Copyright 2022 The Centipede Authors.
//
// 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
//
// https://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.
#ifndef THIRD_PARTY_CENTIPEDE_RUNNER_CMP_TRACE_H_
#define THIRD_PARTY_CENTIPEDE_RUNNER_CMP_TRACE_H_
// Capturing arguments of CMP instructions, memcmp, and similar.
// WARNING: this code needs to have minimal dependencies.
#include <cstddef>
#include <cstdint>
#include <cstring>
namespace centipede {
// Captures up to `kNumItems` different CMP argument pairs.
// Every argument is `kFixedSize` bytes.
//
// If `kFixedSize` == 0, the argument size is variable.
// Only the first `kNumBytesPerValue` bytes of every argument are captured.
// This is used to capture arguments of memcmp() and similar.
//
// Every new captured pair may overwrite a pair stored previously.
//
// Outside of tests, objects of this class will be created in TLS, thus no CTOR.
template <uint8_t kFixedSize, size_t kNumItems>
class CmpTrace {
public:
// kMaxNumBytesPerValue does not depend on kFixedSize.
static constexpr size_t kMaxNumBytesPerValue = 16;
static constexpr size_t kNumBytesPerValue =
kFixedSize ? kFixedSize : kMaxNumBytesPerValue;
// No CTOR - objects will be created in TLS.
// Clears `this`.
void Clear() { memset(this, 0, sizeof(*this)); }
// Captures one CMP argument pair, as two byte arrays, `size` bytes each.
void Capture(uint8_t size, const uint8_t *value0, const uint8_t *value1) {
if (size > kNumBytesPerValue) size = kNumBytesPerValue;
// We choose a pseudo-random slot each time.
// This way after capturing many pairs we end up with up to `kNumItems`
// pairs which are typically, but not always, the most recent.
rand_seed_ = rand_seed_ * 1103515245 + 12345;
Item &item = items_[rand_seed_ % kNumItems];
item.size.set(size);
memcpy(item.value0, value0, size);
memcpy(item.value1, value1, size);
}
// Captures one CMP argument pair, as two integers of kFixedSize bytes each.
template <typename T>
void Capture(T value0, T value1) {
// If both values are small, ignore them as not very useful.
if (value0 < 256 && value1 < 256) return;
static_assert(sizeof(T) == kFixedSize);
Capture(sizeof(T), reinterpret_cast<const uint8_t *>(&value0),
reinterpret_cast<const uint8_t *>(&value1));
}
// Iterates non-zero CMP pairs.
template <typename Callback>
void ForEachNonZero(Callback callback) {
for (const auto &item : items_) {
if (IsZero(item.value0, item.size.get()) &&
IsZero(item.value1, item.size.get()))
continue;
callback(item.size.get(), item.value0, item.value1);
}
}
private:
// SizeField<kFixedSize> returns kFixedSize as the size, for kFixedSize != 0.
template <uint8_t kSize>
class SizeField {
public:
void set(uint8_t size) {}
size_t get() const { return kSize; }
};
// SizeField<0> actually stores the size.
template <>
class SizeField<0> {
public:
void set(uint8_t size) { size_ = size; }
uint8_t get() const { return size_; }
private:
uint8_t size_;
};
template <typename T>
static bool IsZero(const uint8_t *value) {
T x = {};
__builtin_memcpy(&x, value, sizeof(T));
return x == T{};
}
// Returns true if all value[0:size] are zero.
static bool IsZero(const uint8_t *value, size_t size) {
if constexpr (kFixedSize == 8) return IsZero<uint64_t>(value);
if constexpr (kFixedSize == 4) return IsZero<uint32_t>(value);
if constexpr (kFixedSize == 2) return IsZero<uint16_t>(value);
// The code iterates over bytes, but we expect the compiler to optimize it.
uint64_t ored_bytes = 0;
for (size_t i = 0; i < size; ++i) {
ored_bytes |= value[i];
}
return ored_bytes == 0;
}
// One CMP argument pair.
struct Item {
SizeField<kFixedSize> size;
uint8_t value0[kNumBytesPerValue];
uint8_t value1[kNumBytesPerValue];
};
// All argument pairs.
Item items_[kNumItems];
// Pseudo-random seed.
size_t rand_seed_;
};
} // namespace centipede
#endif // THIRD_PARTY_CENTIPEDE_RUNNER_CMP_TRACE_H_