Commit cfaf60f0 authored by Teng Qin's avatar Teng Qin Committed by Facebook Github Bot

Add Semaphore support for folly's Static Tracepoint

Summary:
This Diff adds new `FOLLY_SDT_WITH_SEMAPHORE` Macro that allows the (potentially expensive) Static Tracepoints be gated with Semaphore and be enabled on-demand.
The Semaphores are compatible with SystemTap's Tracepoint Semaphores, with the following difference:
- Instead of having build Process generate a header file containing the Semaphore variable and check function, and to be included, this Diff uses the approach that users need to explicitly define the semaphore using `FOLLY_SDT_SEMAPHORE_DEFINE` before using `FOLLY_SDT_WITH_SEMAPHORE`. Then the check function could be used in the same file, or in any other linked module with doing `FOLLY_SDT_SEMAPHORE_DECLARE` first. This is inspired from GFlag's defining and declaring.
- We keep allowing original `FOLLY_SDT` with no Semaphore for flexibility.

Differential Revision: D6814268

fbshipit-source-id: e1e4463c71539b4d2071b21880d4ee7689fedce8
parent 24d7e260
...@@ -42,3 +42,25 @@ such case. You may also choose to override the constraint ...@@ -42,3 +42,25 @@ such case. You may also choose to override the constraint
#define FOLLY_SDT_ARG_CONSTRAINT "g" #define FOLLY_SDT_ARG_CONSTRAINT "g"
``` ```
which means the arguments can be any memory or register operands. which means the arguments can be any memory or register operands.
You could also use
```
FOLLY_SDT_WITH_SEMAPHORE(provider, name, arg1, arg2, ...)
```
to create Tracepoints that could be gated on-demand. Before using this, add
```
FOLLY_SDT_DEFINE_SEMAPHORE(provider, name)
```
anywhere outside a local function scope. Then, you could use
```
FOLLY_SDT_IS_ENABLED(provider, name)
```
to check if probing on this Tracepoint is enabled. Tools like BCC and SystemTap
would increase the Semaphore when attaching to the Tracepoint and have the
check return true. This allows the Tracepoint to use more expensive arguments
which would only be calculated when needed. You can also check the enabled
status of the Tracepoint in another module. Add
```
FOLLY_SDT_DECLARE_SEMAPHORE(provider, name)
```
anywhere outside a local function scope first, then call the check Macro.
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#pragma once #pragma once
// clang-format off // clang-format off
#include <cstddef>
// Default constraint for the probe arguments as operands. // Default constraint for the probe arguments as operands.
#ifndef FOLLY_SDT_ARG_CONSTRAINT #ifndef FOLLY_SDT_ARG_CONSTRAINT
...@@ -86,8 +87,27 @@ ...@@ -86,8 +87,27 @@
#define FOLLY_SDT_ARG_TEMPLATE_7 FOLLY_SDT_ARG_TEMPLATE_6 FOLLY_SDT_ARGFMT(7) #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) #define FOLLY_SDT_ARG_TEMPLATE_8 FOLLY_SDT_ARG_TEMPLATE_7 FOLLY_SDT_ARGFMT(8)
// Semaphore define, declare and probe note format
#define FOLLY_SDT_SEMAPHORE(provider, name) \
folly_sdt_semaphore_##provider##_##name
#define FOLLY_SDT_DEFINE_SEMAPHORE(provider, name) \
extern "C" { \
volatile unsigned short FOLLY_SDT_SEMAPHORE(provider, name) = 0; \
}
#define FOLLY_SDT_DECLARE_SEMAPHORE(provider, name) \
extern "C" volatile unsigned short FOLLY_SDT_SEMAPHORE(provider, name)
#define FOLLY_SDT_SEMAPHORE_NOTE_0(provider, name) \
FOLLY_SDT_ASM_1( FOLLY_SDT_ASM_ADDR 0) /*No Semaphore*/ \
#define FOLLY_SDT_SEMAPHORE_NOTE_1(provider, name) \
FOLLY_SDT_ASM_1(FOLLY_SDT_ASM_ADDR FOLLY_SDT_SEMAPHORE(provider, name))
// Structure of note section for the probe. // Structure of note section for the probe.
#define FOLLY_SDT_NOTE_CONTENT(provider, name, arg_template) \ #define FOLLY_SDT_NOTE_CONTENT(provider, name, has_semaphore, arg_template) \
FOLLY_SDT_ASM_1(990: FOLLY_SDT_NOP) \ FOLLY_SDT_ASM_1(990: FOLLY_SDT_NOP) \
FOLLY_SDT_ASM_3( .pushsection .note.stapsdt,"","note") \ FOLLY_SDT_ASM_3( .pushsection .note.stapsdt,"","note") \
FOLLY_SDT_ASM_1( .balign 4) \ FOLLY_SDT_ASM_1( .balign 4) \
...@@ -95,8 +115,8 @@ ...@@ -95,8 +115,8 @@
FOLLY_SDT_ASM_1(991: .asciz FOLLY_SDT_NOTE_NAME) \ FOLLY_SDT_ASM_1(991: .asciz FOLLY_SDT_NOTE_NAME) \
FOLLY_SDT_ASM_1(992: .balign 4) \ FOLLY_SDT_ASM_1(992: .balign 4) \
FOLLY_SDT_ASM_1(993: FOLLY_SDT_ASM_ADDR 990b) \ 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 Base Address*/ \
FOLLY_SDT_ASM_1( FOLLY_SDT_ASM_ADDR 0) /*Reserved for Semaphore name*/ \ FOLLY_SDT_SEMAPHORE_NOTE_##has_semaphore(provider, name) \
FOLLY_SDT_ASM_STRING(provider) \ FOLLY_SDT_ASM_STRING(provider) \
FOLLY_SDT_ASM_STRING(name) \ FOLLY_SDT_ASM_STRING(name) \
FOLLY_SDT_ASM_STRING(arg_template) \ FOLLY_SDT_ASM_STRING(arg_template) \
...@@ -104,9 +124,10 @@ ...@@ -104,9 +124,10 @@
FOLLY_SDT_ASM_1( .popsection) FOLLY_SDT_ASM_1( .popsection)
// Main probe Macro. // Main probe Macro.
#define FOLLY_SDT_PROBE(provider, name, n, arglist) \ #define FOLLY_SDT_PROBE(provider, name, has_semaphore, n, arglist) \
__asm__ __volatile__ ( \ __asm__ __volatile__ ( \
FOLLY_SDT_NOTE_CONTENT(provider, name, FOLLY_SDT_ARG_TEMPLATE_##n) \ FOLLY_SDT_NOTE_CONTENT( \
provider, name, has_semaphore, FOLLY_SDT_ARG_TEMPLATE_##n) \
:: FOLLY_SDT_OPERANDS_##n arglist \ :: FOLLY_SDT_OPERANDS_##n arglist \
) \ ) \
...@@ -114,5 +135,5 @@ ...@@ -114,5 +135,5 @@
#define FOLLY_SDT_NARG_(_0, _1, _2, _3, _4, _5, _6, _7, _8, N, ...) N #define FOLLY_SDT_NARG_(_0, _1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
#define FOLLY_SDT_NARG(...) \ #define FOLLY_SDT_NARG(...) \
FOLLY_SDT_NARG_(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0) FOLLY_SDT_NARG_(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define FOLLY_SDT_PROBE_N(provider, name, N, ...) \ #define FOLLY_SDT_PROBE_N(provider, name, has_semaphore, N, ...) \
FOLLY_SDT_PROBE(provider, name, N, (__VA_ARGS__)) FOLLY_SDT_PROBE(provider, name, has_semaphore, N, (__VA_ARGS__))
...@@ -17,13 +17,28 @@ ...@@ -17,13 +17,28 @@
#pragma once #pragma once
#if defined(__ELF__) && (defined(__x86_64__) || defined(__i386__)) #if defined(__ELF__) && (defined(__x86_64__) || defined(__i386__))
#include <folly/tracing/StaticTracepoint-ELFx86.h> #include <folly/tracing/StaticTracepoint-ELFx86.h>
#define FOLLY_SDT(provider, name, ...) \ #define FOLLY_SDT(provider, name, ...) \
FOLLY_SDT_PROBE_N( \ FOLLY_SDT_PROBE_N( \
provider, name, FOLLY_SDT_NARG(0, ##__VA_ARGS__), ##__VA_ARGS__) provider, name, 0, FOLLY_SDT_NARG(0, ##__VA_ARGS__), ##__VA_ARGS__)
// Use FOLLY_SDT_DEFINE_SEMAPHORE(provider, name) to define the semaphore
// as global variable before using the FOLLY_SDT_WITH_SEMAPHORE macro
#define FOLLY_SDT_WITH_SEMAPHORE(provider, name, ...) \
FOLLY_SDT_PROBE_N( \
provider, name, 1, FOLLY_SDT_NARG(0, ##__VA_ARGS__), ##__VA_ARGS__)
#define FOLLY_SDT_IS_ENABLED(provider, name) \
(FOLLY_SDT_SEMAPHORE(provider, name) > 0)
#else #else
#define FOLLY_SDT(provider, name, ...) \ #define FOLLY_SDT(provider, name, ...) \
do { \ do { \
} while (0) } while (0)
#define FOLLY_SDT_WITH_SEMAPHORE(provider, name, ...) \
do { \
} while (0)
#define FOLLY_SDT_IS_ENABLED(provider, name) (false)
#endif #endif
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
#include <folly/portability/Unistd.h> #include <folly/portability/Unistd.h>
#include <folly/tracing/StaticTracepoint.h> #include <folly/tracing/StaticTracepoint.h>
#include <folly/tracing/test/StaticTracepointTestModule.h>
static const std::string kUSDTSubsectionName = FOLLY_SDT_NOTE_NAME; static const std::string kUSDTSubsectionName = FOLLY_SDT_NOTE_NAME;
static const int kUSDTNoteType = FOLLY_SDT_NOTE_TYPE; static const int kUSDTNoteType = FOLLY_SDT_NOTE_TYPE;
...@@ -169,6 +170,7 @@ static void checkTracepointArguments( ...@@ -169,6 +170,7 @@ static void checkTracepointArguments(
static bool getTracepointArguments( static bool getTracepointArguments(
const std::string& expectedProvider, const std::string& expectedProvider,
const std::string& expectedProbe, const std::string& expectedProbe,
const uintptr_t expectedSemaphore,
std::string& arguments) { std::string& arguments) {
// Read the note and check if it's non-empty. // Read the note and check if it's non-empty.
std::string exe = getExe(); std::string exe = getExe();
...@@ -203,12 +205,11 @@ static bool getTracepointArguments( ...@@ -203,12 +205,11 @@ static bool getTracepointArguments(
CHECK_GT(probeAddr, 0); CHECK_GT(probeAddr, 0);
remaining -= kAddrWidth; remaining -= kAddrWidth;
intptr_t semaphoreAddr = getAddr(note, pos); intptr_t baseAddr = getAddr(note, pos);
CHECK_EQ(0, semaphoreAddr); CHECK_EQ(0, baseAddr);
remaining -= kAddrWidth; remaining -= kAddrWidth;
intptr_t semaphoreBase = getAddr(note, pos); intptr_t semaphoreAddr = getAddr(note, pos);
CHECK_EQ(0, semaphoreBase);
remaining -= kAddrWidth; remaining -= kAddrWidth;
// Read tracepoint provider, probe and argument layout description. // Read tracepoint provider, probe and argument layout description.
...@@ -228,6 +229,7 @@ static bool getTracepointArguments( ...@@ -228,6 +229,7 @@ static bool getTracepointArguments(
align4Bytes(pos); align4Bytes(pos);
if (provider == expectedProvider && probe == expectedProbe) { if (provider == expectedProvider && probe == expectedProbe) {
CHECK_EQ(expectedSemaphore, semaphoreAddr);
return true; return true;
} }
} }
...@@ -248,7 +250,7 @@ TEST(StaticTracepoint, TestArray) { ...@@ -248,7 +250,7 @@ TEST(StaticTracepoint, TestArray) {
std::string arguments; std::string arguments;
ASSERT_TRUE(getTracepointArguments( ASSERT_TRUE(getTracepointArguments(
"folly", "test_static_tracepoint_array", arguments)); "folly", "test_static_tracepoint_array", 0, arguments));
std::array<int, 3> expected{{sizeof(void*), sizeof(int), sizeof(int64_t)}}; std::array<int, 3> expected{{sizeof(void*), sizeof(int), sizeof(int64_t)}};
checkTracepointArguments(arguments, expected); checkTracepointArguments(arguments, expected);
} }
...@@ -267,7 +269,7 @@ TEST(StaticTracepoint, TestPointer) { ...@@ -267,7 +269,7 @@ TEST(StaticTracepoint, TestPointer) {
std::string arguments; std::string arguments;
ASSERT_TRUE(getTracepointArguments( ASSERT_TRUE(getTracepointArguments(
"folly", "test_static_tracepoint_array", arguments)); "folly", "test_static_tracepoint_array", 0, arguments));
std::array<int, 3> expected{{sizeof(void*), sizeof(int), sizeof(void*)}}; std::array<int, 3> expected{{sizeof(void*), sizeof(int), sizeof(void*)}};
checkTracepointArguments(arguments, expected); checkTracepointArguments(arguments, expected);
} }
...@@ -281,10 +283,12 @@ TEST(StaticTracepoint, TestEmpty) { ...@@ -281,10 +283,12 @@ TEST(StaticTracepoint, TestEmpty) {
std::string arguments; std::string arguments;
ASSERT_TRUE(getTracepointArguments( ASSERT_TRUE(getTracepointArguments(
"folly", "test_static_tracepoint_empty", arguments)); "folly", "test_static_tracepoint_empty", 0, arguments));
EXPECT_TRUE(arguments.empty()); EXPECT_TRUE(arguments.empty());
} }
FOLLY_SDT_DEFINE_SEMAPHORE(folly, test_semaphore_local);
static uint32_t manyArgTypesTestFunc() { static uint32_t manyArgTypesTestFunc() {
uint32_t a = folly::Random::rand32(); uint32_t a = folly::Random::rand32();
uint32_t b = folly::Random::rand32(); uint32_t b = folly::Random::rand32();
...@@ -305,6 +309,7 @@ static uint32_t manyArgTypesTestFunc() { ...@@ -305,6 +309,7 @@ static uint32_t manyArgTypesTestFunc() {
long_, long_,
float_, float_,
double_); double_);
FOLLY_SDT_WITH_SEMAPHORE(folly, test_semaphore_local, long_, short_);
return a + b; return a + b;
} }
...@@ -313,7 +318,7 @@ TEST(StaticTracepoint, TestManyArgTypes) { ...@@ -313,7 +318,7 @@ TEST(StaticTracepoint, TestManyArgTypes) {
std::string arguments; std::string arguments;
ASSERT_TRUE(getTracepointArguments( ASSERT_TRUE(getTracepointArguments(
"folly", "test_static_tracepoint_many_arg_types", arguments)); "folly", "test_static_tracepoint_many_arg_types", 0, arguments));
std::array<int, 8> expected{{ std::array<int, 8> expected{{
sizeof(uint32_t), sizeof(uint32_t),
sizeof(uint32_t), sizeof(uint32_t),
...@@ -339,7 +344,7 @@ TEST(StaticTracepoint, TestAlwaysInline) { ...@@ -339,7 +344,7 @@ TEST(StaticTracepoint, TestAlwaysInline) {
std::string arguments; std::string arguments;
ASSERT_TRUE(getTracepointArguments( ASSERT_TRUE(getTracepointArguments(
"folly", "test_static_tracepoint_always_inline", arguments)); "folly", "test_static_tracepoint_always_inline", 0, arguments));
std::array<int, 2> expected{{sizeof(uint32_t), sizeof(uint32_t)}}; std::array<int, 2> expected{{sizeof(uint32_t), sizeof(uint32_t)}};
checkTracepointArguments(arguments, expected); checkTracepointArguments(arguments, expected);
} }
...@@ -359,13 +364,13 @@ TEST(StaticTracepoint, TestBranch) { ...@@ -359,13 +364,13 @@ TEST(StaticTracepoint, TestBranch) {
std::string arguments1; std::string arguments1;
ASSERT_TRUE(getTracepointArguments( ASSERT_TRUE(getTracepointArguments(
"folly", "test_static_tracepoint_branch_1", arguments1)); "folly", "test_static_tracepoint_branch_1", 0, arguments1));
std::array<int, 1> expected1{{sizeof(uint32_t)}}; std::array<int, 1> expected1{{sizeof(uint32_t)}};
checkTracepointArguments(arguments1, expected1); checkTracepointArguments(arguments1, expected1);
std::string arguments2; std::string arguments2;
ASSERT_TRUE(getTracepointArguments( ASSERT_TRUE(getTracepointArguments(
"folly", "test_static_tracepoint_branch_2", arguments2)); "folly", "test_static_tracepoint_branch_2", 0, arguments2));
std::array<int, 1> expected2{{sizeof(double)}}; std::array<int, 1> expected2{{sizeof(double)}};
checkTracepointArguments(arguments2, expected2); checkTracepointArguments(arguments2, expected2);
} }
...@@ -390,7 +395,29 @@ TEST(StaticTracepoint, TestStruct) { ...@@ -390,7 +395,29 @@ TEST(StaticTracepoint, TestStruct) {
std::string arguments; std::string arguments;
ASSERT_TRUE(getTracepointArguments( ASSERT_TRUE(getTracepointArguments(
"folly", "test_static_tracepoint_struct", arguments)); "folly", "test_static_tracepoint_struct", 0, arguments));
std::array<int, 2> expected{{sizeof(testStruct), sizeof(testStruct)}}; std::array<int, 2> expected{{sizeof(testStruct), sizeof(testStruct)}};
checkTracepointArguments(arguments, expected); checkTracepointArguments(arguments, expected);
} }
TEST(StaticTracepoint, TestSemaphoreLocal) {
manyArgTypesTestFunc();
std::string arguments;
ASSERT_TRUE(getTracepointArguments(
"folly",
"test_semaphore_local",
(uintptr_t)((void*)&FOLLY_SDT_SEMAPHORE(folly, test_semaphore_local)),
arguments));
std::array<int, 2> expected{{sizeof(long), sizeof(short)}};
checkTracepointArguments(arguments, expected);
EXPECT_FALSE(FOLLY_SDT_IS_ENABLED(folly, test_semaphore_local));
}
FOLLY_SDT_DECLARE_SEMAPHORE(folly, test_semaphore_extern);
TEST(StaticTracepoint, TestSemaphoreExtern) {
int v = folly::Random::rand32();
CHECK_EQ(v * v, folly::test::staticTracepointTestFunc(v));
EXPECT_FALSE(FOLLY_SDT_IS_ENABLED(folly, test_semaphore_extern));
}
/*
* Copyright 2017-present 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 <folly/tracing/StaticTracepoint.h>
namespace folly {
namespace test {
FOLLY_SDT_DEFINE_SEMAPHORE(folly, test_semaphore_extern);
int staticTracepointTestFunc(int v) {
int res = v * v;
FOLLY_SDT_WITH_SEMAPHORE(folly, test_semaphore_extern, v, res);
return res;
}
} // namespace test
} // namespace folly
/*
* Copyright 2017-present 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
namespace folly {
namespace test {
int staticTracepointTestFunc(int v);
}
} // namespace folly
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment