1 //===-- llvm-debuginfo-analyzer.cpp - LLVM Debug info analysis utility ---===//
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 program is a utility that displays the logical view for the debug
10 // information.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "Options.h"
15 #include "llvm/DebugInfo/LogicalView/Core/LVOptions.h"
16 #include "llvm/DebugInfo/LogicalView/LVReaderHandler.h"
17 #include "llvm/Support/COM.h"
18 #include "llvm/Support/CommandLine.h"
19 #include "llvm/Support/InitLLVM.h"
20 #include "llvm/Support/TargetSelect.h"
21 #include "llvm/Support/ToolOutputFile.h"
22 #include "llvm/Support/WithColor.h"
23 
24 using namespace llvm;
25 using namespace logicalview;
26 using namespace cmdline;
27 
28 /// Create formatted StringError object.
29 static StringRef ToolName = "llvm-debuginfo-analyzer";
30 template <typename... Ts>
31 static void error(std::error_code EC, char const *Fmt, const Ts &...Vals) {
32   if (!EC)
33     return;
34   std::string Buffer;
35   raw_string_ostream Stream(Buffer);
36   Stream << format(Fmt, Vals...);
37   WithColor::error(errs(), ToolName) << Stream.str() << "\n";
38   exit(1);
39 }
40 
41 static void error(Error EC) {
42   if (!EC)
43     return;
44   handleAllErrors(std::move(EC), [&](const ErrorInfoBase &EI) {
45     errs() << "\n";
46     WithColor::error(errs(), ToolName) << EI.message() << ".\n";
47     exit(1);
48   });
49 }
50 
51 /// If the input path is a .dSYM bundle (as created by the dsymutil tool),
52 /// replace it with individual entries for each of the object files inside the
53 /// bundle otherwise return the input path.
54 static std::vector<std::string> expandBundle(const std::string &InputPath) {
55   std::vector<std::string> BundlePaths;
56   SmallString<256> BundlePath(InputPath);
57   // Normalize input path. This is necessary to accept `bundle.dSYM/`.
58   sys::path::remove_dots(BundlePath);
59   // Manually open up the bundle to avoid introducing additional dependencies.
60   if (sys::fs::is_directory(BundlePath) &&
61       sys::path::extension(BundlePath) == ".dSYM") {
62     std::error_code EC;
63     sys::path::append(BundlePath, "Contents", "Resources", "DWARF");
64     for (sys::fs::directory_iterator Dir(BundlePath, EC), DirEnd;
65          Dir != DirEnd && !EC; Dir.increment(EC)) {
66       const std::string &Path = Dir->path();
67       sys::fs::file_status Status;
68       EC = sys::fs::status(Path, Status);
69       error(EC, "%s", Path.c_str());
70       switch (Status.type()) {
71       case sys::fs::file_type::regular_file:
72       case sys::fs::file_type::symlink_file:
73       case sys::fs::file_type::type_unknown:
74         BundlePaths.push_back(Path);
75         break;
76       default: /*ignore*/;
77       }
78     }
79   }
80   if (BundlePaths.empty())
81     BundlePaths.push_back(InputPath);
82   return BundlePaths;
83 }
84 
85 int main(int argc, char **argv) {
86   InitLLVM X(argc, argv);
87 
88   // Initialize targets and assembly printers/parsers.
89   llvm::InitializeAllTargetInfos();
90   llvm::InitializeAllTargetMCs();
91   InitializeAllDisassemblers();
92 
93   llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded);
94 
95   cl::extrahelp HelpResponse(
96       "\nPass @FILE as argument to read options from FILE.\n");
97 
98   cl::HideUnrelatedOptions(
99       {&AttributeCategory, &CompareCategory, &InternalCategory, &OutputCategory,
100        &PrintCategory, &ReportCategory, &SelectCategory, &WarningCategory});
101   cl::ParseCommandLineOptions(argc, argv,
102                               "Printing a logical representation of low-level "
103                               "debug information.\n");
104   cl::PrintOptionValues();
105 
106   std::error_code EC;
107   ToolOutputFile OutputFile(OutputFilename, EC, sys::fs::OF_None);
108   error(EC, "Unable to open output file %s", OutputFilename.c_str());
109   // Don't remove output file if we exit with an error.
110   OutputFile.keep();
111 
112   // Defaults to a.out if no filenames specified.
113   if (InputFilenames.empty())
114     InputFilenames.push_back("a.out");
115 
116   // Expand any .dSYM bundles to the individual object files contained therein.
117   std::vector<std::string> Objects;
118   for (const std::string &Filename : InputFilenames) {
119     std::vector<std::string> Objs = expandBundle(Filename);
120     Objects.insert(Objects.end(), Objs.begin(), Objs.end());
121   }
122 
123   propagateOptions();
124   ScopedPrinter W(OutputFile.os());
125   LVReaderHandler ReaderHandler(Objects, W, ReaderOptions);
126 
127   // Print the command line.
128   if (options().getInternalCmdline()) {
129     raw_ostream &Stream = W.getOStream();
130     Stream << "\nCommand line:\n";
131     for (int Index = 0; Index < argc; ++Index)
132       Stream << "  " << argv[Index] << "\n";
133     Stream << "\n";
134   }
135 
136   // Create readers and perform requested tasks on them.
137   if (Error Err = ReaderHandler.process())
138     error(std::move(Err));
139 
140   return EXIT_SUCCESS;
141 }
142