proxygen
StaticTracepointTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2016-present Facebook, Inc.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 #include <algorithm>
18 #include <array>
19 #include <iterator>
20 #include <sstream>
21 #include <string>
22 #include <vector>
23 
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>
35 
38 static const size_t kAddrWidth = sizeof(void*);
39 
40 static uint8_t hexToInt(const std::string& hex) {
41  std::stringstream converter(hex);
42  int value;
43  converter >> std::hex >> value;
44  return uint8_t(value);
45 }
46 
47 static int get4BytesValue(const std::vector<uint8_t>& v, size_t& pos) {
48  pos += 4;
49  return folly::Endian::little(folly::loadUnaligned<int>(v.data() + pos - 4));
50 }
51 
52 static void align4Bytes(size_t& pos) {
53  if (pos % 4 != 0) {
54  pos += 4 - pos % 4;
55  }
56 }
57 
58 static int getNextZero(
59  const std::vector<uint8_t>& v,
60  const size_t curPos,
61  const size_t limit) {
62  auto pos = std::find(v.begin() + curPos, v.begin() + limit, 0);
63  if (pos == v.begin() + limit) {
64  return -1;
65  }
66  return std::distance(v.begin(), pos);
67 }
68 
69 static intptr_t getAddr(const std::vector<uint8_t>& v, size_t& pos) {
70  pos += kAddrWidth;
71  return folly::Endian::little(
72  folly::loadUnaligned<intptr_t>(v.data() + pos - kAddrWidth));
73 }
74 
75 static std::string
76 getStr(const std::vector<uint8_t>& v, size_t& pos, const size_t len) {
77  CHECK_GE(len, 1);
78  std::string res;
79  res.resize(len - 1);
80  for (size_t i = 0; i < len - 1; i++) {
81  CHECK_NE(v[pos + i], 0);
82  res[i] = char(v[pos + i]);
83  }
84  CHECK_EQ(0, v[pos + len - 1]);
85  pos += len;
86  return res;
87 }
88 
89 static std::string getExe() {
90  auto path = folly::sformat("/proc/{}/exe", getpid());
91  return boost::filesystem::read_symlink(path).string();
92 }
93 
94 static std::string getNoteRawContent(const std::string& fileName) {
95  auto subProc = folly::Subprocess(
96  std::vector<std::string>{
97  "objdump",
98  "--full-content",
99  "--section=.note." + kUSDTSubsectionName,
100  fileName,
101  },
103  auto output = subProc.communicate();
104  auto retCode = subProc.wait();
105  CHECK(retCode.exited());
106  CHECK(output.second.empty());
107  return output.first;
108 }
109 
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);
125  // Parse each line.
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));
140  }
141  }
142  }
143  CHECK_EQ(0, res.size() % 4);
144  return res;
145 }
146 
147 template <std::size_t SIZE>
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)));
161  }
162 }
163 
171  const std::string& expectedProvider,
172  const std::string& expectedProbe,
173  const uintptr_t expectedSemaphore,
174  std::string& arguments) {
175  // Read the note and check if it's non-empty.
176  std::string exe = getExe();
177  auto note = readNote(exe);
178  auto len = note.size();
179  CHECK_GT(len, 0);
180  // The loop to read tracepoints one by one.
181  size_t pos = 0;
182  while (pos < len) {
183  // Check size information of the tracepoint.
184  CHECK_LE(pos + 12, len);
185 
186  int headerSize = get4BytesValue(note, pos);
187  CHECK_EQ(kUSDTSubsectionName.size() + 1, headerSize);
188 
189  int contentSize = get4BytesValue(note, pos);
190  size_t remaining = contentSize;
191  CHECK_GE(contentSize, kAddrWidth * 3);
192 
193  int noteType = get4BytesValue(note, pos);
194  CHECK_EQ(kUSDTNoteType, noteType);
195 
196  CHECK_LE(pos + headerSize + contentSize, len);
197 
198  // Check header of the tracepoint.
199  std::string header = getStr(note, pos, headerSize);
200  CHECK_EQ(kUSDTSubsectionName, header);
201  align4Bytes(pos);
202 
203  // Check address information of the tracepoint
204  intptr_t probeAddr = getAddr(note, pos);
205  CHECK_GT(probeAddr, 0);
206  remaining -= kAddrWidth;
207 
208  intptr_t baseAddr = getAddr(note, pos);
209  CHECK_EQ(0, baseAddr);
210  remaining -= kAddrWidth;
211 
212  intptr_t semaphoreAddr = getAddr(note, pos);
213  remaining -= kAddrWidth;
214 
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;
221 
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;
227 
228  arguments = getStr(note, pos, remaining);
229  align4Bytes(pos);
230 
231  if (provider == expectedProvider && probe == expectedProbe) {
232  CHECK_EQ(expectedSemaphore, semaphoreAddr);
233  return true;
234  }
235  }
236  return false;
237 }
238 
242  uint64_t v3 = v1 + v2;
243  uint32_t a[4] = {v1, v2, v1, v2};
244  FOLLY_SDT(folly, test_static_tracepoint_array, a, v1, v3);
245  return v1 + v2;
246 }
247 
248 TEST(StaticTracepoint, TestArray) {
249  arrayTestFunc();
250 
251  std::string arguments;
253  "folly", "test_static_tracepoint_array", 0, arguments));
254  std::array<int, 3> expected{{sizeof(void*), sizeof(int), sizeof(int64_t)}};
255  checkTracepointArguments(arguments, expected);
256 }
257 
261  std::string str = "test string";
262  const char* a = str.c_str();
263  FOLLY_SDT(folly, test_static_tracepoint_pointer, a, v2, &v1);
264  return v1 + v2;
265 }
266 
267 TEST(StaticTracepoint, TestPointer) {
268  pointerTestFunc();
269 
270  std::string arguments;
272  "folly", "test_static_tracepoint_array", 0, arguments));
273  std::array<int, 3> expected{{sizeof(void*), sizeof(int), sizeof(void*)}};
274  checkTracepointArguments(arguments, expected);
275 }
276 
277 static void emptyTestFunc() {
278  FOLLY_SDT(folly, test_static_tracepoint_empty);
279 }
280 
281 TEST(StaticTracepoint, TestEmpty) {
282  emptyTestFunc();
283 
284  std::string arguments;
286  "folly", "test_static_tracepoint_empty", 0, arguments));
287  EXPECT_TRUE(arguments.empty());
288 }
289 
290 FOLLY_SDT_DEFINE_SEMAPHORE(folly, test_semaphore_local)
291 
295  bool bool_ = (a % 2) == (b % 2);
296  char char_ = a & 255;
297  short short_ = b & 32767;
298  long long_ = a;
299  float float_ = float(a) / float(b);
300  double double_ = double(a) / double(b);
301  FOLLY_SDT(
302  folly,
303  test_static_tracepoint_many_arg_types,
304  a,
305  b,
306  bool_,
307  char_,
308  short_,
309  long_,
310  float_,
311  double_);
312  FOLLY_SDT_WITH_SEMAPHORE(folly, test_semaphore_local, long_, short_);
313  return a + b;
314 }
315 
316 TEST(StaticTracepoint, TestManyArgTypes) {
318 
319  std::string arguments;
321  "folly", "test_static_tracepoint_many_arg_types", 0, arguments));
322  std::array<int, 8> expected{{
323  sizeof(uint32_t),
324  sizeof(uint32_t),
325  sizeof(bool),
326  sizeof(char),
327  sizeof(short),
328  sizeof(long),
329  sizeof(float),
330  sizeof(double),
331  }};
332  checkTracepointArguments(arguments, expected);
333 }
334 
338  FOLLY_SDT(folly, test_static_tracepoint_always_inline, a, b);
339  return a + b;
340 }
341 
342 TEST(StaticTracepoint, TestAlwaysInline) {
344 
345  std::string arguments;
347  "folly", "test_static_tracepoint_always_inline", 0, arguments));
348  std::array<int, 2> expected{{sizeof(uint32_t), sizeof(uint32_t)}};
349  checkTracepointArguments(arguments, expected);
350 }
351 
352 static void branchTestFunc() {
355  if (a > b) {
356  FOLLY_SDT(folly, test_static_tracepoint_branch_1, a / b);
357  } else {
358  FOLLY_SDT(folly, test_static_tracepoint_branch_2, double(a) / double(b));
359  }
360 }
361 
362 TEST(StaticTracepoint, TestBranch) {
363  branchTestFunc();
364 
365  std::string arguments1;
367  "folly", "test_static_tracepoint_branch_1", 0, arguments1));
368  std::array<int, 1> expected1{{sizeof(uint32_t)}};
369  checkTracepointArguments(arguments1, expected1);
370 
371  std::string arguments2;
373  "folly", "test_static_tracepoint_branch_2", 0, arguments2));
374  std::array<int, 1> expected2{{sizeof(double)}};
375  checkTracepointArguments(arguments2, expected2);
376 }
377 
378 struct testStruct {
379  int a;
381  char c[32];
382 };
383 
384 static void structTestFunc() {
385  testStruct s, t;
386  s.a = folly::Random::rand32();
387  s.b = folly::Random::rand32();
388  t.a = folly::Random::rand32();
389  t.b = folly::Random::rand32();
390  FOLLY_SDT(folly, test_static_tracepoint_struct, s, t);
391 }
392 
393 TEST(StaticTracepoint, TestStruct) {
394  structTestFunc();
395 
396  std::string arguments;
398  "folly", "test_static_tracepoint_struct", 0, arguments));
399  std::array<int, 2> expected{{sizeof(testStruct), sizeof(testStruct)}};
400  checkTracepointArguments(arguments, expected);
401 }
402 
403 TEST(StaticTracepoint, TestSemaphoreLocal) {
405 
406  std::string arguments;
408  "folly",
409  "test_semaphore_local",
410  (uintptr_t)((void*)&FOLLY_SDT_SEMAPHORE(folly, test_semaphore_local)),
411  arguments));
412  std::array<int, 2> expected{{sizeof(long), sizeof(short)}};
413  checkTracepointArguments(arguments, expected);
414  EXPECT_FALSE(FOLLY_SDT_IS_ENABLED(folly, test_semaphore_local));
415 }
416 
417 FOLLY_SDT_DECLARE_SEMAPHORE(folly, test_semaphore_extern);
418 
419 TEST(StaticTracepoint, TestSemaphoreExtern) {
420  unsigned v = folly::Random::rand32();
421  CHECK_EQ(v * v, folly::test::staticTracepointTestFunc(v));
422  EXPECT_FALSE(FOLLY_SDT_IS_ENABLED(folly, test_semaphore_extern));
423 }
#define FOLLY_SDT_NOTE_NAME
constexpr size_t headerSize()
Definition: RecordIO-inl.h:101
#define FOLLY_SDT_SEMAPHORE(provider, name)
#define FOLLY_SDT(provider, name,...)
static FOLLY_ALWAYS_INLINE uint32_t alwaysInlineTestFunc()
static std::string getExe()
static uint32_t pointerTestFunc()
#define FOLLY_SDT_NOTE_TYPE
#define FOLLY_ALWAYS_INLINE
Definition: CPortability.h:151
char b
LogLevel max
Definition: LogLevel.cpp:31
FOLLY_SDT_DECLARE_SEMAPHORE(folly, test_semaphore_extern)
static int getNextZero(const std::vector< uint8_t > &v, const size_t curPos, const size_t limit)
std::string sformat(StringPiece fmt, Args &&...args)
Definition: Format.h:280
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
static std::string getStr(const std::vector< uint8_t > &v, size_t &pos, const size_t len)
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
static T little(T x)
Definition: Bits.h:263
static std::string getNoteRawContent(const std::string &fileName)
unsigned staticTracepointTestFunc(unsigned v)
void split(const Delim &delimiter, const String &input, std::vector< OutputType > &out, bool ignoreEmpty)
Definition: String-inl.h:382
static void emptyTestFunc()
static void branchTestFunc()
static void structTestFunc()
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
static void align4Bytes(size_t &pos)
constexpr auto empty(C const &c) -> decltype(c.empty())
Definition: Access.h:55
double expectedProbe(std::vector< std::size_t > const &probeLengths)
Definition: F14TestUtil.h:69
#define FOLLY_SDT_WITH_SEMAPHORE(provider, name,...)
static bool getTracepointArguments(const std::string &expectedProvider, const std::string &expectedProbe, const uintptr_t expectedSemaphore, std::string &arguments)
#define FOLLY_SDT_IS_ENABLED(provider, name)
char a
static const size_t kAddrWidth
S lines(StringPiece source)
Definition: String.h:80
static uint32_t arrayTestFunc()
static const char *const value
Definition: Conv.cpp:50
static const std::string kUSDTSubsectionName
TEST(StaticTracepoint, TestArray)
static intptr_t getAddr(const std::vector< uint8_t > &v, size_t &pos)
std::integral_constant< bool, B > bool_
Definition: concept_def.h:443
static void checkTracepointArguments(const std::string &arguments, std::array< int, SIZE > &expectedSize)
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
static uint8_t hexToInt(const std::string &hex)
#define FOLLY_SDT_DEFINE_SEMAPHORE(provider, name)
static int get4BytesValue(const std::vector< uint8_t > &v, size_t &pos)
const char * string
Definition: Conv.cpp:212
#define EXPECT_NE(val1, val2)
Definition: gtest.h:1926
static set< string > s
static const int kUSDTNoteType
InlineExecutor exe
Definition: Benchmark.cpp:337
#define EXPECT_FALSE(condition)
Definition: gtest.h:1862
static uint32_t rand32()
Definition: Random.h:213
#define EXPECT_LT(val1, val2)
Definition: gtest.h:1930
static uint32_t manyArgTypesTestFunc()
static std::vector< uint8_t > readNote(const std::string &fileName)
#define ASSERT_TRUE(condition)
Definition: gtest.h:1865