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/Bits.h>
26 #include <folly/Conv.h>
27 #include <folly/Format.h>
28 #include <folly/Random.h>
29 #include <folly/Shell.h>
30 #include <folly/String.h>
31 #include <folly/Subprocess.h>
32 #include <folly/portability/GTest.h>
33 #include <folly/portability/Unistd.h>
34 #include <folly/tracing/StaticTracepoint.h>
36 static const std::string kUSDTSubsectionName = FOLLY_SDT_NOTE_NAME;
37 static const int kUSDTNoteType = FOLLY_SDT_NOTE_TYPE;
38 static const size_t kAddrWidth = sizeof(void*);
40 static uint8_t hexToInt(const std::string& hex) {
41 std::stringstream converter(hex);
43 converter >> std::hex >> value;
44 return uint8_t(value);
47 static int get4BytesValue(const std::vector<uint8_t>& v, size_t& pos) {
49 return folly::Endian::little(folly::loadUnaligned<int>(v.data() + pos - 4));
52 static void align4Bytes(size_t& pos) {
58 static int getNextZero(
59 const std::vector<uint8_t>& v,
62 auto pos = std::find(v.begin() + curPos, v.begin() + limit, 0);
63 if (pos == v.begin() + limit) {
66 return std::distance(v.begin(), pos);
69 static intptr_t getAddr(const std::vector<uint8_t>& v, size_t& pos) {
71 return folly::Endian::little(
72 folly::loadUnaligned<intptr_t>(v.data() + pos - kAddrWidth));
76 getStr(const std::vector<uint8_t>& v, size_t& pos, const size_t len) {
80 for (size_t i = 0; i < len - 1; i++) {
81 CHECK_NE(v[pos + i], 0);
82 res[i] = char(v[pos + i]);
84 CHECK_EQ(0, v[pos + len - 1]);
89 static std::string getExe() {
90 auto path = folly::sformat("/proc/{}/exe", getpid());
91 return boost::filesystem::read_symlink(path).string();
94 static std::string getNoteRawContent(const std::string& fileName) {
95 auto args = folly::shellify(
96 "objdump --{} --{}={} {}",
99 ".note." + kUSDTSubsectionName,
102 folly::Subprocess(args, folly::Subprocess::Options().pipeStdout());
103 auto output = subProc.communicate();
104 auto retCode = subProc.wait();
105 CHECK(retCode.exited());
106 CHECK(output.second.empty());
110 static std::vector<uint8_t> readNote(const std::string& fileName) {
111 std::vector<uint8_t> res;
112 std::string rawContent = getNoteRawContent(fileName);
113 CHECK(!rawContent.empty());
114 // Strip out the part of output containing raw content, and split by line.
115 std::string contentStart =
116 "Contents of section .note." + kUSDTSubsectionName + ":";
117 auto pos = rawContent.find(contentStart);
118 CHECK_NE(pos, std::string::npos);
119 pos = rawContent.find("\n", pos + 1);
120 CHECK_NE(pos, std::string::npos);
121 rawContent = rawContent.substr(pos + 1);
122 std::vector<std::string> lines;
123 folly::split('\n', rawContent, lines, true);
124 CHECK_GT(lines.size(), 0);
126 for (auto line : lines) {
127 // Empty segments or ASCIIified content after two spaces.
128 auto endPos = line.find(" ");
129 CHECK_NE(endPos, std::string::npos);
130 line = line.substr(0, endPos);
131 std::vector<std::string> segments;
132 folly::split(' ', line, segments, true);
133 CHECK_GE(segments.size(), 2);
134 // First segment is address offset.
135 for (size_t i = 1; i < segments.size(); i++) {
136 CHECK_EQ(8, segments[i].size());
137 for (size_t j = 0; j < 8; j += 2) {
138 std::string hex = segments[i].substr(j, 2);
139 res.push_back(hexToInt(hex));
143 CHECK_EQ(0, res.size() % 4);
147 template <std::size_t SIZE>
148 static void checkTracepointArguments(
149 const std::string& arguments,
150 std::array<int, SIZE>& expectedSize) {
151 std::vector<std::string> args;
152 folly::split(' ', arguments, args);
153 EXPECT_EQ(expectedSize.size(), args.size());
154 for (size_t i = 0; i < args.size(); i++) {
155 EXPECT_FALSE(args[i].empty());
156 auto pos = args[i].find("@");
157 EXPECT_NE(pos, std::string::npos);
158 EXPECT_LT(pos, args[i].size() - 1);
159 std::string argSize = args[i].substr(0, pos);
160 EXPECT_EQ(expectedSize[i], abs(folly::to<int>(argSize)));
165 * This helper reads the .note.stapsdt section of the currently running binary,
166 * checks if the tracepoints listed there are properly formatted, and return the
167 * arguments layout description string for the expected provider and probe
168 * combination if it exists.
170 static bool getTracepointArguments(
171 const std::string& expectedProvider,
172 const std::string& expectedProbe,
173 std::string& arguments) {
174 // Read the note and check if it's non-empty.
175 std::string exe = getExe();
176 auto note = readNote(exe);
177 auto len = note.size();
179 // The loop to read tracepoints one by one.
182 // Check size information of the tracepoint.
183 CHECK_LE(pos + 12, len);
185 int headerSize = get4BytesValue(note, pos);
186 CHECK_EQ(kUSDTSubsectionName.size() + 1, headerSize);
188 int contentSize = get4BytesValue(note, pos);
189 size_t remaining = contentSize;
190 CHECK_GE(contentSize, kAddrWidth * 3);
192 int noteType = get4BytesValue(note, pos);
193 CHECK_EQ(kUSDTNoteType, noteType);
195 CHECK_LE(pos + headerSize + contentSize, len);
197 // Check header of the tracepoint.
198 std::string header = getStr(note, pos, headerSize);
199 CHECK_EQ(kUSDTSubsectionName, header);
202 // Check address information of the tracepoint
203 intptr_t probeAddr = getAddr(note, pos);
204 CHECK_GT(probeAddr, 0);
205 remaining -= kAddrWidth;
207 intptr_t semaphoreAddr = getAddr(note, pos);
208 CHECK_EQ(0, semaphoreAddr);
209 remaining -= kAddrWidth;
211 intptr_t semaphoreBase = getAddr(note, pos);
212 CHECK_EQ(0, semaphoreBase);
213 remaining -= kAddrWidth;
215 // Read tracepoint provider, probe and argument layout description.
216 int providerEnd = getNextZero(note, pos, pos + remaining - 1);
217 CHECK_GE(providerEnd, 0);
218 size_t providerLen = providerEnd - pos + 1;
219 std::string provider = getStr(note, pos, providerLen);
220 remaining -= providerLen;
222 int probeEnd = getNextZero(note, pos, pos + remaining - 1);
223 CHECK_GE(probeEnd, 0);
224 size_t probeLen = probeEnd - pos + 1;
225 std::string probe = getStr(note, pos, probeLen);
226 remaining -= probeLen;
228 arguments = getStr(note, pos, remaining);
231 if (provider == expectedProvider && probe == expectedProbe) {
238 static int arrayTestFunc() {
239 int v1 = folly::Random::rand32();
240 int v2 = folly::Random::rand32();
241 int64_t v3 = v1 + v2;
242 int a[4] = {v1, v2, v1, v2};
243 FOLLY_SDT(folly, test_static_tracepoint_array, a, v1, v3);
247 TEST(StaticTracepoint, TestArray) {
250 std::string arguments;
251 ASSERT_TRUE(getTracepointArguments(
252 "folly", "test_static_tracepoint_array", arguments));
253 std::array<int, 3> expected{{sizeof(void*), sizeof(int), sizeof(int64_t)}};
254 checkTracepointArguments(arguments, expected);
257 static int pointerTestFunc() {
258 int v1 = folly::Random::rand32();
259 int v2 = folly::Random::rand32();
260 std::string str = "test string";
261 const char* a = str.c_str();
262 FOLLY_SDT(folly, test_static_tracepoint_pointer, a, v2, &v1);
266 TEST(StaticTracepoint, TestPointer) {
269 std::string arguments;
270 ASSERT_TRUE(getTracepointArguments(
271 "folly", "test_static_tracepoint_array", arguments));
272 std::array<int, 3> expected{{sizeof(void*), sizeof(int), sizeof(void*)}};
273 checkTracepointArguments(arguments, expected);
276 static void emptyTestFunc() {
277 FOLLY_SDT(folly, test_static_tracepoint_empty);
280 TEST(StaticTracepoint, TestEmpty) {
283 std::string arguments;
284 ASSERT_TRUE(getTracepointArguments(
285 "folly", "test_static_tracepoint_empty", arguments));
286 EXPECT_TRUE(arguments.empty());
289 static int manyArgTypesTestFunc() {
290 uint32_t a = folly::Random::rand32();
291 uint32_t b = folly::Random::rand32();
292 bool bool_ = (a % 2) == (b % 2);
293 char char_ = a & 255;
294 short short_ = b & 32767;
296 float float_ = float(a) / float(b);
297 double double_ = double(a) / double(b);
300 test_static_tracepoint_many_arg_types,
312 TEST(StaticTracepoint, TestManyArgTypes) {
313 manyArgTypesTestFunc();
315 std::string arguments;
316 ASSERT_TRUE(getTracepointArguments(
317 "folly", "test_static_tracepoint_many_arg_types", arguments));
318 std::array<int, 8> expected{{
328 checkTracepointArguments(arguments, expected);
331 FOLLY_ALWAYS_INLINE static int alwaysInlineTestFunc() {
332 uint32_t a = folly::Random::rand32();
333 uint32_t b = folly::Random::rand32();
334 FOLLY_SDT(folly, test_static_tracepoint_always_inline, a, b);
338 TEST(StaticTracepoint, TestAlwaysInline) {
339 alwaysInlineTestFunc();
341 std::string arguments;
342 ASSERT_TRUE(getTracepointArguments(
343 "folly", "test_static_tracepoint_always_inline", arguments));
344 std::array<int, 2> expected{{sizeof(uint32_t), sizeof(uint32_t)}};
345 checkTracepointArguments(arguments, expected);
348 static void branchTestFunc() {
349 uint32_t a = folly::Random::rand32();
350 uint32_t b = folly::Random::rand32();
352 FOLLY_SDT(folly, test_static_tracepoint_branch_1, a / b);
354 FOLLY_SDT(folly, test_static_tracepoint_branch_2, double(a) / double(b));
358 TEST(StaticTracepoint, TestBranch) {
361 std::string arguments1;
362 ASSERT_TRUE(getTracepointArguments(
363 "folly", "test_static_tracepoint_branch_1", arguments1));
364 std::array<int, 1> expected1{{sizeof(uint32_t)}};
365 checkTracepointArguments(arguments1, expected1);
367 std::string arguments2;
368 ASSERT_TRUE(getTracepointArguments(
369 "folly", "test_static_tracepoint_branch_2", arguments2));
370 std::array<int, 1> expected2{{sizeof(double)}};
371 checkTracepointArguments(arguments2, expected2);
380 static void structTestFunc() {
382 s.a = folly::Random::rand32();
383 s.b = folly::Random::rand32();
384 t.a = folly::Random::rand32();
385 t.b = folly::Random::rand32();
386 FOLLY_SDT(folly, test_static_tracepoint_struct, s, t);
389 TEST(StaticTracepoint, TestStruct) {
392 std::string arguments;
393 ASSERT_TRUE(getTracepointArguments(
394 "folly", "test_static_tracepoint_struct", arguments));
395 std::array<int, 2> expected{{sizeof(testStruct), sizeof(testStruct)}};
396 checkTracepointArguments(arguments, expected);