Add USDT header file to folly
authorTeng Qin <qinteng@fb.com>
Wed, 23 Nov 2016 22:05:39 +0000 (14:05 -0800)
committerFacebook Github Bot <facebook-github-bot-bot@fb.com>
Wed, 23 Nov 2016 22:08:28 +0000 (14:08 -0800)
Summary:
Derived from `sdt.h` in [[ https://sourceware.org/systemtap/ | SystemTap ]]
Performed changes and clean-ups including:
- Get rid of the `sdt-config.h`.
- Get rid of "Running in Assembler" code.
- Replace the `DTRACE_PROBE_1, ... DTRACE_PROBE_N` Macro family with a single Macro using the "count Macro arguments" trick
- Get rid of argument signed-ness calculation. A **lot** of painful code were there only for this, and it has no real usage except for displaying purposes.
- Re-structure the helper methods (Macros) so that the logic of the code is a bit more readable.
- Add some comments so that it would be easier for users and future developers to understand what this header file is actually doing.
- Update license banner.

Reviewed By: meyering

Differential Revision: D3792966

fbshipit-source-id: 564a1011f7946d30be78191f334e072d94fa264e

folly/tracing/README.md [new file with mode: 0644]
folly/tracing/StaticTracepoint-ELFx86.h [new file with mode: 0644]
folly/tracing/StaticTracepoint.h [new file with mode: 0644]
folly/tracing/test/StaticTracepointTest.cpp [new file with mode: 0644]

diff --git a/folly/tracing/README.md b/folly/tracing/README.md
new file mode 100644 (file)
index 0000000..8df5aef
--- /dev/null
@@ -0,0 +1,44 @@
+# folly/tracing: Utility for User-level Statically Defined Tracing
+----------------------------------------------------------
+
+## StaticTracepoint
+
+The `StaticTracepoint.h` header file defines the Macro
+```
+FOLLY_SDT(provider, name, arg1, arg2, ...)
+```
+Invoking the Macro will add a Static Tracepoint at the calling location. Using a
+tracing toolkit ([BCC](https://github.com/iovisor/bcc) is an excellent example),
+a probe can be attached to the Tracepoint, consume the provided arguments and
+perform other tracing / profiling works.
+
+The Tracepoint defined using `StaticTracepoint.h` is also compatible with any
+toolkit designed for consuming [SystemTap](https://sourceware.org/systemtap/)
+Tracepoints.
+
+Internally, the Macro emits a `nop` operation at the calling location, along
+with an Assembler Instruction with empty template and empty output Operands,
+and the provided arguments and their sizes as input Operands.
+
+The Macro then append to the ELF `.note` section, with information including
+the provider and name of the Tracepoint, address of the `nop` operation, and
+size and location (register name or memory location) of the provided arguments.
+This way, the tracing toolkits would be able to parse the information, attach
+the probes to the correct address, and consume arguments.
+
+The default constraint for the arguments in the Assembler Instruction as
+operands is `"nor"`. It means the argument could be an immediate integer
+operand, a register operand or an offsettable memory operand. This is a good
+default since tracing arguments tend to be integral, and the number of arguments
+is likely to be less than the number of registers.
+
+Otherwise, you may see compiler report errors like
+```
+'asm' requires impossible reload
+```
+You may want to simplify the Tracepoint (fewer and simpler arguments) in
+such case. You may also choose to override the constraint
+```
+#define FOLLY_SDT_ARG_CONSTRAINT "g"
+```
+which means the arguments can be any memory or register operands.
diff --git a/folly/tracing/StaticTracepoint-ELFx86.h b/folly/tracing/StaticTracepoint-ELFx86.h
new file mode 100644 (file)
index 0000000..446a5b4
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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
+ *
+ *   http://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.
+ */
+
+#pragma once
+
+// Default constraint for the probe arguments as operands.
+#ifndef FOLLY_SDT_ARG_CONSTRAINT
+#define FOLLY_SDT_ARG_CONSTRAINT      "nor"
+#endif
+
+// Instruction to emit for the probe.
+#define FOLLY_SDT_NOP                 nop
+
+// Note section properties.
+#define FOLLY_SDT_NOTE_NAME           "stapsdt"
+#define FOLLY_SDT_NOTE_TYPE           3
+
+// Size of address depending on platform.
+#ifdef __LP64__
+#define FOLLY_SDT_ASM_ADDR            .8byte
+#else
+#define FOLLY_SDT_ASM_ADDR            .4byte
+#endif
+
+// Assembler helper Macros.
+#define FOLLY_SDT_S(x)                #x
+#define FOLLY_SDT_ASM_1(x)            FOLLY_SDT_S(x) "\n"
+#define FOLLY_SDT_ASM_2(a, b)         FOLLY_SDT_S(a) "," FOLLY_SDT_S(b) "\n"
+#define FOLLY_SDT_ASM_3(a, b, c)      FOLLY_SDT_S(a) "," FOLLY_SDT_S(b) ","    \
+                                      FOLLY_SDT_S(c) "\n"
+#define FOLLY_SDT_ASM_STRING(x)       FOLLY_SDT_ASM_1(.asciz FOLLY_SDT_S(x))
+
+// Helper to determine the size of an argument.
+#define FOLLY_SDT_ISARRAY(x)  (__builtin_classify_type(x) == 14)
+#define FOLLY_SDT_ARGSIZE(x)  (FOLLY_SDT_ISARRAY(x) ? sizeof(void*) : sizeof(x))
+
+// Format of each probe arguments as operand.
+// Size of the arugment tagged with FOLLY_SDT_Sn, with "n" constraint.
+// Value of the argument tagged with FOLLY_SDT_An, with configured constraint.
+#define FOLLY_SDT_ARG(n, x)                                                    \
+  [FOLLY_SDT_S##n] "n"                ((size_t)FOLLY_SDT_ARGSIZE(x)),          \
+  [FOLLY_SDT_A##n] FOLLY_SDT_ARG_CONSTRAINT (x)
+
+// Templates to append arguments as operands.
+#define FOLLY_SDT_OPERANDS_0()        [__sdt_dummy] "g" (0)
+#define FOLLY_SDT_OPERANDS_1(_1)      FOLLY_SDT_ARG(1, _1)
+#define FOLLY_SDT_OPERANDS_2(_1, _2)                                           \
+  FOLLY_SDT_OPERANDS_1(_1), FOLLY_SDT_ARG(2, _2)
+#define FOLLY_SDT_OPERANDS_3(_1, _2, _3)                                       \
+  FOLLY_SDT_OPERANDS_2(_1, _2), FOLLY_SDT_ARG(3, _3)
+#define FOLLY_SDT_OPERANDS_4(_1, _2, _3, _4)                                   \
+  FOLLY_SDT_OPERANDS_3(_1, _2, _3), FOLLY_SDT_ARG(4, _4)
+#define FOLLY_SDT_OPERANDS_5(_1, _2, _3, _4, _5)                               \
+  FOLLY_SDT_OPERANDS_4(_1, _2, _3, _4), FOLLY_SDT_ARG(5, _5)
+#define FOLLY_SDT_OPERANDS_6(_1, _2, _3, _4, _5, _6)                           \
+  FOLLY_SDT_OPERANDS_5(_1, _2, _3, _4, _5), FOLLY_SDT_ARG(6, _6)
+#define FOLLY_SDT_OPERANDS_7(_1, _2, _3, _4, _5, _6, _7)                       \
+  FOLLY_SDT_OPERANDS_6(_1, _2, _3, _4, _5, _6), FOLLY_SDT_ARG(7, _7)
+#define FOLLY_SDT_OPERANDS_8(_1, _2, _3, _4, _5, _6, _7, _8)                   \
+  FOLLY_SDT_OPERANDS_7(_1, _2, _3, _4, _5, _6, _7), FOLLY_SDT_ARG(8, _8)
+
+// Templates to reference the arguments from operands in note section.
+#define FOLLY_SDT_ARGFMT(no)        %n[FOLLY_SDT_S##no]@%[FOLLY_SDT_A##no]
+#define FOLLY_SDT_ARG_TEMPLATE_0    /*No arguments*/
+#define FOLLY_SDT_ARG_TEMPLATE_1    FOLLY_SDT_ARGFMT(1)
+#define FOLLY_SDT_ARG_TEMPLATE_2    FOLLY_SDT_ARG_TEMPLATE_1 FOLLY_SDT_ARGFMT(2)
+#define FOLLY_SDT_ARG_TEMPLATE_3    FOLLY_SDT_ARG_TEMPLATE_2 FOLLY_SDT_ARGFMT(3)
+#define FOLLY_SDT_ARG_TEMPLATE_4    FOLLY_SDT_ARG_TEMPLATE_3 FOLLY_SDT_ARGFMT(4)
+#define FOLLY_SDT_ARG_TEMPLATE_5    FOLLY_SDT_ARG_TEMPLATE_4 FOLLY_SDT_ARGFMT(5)
+#define FOLLY_SDT_ARG_TEMPLATE_6    FOLLY_SDT_ARG_TEMPLATE_5 FOLLY_SDT_ARGFMT(6)
+#define FOLLY_SDT_ARG_TEMPLATE_7    FOLLY_SDT_ARG_TEMPLATE_6 FOLLY_SDT_ARGFMT(7)
+#define FOLLY_SDT_ARG_TEMPLATE_8    FOLLY_SDT_ARG_TEMPLATE_7 FOLLY_SDT_ARGFMT(8)
+
+// Structure of note section for the probe.
+#define FOLLY_SDT_NOTE_CONTENT(provider, name, arg_template)                   \
+  FOLLY_SDT_ASM_1(990: FOLLY_SDT_NOP)                                          \
+  FOLLY_SDT_ASM_3(     .pushsection .note.stapsdt,"","note")                   \
+  FOLLY_SDT_ASM_1(     .balign 4)                                              \
+  FOLLY_SDT_ASM_3(     .4byte 992f-991f, 994f-993f, FOLLY_SDT_NOTE_TYPE)       \
+  FOLLY_SDT_ASM_1(991: .asciz FOLLY_SDT_NOTE_NAME)                             \
+  FOLLY_SDT_ASM_1(992: .balign 4)                                              \
+  FOLLY_SDT_ASM_1(993: FOLLY_SDT_ASM_ADDR 990b)                                \
+  FOLLY_SDT_ASM_1(     FOLLY_SDT_ASM_ADDR 0) /*Reserved for Semaphore address*/\
+  FOLLY_SDT_ASM_1(     FOLLY_SDT_ASM_ADDR 0) /*Reserved for Semaphore name*/   \
+  FOLLY_SDT_ASM_STRING(provider)                                               \
+  FOLLY_SDT_ASM_STRING(name)                                                   \
+  FOLLY_SDT_ASM_STRING(arg_template)                                           \
+  FOLLY_SDT_ASM_1(994: .balign 4)                                              \
+  FOLLY_SDT_ASM_1(     .popsection)
+
+// Main probe Macro.
+#define FOLLY_SDT_PROBE(provider, name, n, arglist)                            \
+    __asm__ __volatile__ (                                                     \
+      FOLLY_SDT_NOTE_CONTENT(provider, name, FOLLY_SDT_ARG_TEMPLATE_##n)       \
+      :: FOLLY_SDT_OPERANDS_##n arglist                                        \
+    )                                                                          \
+
+// Helper Macros to handle variadic arguments.
+#define FOLLY_SDT_NARG_(_0, _1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
+#define FOLLY_SDT_NARG(...)                                                    \
+  FOLLY_SDT_NARG_(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+#define FOLLY_SDT_PROBE_N(provider, name, N, ...)                              \
+  FOLLY_SDT_PROBE(provider, name, N, (__VA_ARGS__))
diff --git a/folly/tracing/StaticTracepoint.h b/folly/tracing/StaticTracepoint.h
new file mode 100644 (file)
index 0000000..a2fb1e3
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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
+ *
+ *   http://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.
+ */
+
+#pragma once
+
+#if defined(__ELF__) && (defined(__x86_64__) || defined(__i386__))
+#include <folly/tracing/StaticTracepoint-ELFx86.h>
+
+#define FOLLY_SDT(provider, name, ...)                                         \
+  FOLLY_SDT_PROBE_N(                                                           \
+    provider, name, FOLLY_SDT_NARG(0, ##__VA_ARGS__), ##__VA_ARGS__)
+#else
+#define FOLLY_SDT(provider, name, ...) do {} while(0)
+#endif
diff --git a/folly/tracing/test/StaticTracepointTest.cpp b/folly/tracing/test/StaticTracepointTest.cpp
new file mode 100644 (file)
index 0000000..9f5ef8a
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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
+ *
+ *   http://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.
+ */
+
+#include <algorithm>
+#include <array>
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <boost/filesystem.hpp>
+#include <folly/Bits.h>
+#include <folly/Conv.h>
+#include <folly/Format.h>
+#include <folly/Random.h>
+#include <folly/Shell.h>
+#include <folly/String.h>
+#include <folly/Subprocess.h>
+#include <folly/portability/GTest.h>
+#include <folly/portability/Unistd.h>
+#include <folly/tracing/StaticTracepoint.h>
+
+static const std::string kUSDTSubsectionName = FOLLY_SDT_NOTE_NAME;
+static const int kUSDTNoteType = FOLLY_SDT_NOTE_TYPE;
+static const size_t kAddrWidth = sizeof(void*);
+
+static uint8_t hexToInt(const std::string& hex) {
+  std::stringstream converter(hex);
+  int value;
+  converter >> std::hex >> value;
+  return uint8_t(value);
+}
+
+static int get4BytesValue(const std::vector<uint8_t>& v, size_t& pos) {
+  pos += 4;
+  return folly::Endian::little(folly::loadUnaligned<int>(v.data() + pos - 4));
+}
+
+static void align4Bytes(size_t& pos) {
+  if (pos % 4 != 0) {
+    pos += 4 - pos % 4;
+  }
+}
+
+static int getNextZero(
+    const std::vector<uint8_t>& v,
+    const size_t curPos,
+    const size_t limit) {
+  auto pos = std::find(v.begin() + curPos, v.begin() + limit, 0);
+  if (pos == v.begin() + limit) {
+    return -1;
+  }
+  return std::distance(v.begin(), pos);
+}
+
+static intptr_t getAddr(const std::vector<uint8_t>& v, size_t& pos) {
+  pos += kAddrWidth;
+  return folly::Endian::little(
+      folly::loadUnaligned<intptr_t>(v.data() + pos - kAddrWidth));
+}
+
+static std::string
+getStr(const std::vector<uint8_t>& v, size_t& pos, const size_t len) {
+  CHECK_GE(len, 1);
+  std::string res;
+  res.resize(len - 1);
+  for (size_t i = 0; i < len - 1; i++) {
+    CHECK_NE(v[pos + i], 0);
+    res[i] = char(v[pos + i]);
+  }
+  CHECK_EQ(0, v[pos + len - 1]);
+  pos += len;
+  return res;
+}
+
+static std::string getExe() {
+  auto path = folly::sformat("/proc/{}/exe", getpid());
+  return boost::filesystem::read_symlink(path).string();
+}
+
+static std::string getNoteRawContent(const std::string& fileName) {
+  auto args = folly::shellify(
+      "objdump --{} --{}={} {}",
+      "full-content",
+      "section",
+      ".note." + kUSDTSubsectionName,
+      fileName);
+  auto subProc = folly::Subprocess(args, folly::Subprocess::pipeStdout());
+  auto output = subProc.communicate();
+  auto retCode = subProc.wait();
+  CHECK(retCode.exited());
+  CHECK(output.second.empty());
+  return output.first;
+}
+
+static std::vector<uint8_t> readNote(const std::string& fileName) {
+  std::vector<uint8_t> res;
+  std::string rawContent = getNoteRawContent(fileName);
+  CHECK(!rawContent.empty());
+  // Strip out the part of output containing raw content, and split by line.
+  std::string contentStart =
+      "Contents of section .note." + kUSDTSubsectionName + ":";
+  auto pos = rawContent.find(contentStart);
+  CHECK_NE(pos, std::string::npos);
+  pos = rawContent.find("\n", pos + 1);
+  CHECK_NE(pos, std::string::npos);
+  rawContent = rawContent.substr(pos + 1);
+  std::vector<std::string> lines;
+  folly::split('\n', rawContent, lines, true);
+  CHECK_GT(lines.size(), 0);
+  // Parse each line.
+  for (auto line : lines) {
+    // Empty segments or ASCIIified content after two spaces.
+    auto endPos = line.find("  ");
+    CHECK_NE(endPos, std::string::npos);
+    line = line.substr(0, endPos);
+    std::vector<std::string> segments;
+    folly::split(' ', line, segments, true);
+    CHECK_GE(segments.size(), 2);
+    // First segment is address offset.
+    for (size_t i = 1; i < segments.size(); i++) {
+      CHECK_EQ(8, segments[i].size());
+      for (size_t j = 0; j < 8; j += 2) {
+        std::string hex = segments[i].substr(j, 2);
+        res.push_back(hexToInt(hex));
+      }
+    }
+  }
+  CHECK_EQ(0, res.size() % 4);
+  return res;
+}
+
+template <std::size_t SIZE>
+static void checkTracepointArguments(
+    const std::string& arguments,
+    std::array<int, SIZE>& expectedSize) {
+  std::vector<std::string> args;
+  folly::split(' ', arguments, args);
+  EXPECT_EQ(expectedSize.size(), args.size());
+  for (size_t i = 0; i < args.size(); i++) {
+    EXPECT_FALSE(args[i].empty());
+    auto pos = args[i].find("@");
+    EXPECT_NE(pos, std::string::npos);
+    EXPECT_LT(pos, args[i].size() - 1);
+    std::string argSize = args[i].substr(0, pos);
+    EXPECT_EQ(expectedSize[i], abs(folly::to<int>(argSize)));
+  }
+}
+
+/**
+ * This helper reads the .note.stapsdt section of the currently running binary,
+ * checks if the tracepoints listed there are properly formatted, and return the
+ * arguments layout description string for the expected provider and probe
+ * combination if it exists.
+ */
+static bool getTracepointArguments(
+    const std::string& expectedProvider,
+    const std::string& expectedProbe,
+    std::string& arguments) {
+  // Read the note and check if it's non-empty.
+  std::string exe = getExe();
+  auto note = readNote(exe);
+  auto len = note.size();
+  CHECK_GT(len, 0);
+  // The loop to read tracepoints one by one.
+  size_t pos = 0;
+  while (pos < len) {
+    // Check size information of the tracepoint.
+    CHECK_LE(pos + 12, len);
+
+    int headerSize = get4BytesValue(note, pos);
+    CHECK_EQ(kUSDTSubsectionName.size() + 1, headerSize);
+
+    int contentSize = get4BytesValue(note, pos);
+    size_t remaining = contentSize;
+    CHECK_GE(contentSize, kAddrWidth * 3);
+
+    int noteType = get4BytesValue(note, pos);
+    CHECK_EQ(kUSDTNoteType, noteType);
+
+    CHECK_LE(pos + headerSize + contentSize, len);
+
+    // Check header of the tracepoint.
+    std::string header = getStr(note, pos, headerSize);
+    CHECK_EQ(kUSDTSubsectionName, header);
+    align4Bytes(pos);
+
+    // Check address information of the tracepoint
+    intptr_t probeAddr = getAddr(note, pos);
+    CHECK_GT(probeAddr, 0);
+    remaining -= kAddrWidth;
+
+    intptr_t semaphoreAddr = getAddr(note, pos);
+    CHECK_EQ(0, semaphoreAddr);
+    remaining -= kAddrWidth;
+
+    intptr_t semaphoreBase = getAddr(note, pos);
+    CHECK_EQ(0, semaphoreBase);
+    remaining -= kAddrWidth;
+
+    // Read tracepoint provider, probe and argument layout description.
+    int providerEnd = getNextZero(note, pos, pos + remaining - 1);
+    CHECK_GE(providerEnd, 0);
+    size_t providerLen = providerEnd - pos + 1;
+    std::string provider = getStr(note, pos, providerLen);
+    remaining -= providerLen;
+
+    int probeEnd = getNextZero(note, pos, pos + remaining - 1);
+    CHECK_GE(probeEnd, 0);
+    size_t probeLen = probeEnd - pos + 1;
+    std::string probe = getStr(note, pos, probeLen);
+    remaining -= probeLen;
+
+    arguments = getStr(note, pos, remaining);
+    align4Bytes(pos);
+
+    if (provider == expectedProvider && probe == expectedProbe) {
+      return true;
+    }
+  }
+  return false;
+}
+
+static int arrayTestFunc() {
+  int v1 = folly::Random::rand32();
+  int v2 = folly::Random::rand32();
+  int64_t v3 = v1 + v2;
+  int a[4] = {v1, v2, v1, v2};
+  FOLLY_SDT(folly, test_static_tracepoint_array, a, v1, v3);
+  return v1 + v2;
+}
+
+TEST(StaticTracepoint, TestArray) {
+  arrayTestFunc();
+
+  std::string arguments;
+  ASSERT_TRUE(getTracepointArguments(
+      "folly", "test_static_tracepoint_array", arguments));
+  std::array<int, 3> expected{{sizeof(void*), sizeof(int), sizeof(int64_t)}};
+  checkTracepointArguments(arguments, expected);
+}
+
+static int pointerTestFunc() {
+  int v1 = folly::Random::rand32();
+  int v2 = folly::Random::rand32();
+  std::string str = "test string";
+  const char* a = str.c_str();
+  FOLLY_SDT(folly, test_static_tracepoint_pointer, a, v2, &v1);
+  return v1 + v2;
+}
+
+TEST(StaticTracepoint, TestPointer) {
+  pointerTestFunc();
+
+  std::string arguments;
+  ASSERT_TRUE(getTracepointArguments(
+      "folly", "test_static_tracepoint_array", arguments));
+  std::array<int, 3> expected{{sizeof(void*), sizeof(int), sizeof(void*)}};
+  checkTracepointArguments(arguments, expected);
+}
+
+static void emptyTestFunc() {
+  FOLLY_SDT(folly, test_static_tracepoint_empty);
+}
+
+TEST(StaticTracepoint, TestEmpty) {
+  emptyTestFunc();
+
+  std::string arguments;
+  ASSERT_TRUE(getTracepointArguments(
+      "folly", "test_static_tracepoint_empty", arguments));
+  EXPECT_TRUE(arguments.empty());
+}
+
+static int manyArgTypesTestFunc() {
+  uint32_t a = folly::Random::rand32();
+  uint32_t b = folly::Random::rand32();
+  bool bool_ = (a % 2) == (b % 2);
+  char char_ = a & 255;
+  short short_ = b & 32767;
+  long long_ = a;
+  float float_ = float(a) / float(b);
+  double double_ = double(a) / double(b);
+  FOLLY_SDT(
+      folly,
+      test_static_tracepoint_many_arg_types,
+      a,
+      b,
+      bool_,
+      char_,
+      short_,
+      long_,
+      float_,
+      double_);
+  return a + b;
+}
+
+TEST(StaticTracepoint, TestManyArgTypes) {
+  manyArgTypesTestFunc();
+
+  std::string arguments;
+  ASSERT_TRUE(getTracepointArguments(
+      "folly", "test_static_tracepoint_many_arg_types", arguments));
+  std::array<int, 8> expected{{
+      sizeof(uint32_t),
+      sizeof(uint32_t),
+      sizeof(bool),
+      sizeof(char),
+      sizeof(short),
+      sizeof(long),
+      sizeof(float),
+      sizeof(double),
+  }};
+  checkTracepointArguments(arguments, expected);
+}
+
+FOLLY_ALWAYS_INLINE static int alwaysInlineTestFunc() {
+  uint32_t a = folly::Random::rand32();
+  uint32_t b = folly::Random::rand32();
+  FOLLY_SDT(folly, test_static_tracepoint_always_inline, a, b);
+  return a + b;
+}
+
+TEST(StaticTracepoint, TestAlwaysInline) {
+  alwaysInlineTestFunc();
+
+  std::string arguments;
+  ASSERT_TRUE(getTracepointArguments(
+      "folly", "test_static_tracepoint_always_inline", arguments));
+  std::array<int, 2> expected{{sizeof(uint32_t), sizeof(uint32_t)}};
+  checkTracepointArguments(arguments, expected);
+}
+
+static void branchTestFunc() {
+  uint32_t a = folly::Random::rand32();
+  uint32_t b = folly::Random::rand32();
+  if (a > b) {
+    FOLLY_SDT(folly, test_static_tracepoint_branch_1, a / b);
+  } else {
+    FOLLY_SDT(folly, test_static_tracepoint_branch_2, double(a) / double(b));
+  }
+}
+
+TEST(StaticTracepoint, TestBranch) {
+  branchTestFunc();
+
+  std::string arguments1;
+  ASSERT_TRUE(getTracepointArguments(
+      "folly", "test_static_tracepoint_branch_1", arguments1));
+  std::array<int, 1> expected1{{sizeof(uint32_t)}};
+  checkTracepointArguments(arguments1, expected1);
+
+  std::string arguments2;
+  ASSERT_TRUE(getTracepointArguments(
+      "folly", "test_static_tracepoint_branch_2", arguments2));
+  std::array<int, 1> expected2{{sizeof(double)}};
+  checkTracepointArguments(arguments2, expected2);
+}
+
+struct testStruct {
+  int a;
+  int64_t b;
+  char c[32];
+};
+
+static void structTestFunc() {
+  testStruct s, t;
+  s.a = folly::Random::rand32();
+  s.b = folly::Random::rand32();
+  t.a = folly::Random::rand32();
+  t.b = folly::Random::rand32();
+  FOLLY_SDT(folly, test_static_tracepoint_struct, s, t);
+}
+
+TEST(StaticTracepoint, TestStruct) {
+  structTestFunc();
+
+  std::string arguments;
+  ASSERT_TRUE(getTracepointArguments(
+      "folly", "test_static_tracepoint_struct", arguments));
+  std::array<int, 2> expected{{sizeof(testStruct), sizeof(testStruct)}};
+  checkTracepointArguments(arguments, expected);
+}