diff --git a/.bpftrace.metadata b/.bpftrace.metadata new file mode 100644 index 0000000..ceead33 --- /dev/null +++ b/.bpftrace.metadata @@ -0,0 +1 @@ +a6409319efe13b553b05b8343077e2976b03a951 SOURCES/bpftrace-0.9.tar.gz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6d7a989 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/bpftrace-0.9.tar.gz diff --git a/SOURCES/bpftrace-0.9-Ban-kprobes-that-cause-CPU-deadlocks.patch b/SOURCES/bpftrace-0.9-Ban-kprobes-that-cause-CPU-deadlocks.patch new file mode 100644 index 0000000..a3d8322 --- /dev/null +++ b/SOURCES/bpftrace-0.9-Ban-kprobes-that-cause-CPU-deadlocks.patch @@ -0,0 +1,83 @@ +From 40cf19078e0f5aca77bb53a17ab2913d1c58417d Mon Sep 17 00:00:00 2001 +From: Javier Honduvilla Coto +Date: Thu, 11 Apr 2019 18:59:23 +0100 +Subject: [PATCH] Ban kprobes that cause CPU deadlocks + +--- + src/attached_probe.cpp | 18 ++++++++++++++++++ + tests/runtime/banned_probes | 19 +++++++++++++++++++ + 2 files changed, 37 insertions(+) + create mode 100644 tests/runtime/banned_probes + +diff --git a/src/attached_probe.cpp b/src/attached_probe.cpp +index f83634a..073be78 100644 +--- a/src/attached_probe.cpp ++++ b/src/attached_probe.cpp +@@ -24,6 +24,15 @@ + namespace bpftrace { + + const int BPF_LOG_SIZE = 100 * 1024; ++/* ++ * Kernel functions that are unsafe to trace are excluded in the Kernel with ++ * `notrace`. However, the ones below are not excluded. ++ */ ++const std::set banned_kretprobes = { ++ "_raw_spin_lock", "_raw_spin_lock_irqsave", "_raw_spin_unlock_irqrestore", ++ "queued_spin_lock_slowpath", ++}; ++ + + bpf_probe_attach_type attachtype(ProbeType t) + { +@@ -60,6 +69,12 @@ bpf_prog_type progtype(ProbeType t) + } + } + ++void check_banned_kretprobes(std::string const& kprobe_name) { ++ if (banned_kretprobes.find(kprobe_name) != banned_kretprobes.end()) { ++ std::cerr << "error: kretprobe:" << kprobe_name << " can't be used as it might lock up your system." << std::endl; ++ exit(1); ++ } ++} + + AttachedProbe::AttachedProbe(Probe &probe, std::tuple func) + : probe_(probe), func_(func) +@@ -70,7 +85,10 @@ AttachedProbe::AttachedProbe(Probe &probe, std::tuple func + switch (probe_.type) + { + case ProbeType::kprobe: ++ attach_kprobe(); ++ break; + case ProbeType::kretprobe: ++ check_banned_kretprobes(probe_.attach_point); + attach_kprobe(); + break; + case ProbeType::uprobe: +diff --git a/tests/runtime/banned_probes b/tests/runtime/banned_probes +new file mode 100644 +index 0000000..e892bd2 +--- /dev/null ++++ b/tests/runtime/banned_probes +@@ -0,0 +1,19 @@ ++NAME kretprobe:_raw_spin_lock is banned ++RUN bpftrace -e 'kretprobe:_raw_spin_lock { exit(); }' ++EXPECT error: kretprobe:_raw_spin_lock can't be used as it might lock up your system. ++TIMEOUT 1 ++ ++NAME kretprobe:queued_spin_lock_slowpath is banned ++RUN bpftrace -e 'kretprobe:queued_spin_lock_slowpath { exit(); }' ++EXPECT error: kretprobe:queued_spin_lock_slowpath can't be used as it might lock up your system. ++TIMEOUT 1 ++ ++NAME kretprobe:_raw_spin_unlock_irqrestore is banned ++RUN bpftrace -e 'kretprobe:_raw_spin_unlock_irqrestore { exit(); }' ++EXPECT error: kretprobe:_raw_spin_unlock_irqrestore can't be used as it might lock up your system. ++TIMEOUT 1 ++ ++NAME kretprobe:_raw_spin_lock_irqsave is banned ++RUN bpftrace -e 'kretprobe:_raw_spin_lock_irqsave { exit(); }' ++EXPECT error: kretprobe:_raw_spin_lock_irqsave can't be used as it might lock up your system. ++TIMEOUT 1 +-- +2.20.1 + diff --git a/SOURCES/bpftrace-0.9-RHEL-8-fixes.patch b/SOURCES/bpftrace-0.9-RHEL-8-fixes.patch new file mode 100644 index 0000000..b15f98a --- /dev/null +++ b/SOURCES/bpftrace-0.9-RHEL-8-fixes.patch @@ -0,0 +1,43 @@ +From 1dce61acfec57712f84cfdf2a8f4c69d27744b04 Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Tue, 11 Jun 2019 16:41:59 +0200 +Subject: RHEL 8 fixes + +Fixes the following RHEL 8 specific issues: + - library path in gethostlatency + +--- + tools/gethostlatency.bt | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/tools/gethostlatency.bt b/tools/gethostlatency.bt +index a1ac1b2..ade1005 100755 +--- a/tools/gethostlatency.bt ++++ b/tools/gethostlatency.bt +@@ -26,17 +26,17 @@ BEGIN + "HOST"); + } + +-uprobe:/lib/x86_64-linux-gnu/libc.so.6:getaddrinfo, +-uprobe:/lib/x86_64-linux-gnu/libc.so.6:gethostbyname, +-uprobe:/lib/x86_64-linux-gnu/libc.so.6:gethostbyname2 ++uprobe:/lib64/libc.so.6:getaddrinfo, ++uprobe:/lib64/libc.so.6:gethostbyname, ++uprobe:/lib64/libc.so.6:gethostbyname2 + { + @start[tid] = nsecs; + @name[tid] = arg0; + } + +-uretprobe:/lib/x86_64-linux-gnu/libc.so.6:getaddrinfo, +-uretprobe:/lib/x86_64-linux-gnu/libc.so.6:gethostbyname, +-uretprobe:/lib/x86_64-linux-gnu/libc.so.6:gethostbyname2 ++uretprobe:/lib64/libc.so.6:getaddrinfo, ++uretprobe:/lib64/libc.so.6:gethostbyname, ++uretprobe:/lib64/libc.so.6:gethostbyname2 + /@start[tid]/ + { + $latms = (nsecs - @start[tid]) / 1000000; +-- +2.20.1 + diff --git a/SOURCES/bpftrace-0.9-clang_parser-fix-clang_parser-for-LLVM-8.patch b/SOURCES/bpftrace-0.9-clang_parser-fix-clang_parser-for-LLVM-8.patch new file mode 100644 index 0000000..2b71fda --- /dev/null +++ b/SOURCES/bpftrace-0.9-clang_parser-fix-clang_parser-for-LLVM-8.patch @@ -0,0 +1,255 @@ +From acb0ee316d5ed776253b6d7bfccfb21e0005919b Mon Sep 17 00:00:00 2001 +From: Jerome Marchand +Date: Thu, 16 May 2019 14:56:50 +0200 +Subject: [PATCH] fix clang_parser for LLVM 8+ + +LLVM changed the behavior of clang_Cursor_isAnonymous in [1]. The old +behavior would returned false for direct-acccess anonymous structs +within other structs, whereas LLVM 8 returns true. To circumvent this +behavior change among LLVM versions, we keep track of all structs +defined within a struct. We don't parse the substruct recursively (if we +do it might be parsed multiple times, and since we don't know yet if +this is a direct or indirect strucutre, we might parse it incorrectly), +instead we keep the cursor saved in a map. If this substruct is later +declared as an attribute of the supersctruct, that means we have a +direct-accessible struct. We remove it from our map, and we parse +recursively (parsing recursively the cursor pointing to the declaration +will effectively parse the struct definition). + +After the first parse, any struct left in our map is an indirect struct. +Since we skipped the parsing stage for those, we need to run +`clang_visitChildren` again for each struct cursor we have saved. We +repeat this until there's no unvisited structs in the map. Keep in mind +that while visiting a new struct we might find more indirect structs. + +Also add Travis jobs to test against LLVM and clang 8 on Ubuntu. + +[1]: llvm/llvm-project@c05e6f4 +--- + .travis.yml | 4 ++ + docker/Dockerfile.bionic-llvm8 | 36 ++++++++++++++ + src/bpforc.h | 9 ++++ + src/clang_parser.cpp | 87 +++++++++++++++++++++++----------- + 4 files changed, 108 insertions(+), 28 deletions(-) + create mode 100644 docker/Dockerfile.bionic-llvm8 + +diff --git a/.travis.yml b/.travis.yml +index 7fbff63..b539868 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -20,6 +20,10 @@ sudo: required + env: BASE=fedora29 TYPE=Debug + - name: "LLVM 7 Release" + env: BASE=fedora29 TYPE=Release ++ - name: "LLVM 8 Debug" ++ env: BASE=bionic-llvm8 TYPE=Debug ++ - name: "LLVM 8 Release" ++ env: BASE=bionic-llvm8 TYPE=Release + allow_failures: + - name: "Static LLVM 5 Debug" + env: BASE=alpine TYPE=Debug STATIC_LINKING=ON TEST_ARGS="--gtest_filter=-codegen.string_equal_comparison:codegen.string_not_equal_comparison" +diff --git a/docker/Dockerfile.bionic-llvm8 b/docker/Dockerfile.bionic-llvm8 +new file mode 100644 +index 0000000..ebf10d3 +--- /dev/null ++++ b/docker/Dockerfile.bionic-llvm8 +@@ -0,0 +1,36 @@ ++FROM ubuntu:bionic ++ ++ENV llvmVersion=8 ++ ++RUN apt-get update && apt-get install -y curl gnupg &&\ ++ llvmRepository='\n\ ++deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic main\n\ ++deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic main\n\ ++# 8\n\ ++deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main\n\ ++deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main' && \ ++ echo $llvmRepository >> /etc/apt/sources.list && \ ++ curl -L https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \ ++ apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD && \ ++ echo "deb https://repo.iovisor.org/apt/bionic bionic main" | tee /etc/apt/sources.list.d/iovisor.list ++ ++RUN apt-get update && apt-get install -y \ ++ bison \ ++ cmake \ ++ flex \ ++ g++ \ ++ git \ ++ libelf-dev \ ++ zlib1g-dev \ ++ libbcc \ ++ clang-${llvmVersion} \ ++ libclang-${llvmVersion}-dev \ ++ libclang-common-${llvmVersion}-dev \ ++ libclang1-${llvmVersion} \ ++ llvm-${llvmVersion} \ ++ llvm-${llvmVersion}-dev \ ++ llvm-${llvmVersion}-runtime \ ++ libllvm${llvmVersion} ++ ++COPY build.sh /build.sh ++ENTRYPOINT ["bash", "/build.sh"] +diff --git a/src/bpforc.h b/src/bpforc.h +index 1c134d0..8eede31 100644 +--- a/src/bpforc.h ++++ b/src/bpforc.h +@@ -80,8 +80,13 @@ class BpfOrc + ExecutionSession ES; + std::unique_ptr TM; + std::shared_ptr Resolver; ++#if LLVM_VERSION_MAJOR >= 8 ++ LegacyRTDyldObjectLinkingLayer ObjectLayer; ++ LegacyIRCompileLayer CompileLayer; ++#else + RTDyldObjectLinkingLayer ObjectLayer; + IRCompileLayer CompileLayer; ++#endif + + public: + std::map> sections_; +@@ -91,7 +96,11 @@ class BpfOrc + Resolver(createLegacyLookupResolver(ES, + [](const std::string &Name __attribute__((unused))) -> JITSymbol { return nullptr; }, + [](Error Err) { cantFail(std::move(Err), "lookup failed"); })), ++#if LLVM_VERSION_MAJOR >= 8 ++ ObjectLayer(ES, [this](VModuleKey) { return LegacyRTDyldObjectLinkingLayer::Resources{std::make_shared(sections_), Resolver}; }), ++#else + ObjectLayer(ES, [this](VModuleKey) { return RTDyldObjectLinkingLayer::Resources{std::make_shared(sections_), Resolver}; }), ++#endif + CompileLayer(ObjectLayer, SimpleCompiler(*TM)) {} + + void compileModule(std::unique_ptr M) { +diff --git a/src/clang_parser.cpp b/src/clang_parser.cpp +index 4bb8f87..7bb8d6a 100644 +--- a/src/clang_parser.cpp ++++ b/src/clang_parser.cpp +@@ -1,5 +1,7 @@ + #include + #include ++#include ++#include + #include + #include + #include +@@ -13,6 +15,9 @@ + + namespace bpftrace { + ++std::unordered_map indirect_structs; ++std::unordered_set unvisited_indirect_structs; ++ + static std::string get_clang_string(CXString string) + { + std::string str = clang_getCString(string); +@@ -30,8 +35,9 @@ static CXCursor get_indirect_field_parent_struct(CXCursor c) + { + CXCursor parent = clang_getCursorSemanticParent(c); + +- while (!clang_Cursor_isNull(parent) && clang_Cursor_isAnonymous(parent)) +- parent = clang_getCursorSemanticParent(parent); ++ while (!clang_Cursor_isNull(parent) && indirect_structs.count(get_clang_string(clang_getTypeSpelling(clang_getCanonicalType(clang_getCursorType(parent))))) > 0) { ++ parent = clang_getCursorSemanticParent(parent); ++ } + + return parent; + } +@@ -253,44 +259,69 @@ void ClangParser::parse(ast::Program *program, BPFtrace &bpftrace) + std::cerr << "Input (" << input.size() << "): " << input << std::endl; + } + +- CXCursor cursor = clang_getTranslationUnitCursor(translation_unit); ++ indirect_structs.clear(); ++ unvisited_indirect_structs.clear(); + +- clang_visitChildren( +- cursor, +- [](CXCursor c, CXCursor parent, CXClientData client_data) +- { ++ CXCursor cursor = clang_getTranslationUnitCursor(translation_unit); + +- if (clang_getCursorKind(parent) != CXCursor_StructDecl && +- clang_getCursorKind(parent) != CXCursor_UnionDecl) +- return CXChildVisit_Recurse; ++ bool iterate = true; + +- if (clang_getCursorKind(c) == CXCursor_FieldDecl) ++ do { ++ clang_visitChildren( ++ cursor, ++ [](CXCursor c, CXCursor parent, CXClientData client_data) + { +- auto &structs = static_cast(client_data)->structs_; +- auto struct_name = get_parent_struct_name(c); +- auto ident = get_clang_string(clang_getCursorSpelling(c)); +- auto offset = clang_Cursor_getOffsetOfField(c) / 8; +- auto type = clang_getCanonicalType(clang_getCursorType(c)); ++ if (clang_getCursorKind(parent) != CXCursor_StructDecl && ++ clang_getCursorKind(parent) != CXCursor_UnionDecl) ++ return CXChildVisit_Recurse; + + auto ptype = clang_getCanonicalType(clang_getCursorType(parent)); + auto ptypestr = get_clang_string(clang_getTypeSpelling(ptype)); + auto ptypesize = clang_Type_getSizeOf(ptype); + +- if(clang_Cursor_isAnonymous(parent)) +- offset = get_indirect_field_offset(c); ++ if (clang_getCursorKind(c) == CXCursor_StructDecl || ++ clang_getCursorKind(c) == CXCursor_UnionDecl) { ++ auto struct_name = get_clang_string(clang_getTypeSpelling(clang_getCanonicalType(clang_getCursorType(c)))); ++ indirect_structs[struct_name] = c; ++ unvisited_indirect_structs.insert(struct_name); + +- if (struct_name == "") +- struct_name = ptypestr; +- remove_struct_prefix(struct_name); ++ return CXChildVisit_Continue; ++ } + +- structs[struct_name].fields[ident].offset = offset; +- structs[struct_name].fields[ident].type = get_sized_type(type); +- structs[struct_name].size = ptypesize; +- } ++ if (clang_getCursorKind(c) == CXCursor_FieldDecl) ++ { ++ auto &structs = static_cast(client_data)->structs_; ++ auto struct_name = get_parent_struct_name(c); ++ auto ident = get_clang_string(clang_getCursorSpelling(c)); ++ auto offset = clang_Cursor_getOffsetOfField(c) / 8; ++ auto type = clang_getCanonicalType(clang_getCursorType(c)); ++ auto typestr = get_clang_string(clang_getTypeSpelling(type)); + +- return CXChildVisit_Recurse; +- }, +- &bpftrace); ++ if (indirect_structs.count(typestr)) ++ indirect_structs.erase(typestr); ++ ++ if(indirect_structs.count(ptypestr)) ++ offset = get_indirect_field_offset(c); ++ ++ if (struct_name == "") ++ struct_name = ptypestr; ++ remove_struct_prefix(struct_name); ++ ++ structs[struct_name].fields[ident].offset = offset; ++ structs[struct_name].fields[ident].type = get_sized_type(type); ++ structs[struct_name].size = ptypesize; ++ } ++ ++ return CXChildVisit_Recurse; ++ }, ++ &bpftrace); ++ if (unvisited_indirect_structs.size()) { ++ cursor = indirect_structs[*unvisited_indirect_structs.begin()]; ++ unvisited_indirect_structs.erase(unvisited_indirect_structs.begin()); ++ } else { ++ iterate = false; ++ } ++ } while (iterate); + + clang_disposeTranslationUnit(translation_unit); + clang_disposeIndex(index); +-- +2.20.1 + diff --git a/SOURCES/bpftrace-0.9-clang_parser-pass-BPFtrace-as-arg-instead-of-StructM.patch b/SOURCES/bpftrace-0.9-clang_parser-pass-BPFtrace-as-arg-instead-of-StructM.patch new file mode 100644 index 0000000..387b3d3 --- /dev/null +++ b/SOURCES/bpftrace-0.9-clang_parser-pass-BPFtrace-as-arg-instead-of-StructM.patch @@ -0,0 +1,356 @@ +From 5143209e8744d242431229972d9affa32ba3cc1a Mon Sep 17 00:00:00 2001 +From: Matheus Marchini +Date: Fri, 12 Apr 2019 16:27:51 -0700 +Subject: [PATCH 1/3] [clang_parser] pass BPFtrace as arg instead of StructMap + +--- + src/clang_parser.cpp | 6 +-- + src/clang_parser.h | 3 +- + src/main.cpp | 2 +- + tests/clang_parser.cpp | 71 +++++++++++++++++++++++------------ + tests/codegen/call_kstack.cpp | 4 +- + tests/codegen/call_ustack.cpp | 4 +- + tests/codegen/common.h | 2 +- + tests/codegen/general.cpp | 2 +- + tests/probe.cpp | 2 +- + tests/semantic_analyser.cpp | 2 +- + 10 files changed, 61 insertions(+), 37 deletions(-) + +diff --git a/src/clang_parser.cpp b/src/clang_parser.cpp +index b1db8ff..4bb8f87 100644 +--- a/src/clang_parser.cpp ++++ b/src/clang_parser.cpp +@@ -172,7 +172,7 @@ static std::tuple get_kernel_dirs(const struct utsname + return std::make_tuple(ksrc, kobj); + } + +-void ClangParser::parse(ast::Program *program, StructMap &structs) ++void ClangParser::parse(ast::Program *program, BPFtrace &bpftrace) + { + auto input = program->c_definitions; + if (input.size() == 0) +@@ -259,7 +259,6 @@ void ClangParser::parse(ast::Program *program, StructMap &structs) + cursor, + [](CXCursor c, CXCursor parent, CXClientData client_data) + { +- auto &structs = *static_cast(client_data); + + if (clang_getCursorKind(parent) != CXCursor_StructDecl && + clang_getCursorKind(parent) != CXCursor_UnionDecl) +@@ -267,6 +266,7 @@ void ClangParser::parse(ast::Program *program, StructMap &structs) + + if (clang_getCursorKind(c) == CXCursor_FieldDecl) + { ++ auto &structs = static_cast(client_data)->structs_; + auto struct_name = get_parent_struct_name(c); + auto ident = get_clang_string(clang_getCursorSpelling(c)); + auto offset = clang_Cursor_getOffsetOfField(c) / 8; +@@ -290,7 +290,7 @@ void ClangParser::parse(ast::Program *program, StructMap &structs) + + return CXChildVisit_Recurse; + }, +- &structs); ++ &bpftrace); + + clang_disposeTranslationUnit(translation_unit); + clang_disposeIndex(index); +diff --git a/src/clang_parser.h b/src/clang_parser.h +index d2ada5d..4289796 100644 +--- a/src/clang_parser.h ++++ b/src/clang_parser.h +@@ -1,6 +1,7 @@ + #pragma once + + #include "struct.h" ++#include "bpftrace.h" + + namespace bpftrace { + +@@ -11,7 +12,7 @@ using StructMap = std::map; + class ClangParser + { + public: +- void parse(ast::Program *program, StructMap &structs); ++ void parse(ast::Program *program, BPFtrace &bpftrace); + }; + + } // namespace bpftrace +diff --git a/src/main.cpp b/src/main.cpp +index ec3882d..f6659bf 100644 +--- a/src/main.cpp ++++ b/src/main.cpp +@@ -272,7 +272,7 @@ int main(int argc, char *argv[]) + } + + ClangParser clang; +- clang.parse(driver.root_, bpftrace.structs_); ++ clang.parse(driver.root_, bpftrace); + + ast::SemanticAnalyser semantics(driver.root_, bpftrace); + err = semantics.analyse(); +diff --git a/tests/clang_parser.cpp b/tests/clang_parser.cpp +index f12a5e4..0c1ca31 100644 +--- a/tests/clang_parser.cpp ++++ b/tests/clang_parser.cpp +@@ -1,25 +1,28 @@ + #include "gtest/gtest.h" + #include "clang_parser.h" + #include "driver.h" ++#include "bpftrace.h" + + namespace bpftrace { + namespace test { + namespace clang_parser { + +-void parse(const std::string &input, StructMap &structs) ++void parse(const std::string &input, BPFtrace &bpftrace) + { + auto extended_input = input + "kprobe:sys_read { 1 }"; + Driver driver; + ASSERT_EQ(driver.parse_str(extended_input), 0); + + ClangParser clang; +- clang.parse(driver.root_, structs); ++ clang.parse(driver.root_, bpftrace); + } + + TEST(clang_parser, integers) + { +- StructMap structs; +- parse("struct Foo { int x; int y, z; }", structs); ++ BPFtrace bpftrace; ++ parse("struct Foo { int x; int y, z; }", bpftrace); ++ ++ StructMap &structs = bpftrace.structs_; + + ASSERT_EQ(structs.size(), 1U); + ASSERT_EQ(structs.count("Foo"), 1U); +@@ -45,8 +48,10 @@ TEST(clang_parser, integers) + + TEST(clang_parser, c_union) + { +- StructMap structs; +- parse("union Foo { char c; short s; int i; long l; }", structs); ++ BPFtrace bpftrace; ++ parse("union Foo { char c; short s; int i; long l; }", bpftrace); ++ ++ StructMap &structs = bpftrace.structs_; + + ASSERT_EQ(structs.size(), 1U); + ASSERT_EQ(structs.count("Foo"), 1U); +@@ -77,8 +82,10 @@ TEST(clang_parser, c_union) + + TEST(clang_parser, integer_ptr) + { +- StructMap structs; +- parse("struct Foo { int *x; }", structs); ++ BPFtrace bpftrace; ++ parse("struct Foo { int *x; }", bpftrace); ++ ++ StructMap &structs = bpftrace.structs_; + + ASSERT_EQ(structs.size(), 1U); + ASSERT_EQ(structs.count("Foo"), 1U); +@@ -96,8 +103,10 @@ TEST(clang_parser, integer_ptr) + + TEST(clang_parser, string_ptr) + { +- StructMap structs; +- parse("struct Foo { char *str; }", structs); ++ BPFtrace bpftrace; ++ parse("struct Foo { char *str; }", bpftrace); ++ ++ StructMap &structs = bpftrace.structs_; + + ASSERT_EQ(structs.size(), 1U); + ASSERT_EQ(structs.count("Foo"), 1U); +@@ -115,8 +124,10 @@ TEST(clang_parser, string_ptr) + + TEST(clang_parser, string_array) + { +- StructMap structs; +- parse("struct Foo { char str[32]; }", structs); ++ BPFtrace bpftrace; ++ parse("struct Foo { char str[32]; }", bpftrace); ++ ++ StructMap &structs = bpftrace.structs_; + + ASSERT_EQ(structs.size(), 1U); + ASSERT_EQ(structs.count("Foo"), 1U); +@@ -132,8 +143,10 @@ TEST(clang_parser, string_array) + + TEST(clang_parser, nested_struct_named) + { +- StructMap structs; +- parse("struct Bar { int x; } struct Foo { struct Bar bar; }", structs); ++ BPFtrace bpftrace; ++ parse("struct Bar { int x; } struct Foo { struct Bar bar; }", bpftrace); ++ ++ StructMap &structs = bpftrace.structs_; + + ASSERT_EQ(structs.size(), 2U); + ASSERT_EQ(structs.count("Foo"), 1U); +@@ -151,8 +164,10 @@ TEST(clang_parser, nested_struct_named) + + TEST(clang_parser, nested_struct_ptr_named) + { +- StructMap structs; +- parse("struct Bar { int x; } struct Foo { struct Bar *bar; }", structs); ++ BPFtrace bpftrace; ++ parse("struct Bar { int x; } struct Foo { struct Bar *bar; }", bpftrace); ++ ++ StructMap &structs = bpftrace.structs_; + + ASSERT_EQ(structs.size(), 2U); + ASSERT_EQ(structs.count("Foo"), 1U); +@@ -172,8 +187,10 @@ TEST(clang_parser, nested_struct_ptr_named) + + TEST(clang_parser, nested_struct_anon) + { +- StructMap structs; +- parse("struct Foo { struct { int x; } bar; }", structs); ++ BPFtrace bpftrace; ++ parse("struct Foo { struct { int x; } bar; }", bpftrace); ++ ++ StructMap &structs = bpftrace.structs_; + + ASSERT_EQ(structs.size(), 2U); + ASSERT_EQ(structs.count("Foo"), 1U); +@@ -190,8 +207,10 @@ TEST(clang_parser, nested_struct_anon) + + TEST(clang_parser, nested_struct_indirect_fields) + { +- StructMap structs; +- parse("struct Foo { struct { int x; int y;}; int a; struct { int z; }; }", structs); ++ BPFtrace bpftrace; ++ parse("struct Foo { struct { int x; int y;}; int a; struct { int z; }; }", bpftrace); ++ ++ StructMap &structs = bpftrace.structs_; + + ASSERT_EQ(structs["Foo"].fields.size(), 4U); + EXPECT_EQ(structs["Foo"].fields["x"].offset, 0); +@@ -206,8 +225,10 @@ TEST(clang_parser, nested_struct_indirect_fields) + + TEST(clang_parser, nested_struct_anon_union_struct) + { +- StructMap structs; +- parse("struct Foo { union { long long _xy; struct { int x; int y;}; }; int a; struct { int z; }; }", structs); ++ BPFtrace bpftrace; ++ parse("struct Foo { union { long long _xy; struct { int x; int y;}; }; int a; struct { int z; }; }", bpftrace); ++ ++ StructMap &structs = bpftrace.structs_; + + ASSERT_EQ(structs["Foo"].fields.size(), 5U); + EXPECT_EQ(structs["Foo"].fields["_xy"].offset, 0); +@@ -225,8 +246,10 @@ TEST(clang_parser, nested_struct_anon_union_struct) + TEST(clang_parser, builtin_headers) + { + // size_t is definied in stddef.h +- StructMap structs; +- parse("#include \nstruct Foo { size_t x, y, z; }", structs); ++ BPFtrace bpftrace; ++ parse("#include \nstruct Foo { size_t x, y, z; }", bpftrace); ++ ++ StructMap &structs = bpftrace.structs_; + + ASSERT_EQ(structs.count("Foo"), 1U); + +diff --git a/tests/codegen/call_kstack.cpp b/tests/codegen/call_kstack.cpp +index a184af2..e64d498 100644 +--- a/tests/codegen/call_kstack.cpp ++++ b/tests/codegen/call_kstack.cpp +@@ -68,7 +68,7 @@ TEST(codegen, call_kstack_mapids) + ASSERT_EQ(driver.parse_str("kprobe:f { @x = kstack(5); @y = kstack(6); @z = kstack(6) }"), 0); + + ClangParser clang; +- clang.parse(driver.root_, bpftrace.structs_); ++ clang.parse(driver.root_, bpftrace); + + ast::SemanticAnalyser semantics(driver.root_, bpftrace); + ASSERT_EQ(semantics.analyse(), 0); +@@ -96,7 +96,7 @@ TEST(codegen, call_kstack_modes_mapids) + ASSERT_EQ(driver.parse_str("kprobe:f { @x = kstack(perf); @y = kstack(bpftrace); @z = kstack() }"), 0); + + ClangParser clang; +- clang.parse(driver.root_, bpftrace.structs_); ++ clang.parse(driver.root_, bpftrace); + + ast::SemanticAnalyser semantics(driver.root_, bpftrace); + ASSERT_EQ(semantics.analyse(), 0); +diff --git a/tests/codegen/call_ustack.cpp b/tests/codegen/call_ustack.cpp +index 8e80558..1941d36 100644 +--- a/tests/codegen/call_ustack.cpp ++++ b/tests/codegen/call_ustack.cpp +@@ -74,7 +74,7 @@ TEST(codegen, call_ustack_mapids) + ASSERT_EQ(driver.parse_str("kprobe:f { @x = ustack(5); @y = ustack(6); @z = ustack(6) }"), 0); + + ClangParser clang; +- clang.parse(driver.root_, bpftrace.structs_); ++ clang.parse(driver.root_, bpftrace); + + ast::SemanticAnalyser semantics(driver.root_, bpftrace); + ASSERT_EQ(semantics.analyse(), 0); +@@ -102,7 +102,7 @@ TEST(codegen, call_ustack_modes_mapids) + ASSERT_EQ(driver.parse_str("kprobe:f { @x = ustack(perf); @y = ustack(bpftrace); @z = ustack() }"), 0); + + ClangParser clang; +- clang.parse(driver.root_, bpftrace.structs_); ++ clang.parse(driver.root_, bpftrace); + + ast::SemanticAnalyser semantics(driver.root_, bpftrace); + ASSERT_EQ(semantics.analyse(), 0); +diff --git a/tests/codegen/common.h b/tests/codegen/common.h +index 32f8bc8..bdf733a 100644 +--- a/tests/codegen/common.h ++++ b/tests/codegen/common.h +@@ -30,7 +30,7 @@ static void test(const std::string &input, const std::string expected_output) + ASSERT_EQ(driver.parse_str(input), 0); + + ClangParser clang; +- clang.parse(driver.root_, bpftrace.structs_); ++ clang.parse(driver.root_, bpftrace); + + ast::SemanticAnalyser semantics(driver.root_, bpftrace); + ASSERT_EQ(semantics.analyse(), 0); +diff --git a/tests/codegen/general.cpp b/tests/codegen/general.cpp +index e7e7439..e67ae10 100644 +--- a/tests/codegen/general.cpp ++++ b/tests/codegen/general.cpp +@@ -45,7 +45,7 @@ TEST(codegen, printf_offsets) + // TODO (mmarchini): also test printf with a string argument + ASSERT_EQ(driver.parse_str("struct Foo { char c; int i; } kprobe:f { $foo = (Foo*)0; printf(\"%c %u\\n\", $foo->c, $foo->i) }"), 0); + ClangParser clang; +- clang.parse(driver.root_, bpftrace.structs_); ++ clang.parse(driver.root_, bpftrace); + ast::SemanticAnalyser semantics(driver.root_, bpftrace); + ASSERT_EQ(semantics.analyse(), 0); + ASSERT_EQ(semantics.create_maps(true), 0); +diff --git a/tests/probe.cpp b/tests/probe.cpp +index e030830..cb9b765 100644 +--- a/tests/probe.cpp ++++ b/tests/probe.cpp +@@ -28,7 +28,7 @@ void gen_bytecode(const std::string &input, std::stringstream &out) + ASSERT_EQ(driver.parse_str(input), 0); + + ClangParser clang; +- clang.parse(driver.root_, bpftrace.structs_); ++ clang.parse(driver.root_, bpftrace); + + ast::SemanticAnalyser semantics(driver.root_, bpftrace); + ASSERT_EQ(semantics.analyse(), 0); +diff --git a/tests/semantic_analyser.cpp b/tests/semantic_analyser.cpp +index 2067ed9..4e2485b 100644 +--- a/tests/semantic_analyser.cpp ++++ b/tests/semantic_analyser.cpp +@@ -16,7 +16,7 @@ void test(BPFtrace &bpftrace, Driver &driver, const std::string &input, int expe + ASSERT_EQ(driver.parse_str(input), 0); + + ClangParser clang; +- clang.parse(driver.root_, bpftrace.structs_); ++ clang.parse(driver.root_, bpftrace); + + std::stringstream out; + ast::SemanticAnalyser semantics(driver.root_, bpftrace, out); +-- +2.20.1 + diff --git a/SOURCES/bpftrace-0.9-fix-struct-definition-from-headers.patch b/SOURCES/bpftrace-0.9-fix-struct-definition-from-headers.patch new file mode 100644 index 0000000..3845c4a --- /dev/null +++ b/SOURCES/bpftrace-0.9-fix-struct-definition-from-headers.patch @@ -0,0 +1,32 @@ +From 151351ea22c855e4e605b8fd2058c19dc4ae5ed6 Mon Sep 17 00:00:00 2001 +From: Matheus Marchini +Date: Tue, 16 Apr 2019 12:06:01 -0700 +Subject: [PATCH 2/2] fix struct definition from headers + +Regression caused by iovisor/bpftrace@80ce138c7. With the changes on how +we identify the parent struct, we ended up with our parent cursor in the +header file sometimes instead of a valid cursor. This PR checks if the +parent cursor is a struct, otherwise returns an empty string and let the +caller handle the situation (which is similar to the previous behavior). +--- + src/clang_parser.cpp | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/clang_parser.cpp b/src/clang_parser.cpp +index b1db8ff..b787b67 100644 +--- a/src/clang_parser.cpp ++++ b/src/clang_parser.cpp +@@ -40,6 +40,10 @@ static std::string get_parent_struct_name(CXCursor c) + { + CXCursor parent = get_indirect_field_parent_struct(c); + ++ if (clang_getCursorKind(parent) != CXCursor_StructDecl && ++ clang_getCursorKind(parent) != CXCursor_UnionDecl) ++ return ""; ++ + return get_clang_string(clang_getCursorSpelling(parent)); + } + +-- +2.20.1 + diff --git a/SPECS/bpftrace.spec b/SPECS/bpftrace.spec new file mode 100644 index 0000000..b9dd088 --- /dev/null +++ b/SPECS/bpftrace.spec @@ -0,0 +1,115 @@ +%bcond_without llvm_static + +Name: bpftrace +Version: 0.9 +Release: 3%{?dist} +Summary: High-level tracing language for Linux eBPF +License: ASL 2.0 + +URL: https://github.com/iovisor/bpftrace +Source0: %{url}/archive/v%{version}/%{name}-%{version}.tar.gz +Patch0: %{name}-%{version}-clang_parser-pass-BPFtrace-as-arg-instead-of-StructM.patch +Patch1: %{name}-%{version}-clang_parser-fix-clang_parser-for-LLVM-8.patch +Patch2: %{name}-%{version}-fix-struct-definition-from-headers.patch +Patch3: %{name}-%{version}-RHEL-8-fixes.patch +Patch4: %{name}-%{version}-Ban-kprobes-that-cause-CPU-deadlocks.patch + +# Arches will be included as upstream support is added and dependencies are +# satisfied in the respective arches +ExclusiveArch: x86_64 %{power64} aarch64 s390x + +BuildRequires: gcc-c++ +BuildRequires: bison +BuildRequires: flex +BuildRequires: cmake +BuildRequires: elfutils-libelf-devel +BuildRequires: zlib-devel +BuildRequires: llvm-devel +BuildRequires: clang-devel +BuildRequires: bcc-devel + +%if %{with llvm_static} +BuildRequires: llvm-static +%endif + +%description +BPFtrace is a high-level tracing language for Linux enhanced Berkeley Packet +Filter (eBPF) available in recent Linux kernels (4.x). BPFtrace uses LLVM as a +backend to compile scripts to BPF-bytecode and makes use of BCC for +interacting with the Linux BPF system, as well as existing Linux tracing +capabilities: kernel dynamic tracing (kprobes), user-level dynamic tracing +(uprobes), and tracepoints. The BPFtrace language is inspired by awk and C, +and predecessor tracers such as DTrace and SystemTap + + +%prep +%autosetup -p1 + + +%build +%cmake . \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DBUILD_TESTING:BOOL=OFF \ + -DBUILD_SHARED_LIBS:BOOL=OFF \ + -DLLVM_DIR=/usr/lib64/llvm7.0/lib/cmake/llvm/ +%make_build + + +%install +%make_install + +# Fix shebangs (https://fedoraproject.org/wiki/Packaging:Guidelines#Shebang_lines) +find %{buildroot}%{_datadir}/%{name}/tools -type f -exec \ + sed -i -e '1s=^#!/usr/bin/env %{name}\([0-9.]\+\)\?$=#!%{_bindir}/%{name}=' {} \; + +# Move man pages to the right location +mkdir -p %{buildroot}%{_mandir} +mv %{buildroot}%{_prefix}/man/* %{buildroot}%{_mandir}/ + + +%files +%doc README.md CONTRIBUTING-TOOLS.md +%doc docs/reference_guide.md docs/tutorial_one_liners.md +%license LICENSE +%dir %{_datadir}/%{name} +%dir %{_datadir}/%{name}/tools +%dir %{_datadir}/%{name}/tools/doc +%{_bindir}/%{name} +%{_mandir}/man8/* +%attr(0755,-,-) %{_datadir}/%{name}/tools/*.bt +%{_datadir}/%{name}/tools/doc/*.txt + + +%changelog +* Tue Jun 18 2019 Jerome Marchand - 0.9.3 +- Don't allow to raw_spin_lock* kprobes that can deadlock the kernel. + +* Wed Jun 12 2019 Jerome Marchand - 0.9.2 +- Fixes gethostlatency +- Fixes a struct definition issue that made several tools fail +- Add CI gating + +* Wed May 15 2019 Jerome Marchand - 0.9.1 +- Original build on RHEL 8 + +* Thu Apr 25 2019 Augusto Caringi - 0.9-3 +- Rebuilt for bcc 0.9.0 + +* Mon Apr 22 2019 Neal Gompa - 0.9-2 +- Fix Source0 reference +- Use make_build macro for calling make + +* Mon Apr 1 2019 Peter Robinson 0.9-1 +- Build on aarch64 and s390x + +* Mon Mar 25 2019 Augusto Caringi - 0.9-0 +- Updated to version 0.9 + +* Thu Jan 31 2019 Fedora Release Engineering - 0.0-2.20181210gitc49b333 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Mon Dec 10 2018 Augusto Caringi - 0.0-1.20181210gitc49b333 +- Updated to latest upstream (c49b333c034a6d29a7ce90f565e27da1061af971) + +* Wed Nov 07 2018 Augusto Caringi - 0.0-1.20181107git029717b +- Initial import