//===- lib/DebugInfo/Symbolize/DIPrinter.cpp ------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines the DIPrinter class, which is responsible for printing // structures defined in DebugInfo/DIContext.h // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/Symbolize/DIPrinter.h" #include "llvm/ADT/StringRef.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/Format.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include namespace llvm { namespace symbolize { class SourceCode { std::unique_ptr MemBuf; std::optional load(StringRef FileName, const std::optional &EmbeddedSource) { if (Lines <= 0) return std::nullopt; if (EmbeddedSource) return EmbeddedSource; else { ErrorOr> BufOrErr = MemoryBuffer::getFile(FileName); if (!BufOrErr) return std::nullopt; MemBuf = std::move(*BufOrErr); return MemBuf->getBuffer(); } } std::optional pruneSource(const std::optional &Source) { if (!Source) return std::nullopt; size_t FirstLinePos = StringRef::npos, Pos = 0; for (int64_t L = 1; L <= LastLine; ++L, ++Pos) { if (L == FirstLine) FirstLinePos = Pos; Pos = Source->find('\n', Pos); if (Pos == StringRef::npos) break; } if (FirstLinePos == StringRef::npos) return std::nullopt; return Source->substr(FirstLinePos, (Pos == StringRef::npos) ? StringRef::npos : Pos - FirstLinePos); } public: const int64_t Line; const int Lines; const int64_t FirstLine; const int64_t LastLine; const std::optional PrunedSource; SourceCode(StringRef FileName, int64_t Line, int Lines, const std::optional &EmbeddedSource = std::optional()) : Line(Line), Lines(Lines), FirstLine(std::max(static_cast(1), Line - Lines / 2)), LastLine(FirstLine + Lines - 1), PrunedSource(pruneSource(load(FileName, EmbeddedSource))) {} void format(raw_ostream &OS) { if (!PrunedSource) return; size_t MaxLineNumberWidth = std::ceil(std::log10(LastLine)); int64_t L = FirstLine; for (size_t Pos = 0; Pos < PrunedSource->size(); ++L) { size_t PosEnd = PrunedSource->find('\n', Pos); StringRef String = PrunedSource->substr( Pos, (PosEnd == StringRef::npos) ? StringRef::npos : (PosEnd - Pos)); if (String.endswith("\r")) String = String.drop_back(1); OS << format_decimal(L, MaxLineNumberWidth); if (L == Line) OS << " >: "; else OS << " : "; OS << String << '\n'; if (PosEnd == StringRef::npos) break; Pos = PosEnd + 1; } } }; void PlainPrinterBase::printHeader(uint64_t Address) { if (Config.PrintAddress) { OS << "0x"; OS.write_hex(Address); StringRef Delimiter = Config.Pretty ? ": " : "\n"; OS << Delimiter; } } // Prints source code around in the FileName the Line. void PlainPrinterBase::printContext(SourceCode SourceCode) { SourceCode.format(OS); } void PlainPrinterBase::printFunctionName(StringRef FunctionName, bool Inlined) { if (Config.PrintFunctions) { if (FunctionName == DILineInfo::BadString) FunctionName = DILineInfo::Addr2LineBadString; StringRef Delimiter = Config.Pretty ? " at " : "\n"; StringRef Prefix = (Config.Pretty && Inlined) ? " (inlined by) " : ""; OS << Prefix << FunctionName << Delimiter; } } void LLVMPrinter::printSimpleLocation(StringRef Filename, const DILineInfo &Info) { OS << Filename << ':' << Info.Line << ':' << Info.Column << '\n'; printContext( SourceCode(Filename, Info.Line, Config.SourceContextLines, Info.Source)); } void GNUPrinter::printSimpleLocation(StringRef Filename, const DILineInfo &Info) { OS << Filename << ':' << Info.Line; if (Info.Discriminator) OS << " (discriminator " << Info.Discriminator << ')'; OS << '\n'; printContext( SourceCode(Filename, Info.Line, Config.SourceContextLines, Info.Source)); } void PlainPrinterBase::printVerbose(StringRef Filename, const DILineInfo &Info) { OS << " Filename: " << Filename << '\n'; if (Info.StartLine) { OS << " Function start filename: " << Info.StartFileName << '\n'; OS << " Function start line: " << Info.StartLine << '\n'; } printStartAddress(Info); OS << " Line: " << Info.Line << '\n'; OS << " Column: " << Info.Column << '\n'; if (Info.Discriminator) OS << " Discriminator: " << Info.Discriminator << '\n'; } void LLVMPrinter::printStartAddress(const DILineInfo &Info) { if (Info.StartAddress) { OS << " Function start address: 0x"; OS.write_hex(*Info.StartAddress); OS << '\n'; } } void LLVMPrinter::printFooter() { OS << '\n'; } void PlainPrinterBase::print(const DILineInfo &Info, bool Inlined) { printFunctionName(Info.FunctionName, Inlined); StringRef Filename = Info.FileName; if (Filename == DILineInfo::BadString) Filename = DILineInfo::Addr2LineBadString; if (Config.Verbose) printVerbose(Filename, Info); else printSimpleLocation(Filename, Info); } void PlainPrinterBase::print(const Request &Request, const DILineInfo &Info) { printHeader(*Request.Address); print(Info, false); printFooter(); } void PlainPrinterBase::print(const Request &Request, const DIInliningInfo &Info) { printHeader(*Request.Address); uint32_t FramesNum = Info.getNumberOfFrames(); if (FramesNum == 0) print(DILineInfo(), false); else for (uint32_t I = 0; I < FramesNum; ++I) print(Info.getFrame(I), I > 0); printFooter(); } void PlainPrinterBase::print(const Request &Request, const DIGlobal &Global) { printHeader(*Request.Address); StringRef Name = Global.Name; if (Name == DILineInfo::BadString) Name = DILineInfo::Addr2LineBadString; OS << Name << "\n"; OS << Global.Start << " " << Global.Size << "\n"; if (Global.DeclFile.empty()) OS << "??:?\n"; else OS << Global.DeclFile << ":" << Global.DeclLine << "\n"; printFooter(); } void PlainPrinterBase::print(const Request &Request, const std::vector &Locals) { printHeader(*Request.Address); if (Locals.empty()) OS << DILineInfo::Addr2LineBadString << '\n'; else for (const DILocal &L : Locals) { if (L.FunctionName.empty()) OS << DILineInfo::Addr2LineBadString; else OS << L.FunctionName; OS << '\n'; if (L.Name.empty()) OS << DILineInfo::Addr2LineBadString; else OS << L.Name; OS << '\n'; if (L.DeclFile.empty()) OS << DILineInfo::Addr2LineBadString; else OS << L.DeclFile; OS << ':' << L.DeclLine << '\n'; if (L.FrameOffset) OS << *L.FrameOffset; else OS << DILineInfo::Addr2LineBadString; OS << ' '; if (L.Size) OS << *L.Size; else OS << DILineInfo::Addr2LineBadString; OS << ' '; if (L.TagOffset) OS << *L.TagOffset; else OS << DILineInfo::Addr2LineBadString; OS << '\n'; } printFooter(); } void PlainPrinterBase::printInvalidCommand(const Request &Request, StringRef Command) { OS << Command << '\n'; } bool PlainPrinterBase::printError(const Request &Request, const ErrorInfoBase &ErrorInfo) { ErrHandler(ErrorInfo, Request.ModuleName); // Print an empty struct too. return true; } static std::string toHex(uint64_t V) { return ("0x" + Twine::utohexstr(V)).str(); } static json::Object toJSON(const Request &Request, StringRef ErrorMsg = "") { json::Object Json({{"ModuleName", Request.ModuleName.str()}}); if (Request.Address) Json["Address"] = toHex(*Request.Address); if (!ErrorMsg.empty()) Json["Error"] = json::Object({{"Message", ErrorMsg.str()}}); return Json; } static json::Object toJSON(const DILineInfo &LineInfo) { return json::Object( {{"FunctionName", LineInfo.FunctionName != DILineInfo::BadString ? LineInfo.FunctionName : ""}, {"StartFileName", LineInfo.StartFileName != DILineInfo::BadString ? LineInfo.StartFileName : ""}, {"StartLine", LineInfo.StartLine}, {"StartAddress", LineInfo.StartAddress ? toHex(*LineInfo.StartAddress) : ""}, {"FileName", LineInfo.FileName != DILineInfo::BadString ? LineInfo.FileName : ""}, {"Line", LineInfo.Line}, {"Column", LineInfo.Column}, {"Discriminator", LineInfo.Discriminator}}); } void JSONPrinter::print(const Request &Request, const DILineInfo &Info) { DIInliningInfo InliningInfo; InliningInfo.addFrame(Info); print(Request, InliningInfo); } void JSONPrinter::print(const Request &Request, const DIInliningInfo &Info) { json::Array Array; for (uint32_t I = 0, N = Info.getNumberOfFrames(); I < N; ++I) { const DILineInfo &LineInfo = Info.getFrame(I); json::Object Object = toJSON(LineInfo); SourceCode SourceCode(LineInfo.FileName, LineInfo.Line, Config.SourceContextLines, LineInfo.Source); std::string FormattedSource; raw_string_ostream Stream(FormattedSource); SourceCode.format(Stream); if (!FormattedSource.empty()) Object["Source"] = std::move(FormattedSource); Array.push_back(std::move(Object)); } json::Object Json = toJSON(Request); Json["Symbol"] = std::move(Array); if (ObjectList) ObjectList->push_back(std::move(Json)); else printJSON(std::move(Json)); } void JSONPrinter::print(const Request &Request, const DIGlobal &Global) { json::Object Data( {{"Name", Global.Name != DILineInfo::BadString ? Global.Name : ""}, {"Start", toHex(Global.Start)}, {"Size", toHex(Global.Size)}}); json::Object Json = toJSON(Request); Json["Data"] = std::move(Data); if (ObjectList) ObjectList->push_back(std::move(Json)); else printJSON(std::move(Json)); } void JSONPrinter::print(const Request &Request, const std::vector &Locals) { json::Array Frame; for (const DILocal &Local : Locals) { json::Object FrameObject( {{"FunctionName", Local.FunctionName}, {"Name", Local.Name}, {"DeclFile", Local.DeclFile}, {"DeclLine", int64_t(Local.DeclLine)}, {"Size", Local.Size ? toHex(*Local.Size) : ""}, {"TagOffset", Local.TagOffset ? toHex(*Local.TagOffset) : ""}}); if (Local.FrameOffset) FrameObject["FrameOffset"] = *Local.FrameOffset; Frame.push_back(std::move(FrameObject)); } json::Object Json = toJSON(Request); Json["Frame"] = std::move(Frame); if (ObjectList) ObjectList->push_back(std::move(Json)); else printJSON(std::move(Json)); } void JSONPrinter::printInvalidCommand(const Request &Request, StringRef Command) { printError(Request, StringError("unable to parse arguments: " + Command, std::make_error_code(std::errc::invalid_argument))); } bool JSONPrinter::printError(const Request &Request, const ErrorInfoBase &ErrorInfo) { json::Object Json = toJSON(Request, ErrorInfo.message()); if (ObjectList) ObjectList->push_back(std::move(Json)); else printJSON(std::move(Json)); return false; } void JSONPrinter::listBegin() { assert(!ObjectList); ObjectList = std::make_unique(); } void JSONPrinter::listEnd() { assert(ObjectList); printJSON(std::move(*ObjectList)); ObjectList.reset(); } } // end namespace symbolize } // end namespace llvm