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 void PlainPrinterBase::printHeader(uint64_t Address) { 34 if (Config.PrintAddress) { 35 OS << "0x"; 36 OS.write_hex(Address); 37 StringRef Delimiter = Config.Pretty ? ": " : "\n"; 38 OS << Delimiter; 39 } 40 } 41 42 // Prints source code around in the FileName the Line. 43 void PlainPrinterBase::printContext(StringRef FileName, int64_t Line) { 44 if (Config.SourceContextLines <= 0) 45 return; 46 47 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = 48 MemoryBuffer::getFile(FileName); 49 if (!BufOrErr) 50 return; 51 52 std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get()); 53 int64_t FirstLine = 54 std::max(static_cast<int64_t>(1), Line - Config.SourceContextLines / 2); 55 int64_t LastLine = FirstLine + Config.SourceContextLines; 56 size_t MaxLineNumberWidth = std::ceil(std::log10(LastLine)); 57 58 for (line_iterator I = line_iterator(*Buf, false); 59 !I.is_at_eof() && I.line_number() <= LastLine; ++I) { 60 int64_t L = I.line_number(); 61 if (L >= FirstLine && L <= LastLine) { 62 OS << format_decimal(L, MaxLineNumberWidth); 63 if (L == Line) 64 OS << " >: "; 65 else 66 OS << " : "; 67 OS << *I << "\n"; 68 } 69 } 70 } 71 72 void PlainPrinterBase::printFunctionName(StringRef FunctionName, bool Inlined) { 73 if (Config.PrintFunctions) { 74 if (FunctionName == DILineInfo::BadString) 75 FunctionName = DILineInfo::Addr2LineBadString; 76 StringRef Delimiter = Config.Pretty ? " at " : "\n"; 77 StringRef Prefix = (Config.Pretty && Inlined) ? " (inlined by) " : ""; 78 OS << Prefix << FunctionName << Delimiter; 79 } 80 } 81 82 void LLVMPrinter::printSimpleLocation(StringRef Filename, 83 const DILineInfo &Info) { 84 OS << Filename << ':' << Info.Line << ':' << Info.Column << '\n'; 85 printContext(Filename, Info.Line); 86 } 87 88 void GNUPrinter::printSimpleLocation(StringRef Filename, 89 const DILineInfo &Info) { 90 OS << Filename << ':' << Info.Line; 91 if (Info.Discriminator) 92 OS << " (discriminator " << Info.Discriminator << ')'; 93 OS << '\n'; 94 printContext(Filename, Info.Line); 95 } 96 97 void PlainPrinterBase::printVerbose(StringRef Filename, 98 const DILineInfo &Info) { 99 OS << " Filename: " << Filename << '\n'; 100 if (Info.StartLine) { 101 OS << " Function start filename: " << Info.StartFileName << '\n'; 102 OS << " Function start line: " << Info.StartLine << '\n'; 103 } 104 OS << " Line: " << Info.Line << '\n'; 105 OS << " Column: " << Info.Column << '\n'; 106 if (Info.Discriminator) 107 OS << " Discriminator: " << Info.Discriminator << '\n'; 108 } 109 110 void LLVMPrinter::printFooter() { OS << '\n'; } 111 112 void PlainPrinterBase::print(const DILineInfo &Info, bool Inlined) { 113 printFunctionName(Info.FunctionName, Inlined); 114 StringRef Filename = Info.FileName; 115 if (Filename == DILineInfo::BadString) 116 Filename = DILineInfo::Addr2LineBadString; 117 if (Config.Verbose) 118 printVerbose(Filename, Info); 119 else 120 printSimpleLocation(Filename, Info); 121 } 122 123 void PlainPrinterBase::print(const Request &Request, const DILineInfo &Info) { 124 printHeader(*Request.Address); 125 print(Info, false); 126 printFooter(); 127 } 128 129 void PlainPrinterBase::print(const Request &Request, 130 const DIInliningInfo &Info) { 131 printHeader(*Request.Address); 132 uint32_t FramesNum = Info.getNumberOfFrames(); 133 if (FramesNum == 0) 134 print(DILineInfo(), false); 135 else 136 for (uint32_t I = 0; I < FramesNum; ++I) 137 print(Info.getFrame(I), I > 0); 138 printFooter(); 139 } 140 141 void PlainPrinterBase::print(const Request &Request, const DIGlobal &Global) { 142 printHeader(*Request.Address); 143 StringRef Name = Global.Name; 144 if (Name == DILineInfo::BadString) 145 Name = DILineInfo::Addr2LineBadString; 146 OS << Name << "\n"; 147 OS << Global.Start << " " << Global.Size << "\n"; 148 printFooter(); 149 } 150 151 void PlainPrinterBase::print(const Request &Request, 152 const std::vector<DILocal> &Locals) { 153 printHeader(*Request.Address); 154 if (Locals.empty()) 155 OS << DILineInfo::Addr2LineBadString << '\n'; 156 else 157 for (const DILocal &L : Locals) { 158 if (L.FunctionName.empty()) 159 OS << DILineInfo::Addr2LineBadString; 160 else 161 OS << L.FunctionName; 162 OS << '\n'; 163 164 if (L.Name.empty()) 165 OS << DILineInfo::Addr2LineBadString; 166 else 167 OS << L.Name; 168 OS << '\n'; 169 170 if (L.DeclFile.empty()) 171 OS << DILineInfo::Addr2LineBadString; 172 else 173 OS << L.DeclFile; 174 175 OS << ':' << L.DeclLine << '\n'; 176 177 if (L.FrameOffset) 178 OS << *L.FrameOffset; 179 else 180 OS << DILineInfo::Addr2LineBadString; 181 OS << ' '; 182 183 if (L.Size) 184 OS << *L.Size; 185 else 186 OS << DILineInfo::Addr2LineBadString; 187 OS << ' '; 188 189 if (L.TagOffset) 190 OS << *L.TagOffset; 191 else 192 OS << DILineInfo::Addr2LineBadString; 193 OS << '\n'; 194 } 195 printFooter(); 196 } 197 198 void PlainPrinterBase::printInvalidCommand(const Request &Request, 199 StringRef Command) { 200 OS << Command << '\n'; 201 } 202 203 bool PlainPrinterBase::printError(const Request &Request, 204 const ErrorInfoBase &ErrorInfo, 205 StringRef ErrorBanner) { 206 ES << ErrorBanner; 207 ErrorInfo.log(ES); 208 ES << '\n'; 209 // Print an empty struct too. 210 return true; 211 } 212 213 static std::string toHex(uint64_t V) { 214 return ("0x" + Twine::utohexstr(V)).str(); 215 } 216 217 static json::Object toJSON(const Request &Request, StringRef ErrorMsg = "") { 218 json::Object Json({{"ModuleName", Request.ModuleName.str()}}); 219 if (Request.Address) 220 Json["Address"] = toHex(*Request.Address); 221 if (!ErrorMsg.empty()) 222 Json["Error"] = json::Object({{"Message", ErrorMsg.str()}}); 223 return Json; 224 } 225 226 void JSONPrinter::print(const Request &Request, const DILineInfo &Info) { 227 DIInliningInfo InliningInfo; 228 InliningInfo.addFrame(Info); 229 print(Request, InliningInfo); 230 } 231 232 void JSONPrinter::print(const Request &Request, const DIInliningInfo &Info) { 233 json::Array Array; 234 for (uint32_t I = 0, N = Info.getNumberOfFrames(); I < N; ++I) { 235 const DILineInfo &LineInfo = Info.getFrame(I); 236 Array.push_back(json::Object( 237 {{"FunctionName", LineInfo.FunctionName != DILineInfo::BadString 238 ? LineInfo.FunctionName 239 : ""}, 240 {"StartFileName", LineInfo.StartFileName != DILineInfo::BadString 241 ? LineInfo.StartFileName 242 : ""}, 243 {"StartLine", LineInfo.StartLine}, 244 {"FileName", 245 LineInfo.FileName != DILineInfo::BadString ? LineInfo.FileName : ""}, 246 {"Line", LineInfo.Line}, 247 {"Column", LineInfo.Column}, 248 {"Discriminator", LineInfo.Discriminator}})); 249 } 250 json::Object Json = toJSON(Request); 251 Json["Symbol"] = std::move(Array); 252 if (ObjectList) 253 ObjectList->push_back(std::move(Json)); 254 else 255 printJSON(std::move(Json)); 256 } 257 258 void JSONPrinter::print(const Request &Request, const DIGlobal &Global) { 259 json::Object Data( 260 {{"Name", Global.Name != DILineInfo::BadString ? Global.Name : ""}, 261 {"Start", toHex(Global.Start)}, 262 {"Size", toHex(Global.Size)}}); 263 json::Object Json = toJSON(Request); 264 Json["Data"] = std::move(Data); 265 if (ObjectList) 266 ObjectList->push_back(std::move(Json)); 267 else 268 printJSON(std::move(Json)); 269 } 270 271 void JSONPrinter::print(const Request &Request, 272 const std::vector<DILocal> &Locals) { 273 json::Array Frame; 274 for (const DILocal &Local : Locals) { 275 json::Object FrameObject( 276 {{"FunctionName", Local.FunctionName}, 277 {"Name", Local.Name}, 278 {"DeclFile", Local.DeclFile}, 279 {"DeclLine", int64_t(Local.DeclLine)}, 280 {"Size", Local.Size ? toHex(*Local.Size) : ""}, 281 {"TagOffset", Local.TagOffset ? toHex(*Local.TagOffset) : ""}}); 282 if (Local.FrameOffset) 283 FrameObject["FrameOffset"] = *Local.FrameOffset; 284 Frame.push_back(std::move(FrameObject)); 285 } 286 json::Object Json = toJSON(Request); 287 Json["Frame"] = std::move(Frame); 288 if (ObjectList) 289 ObjectList->push_back(std::move(Json)); 290 else 291 printJSON(std::move(Json)); 292 } 293 294 void JSONPrinter::printInvalidCommand(const Request &Request, 295 StringRef Command) { 296 printError(Request, 297 StringError("unable to parse arguments: " + Command, 298 std::make_error_code(std::errc::invalid_argument)), 299 ""); 300 } 301 302 bool JSONPrinter::printError(const Request &Request, 303 const ErrorInfoBase &ErrorInfo, 304 StringRef ErrorBanner) { 305 json::Object Json = toJSON(Request, ErrorInfo.message()); 306 if (ObjectList) 307 ObjectList->push_back(std::move(Json)); 308 else 309 printJSON(std::move(Json)); 310 return false; 311 } 312 313 void JSONPrinter::listBegin() { 314 assert(!ObjectList); 315 ObjectList = std::make_unique<json::Array>(); 316 } 317 318 void JSONPrinter::listEnd() { 319 assert(ObjectList); 320 printJSON(std::move(*ObjectList)); 321 ObjectList.release(); 322 } 323 324 } // end namespace symbolize 325 } // end namespace llvm 326