2 * Copyright 2017 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
24 #include <boost/filesystem.hpp>
25 #include <folly/Conv.h>
26 #include <folly/Format.h>
27 #include <folly/Random.h>
28 #include <folly/String.h>
29 #include <folly/Subprocess.h>
30 #include <folly/lang/Bits.h>
31 #include <folly/portability/GTest.h>
32 #include <folly/portability/Unistd.h>
33 #include <folly/tracing/StaticTracepoint.h>
35 static const std::string kUSDTSubsectionName = FOLLY_SDT_NOTE_NAME;
36 static const int kUSDTNoteType = FOLLY_SDT_NOTE_TYPE;
37 static const size_t kAddrWidth = sizeof(void*);
39 static uint8_t hexToInt(const std::string& hex) {
40 std::stringstream converter(hex);
42 converter >> std::hex >> value;
43 return uint8_t(value);
46 static int get4BytesValue(const std::vector<uint8_t>& v, size_t& pos) {
48 return folly::Endian::little(folly::loadUnaligned<int>(v.data() + pos - 4));
51 static void align4Bytes(size_t& pos) {
57 static int getNextZero(
58 const std::vector<uint8_t>& v,
61 auto pos = std::find(v.begin() + curPos, v.begin() + limit, 0);
62 if (pos == v.begin() + limit) {
65 return std::distance(v.begin(), pos);
68 static intptr_t getAddr(const std::vector<uint8_t>& v, size_t& pos) {
70 return folly::Endian::little(
71 folly::loadUnaligned<intptr_t>(v.data() + pos - kAddrWidth));
75 getStr(const std::vector<uint8_t>& v, size_t& pos, const size_t len) {
79 for (size_t i = 0; i < len - 1; i++) {
80 CHECK_NE(v[pos + i], 0);
81 res[i] = char(v[pos + i]);
83 CHECK_EQ(0, v[pos + len - 1]);
88 static std::string getExe() {
89 auto path = folly::sformat("/proc/{}/exe", getpid());
90 return boost::filesystem::read_symlink(path).string();
93 static std::string getNoteRawContent(const std::string& fileName) {
94 auto subProc = folly::Subprocess(
95 std::vector<std::string>{
98 "--section=.note." + kUSDTSubsectionName,
101 folly::Subprocess::Options().pipeStdout().usePath());
102 auto output = subProc.communicate();
103 auto retCode = subProc.wait();
104 CHECK(retCode.exited());
105 CHECK(output.second.empty());
109 static std::vector<uint8_t> readNote(const std::string& fileName) {
110 std::vector<uint8_t> res;
111 std::string rawContent = getNoteRawContent(fileName);
112 CHECK(!rawContent.empty());
113 // Strip out the part of output containing raw content, and split by line.
114 std::string contentStart =
115 "Contents of section .note." + kUSDTSubsectionName + ":";
116 auto pos = rawContent.find(contentStart);
117 CHECK_NE(pos, std::string::npos);
118 pos = rawContent.find("\n", pos + 1);
119 CHECK_NE(pos, std::string::npos);
120 rawContent = rawContent.substr(pos + 1);
121 std::vector<std::string> lines;
122 folly::split('\n', rawContent, lines, true);
123 CHECK_GT(lines.size(), 0);
125 for (auto line : lines) {
126 // Empty segments or ASCIIified content after two spaces.
127 auto endPos = line.find(" ");
128 CHECK_NE(endPos, std::string::npos);
129 line = line.substr(0, endPos);
130 std::vector<std::string> segments;
131 folly::split(' ', line, segments, true);
132 CHECK_GE(segments.size(), 2);
133 // First segment is address offset.
134 for (size_t i = 1; i < segments.size(); i++) {
135 CHECK_EQ(8, segments[i].size());
136 for (size_t j = 0; j < 8; j += 2) {
137 std::string hex = segments[i].substr(j, 2);
138 res.push_back(hexToInt(hex));
142 CHECK_EQ(0, res.size() % 4);
146 template <std::size_t SIZE>
147 static void checkTracepointArguments(
148 const std::string& arguments,
149 std::array<int, SIZE>& expectedSize) {
150 std::vector<std::string> args;
151 folly::split(' ', arguments, args);
152 EXPECT_EQ(expectedSize.size(), args.size());
153 for (size_t i = 0; i < args.size(); i++) {
154 EXPECT_FALSE(args[i].empty());
155 auto pos = args[i].find("@");
156 EXPECT_NE(pos, std::string::npos);
157 EXPECT_LT(pos, args[i].size() - 1);
158 std::string argSize = args[i].substr(0, pos);
159 EXPECT_EQ(expectedSize[i], abs(folly::to<int>(argSize)));
164 * This helper reads the .note.stapsdt section of the currently running binary,
165 * checks if the tracepoints listed there are properly formatted, and return the
166 * arguments layout description string for the expected provider and probe
167 * combination if it exists.
169 static bool getTracepointArguments(
170 const std::string& expectedProvider,
171 const std::string& expectedProbe,
172 std::string& arguments) {
173 // Read the note and check if it's non-empty.
174 std::string exe = getExe();
175 auto note = readNote(exe);
176 auto len = note.size();
178 // The loop to read tracepoints one by one.
181 // Check size information of the tracepoint.
182 CHECK_LE(pos + 12, len);
184 int headerSize = get4BytesValue(note, pos);
185 CHECK_EQ(kUSDTSubsectionName.size() + 1, headerSize);
187 int contentSize = get4BytesValue(note, pos);
188 size_t remaining = contentSize;
189 CHECK_GE(contentSize, kAddrWidth * 3);
191 int noteType = get4BytesValue(note, pos);
192 CHECK_EQ(kUSDTNoteType, noteType);
194 CHECK_LE(pos + headerSize + contentSize, len);
196 // Check header of the tracepoint.
197 std::string header = getStr(note, pos, headerSize);
198 CHECK_EQ(kUSDTSubsectionName, header);
201 // Check address information of the tracepoint
202 intptr_t probeAddr = getAddr(note, pos);
203 CHECK_GT(probeAddr, 0);
204 remaining -= kAddrWidth;
206 intptr_t semaphoreAddr = getAddr(note, pos);
207 CHECK_EQ(0, semaphoreAddr);
208 remaining -= kAddrWidth;
210 intptr_t semaphoreBase = getAddr(note, pos);
211 CHECK_EQ(0, semaphoreBase);
212 remaining -= kAddrWidth;
214 // Read tracepoint provider, probe and argument layout description.
215 int providerEnd = getNextZero(note, pos, pos + remaining - 1);
216 CHECK_GE(providerEnd, 0);
217 size_t providerLen = providerEnd - pos + 1;
218 std::string provider = getStr(note, pos, providerLen);
219 remaining -= providerLen;
221 int probeEnd = getNextZero(note, pos, pos + remaining - 1);
222 CHECK_GE(probeEnd, 0);
223 size_t probeLen = probeEnd - pos + 1;
224 std::string probe = getStr(note, pos, probeLen);
225 remaining -= probeLen;
227 arguments = getStr(note, pos, remaining);
230 if (provider == expectedProvider && probe == expectedProbe) {
237 static int arrayTestFunc() {
238 int v1 = folly::Random::rand32();
239 int v2 = folly::Random::rand32();
240 int64_t v3 = v1 + v2;
241 int a[4] = {v1, v2, v1, v2};
242 FOLLY_SDT(folly, test_static_tracepoint_array, a, v1, v3);
246 TEST(StaticTracepoint, TestArray) {
249 std::string arguments;
250 ASSERT_TRUE(getTracepointArguments(
251 "folly", "test_static_tracepoint_array", arguments));
252 std::array<int, 3> expected{{sizeof(void*), sizeof(int), sizeof(int64_t)}};
253 checkTracepointArguments(arguments, expected);
256 static int pointerTestFunc() {
257 int v1 = folly::Random::rand32();
258 int v2 = folly::Random::rand32();
259 std::string str = "test string";
260 const char* a = str.c_str();
261 FOLLY_SDT(folly, test_static_tracepoint_pointer, a, v2, &v1);
265 TEST(StaticTracepoint, TestPointer) {
268 std::string arguments;
269 ASSERT_TRUE(getTracepointArguments(
270 "folly", "test_static_tracepoint_array", arguments));
271 std::array<int, 3> expected{{sizeof(void*), sizeof(int), sizeof(void*)}};
272 checkTracepointArguments(arguments, expected);
275 static void emptyTestFunc() {
276 FOLLY_SDT(folly, test_static_tracepoint_empty);
279 TEST(StaticTracepoint, TestEmpty) {
282 std::string arguments;
283 ASSERT_TRUE(getTracepointArguments(
284 "folly", "test_static_tracepoint_empty", arguments));
285 EXPECT_TRUE(arguments.empty());
288 static int manyArgTypesTestFunc() {
289 uint32_t a = folly::Random::rand32();
290 uint32_t b = folly::Random::rand32();
291 bool bool_ = (a % 2) == (b % 2);
292 char char_ = a & 255;
293 short short_ = b & 32767;
295 float float_ = float(a) / float(b);
296 double double_ = double(a) / double(b);
299 test_static_tracepoint_many_arg_types,
311 TEST(StaticTracepoint, TestManyArgTypes) {
312 manyArgTypesTestFunc();
314 std::string arguments;
315 ASSERT_TRUE(getTracepointArguments(
316 "folly", "test_static_tracepoint_many_arg_types", arguments));
317 std::array<int, 8> expected{{
327 checkTracepointArguments(arguments, expected);
330 FOLLY_ALWAYS_INLINE static int alwaysInlineTestFunc() {
331 uint32_t a = folly::Random::rand32();
332 uint32_t b = folly::Random::rand32();
333 FOLLY_SDT(folly, test_static_tracepoint_always_inline, a, b);
337 TEST(StaticTracepoint, TestAlwaysInline) {
338 alwaysInlineTestFunc();
340 std::string arguments;
341 ASSERT_TRUE(getTracepointArguments(
342 "folly", "test_static_tracepoint_always_inline", arguments));
343 std::array<int, 2> expected{{sizeof(uint32_t), sizeof(uint32_t)}};
344 checkTracepointArguments(arguments, expected);
347 static void branchTestFunc() {
348 uint32_t a = folly::Random::rand32();
349 uint32_t b = folly::Random::rand32();
351 FOLLY_SDT(folly, test_static_tracepoint_branch_1, a / b);
353 FOLLY_SDT(folly, test_static_tracepoint_branch_2, double(a) / double(b));
357 TEST(StaticTracepoint, TestBranch) {
360 std::string arguments1;
361 ASSERT_TRUE(getTracepointArguments(
362 "folly", "test_static_tracepoint_branch_1", arguments1));
363 std::array<int, 1> expected1{{sizeof(uint32_t)}};
364 checkTracepointArguments(arguments1, expected1);
366 std::string arguments2;
367 ASSERT_TRUE(getTracepointArguments(
368 "folly", "test_static_tracepoint_branch_2", arguments2));
369 std::array<int, 1> expected2{{sizeof(double)}};
370 checkTracepointArguments(arguments2, expected2);
379 static void structTestFunc() {
381 s.a = folly::Random::rand32();
382 s.b = folly::Random::rand32();
383 t.a = folly::Random::rand32();
384 t.b = folly::Random::rand32();
385 FOLLY_SDT(folly, test_static_tracepoint_struct, s, t);
388 TEST(StaticTracepoint, TestStruct) {
391 std::string arguments;
392 ASSERT_TRUE(getTracepointArguments(
393 "folly", "test_static_tracepoint_struct", arguments));
394 std::array<int, 2> expected{{sizeof(testStruct), sizeof(testStruct)}};
395 checkTracepointArguments(arguments, expected);