Home | History | Annotate | Line # | Download | only in Symbolize
      1 //===- lib/DebugInfo/Symbolize/DIPrinter.cpp ------------------------------===//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 //
      9 // This file defines the DIPrinter class, which is responsible for printing
     10 // structures defined in DebugInfo/DIContext.h
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "llvm/DebugInfo/Symbolize/DIPrinter.h"
     15 #include "llvm/ADT/StringRef.h"
     16 #include "llvm/DebugInfo/DIContext.h"
     17 #include "llvm/Support/ErrorOr.h"
     18 #include "llvm/Support/Format.h"
     19 #include "llvm/Support/LineIterator.h"
     20 #include "llvm/Support/MemoryBuffer.h"
     21 #include "llvm/Support/Path.h"
     22 #include "llvm/Support/raw_ostream.h"
     23 #include <algorithm>
     24 #include <cmath>
     25 #include <cstddef>
     26 #include <cstdint>
     27 #include <memory>
     28 #include <string>
     29 
     30 namespace llvm {
     31 namespace symbolize {
     32 
     33 class SourceCode {
     34   std::unique_ptr<MemoryBuffer> MemBuf;
     35 
     36   const Optional<StringRef> load(StringRef FileName,
     37                                  const Optional<StringRef> &EmbeddedSource) {
     38     if (Lines <= 0)
     39       return None;
     40 
     41     if (EmbeddedSource)
     42       return EmbeddedSource;
     43     else {
     44       ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
     45           MemoryBuffer::getFile(FileName);
     46       if (!BufOrErr)
     47         return None;
     48       MemBuf = std::move(*BufOrErr);
     49       return MemBuf->getBuffer();
     50     }
     51   }
     52 
     53   const Optional<StringRef> pruneSource(const Optional<StringRef> &Source) {
     54     if (!Source)
     55       return None;
     56     size_t FirstLinePos = StringRef::npos, Pos = 0;
     57     for (int64_t L = 1; L <= LastLine; ++L, ++Pos) {
     58       if (L == FirstLine)
     59         FirstLinePos = Pos;
     60       Pos = Source->find('\n', Pos);
     61       if (Pos == StringRef::npos)
     62         break;
     63     }
     64     if (FirstLinePos == StringRef::npos)
     65       return None;
     66     return Source->substr(FirstLinePos, (Pos == StringRef::npos)
     67                                             ? StringRef::npos
     68                                             : Pos - FirstLinePos);
     69   }
     70 
     71 public:
     72   const int64_t Line;
     73   const int Lines;
     74   const int64_t FirstLine;
     75   const int64_t LastLine;
     76   const Optional<StringRef> PrunedSource;
     77 
     78   SourceCode(
     79       StringRef FileName, int64_t Line, int Lines,
     80       const Optional<StringRef> &EmbeddedSource = Optional<StringRef>(None))
     81       : Line(Line), Lines(Lines),
     82         FirstLine(std::max(static_cast<int64_t>(1), Line - Lines / 2)),
     83         LastLine(FirstLine + Lines - 1),
     84         PrunedSource(pruneSource(load(FileName, EmbeddedSource))) {}
     85 
     86   void format(raw_ostream &OS) {
     87     if (!PrunedSource)
     88       return;
     89     size_t MaxLineNumberWidth = std::ceil(std::log10(LastLine));
     90     int64_t L = FirstLine;
     91     for (size_t Pos = 0; Pos < PrunedSource->size(); ++L) {
     92       size_t PosEnd = PrunedSource->find('\n', Pos);
     93       StringRef String = PrunedSource->substr(
     94           Pos, (PosEnd == StringRef::npos) ? StringRef::npos : (PosEnd - Pos));
     95       if (String.endswith("\r"))
     96         String = String.drop_back(1);
     97       OS << format_decimal(L, MaxLineNumberWidth);
     98       if (L == Line)
     99         OS << " >: ";
    100       else
    101         OS << "  : ";
    102       OS << String << '\n';
    103       if (PosEnd == StringRef::npos)
    104         break;
    105       Pos = PosEnd + 1;
    106     }
    107   }
    108 };
    109 
    110 void PlainPrinterBase::printHeader(uint64_t Address) {
    111   if (Config.PrintAddress) {
    112     OS << "0x";
    113     OS.write_hex(Address);
    114     StringRef Delimiter = Config.Pretty ? ": " : "\n";
    115     OS << Delimiter;
    116   }
    117 }
    118 
    119 // Prints source code around in the FileName the Line.
    120 void PlainPrinterBase::printContext(SourceCode SourceCode) {
    121   SourceCode.format(OS);
    122 }
    123 
    124 void PlainPrinterBase::printFunctionName(StringRef FunctionName, bool Inlined) {
    125   if (Config.PrintFunctions) {
    126     if (FunctionName == DILineInfo::BadString)
    127       FunctionName = DILineInfo::Addr2LineBadString;
    128     StringRef Delimiter = Config.Pretty ? " at " : "\n";
    129     StringRef Prefix = (Config.Pretty && Inlined) ? " (inlined by) " : "";
    130     OS << Prefix << FunctionName << Delimiter;
    131   }
    132 }
    133 
    134 void LLVMPrinter::printSimpleLocation(StringRef Filename,
    135                                       const DILineInfo &Info) {
    136   OS << Filename << ':' << Info.Line << ':' << Info.Column << '\n';
    137   printContext(
    138       SourceCode(Filename, Info.Line, Config.SourceContextLines, Info.Source));
    139 }
    140 
    141 void GNUPrinter::printSimpleLocation(StringRef Filename,
    142                                      const DILineInfo &Info) {
    143   OS << Filename << ':' << Info.Line;
    144   if (Info.Discriminator)
    145     OS << " (discriminator " << Info.Discriminator << ')';
    146   OS << '\n';
    147   printContext(
    148       SourceCode(Filename, Info.Line, Config.SourceContextLines, Info.Source));
    149 }
    150 
    151 void PlainPrinterBase::printVerbose(StringRef Filename,
    152                                     const DILineInfo &Info) {
    153   OS << "  Filename: " << Filename << '\n';
    154   if (Info.StartLine) {
    155     OS << "  Function start filename: " << Info.StartFileName << '\n';
    156     OS << "  Function start line: " << Info.StartLine << '\n';
    157   }
    158   printStartAddress(Info);
    159   OS << "  Line: " << Info.Line << '\n';
    160   OS << "  Column: " << Info.Column << '\n';
    161   if (Info.Discriminator)
    162     OS << "  Discriminator: " << Info.Discriminator << '\n';
    163 }
    164 
    165 void LLVMPrinter::printStartAddress(const DILineInfo &Info) {
    166   if (Info.StartAddress) {
    167     OS << "  Function start address: 0x";
    168     OS.write_hex(*Info.StartAddress);
    169     OS << '\n';
    170   }
    171 }
    172 
    173 void LLVMPrinter::printFooter() { OS << '\n'; }
    174 
    175 void PlainPrinterBase::print(const DILineInfo &Info, bool Inlined) {
    176   printFunctionName(Info.FunctionName, Inlined);
    177   StringRef Filename = Info.FileName;
    178   if (Filename == DILineInfo::BadString)
    179     Filename = DILineInfo::Addr2LineBadString;
    180   if (Config.Verbose)
    181     printVerbose(Filename, Info);
    182   else
    183     printSimpleLocation(Filename, Info);
    184 }
    185 
    186 void PlainPrinterBase::print(const Request &Request, const DILineInfo &Info) {
    187   printHeader(*Request.Address);
    188   print(Info, false);
    189   printFooter();
    190 }
    191 
    192 void PlainPrinterBase::print(const Request &Request,
    193                              const DIInliningInfo &Info) {
    194   printHeader(*Request.Address);
    195   uint32_t FramesNum = Info.getNumberOfFrames();
    196   if (FramesNum == 0)
    197     print(DILineInfo(), false);
    198   else
    199     for (uint32_t I = 0; I < FramesNum; ++I)
    200       print(Info.getFrame(I), I > 0);
    201   printFooter();
    202 }
    203 
    204 void PlainPrinterBase::print(const Request &Request, const DIGlobal &Global) {
    205   printHeader(*Request.Address);
    206   StringRef Name = Global.Name;
    207   if (Name == DILineInfo::BadString)
    208     Name = DILineInfo::Addr2LineBadString;
    209   OS << Name << "\n";
    210   OS << Global.Start << " " << Global.Size << "\n";
    211   printFooter();
    212 }
    213 
    214 void PlainPrinterBase::print(const Request &Request,
    215                              const std::vector<DILocal> &Locals) {
    216   printHeader(*Request.Address);
    217   if (Locals.empty())
    218     OS << DILineInfo::Addr2LineBadString << '\n';
    219   else
    220     for (const DILocal &L : Locals) {
    221       if (L.FunctionName.empty())
    222         OS << DILineInfo::Addr2LineBadString;
    223       else
    224         OS << L.FunctionName;
    225       OS << '\n';
    226 
    227       if (L.Name.empty())
    228         OS << DILineInfo::Addr2LineBadString;
    229       else
    230         OS << L.Name;
    231       OS << '\n';
    232 
    233       if (L.DeclFile.empty())
    234         OS << DILineInfo::Addr2LineBadString;
    235       else
    236         OS << L.DeclFile;
    237 
    238       OS << ':' << L.DeclLine << '\n';
    239 
    240       if (L.FrameOffset)
    241         OS << *L.FrameOffset;
    242       else
    243         OS << DILineInfo::Addr2LineBadString;
    244       OS << ' ';
    245 
    246       if (L.Size)
    247         OS << *L.Size;
    248       else
    249         OS << DILineInfo::Addr2LineBadString;
    250       OS << ' ';
    251 
    252       if (L.TagOffset)
    253         OS << *L.TagOffset;
    254       else
    255         OS << DILineInfo::Addr2LineBadString;
    256       OS << '\n';
    257     }
    258   printFooter();
    259 }
    260 
    261 void PlainPrinterBase::printInvalidCommand(const Request &Request,
    262                                            StringRef Command) {
    263   OS << Command << '\n';
    264 }
    265 
    266 bool PlainPrinterBase::printError(const Request &Request,
    267                                   const ErrorInfoBase &ErrorInfo,
    268                                   StringRef ErrorBanner) {
    269   ES << ErrorBanner;
    270   ErrorInfo.log(ES);
    271   ES << '\n';
    272   // Print an empty struct too.
    273   return true;
    274 }
    275 
    276 static std::string toHex(uint64_t V) {
    277   return ("0x" + Twine::utohexstr(V)).str();
    278 }
    279 
    280 static json::Object toJSON(const Request &Request, StringRef ErrorMsg = "") {
    281   json::Object Json({{"ModuleName", Request.ModuleName.str()}});
    282   if (Request.Address)
    283     Json["Address"] = toHex(*Request.Address);
    284   if (!ErrorMsg.empty())
    285     Json["Error"] = json::Object({{"Message", ErrorMsg.str()}});
    286   return Json;
    287 }
    288 
    289 void JSONPrinter::print(const Request &Request, const DILineInfo &Info) {
    290   DIInliningInfo InliningInfo;
    291   InliningInfo.addFrame(Info);
    292   print(Request, InliningInfo);
    293 }
    294 
    295 void JSONPrinter::print(const Request &Request, const DIInliningInfo &Info) {
    296   json::Array Array;
    297   for (uint32_t I = 0, N = Info.getNumberOfFrames(); I < N; ++I) {
    298     const DILineInfo &LineInfo = Info.getFrame(I);
    299     json::Object Object(
    300         {{"FunctionName", LineInfo.FunctionName != DILineInfo::BadString
    301                               ? LineInfo.FunctionName
    302                               : ""},
    303          {"StartFileName", LineInfo.StartFileName != DILineInfo::BadString
    304                                ? LineInfo.StartFileName
    305                                : ""},
    306          {"StartLine", LineInfo.StartLine},
    307          {"StartAddress",
    308           LineInfo.StartAddress ? toHex(*LineInfo.StartAddress) : ""},
    309          {"FileName",
    310           LineInfo.FileName != DILineInfo::BadString ? LineInfo.FileName : ""},
    311          {"Line", LineInfo.Line},
    312          {"Column", LineInfo.Column},
    313          {"Discriminator", LineInfo.Discriminator}});
    314     SourceCode SourceCode(LineInfo.FileName, LineInfo.Line,
    315                           Config.SourceContextLines, LineInfo.Source);
    316     std::string FormattedSource;
    317     raw_string_ostream Stream(FormattedSource);
    318     SourceCode.format(Stream);
    319     if (!FormattedSource.empty())
    320       Object["Source"] = std::move(FormattedSource);
    321     Array.push_back(std::move(Object));
    322   }
    323   json::Object Json = toJSON(Request);
    324   Json["Symbol"] = std::move(Array);
    325   if (ObjectList)
    326     ObjectList->push_back(std::move(Json));
    327   else
    328     printJSON(std::move(Json));
    329 }
    330 
    331 void JSONPrinter::print(const Request &Request, const DIGlobal &Global) {
    332   json::Object Data(
    333       {{"Name", Global.Name != DILineInfo::BadString ? Global.Name : ""},
    334        {"Start", toHex(Global.Start)},
    335        {"Size", toHex(Global.Size)}});
    336   json::Object Json = toJSON(Request);
    337   Json["Data"] = std::move(Data);
    338   if (ObjectList)
    339     ObjectList->push_back(std::move(Json));
    340   else
    341     printJSON(std::move(Json));
    342 }
    343 
    344 void JSONPrinter::print(const Request &Request,
    345                         const std::vector<DILocal> &Locals) {
    346   json::Array Frame;
    347   for (const DILocal &Local : Locals) {
    348     json::Object FrameObject(
    349         {{"FunctionName", Local.FunctionName},
    350          {"Name", Local.Name},
    351          {"DeclFile", Local.DeclFile},
    352          {"DeclLine", int64_t(Local.DeclLine)},
    353          {"Size", Local.Size ? toHex(*Local.Size) : ""},
    354          {"TagOffset", Local.TagOffset ? toHex(*Local.TagOffset) : ""}});
    355     if (Local.FrameOffset)
    356       FrameObject["FrameOffset"] = *Local.FrameOffset;
    357     Frame.push_back(std::move(FrameObject));
    358   }
    359   json::Object Json = toJSON(Request);
    360   Json["Frame"] = std::move(Frame);
    361   if (ObjectList)
    362     ObjectList->push_back(std::move(Json));
    363   else
    364     printJSON(std::move(Json));
    365 }
    366 
    367 void JSONPrinter::printInvalidCommand(const Request &Request,
    368                                       StringRef Command) {
    369   printError(Request,
    370              StringError("unable to parse arguments: " + Command,
    371                          std::make_error_code(std::errc::invalid_argument)),
    372              "");
    373 }
    374 
    375 bool JSONPrinter::printError(const Request &Request,
    376                              const ErrorInfoBase &ErrorInfo,
    377                              StringRef ErrorBanner) {
    378   json::Object Json = toJSON(Request, ErrorInfo.message());
    379   if (ObjectList)
    380     ObjectList->push_back(std::move(Json));
    381   else
    382     printJSON(std::move(Json));
    383   return false;
    384 }
    385 
    386 void JSONPrinter::listBegin() {
    387   assert(!ObjectList);
    388   ObjectList = std::make_unique<json::Array>();
    389 }
    390 
    391 void JSONPrinter::listEnd() {
    392   assert(ObjectList);
    393   printJSON(std::move(*ObjectList));
    394   ObjectList.reset();
    395 }
    396 
    397 } // end namespace symbolize
    398 } // end namespace llvm
    399