Skip to content

Commit

Permalink
add debug option to dump asm insns embedded with source
Browse files Browse the repository at this point in the history
The patch adds a new debug option "DEBUG_SOURCE = 8" to
dump insns embedded with source. In C++ API, users
can change BPF constructor "flag" value to enable debug output.
In Python API, users can change "debug" value to enable
debug output. For example, for python test program test_usdt.py,
the debug output looks like below:

......
Disassembly of section .bpf.fn.do_trace1:
do_trace1:
; int do_trace1(struct pt_regs *ctx) { // Line 110
   0:   bf 16 00 00 00 00 00 00         r6 = r1
   1:   b7 01 00 00 00 00 00 00         r1 = 0
; struct probe_result_t1 result = {}; // Line 111
   2:   7b 1a f0 ff 00 00 00 00         *(u64 *)(r10 - 16) = r1
; switch(ctx->ip) { // Line   5
   3:   79 61 80 00 00 00 00 00         r1 = *(u64 *)(r6 + 128)
   4:   15 01 04 00 d7 06 40 00         if r1 == 4196055 goto 4
   5:   55 01 06 00 ce 06 40 00         if r1 != 4196046 goto 6
; case 0x4006ceULL: *((int8_t *)dest) = ctx->ax; __asm__ __volatile__("": : :"memory"); return 0; // Line   6
   6:   79 61 50 00 00 00 00 00         r1 = *(u64 *)(r6 + 80)
......

For asm insns, byte code is also dumped out (similar to objdump).
For source codes, only lines in the module file are printed (as expected).
The line number is added at the end of source code, which is
especially helpful for inlined functions.

This functionality is only in llvm 6.x (the trunk version), which
provides an public interface to create a dwarf context based on
a set of in-memory debug sections. llvm 5.x also provides
such a public interface in a different way, and this patch
does not support it in bcc yet. llvm 4.x and lower do not
have such a public interface and hence will not be supported
in bcc.

In this patch, the debug output only goes to stderr.
A subsequent patch will dump the per-function output into
<BCC_PROG_TAG_DIR>/bpf_prog_<tag>/ if it is available.

Signed-off-by: Yonghong Song <[email protected]>
  • Loading branch information
yonghong-song authored and drzaeus77 committed Oct 3, 2017
1 parent 2a6a5c5 commit 91837ca
Show file tree
Hide file tree
Showing 13 changed files with 344 additions and 21 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ if(NOT PYTHON_ONLY AND ENABLE_CLANG_JIT)
find_package(BISON)
find_package(FLEX)
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM: ${LLVM_INCLUDE_DIRS}")
message(STATUS "Found LLVM: ${LLVM_INCLUDE_DIRS} ${LLVM_PACKAGE_VERSION}")
find_package(LibElf REQUIRED)

# clang is linked as a library, but the library path searching is
Expand Down
5 changes: 4 additions & 1 deletion cmake/clang_libs.cmake
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
set(llvm_raw_libs bitwriter bpfcodegen irreader linker
set(llvm_raw_libs bitwriter bpfcodegen debuginfodwarf irreader linker
mcjit objcarcopts option passes nativecodegen lto)
list(FIND LLVM_AVAILABLE_LIBS "LLVMCoverage" _llvm_coverage)
if (${_llvm_coverage} GREATER -1)
Expand All @@ -8,6 +8,9 @@ list(FIND LLVM_AVAILABLE_LIBS "LLVMCoroutines" _llvm_coroutines)
if (${_llvm_coroutines} GREATER -1)
list(APPEND llvm_raw_libs coroutines)
endif()
if (${LLVM_PACKAGE_VERSION} VERSION_GREATER "5")
list(APPEND llvm_raw_libs bpfdisassembler)
endif()
llvm_map_components_to_libnames(_llvm_libs ${llvm_raw_libs})
llvm_expand_dependencies(llvm_libs ${_llvm_libs})

Expand Down
7 changes: 7 additions & 0 deletions src/cc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ configure_file(libbcc.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libbcc.pc @ONLY)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -DBCC_PROG_TAG_DIR='\"${BCC_PROG_TAG_DIR}\"'")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")

string(REGEX MATCH "^([0-9]+).*" _ ${LLVM_PACKAGE_VERSION})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DLLVM_MAJOR_VERSION=${CMAKE_MATCH_1}")

include(static_libstdc++)

add_library(bpf-static STATIC libbpf.c perf_reader.c)
Expand All @@ -25,6 +28,10 @@ add_library(bpf-shared SHARED libbpf.c perf_reader.c)
set_target_properties(bpf-shared PROPERTIES OUTPUT_NAME bpf)

set(bcc_common_sources bpf_common.cc bpf_module.cc exported_files.cc)
if (${LLVM_PACKAGE_VERSION} VERSION_GREATER "5")
set(bcc_common_sources ${bcc_common_sources} bcc_debug.cc)
endif()

set(bcc_table_sources table_storage.cc shared_table.cc bpffs_table.cc json_map_decl_visitor.cc)
set(bcc_util_sources ns_guard.cc common.cc)
set(bcc_sym_sources bcc_syms.cc bcc_elf.c bcc_perf_map.c bcc_proc.c)
Expand Down
213 changes: 213 additions & 0 deletions src/cc/bcc_debug.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
/*
* Copyright (c) 2017 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:https://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 <map>
#include <string>
#include <tuple>
#include <vector>

#include <llvm/DebugInfo/DWARF/DWARFContext.h>
#include <llvm/DebugInfo/DWARF/DWARFDebugLine.h>
#include <llvm/IR/Module.h>
#include <llvm/MC/MCAsmInfo.h>
#include <llvm/MC/MCContext.h>
#include <llvm/MC/MCDisassembler/MCDisassembler.h>
#include <llvm/MC/MCInstPrinter.h>
#include <llvm/MC/MCInstrInfo.h>
#include <llvm/MC/MCObjectFileInfo.h>
#include <llvm/MC/MCRegisterInfo.h>
#include <llvm/Support/TargetRegistry.h>

#include "bcc_debug.h"

namespace ebpf {

// ld_pseudo can only be disassembled properly
// in llvm 6.0, so having this workaround now
// until disto llvm versions catch up
#define WORKAROUND_FOR_LD_PSEUDO

using std::get;
using std::map;
using std::string;
using std::tuple;
using std::vector;
using namespace llvm;
using DWARFLineTable = DWARFDebugLine::LineTable;

void SourceDebugger::adjustInstSize(uint64_t &Size, uint8_t byte0,
uint8_t byte1) {
#ifdef WORKAROUND_FOR_LD_PSEUDO
bool isLittleEndian = mod_->getDataLayout().isLittleEndian();
if (byte0 == 0x18 && ((isLittleEndian && (byte1 & 0xf) == 0x1) ||
(!isLittleEndian && (byte1 & 0xf0) == 0x10)))
Size = 16;
#endif
}

vector<string> SourceDebugger::buildLineCache() {
vector<string> LineCache;
size_t FileBufSize = mod_src_.size();

for (uint32_t start = 0, end = start; end < FileBufSize; end++)
if (mod_src_[end] == '\n' || end == FileBufSize - 1 ||
(mod_src_[end] == '\r' && mod_src_[end + 1] == '\n')) {
// Not including the endline
LineCache.push_back(string(mod_src_.substr(start, end - start)));
if (mod_src_[end] == '\r')
end++;
start = end + 1;
}

return LineCache;
}

void SourceDebugger::dumpSrcLine(const vector<string> &LineCache,
const string &FileName, uint32_t Line,
uint32_t &CurrentSrcLine) {
if (Line != 0 && Line != CurrentSrcLine && Line < LineCache.size() &&
FileName == mod_->getSourceFileName()) {
errs() << "; " << StringRef(LineCache[Line - 1]).ltrim()
<< format(
" // Line"
"%4" PRIu64 "\n",
Line);
CurrentSrcLine = Line;
}
}

void SourceDebugger::getDebugSections(
StringMap<std::unique_ptr<MemoryBuffer>> &DebugSections) {
for (auto section : sections_) {
if (strncmp(section.first.c_str(), ".debug", 6) == 0) {
StringRef SecData(reinterpret_cast<const char *>(get<0>(section.second)),
get<1>(section.second));
DebugSections[section.first.substr(1)] =
MemoryBuffer::getMemBufferCopy(SecData);
}
}
}

void SourceDebugger::dump() {
string Error;
string TripleStr(mod_->getTargetTriple());
Triple TheTriple(TripleStr);
const Target *T = TargetRegistry::lookupTarget(TripleStr, Error);
if (!T) {
errs() << "Debug Error: cannot get target\n";
return;
}

std::unique_ptr<MCRegisterInfo> MRI(T->createMCRegInfo(TripleStr));
if (!MRI) {
errs() << "Debug Error: cannot get register info\n";
return;
}
std::unique_ptr<MCAsmInfo> MAI(T->createMCAsmInfo(*MRI, TripleStr));
if (!MAI) {
errs() << "Debug Error: cannot get assembly info\n";
return;
}

MCObjectFileInfo MOFI;
MCContext Ctx(MAI.get(), MRI.get(), &MOFI, nullptr);
MOFI.InitMCObjectFileInfo(TheTriple, false, Ctx, false);
std::unique_ptr<MCSubtargetInfo> STI(
T->createMCSubtargetInfo(TripleStr, "", ""));

std::unique_ptr<MCInstrInfo> MCII(T->createMCInstrInfo());
MCInstPrinter *IP = T->createMCInstPrinter(TheTriple, 0, *MAI, *MCII, *MRI);
if (!IP) {
errs() << "Debug Error: unable to create instruction printer\n";
return;
}

std::unique_ptr<const MCDisassembler> DisAsm(
T->createMCDisassembler(*STI, Ctx));
if (!DisAsm) {
errs() << "Debug Error: no disassembler\n";
return;
}

// Set up the dwarf debug context
StringMap<std::unique_ptr<MemoryBuffer>> DebugSections;
getDebugSections(DebugSections);
std::unique_ptr<DWARFContext> DwarfCtx =
DWARFContext::create(DebugSections, 8);
if (!DwarfCtx) {
errs() << "Debug Error: dwarf context creation failed\n";
return;
}

// bcc has only one compilation unit
DWARFCompileUnit *CU = DwarfCtx->getCompileUnitAtIndex(0);
if (!CU) {
errs() << "Debug Error: dwarf context failed to get compile unit\n";
return;
}

const DWARFLineTable *LineTable = DwarfCtx->getLineTableForUnit(CU);
if (!LineTable) {
errs() << "Debug Error: dwarf context failed to get line table\n";
return;
}

// Build LineCache for later source code printing
vector<string> LineCache = buildLineCache();

// Start to disassemble with source code annotation section by section
for (auto section : sections_)
if (!strncmp(fn_prefix_.c_str(), section.first.c_str(),
fn_prefix_.size())) {
MCDisassembler::DecodeStatus S;
MCInst Inst;
uint64_t Size;
uint8_t *FuncStart = get<0>(section.second);
uint64_t FuncSize = get<1>(section.second);
ArrayRef<uint8_t> Data(FuncStart, FuncSize);
uint32_t CurrentSrcLine = 0;

errs() << "Disassembly of section " << section.first << ":\n"
<< section.first.substr(fn_prefix_.size()) << ":\n";

for (uint64_t Index = 0; Index < FuncSize; Index += Size) {
S = DisAsm->getInstruction(Inst, Size, Data.slice(Index), Index,
nulls(), nulls());
if (S != MCDisassembler::Success) {
errs() << "Debug Error: disassembler failed: " << std::to_string(S)
<< '\n';
break;
} else {
DILineInfo LineInfo;
LineTable->getFileLineInfoForAddress(
(uint64_t)FuncStart + Index, CU->getCompilationDir(),
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
LineInfo);

adjustInstSize(Size, Data[Index], Data[Index + 1]);
dumpSrcLine(LineCache, LineInfo.FileName, LineInfo.Line,
CurrentSrcLine);
errs() << format("%4" PRIu64 ":", Index >> 3) << '\t';
dumpBytes(Data.slice(Index, Size), errs());
IP->printInst(&Inst, errs(), "", *STI);
errs() << '\n';
}
}
errs() << '\n';
}
}

} // namespace ebpf
58 changes: 58 additions & 0 deletions src/cc/bcc_debug.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2017 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:https://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.
*/
namespace ebpf {

class SourceDebugger {
public:
SourceDebugger(
llvm::Module *mod,
std::map<std::string, std::tuple<uint8_t *, uintptr_t>> &sections,
const std::string &fn_prefix, const std::string &mod_src)
: mod_(mod),
sections_(sections),
fn_prefix_(fn_prefix),
mod_src_(mod_src) {}
// Only support dump for llvm 6.x and later.
//
// The llvm 5.x, but not earlier versions, also supports create
// a dwarf context for source debugging based
// on a set of in-memory sections with slightly different interfaces.
// FIXME: possibly to support 5.x
//
#if LLVM_MAJOR_VERSION >= 6
void dump();

private:
void adjustInstSize(uint64_t &Size, uint8_t byte0, uint8_t byte1);
std::vector<std::string> buildLineCache();
void dumpSrcLine(const std::vector<std::string> &LineCache,
const std::string &FileName, uint32_t Line,
uint32_t &CurrentSrcLine);
void getDebugSections(
llvm::StringMap<std::unique_ptr<llvm::MemoryBuffer>> &DebugSections);
#else
void dump() {
}
#endif

private:
llvm::Module *mod_;
const std::map<std::string, std::tuple<uint8_t *, uintptr_t>> &sections_;
const std::string &fn_prefix_;
const std::string &mod_src_;
};

} // namespace ebpf
24 changes: 20 additions & 4 deletions src/cc/bpf_module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
#include <llvm-c/Transforms/IPO.h>

#include "common.h"
#include "bcc_debug.h"
#include "bcc_exception.h"
#include "frontends/b/loader.h"
#include "frontends/clang/loader.h"
Expand Down Expand Up @@ -108,6 +110,10 @@ BPFModule::BPFModule(unsigned flags, TableStorage *ts)
LLVMInitializeBPFTargetMC();
LLVMInitializeBPFTargetInfo();
LLVMInitializeBPFAsmPrinter();
#if LLVM_MAJOR_VERSION >= 6
if (flags & DEBUG_SOURCE)
LLVMInitializeBPFDisassembler();
#endif
LLVMLinkInMCJIT(); /* call empty function to force linking of MCJIT */
if (!ts_) {
local_ts_ = createSharedTableStorage();
Expand Down Expand Up @@ -458,7 +464,8 @@ unique_ptr<ExecutionEngine> BPFModule::finalize_rw(unique_ptr<Module> m) {
// load an entire c file as a module
int BPFModule::load_cfile(const string &file, bool in_memory, const char *cflags[], int ncflags) {
clang_loader_ = ebpf::make_unique<ClangLoader>(&*ctx_, flags_);
if (clang_loader_->parse(&mod_, *ts_, file, in_memory, cflags, ncflags, id_, *func_src_))
if (clang_loader_->parse(&mod_, *ts_, file, in_memory, cflags, ncflags, id_,
*func_src_, mod_src_))
return -1;
return 0;
}
Expand All @@ -470,7 +477,8 @@ int BPFModule::load_cfile(const string &file, bool in_memory, const char *cflags
// build an ExecutionEngine.
int BPFModule::load_includes(const string &text) {
clang_loader_ = ebpf::make_unique<ClangLoader>(&*ctx_, flags_);
if (clang_loader_->parse(&mod_, *ts_, text, true, nullptr, 0, "", *func_src_))
if (clang_loader_->parse(&mod_, *ts_, text, true, nullptr, 0, "", *func_src_,
mod_src_))
return -1;
return 0;
}
Expand Down Expand Up @@ -552,7 +560,7 @@ void BPFModule::dump_ir(Module &mod) {

int BPFModule::run_pass_manager(Module &mod) {
if (verifyModule(mod, &errs())) {
if (flags_ & 1)
if (flags_ & DEBUG_LLVM_IR)
dump_ir(mod);
return -1;
}
Expand All @@ -570,7 +578,7 @@ int BPFModule::run_pass_manager(Module &mod) {
*/
LLVMAddAlwaysInlinerPass(reinterpret_cast<LLVMPassManagerRef>(&PM));
PMB.populateModulePassManager(PM);
if (flags_ & 1)
if (flags_ & DEBUG_LLVM_IR)
PM.add(createPrintModulePass(outs()));
PM.run(mod);
return 0;
Expand All @@ -594,6 +602,9 @@ int BPFModule::finalize() {
return -1;
}

if (flags_ & DEBUG_SOURCE)
engine_->setProcessAllSections(true);

if (int rc = run_pass_manager(*mod))
return rc;

Expand All @@ -604,6 +615,11 @@ int BPFModule::finalize() {
if (!strncmp(FN_PREFIX.c_str(), section.first.c_str(), FN_PREFIX.size()))
function_names_.push_back(section.first);

if (flags_ & DEBUG_SOURCE) {
SourceDebugger src_debugger(mod, sections_, FN_PREFIX, mod_src_);
src_debugger.dump();
}

return 0;
}

Expand Down
Loading

0 comments on commit 91837ca

Please sign in to comment.