From: Teng Qin Date: Wed, 23 Nov 2016 22:05:39 +0000 (-0800) Subject: Add USDT header file to folly X-Git-Tag: v2016.11.28.00~7 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=f405f9debfe220a065c88a7377fbde25c8063424;p=folly.git Add USDT header file to folly 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 --- diff --git a/folly/tracing/README.md b/folly/tracing/README.md new file mode 100644 index 00000000..8df5aef5 --- /dev/null +++ b/folly/tracing/README.md @@ -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 index 00000000..446a5b47 --- /dev/null +++ b/folly/tracing/StaticTracepoint-ELFx86.h @@ -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 index 00000000..a2fb1e37 --- /dev/null +++ b/folly/tracing/StaticTracepoint.h @@ -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 + +#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 index 00000000..9f5ef8ae --- /dev/null +++ b/folly/tracing/test/StaticTracepointTest.cpp @@ -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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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& v, size_t& pos) { + pos += 4; + return folly::Endian::little(folly::loadUnaligned(v.data() + pos - 4)); +} + +static void align4Bytes(size_t& pos) { + if (pos % 4 != 0) { + pos += 4 - pos % 4; + } +} + +static int getNextZero( + const std::vector& 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& v, size_t& pos) { + pos += kAddrWidth; + return folly::Endian::little( + folly::loadUnaligned(v.data() + pos - kAddrWidth)); +} + +static std::string +getStr(const std::vector& 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 readNote(const std::string& fileName) { + std::vector 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 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 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 +static void checkTracepointArguments( + const std::string& arguments, + std::array& expectedSize) { + std::vector 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(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 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 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 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 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 expected1{{sizeof(uint32_t)}}; + checkTracepointArguments(arguments1, expected1); + + std::string arguments2; + ASSERT_TRUE(getTracepointArguments( + "folly", "test_static_tracepoint_branch_2", arguments2)); + std::array 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 expected{{sizeof(testStruct), sizeof(testStruct)}}; + checkTracepointArguments(arguments, expected); +}