forked from iovisor/bcc
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add debug option to dump asm insns embedded with source
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
1 parent
2a6a5c5
commit 91837ca
Showing
13 changed files
with
344 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>> §ions, | ||
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>> §ions_; | ||
const std::string &fn_prefix_; | ||
const std::string &mod_src_; | ||
}; | ||
|
||
} // namespace ebpf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.