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
24using namespace llvm;
25using namespace logicalview;
26using namespace cmdline;
27
28/// Create formatted StringError object.
29static StringRef ToolName = "llvm-debuginfo-analyzer";
30template <typename... Ts>
31static 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(OS&: errs(), Prefix: ToolName) << Stream.str() << "\n";
38 exit(status: 1);
39}
40
41static void error(Error EC) {
42 if (!EC)
43 return;
44 handleAllErrors(E: std::move(EC), Handlers: [&](const ErrorInfoBase &EI) {
45 errs() << "\n";
46 WithColor::error(OS&: errs(), Prefix: ToolName) << EI.message() << ".\n";
47 exit(status: 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.
54static 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(path&: BundlePath);
59 // Manually open up the bundle to avoid introducing additional dependencies.
60 if (sys::fs::is_directory(Path: BundlePath) &&
61 sys::path::extension(path: BundlePath) == ".dSYM") {
62 std::error_code EC;
63 sys::path::append(path&: BundlePath, a: "Contents", b: "Resources", c: "DWARF");
64 for (sys::fs::directory_iterator Dir(BundlePath, EC), DirEnd;
65 Dir != DirEnd && !EC; Dir.increment(ec&: EC)) {
66 const std::string &Path = Dir->path();
67 sys::fs::file_status Status;
68 EC = sys::fs::status(path: Path, result&: Status);
69 error(EC, Fmt: "%s", Vals: 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(x: Path);
75 break;
76 default: /*ignore*/;
77 }
78 }
79 }
80 if (BundlePaths.empty())
81 BundlePaths.push_back(x: InputPath);
82 return BundlePaths;
83}
84
85int 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 Categories: {&AttributeCategory, &CompareCategory, &InternalCategory, &OutputCategory,
100 &PrintCategory, &ReportCategory, &SelectCategory, &WarningCategory});
101 cl::ParseCommandLineOptions(argc, argv,
102 Overview: "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, Fmt: "Unable to open output file %s", Vals: 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(value: "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(InputPath: Filename);
120 Objects.insert(position: Objects.end(), first: Objs.begin(), last: 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(EC: std::move(Err));
139
140 return EXIT_SUCCESS;
141}
142