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 <javierhonduco@gmail.com>
+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<std::string> 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<uint8_t *, uintptr_t> func)
+   : probe_(probe), func_(func)
+@@ -70,7 +85,10 @@ AttachedProbe::AttachedProbe(Probe &probe, std::tuple<uint8_t *, uintptr_t> 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 <jmarchan@redhat.com>
+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 <jmarchan@redhat.com>
+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<TargetMachine> TM;
+   std::shared_ptr<SymbolResolver> Resolver;
++#if LLVM_VERSION_MAJOR >= 8
++  LegacyRTDyldObjectLinkingLayer ObjectLayer;
++  LegacyIRCompileLayer<decltype(ObjectLayer), SimpleCompiler> CompileLayer;
++#else
+   RTDyldObjectLinkingLayer ObjectLayer;
+   IRCompileLayer<decltype(ObjectLayer), SimpleCompiler> CompileLayer;
++#endif
+ 
+ public:
+   std::map<std::string, std::tuple<uint8_t *, uintptr_t>> 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<MemoryManager>(sections_), Resolver}; }),
++#else
+       ObjectLayer(ES, [this](VModuleKey) { return RTDyldObjectLinkingLayer::Resources{std::make_shared<MemoryManager>(sections_), Resolver}; }),
++#endif
+       CompileLayer(ObjectLayer, SimpleCompiler(*TM)) {}
+ 
+   void compileModule(std::unique_ptr<Module> 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 <clang-c/Index.h>
+ #include <iostream>
++#include <unordered_map>
++#include <unordered_set>
+ #include <string.h>
+ #include <sys/utsname.h>
+ #include <sys/stat.h>
+@@ -13,6 +15,9 @@
+ 
+ namespace bpftrace {
+ 
++std::unordered_map<std::string, CXCursor> indirect_structs;
++std::unordered_set<std::string> 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<BPFtrace*>(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<BPFtrace*>(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 <mat@mmarchini.me>
+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<std::string, std::string> 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<StructMap*>(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<BPFtrace*>(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<std::string, Struct>;
+ 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 <stddef.h>\nstruct Foo { size_t x, y, z; }", structs);
++  BPFtrace bpftrace;
++  parse("#include <stddef.h>\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 <mat@mmarchini.me>
+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 <jmarchan@redhat.com> - 0.9.3
+- Don't allow to raw_spin_lock* kprobes that can deadlock the kernel.
+
+* Wed Jun 12 2019 Jerome Marchand <jmarchan@redhat.com> - 0.9.2
+- Fixes gethostlatency
+- Fixes a struct definition issue that made several tools fail
+- Add CI gating
+
+* Wed May 15 2019 Jerome Marchand <jmarchan@redhat.com> - 0.9.1
+- Original build on RHEL 8
+
+* Thu Apr 25 2019 Augusto Caringi <acaringi@redhat.com> - 0.9-3
+- Rebuilt for bcc 0.9.0
+
+* Mon Apr 22 2019 Neal Gompa <ngompa@datto.com> - 0.9-2
+- Fix Source0 reference
+- Use make_build macro for calling make
+
+* Mon Apr  1 2019 Peter Robinson <pbrobinson@fedoraproject.org> 0.9-1
+- Build on aarch64 and s390x
+
+* Mon Mar 25 2019 Augusto Caringi <acaringi@redhat.com> - 0.9-0
+- Updated to version 0.9
+
+* Thu Jan 31 2019 Fedora Release Engineering <releng@fedoraproject.org> - 0.0-2.20181210gitc49b333
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
+
+* Mon Dec 10 2018 Augusto Caringi <acaringi@redhat.com> - 0.0-1.20181210gitc49b333
+- Updated to latest upstream (c49b333c034a6d29a7ce90f565e27da1061af971)
+
+* Wed Nov 07 2018 Augusto Caringi <acaringi@redhat.com> - 0.0-1.20181107git029717b
+- Initial import