Home | History | Annotate | Download | only in cc
      1 /*
      2  * Copyright (c) 2017 Facebook, Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <map>
     18 #include <string>
     19 #include <tuple>
     20 #include <vector>
     21 
     22 #include <llvm/DebugInfo/DWARF/DWARFContext.h>
     23 #include <llvm/DebugInfo/DWARF/DWARFDebugLine.h>
     24 #include <llvm/IR/Module.h>
     25 #include <llvm/MC/MCAsmInfo.h>
     26 #include <llvm/MC/MCContext.h>
     27 #include <llvm/MC/MCDisassembler/MCDisassembler.h>
     28 #include <llvm/MC/MCInstPrinter.h>
     29 #include <llvm/MC/MCInstrInfo.h>
     30 #include <llvm/MC/MCObjectFileInfo.h>
     31 #include <llvm/MC/MCRegisterInfo.h>
     32 #include <llvm/Support/TargetRegistry.h>
     33 
     34 #include "bcc_debug.h"
     35 
     36 namespace ebpf {
     37 
     38 // ld_pseudo can only be disassembled properly
     39 // in llvm 6.0, so having this workaround now
     40 // until disto llvm versions catch up
     41 #define WORKAROUND_FOR_LD_PSEUDO
     42 
     43 using std::get;
     44 using std::map;
     45 using std::string;
     46 using std::tuple;
     47 using std::vector;
     48 using namespace llvm;
     49 using DWARFLineTable = DWARFDebugLine::LineTable;
     50 
     51 void SourceDebugger::adjustInstSize(uint64_t &Size, uint8_t byte0,
     52                                     uint8_t byte1) {
     53 #ifdef WORKAROUND_FOR_LD_PSEUDO
     54   bool isLittleEndian = mod_->getDataLayout().isLittleEndian();
     55   if (byte0 == 0x18 && ((isLittleEndian && (byte1 & 0xf) == 0x1) ||
     56                         (!isLittleEndian && (byte1 & 0xf0) == 0x10)))
     57     Size = 16;
     58 #endif
     59 }
     60 
     61 vector<string> SourceDebugger::buildLineCache() {
     62   vector<string> LineCache;
     63   size_t FileBufSize = mod_src_.size();
     64 
     65   for (uint32_t start = 0, end = start; end < FileBufSize; end++)
     66     if (mod_src_[end] == '\n' || end == FileBufSize - 1 ||
     67         (mod_src_[end] == '\r' && mod_src_[end + 1] == '\n')) {
     68       // Not including the endline
     69       LineCache.push_back(string(mod_src_.substr(start, end - start)));
     70       if (mod_src_[end] == '\r')
     71         end++;
     72       start = end + 1;
     73     }
     74 
     75   return LineCache;
     76 }
     77 
     78 void SourceDebugger::dumpSrcLine(const vector<string> &LineCache,
     79                                  const string &FileName, uint32_t Line,
     80                                  uint32_t &CurrentSrcLine,
     81                                  llvm::raw_ostream &os) {
     82   if (Line != 0 && Line != CurrentSrcLine && Line < LineCache.size() &&
     83       FileName == mod_->getSourceFileName()) {
     84     os << "; " << StringRef(LineCache[Line - 1]).ltrim()
     85        << format(
     86               " // Line"
     87               "%4" PRIu64 "\n",
     88               Line);
     89     CurrentSrcLine = Line;
     90   }
     91 }
     92 
     93 void SourceDebugger::getDebugSections(
     94     StringMap<std::unique_ptr<MemoryBuffer>> &DebugSections) {
     95   for (auto section : sections_) {
     96     if (strncmp(section.first.c_str(), ".debug", 6) == 0) {
     97       StringRef SecData(reinterpret_cast<const char *>(get<0>(section.second)),
     98                         get<1>(section.second));
     99       DebugSections[section.first.substr(1)] =
    100           MemoryBuffer::getMemBufferCopy(SecData);
    101     }
    102   }
    103 }
    104 
    105 void SourceDebugger::dump() {
    106   string Error;
    107   string TripleStr(mod_->getTargetTriple());
    108   Triple TheTriple(TripleStr);
    109   const Target *T = TargetRegistry::lookupTarget(TripleStr, Error);
    110   if (!T) {
    111     errs() << "Debug Error: cannot get target\n";
    112     return;
    113   }
    114 
    115   std::unique_ptr<MCRegisterInfo> MRI(T->createMCRegInfo(TripleStr));
    116   if (!MRI) {
    117     errs() << "Debug Error: cannot get register info\n";
    118     return;
    119   }
    120   std::unique_ptr<MCAsmInfo> MAI(T->createMCAsmInfo(*MRI, TripleStr));
    121   if (!MAI) {
    122     errs() << "Debug Error: cannot get assembly info\n";
    123     return;
    124   }
    125 
    126   MCObjectFileInfo MOFI;
    127   MCContext Ctx(MAI.get(), MRI.get(), &MOFI, nullptr);
    128   MOFI.InitMCObjectFileInfo(TheTriple, false, Ctx, false);
    129   std::unique_ptr<MCSubtargetInfo> STI(
    130       T->createMCSubtargetInfo(TripleStr, "", ""));
    131 
    132   std::unique_ptr<MCInstrInfo> MCII(T->createMCInstrInfo());
    133   MCInstPrinter *IP = T->createMCInstPrinter(TheTriple, 0, *MAI, *MCII, *MRI);
    134   if (!IP) {
    135     errs() << "Debug Error: unable to create instruction printer\n";
    136     return;
    137   }
    138 
    139   std::unique_ptr<const MCDisassembler> DisAsm(
    140       T->createMCDisassembler(*STI, Ctx));
    141   if (!DisAsm) {
    142     errs() << "Debug Error: no disassembler\n";
    143     return;
    144   }
    145 
    146   // Set up the dwarf debug context
    147   StringMap<std::unique_ptr<MemoryBuffer>> DebugSections;
    148   getDebugSections(DebugSections);
    149   std::unique_ptr<DWARFContext> DwarfCtx =
    150       DWARFContext::create(DebugSections, 8);
    151   if (!DwarfCtx) {
    152     errs() << "Debug Error: dwarf context creation failed\n";
    153     return;
    154   }
    155 
    156   // bcc has only one compilation unit
    157   // getCompileUnitAtIndex() was gone in llvm 8.0 (https://reviews.llvm.org/D49741)
    158 #if LLVM_MAJOR_VERSION >= 8
    159   DWARFCompileUnit *CU = cast<DWARFCompileUnit>(DwarfCtx->getUnitAtIndex(0));
    160 #else
    161   DWARFCompileUnit *CU = DwarfCtx->getCompileUnitAtIndex(0);
    162 #endif
    163   if (!CU) {
    164     errs() << "Debug Error: dwarf context failed to get compile unit\n";
    165     return;
    166   }
    167 
    168   const DWARFLineTable *LineTable = DwarfCtx->getLineTableForUnit(CU);
    169   if (!LineTable) {
    170     errs() << "Debug Error: dwarf context failed to get line table\n";
    171     return;
    172   }
    173 
    174   // Build LineCache for later source code printing
    175   vector<string> LineCache = buildLineCache();
    176 
    177   // Start to disassemble with source code annotation section by section
    178   for (auto section : sections_)
    179     if (!strncmp(fn_prefix_.c_str(), section.first.c_str(),
    180                  fn_prefix_.size())) {
    181       MCDisassembler::DecodeStatus S;
    182       MCInst Inst;
    183       uint64_t Size;
    184       uint8_t *FuncStart = get<0>(section.second);
    185       uint64_t FuncSize = get<1>(section.second);
    186       ArrayRef<uint8_t> Data(FuncStart, FuncSize);
    187       uint32_t CurrentSrcLine = 0;
    188       string func_name = section.first.substr(fn_prefix_.size());
    189 
    190       errs() << "Disassembly of section " << section.first << ":\n"
    191              << func_name << ":\n";
    192 
    193       string src_dbg_str;
    194       llvm::raw_string_ostream os(src_dbg_str);
    195       for (uint64_t Index = 0; Index < FuncSize; Index += Size) {
    196         S = DisAsm->getInstruction(Inst, Size, Data.slice(Index), Index,
    197                                    nulls(), nulls());
    198         if (S != MCDisassembler::Success) {
    199           os << "Debug Error: disassembler failed: " << std::to_string(S)
    200              << '\n';
    201           break;
    202         } else {
    203           DILineInfo LineInfo;
    204           LineTable->getFileLineInfoForAddress(
    205               (uint64_t)FuncStart + Index, CU->getCompilationDir(),
    206               DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
    207               LineInfo);
    208 
    209           adjustInstSize(Size, Data[Index], Data[Index + 1]);
    210           dumpSrcLine(LineCache, LineInfo.FileName, LineInfo.Line,
    211                       CurrentSrcLine, os);
    212           os << format("%4" PRIu64 ":", Index >> 3) << '\t';
    213           dumpBytes(Data.slice(Index, Size), os);
    214           IP->printInst(&Inst, os, "", *STI);
    215           os << '\n';
    216         }
    217       }
    218       os.flush();
    219       errs() << src_dbg_str << '\n';
    220       src_dbg_fmap_[func_name] = src_dbg_str;
    221     }
    222 }
    223 
    224 }  // namespace ebpf
    225