--- /dev/null
+/*
+ * 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__))
--- /dev/null
+/*
+ * 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);
+}