[llvm] 1f35d7b - [llvm-readobj] Add JSONScopedPrinter to llvm-readelf

Jayson Yan via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 10 15:35:17 PST 2021


Author: Jayson Yan
Date: 2021-12-10T23:34:27Z
New Revision: 1f35d7b5de62d17c1702f84b0217151bf1f31b1a

URL: https://github.com/llvm/llvm-project/commit/1f35d7b5de62d17c1702f84b0217151bf1f31b1a
DIFF: https://github.com/llvm/llvm-project/commit/1f35d7b5de62d17c1702f84b0217151bf1f31b1a.diff

LOG: [llvm-readobj] Add JSONScopedPrinter to llvm-readelf

Adds JSONScopedPrinter to llvm-readelf. It includes an empty
JSONELFDumper class which will be used to override any LLVMELFDumper
methods which utilize startLine() which JSONScopedPrinter cannot
provide.

This introduces a change where calls to llvm-readelf with non-ELF object
files that specify --elf-output-style=GNU will now print file summary
information where it previously didn't.

Fixes previous Windows test failure which occured due to JSON escaping
of '\' by not relying on LIT substitution.

Reviewed By: jhenderson

Differential Revision: https://reviews.llvm.org/D114225

Added: 
    llvm/test/tools/llvm-readobj/ELF/file-summary-json.test
    llvm/test/tools/llvm-readobj/ELF/pretty-print.test

Modified: 
    llvm/docs/CommandGuide/llvm-readelf.rst
    llvm/docs/CommandGuide/llvm-readobj.rst
    llvm/test/tools/llvm-readobj/ELF/output-style.test
    llvm/tools/llvm-readobj/ELFDumper.cpp
    llvm/tools/llvm-readobj/ObjDumper.cpp
    llvm/tools/llvm-readobj/ObjDumper.h
    llvm/tools/llvm-readobj/Opts.td
    llvm/tools/llvm-readobj/llvm-readobj.cpp
    llvm/tools/llvm-readobj/llvm-readobj.h

Removed: 
    


################################################################################
diff  --git a/llvm/docs/CommandGuide/llvm-readelf.rst b/llvm/docs/CommandGuide/llvm-readelf.rst
index d83d566b2f82b..dd4d82190eb55 100644
--- a/llvm/docs/CommandGuide/llvm-readelf.rst
+++ b/llvm/docs/CommandGuide/llvm-readelf.rst
@@ -72,9 +72,10 @@ OPTIONS
 
 .. option:: --elf-output-style=<value>
 
- Format ELF information in the specified style. Valid options are ``LLVM`` and
- ``GNU``. ``LLVM`` output is an expanded and structured format, whilst ``GNU``
- (the default) output mimics the equivalent GNU :program:`readelf` output.
+ Format ELF information in the specified style. Valid options are ``LLVM``,
+ ``GNU``, and ``JSON``. ``LLVM`` output is an expanded and structured format.
+ ``GNU`` (the default) output mimics the equivalent GNU :program:`readelf`
+ output. ``JSON`` is JSON formatted output intended for machine consumption.
 
 .. option:: --section-groups, -g
 
@@ -127,6 +128,11 @@ OPTIONS
 
  Display all notes.
 
+.. option:: --pretty-print
+
+ When used with :option:`--elf-output-style`, JSON output will be formatted in
+ a more readable format.
+
 .. option:: --program-headers, --segments, -l
 
  Display the program headers.

diff  --git a/llvm/docs/CommandGuide/llvm-readobj.rst b/llvm/docs/CommandGuide/llvm-readobj.rst
index e7e6c73b3148c..ef2ea3eb0e587 100644
--- a/llvm/docs/CommandGuide/llvm-readobj.rst
+++ b/llvm/docs/CommandGuide/llvm-readobj.rst
@@ -183,9 +183,10 @@ The following options are implemented only for the ELF file format.
 
 .. option:: --elf-output-style=<value>
 
- Format ELF information in the specified style. Valid options are ``LLVM`` and
- ``GNU``. ``LLVM`` output (the default) is an expanded and structured format,
- whilst ``GNU`` output mimics the equivalent GNU :program:`readelf` output.
+ Format ELF information in the specified style. Valid options are ``LLVM``,
+ ``GNU``, and ``JSON``. ``LLVM`` output (the default) is an expanded and
+ structured format. ``GNU`` output mimics the equivalent GNU :program:`readelf`
+ output. ``JSON`` is JSON formatted output intended for machine consumption.
 
 .. option:: --section-groups, -g
 
@@ -207,6 +208,11 @@ The following options are implemented only for the ELF file format.
 
  Display all notes.
 
+.. option:: --pretty-print
+
+ When used with :option:`--elf-output-style`, JSON output will be formatted in
+ a more readable format.
+
 .. option:: --program-headers, --segments, -l
 
  Display the program headers.

diff  --git a/llvm/test/tools/llvm-readobj/ELF/file-summary-json.test b/llvm/test/tools/llvm-readobj/ELF/file-summary-json.test
new file mode 100644
index 0000000000000..7af0a01314136
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/file-summary-json.test
@@ -0,0 +1,114 @@
+## Test how we output JSON file summaries.
+
+# RUN: rm -rf %t.dir
+# RUN: mkdir -p %t.dir
+# RUN: yaml2obj %s -o %t.dir/obj
+
+## Test outputting file summary for a single file.
+# RUN: llvm-readobj --elf-output-style=JSON --pretty-print %t.dir/obj | \
+# RUN:   FileCheck %s --check-prefix=SINGLE \
+# RUN:     --match-full-lines --strict-whitespace --implicit-check-not={{.}}
+
+#      SINGLE:[
+# SINGLE-NEXT:  {
+# SINGLE-NEXT:    "{{.*}}/obj": {
+# SINGLE-NEXT:      "FileSummary": {
+# SINGLE-NEXT:        "File": "{{.*}}/obj",
+# SINGLE-NEXT:        "Format": "elf64-x86-64",
+# SINGLE-NEXT:        "Arch": "x86_64",
+# SINGLE-NEXT:        "AddressSize": "64bit",
+# SINGLE-NEXT:        "LoadName": "<Not found>"
+# SINGLE-NEXT:      }
+# SINGLE-NEXT:    }
+# SINGLE-NEXT:  }
+# SINGLE-NEXT:]
+
+## Test outputting file summary for multiple files.
+# RUN: llvm-readobj --elf-output-style=JSON --pretty-print %t.dir/obj %t.dir/obj | \
+# RUN:   FileCheck %s --check-prefix=MULTI \
+# RUN:     --match-full-lines --strict-whitespace --implicit-check-not={{.}}
+
+#      MULTI:[
+# MULTI-NEXT:  {
+# MULTI-NEXT:    "{{.*}}/obj": {
+# MULTI-NEXT:      "FileSummary": {
+# MULTI-NEXT:        "File": "{{.*}}/obj",
+# MULTI-NEXT:        "Format": "elf64-x86-64",
+# MULTI-NEXT:        "Arch": "x86_64",
+# MULTI-NEXT:        "AddressSize": "64bit",
+# MULTI-NEXT:        "LoadName": "<Not found>"
+# MULTI-NEXT:      }
+# MULTI-NEXT:    }
+# MULTI-NEXT:  },
+# MULTI-NEXT:  {
+# MULTI-NEXT:    "{{.*}}/obj": {
+# MULTI-NEXT:      "FileSummary": {
+# MULTI-NEXT:        "File": "{{.*}}/obj",
+# MULTI-NEXT:        "Format": "elf64-x86-64",
+# MULTI-NEXT:        "Arch": "x86_64",
+# MULTI-NEXT:        "AddressSize": "64bit",
+# MULTI-NEXT:        "LoadName": "<Not found>"
+# MULTI-NEXT:      }
+# MULTI-NEXT:    }
+# MULTI-NEXT:  }
+# MULTI-NEXT:]
+
+## Test outputting file summary for an archive with a single file.
+# RUN: rm -f %t.archive-single
+# RUN: llvm-ar rc %t.archive-single %t.dir/obj
+# RUN: llvm-readobj --elf-output-style=JSON --pretty-print %t.archive-single | \
+# RUN:   FileCheck %s --check-prefix=ARCH-SINGLE \
+# RUN:     --match-full-lines --strict-whitespace --implicit-check-not={{.}}
+
+#      ARCH-SINGLE:[
+# ARCH-SINGLE-NEXT:  {
+# ARCH-SINGLE-NEXT:    "{{.*}}.archive-single(obj)": {
+# ARCH-SINGLE-NEXT:      "FileSummary": {
+# ARCH-SINGLE-NEXT:        "File": "{{.*}}.archive-single(obj)",
+# ARCH-SINGLE-NEXT:        "Format": "elf64-x86-64",
+# ARCH-SINGLE-NEXT:        "Arch": "x86_64",
+# ARCH-SINGLE-NEXT:        "AddressSize": "64bit",
+# ARCH-SINGLE-NEXT:        "LoadName": "<Not found>"
+# ARCH-SINGLE-NEXT:      }
+# ARCH-SINGLE-NEXT:    }
+# ARCH-SINGLE-NEXT:  }
+# ARCH-SINGLE-NEXT:]
+
+## Test outputting file summary for an archive with multiple files.
+# RUN: rm -f %t.archive-multiple
+# RUN: llvm-ar rc %t.archive-multiple %t.dir/obj %t.dir/obj
+# RUN: llvm-readobj --elf-output-style=JSON --pretty-print %t.archive-multiple | \
+# RUN:   FileCheck %s --check-prefix=ARCH-MULTI \
+# RUN:     --match-full-lines --strict-whitespace --implicit-check-not={{.}}
+
+#      ARCH-MULTI:[
+# ARCH-MULTI-NEXT:  {
+# ARCH-MULTI-NEXT:    "{{.*}}.archive-multiple(obj)": {
+# ARCH-MULTI-NEXT:      "FileSummary": {
+# ARCH-MULTI-NEXT:        "File": "{{.*}}.archive-multiple(obj)",
+# ARCH-MULTI-NEXT:        "Format": "elf64-x86-64",
+# ARCH-MULTI-NEXT:        "Arch": "x86_64",
+# ARCH-MULTI-NEXT:        "AddressSize": "64bit",
+# ARCH-MULTI-NEXT:        "LoadName": "<Not found>"
+# ARCH-MULTI-NEXT:      }
+# ARCH-MULTI-NEXT:    }
+# ARCH-MULTI-NEXT:  },
+# ARCH-MULTI-NEXT:  {
+# ARCH-MULTI-NEXT:    "{{.*}}.archive-multiple(obj)": {
+# ARCH-MULTI-NEXT:      "FileSummary": {
+# ARCH-MULTI-NEXT:        "File": "{{.*}}.archive-multiple(obj)",
+# ARCH-MULTI-NEXT:        "Format": "elf64-x86-64",
+# ARCH-MULTI-NEXT:        "Arch": "x86_64",
+# ARCH-MULTI-NEXT:        "AddressSize": "64bit",
+# ARCH-MULTI-NEXT:        "LoadName": "<Not found>"
+# ARCH-MULTI-NEXT:      }
+# ARCH-MULTI-NEXT:    }
+# ARCH-MULTI-NEXT:  }
+# ARCH-MULTI-NEXT:]
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_REL
+  Machine: EM_X86_64

diff  --git a/llvm/test/tools/llvm-readobj/ELF/output-style.test b/llvm/test/tools/llvm-readobj/ELF/output-style.test
index 1ad347a081704..a79c6e993399d 100644
--- a/llvm/test/tools/llvm-readobj/ELF/output-style.test
+++ b/llvm/test/tools/llvm-readobj/ELF/output-style.test
@@ -1,4 +1,4 @@
 ## Error for an unknown output style.
 RUN: not llvm-readobj --elf-output-style=unknown 2>&1 | FileCheck %s
 
-CHECK: error: --elf-output-style value should be either 'LLVM' or 'GNU'
+CHECK: error: --elf-output-style value should be either 'LLVM', 'GNU', or 'JSON', but was 'unknown'

diff  --git a/llvm/test/tools/llvm-readobj/ELF/pretty-print.test b/llvm/test/tools/llvm-readobj/ELF/pretty-print.test
new file mode 100644
index 0000000000000..0f27db3992dfd
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/pretty-print.test
@@ -0,0 +1,48 @@
+## Test the JSON pretty-print flag.
+#
+# RUN: yaml2obj %s -o %t.pretty
+
+## Test JSON with pretty-print off.
+# RUN: llvm-readobj --elf-output-style=JSON %t.pretty | \
+# RUN:   FileCheck %s --check-prefix=NO-PRETTY \
+# RUN:     --strict-whitespace --implicit-check-not={{.}}
+
+#      NO-PRETTY:[
+# NO-PRETTY-SAME:{
+# NO-PRETTY-SAME:"{{.*}}.pretty":{
+# NO-PRETTY-SAME:"FileSummary":{
+# NO-PRETTY-SAME:"File":"{{.*}}.pretty",
+# NO-PRETTY-SAME:"Format":"elf64-x86-64",
+# NO-PRETTY-SAME:"Arch":"x86_64",
+# NO-PRETTY-SAME:"AddressSize":"64bit",
+# NO-PRETTY-SAME:"LoadName":"<Not found>"
+# NO-PRETTY-SAME:}
+# NO-PRETTY-SAME:}
+# NO-PRETTY-SAME:}
+# NO-PRETTY-SAME:]
+
+## Test JSON with pretty-print on.
+# RUN: llvm-readobj --elf-output-style=JSON --pretty-print %t.pretty | \
+# RUN:   FileCheck %s --check-prefix=PRETTY \
+# RUN:     --match-full-lines --strict-whitespace --implicit-check-not={{.}}
+
+#      PRETTY:[
+# PRETTY-NEXT:  {
+# PRETTY-NEXT:    "{{.*}}.pretty": {
+# PRETTY-NEXT:      "FileSummary": {
+# PRETTY-NEXT:        "File": "{{.*}}.pretty",
+# PRETTY-NEXT:        "Format": "elf64-x86-64",
+# PRETTY-NEXT:        "Arch": "x86_64",
+# PRETTY-NEXT:        "AddressSize": "64bit",
+# PRETTY-NEXT:        "LoadName": "<Not found>"
+# PRETTY-NEXT:      }
+# PRETTY-NEXT:    }
+# PRETTY-NEXT:  }
+# PRETTY-NEXT:]
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_REL
+  Machine: EM_X86_64

diff  --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 431b20f77a589..2f64b079d612d 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -31,6 +31,7 @@
 #include "llvm/BinaryFormat/AMDGPUMetadataVerifier.h"
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/Demangle/Demangle.h"
+#include "llvm/Object/Archive.h"
 #include "llvm/Object/ELF.h"
 #include "llvm/Object/ELFObjectFile.h"
 #include "llvm/Object/ELFTypes.h"
@@ -548,6 +549,9 @@ template <typename ELFT> class GNUELFDumper : public ELFDumper<ELFT> {
     assert(&this->W.getOStream() == &llvm::fouts());
   }
 
+  void printFileSummary(StringRef FileStr, ObjectFile &Obj,
+                        ArrayRef<std::string> InputFilenames,
+                        const Archive *A) override;
   void printFileHeaders() override;
   void printGroupSections() override;
   void printRelocations() override;
@@ -697,9 +701,27 @@ template <typename ELFT> class LLVMELFDumper : public ELFDumper<ELFT> {
   void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override;
   void printMipsABIFlags() override;
 
+protected:
   ScopedPrinter &W;
 };
 
+// JSONELFDumper shares most of the same implementation as LLVMELFDumper except
+// it uses a JSONScopedPrinter.
+template <typename ELFT> class JSONELFDumper : public LLVMELFDumper<ELFT> {
+public:
+  LLVM_ELF_IMPORT_TYPES_ELFT(ELFT)
+
+  JSONELFDumper(const object::ELFObjectFile<ELFT> &ObjF, ScopedPrinter &Writer)
+      : LLVMELFDumper<ELFT>(ObjF, Writer) {}
+
+  void printFileSummary(StringRef FileStr, ObjectFile &Obj,
+                        ArrayRef<std::string> InputFilenames,
+                        const Archive *A) override;
+
+private:
+  std::unique_ptr<DictScope> FileScope;
+};
+
 } // end anonymous namespace
 
 namespace llvm {
@@ -709,6 +731,8 @@ static std::unique_ptr<ObjDumper>
 createELFDumper(const ELFObjectFile<ELFT> &Obj, ScopedPrinter &Writer) {
   if (opts::Output == opts::GNU)
     return std::make_unique<GNUELFDumper<ELFT>>(Obj, Writer);
+  else if (opts::Output == opts::JSON)
+    return std::make_unique<JSONELFDumper<ELFT>>(Obj, Writer);
   return std::make_unique<LLVMELFDumper<ELFT>>(Obj, Writer);
 }
 
@@ -3225,6 +3249,16 @@ static const EnumEntry<unsigned> *getObjectFileEnumEntry(unsigned Type) {
   return nullptr;
 }
 
+template <class ELFT>
+void GNUELFDumper<ELFT>::printFileSummary(StringRef FileStr, ObjectFile &Obj,
+                                          ArrayRef<std::string> InputFilenames,
+                                          const Archive *A) {
+  if (InputFilenames.size() > 1 || A) {
+    this->W.startLine() << "\n";
+    this->W.printString("File", FileStr);
+  }
+}
+
 template <class ELFT> void GNUELFDumper<ELFT>::printFileHeaders() {
   const Elf_Ehdr &e = this->Obj.getHeader();
   OS << "ELF Header:\n";
@@ -7306,3 +7340,18 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printMipsABIFlags() {
   W.printFlags("Flags 1", Flags->flags1, makeArrayRef(ElfMipsFlags1));
   W.printHex("Flags 2", Flags->flags2);
 }
+
+template <class ELFT>
+void JSONELFDumper<ELFT>::printFileSummary(StringRef FileStr, ObjectFile &Obj,
+                                           ArrayRef<std::string> InputFilenames,
+                                           const Archive *A) {
+  FileScope = std::make_unique<DictScope>(this->W, FileStr);
+  DictScope D(this->W, "FileSummary");
+  this->W.printString("File", FileStr);
+  this->W.printString("Format", Obj.getFileFormatName());
+  this->W.printString("Arch", Triple::getArchTypeName(Obj.getArch()));
+  this->W.printString(
+      "AddressSize",
+      std::string(formatv("{0}bit", 8 * Obj.getBytesInAddress())));
+  this->printLoadName();
+}

diff  --git a/llvm/tools/llvm-readobj/ObjDumper.cpp b/llvm/tools/llvm-readobj/ObjDumper.cpp
index dc4a3031f914f..6dde3725b4d65 100644
--- a/llvm/tools/llvm-readobj/ObjDumper.cpp
+++ b/llvm/tools/llvm-readobj/ObjDumper.cpp
@@ -13,6 +13,7 @@
 
 #include "ObjDumper.h"
 #include "llvm-readobj.h"
+#include "llvm/Object/Archive.h"
 #include "llvm/Object/ObjectFile.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/FormatVariadic.h"
@@ -85,6 +86,18 @@ void ObjDumper::printAsStringList(StringRef StringContent,
   }
 }
 
+void ObjDumper::printFileSummary(StringRef FileStr, object::ObjectFile &Obj,
+                                 ArrayRef<std::string> InputFilenames,
+                                 const object::Archive *A) {
+  W.startLine() << "\n";
+  W.printString("File", FileStr);
+  W.printString("Format", Obj.getFileFormatName());
+  W.printString("Arch", Triple::getArchTypeName(Obj.getArch()));
+  W.printString("AddressSize",
+                std::string(formatv("{0}bit", 8 * Obj.getBytesInAddress())));
+  this->printLoadName();
+}
+
 static std::vector<object::SectionRef>
 getSectionRefsByNameOrIndex(const object::ObjectFile &Obj,
                             ArrayRef<std::string> Sections) {

diff  --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h
index b395a95f3cb47..a09a243d381ec 100644
--- a/llvm/tools/llvm-readobj/ObjDumper.h
+++ b/llvm/tools/llvm-readobj/ObjDumper.h
@@ -20,6 +20,7 @@
 
 namespace llvm {
 namespace object {
+class Archive;
 class COFFImportFile;
 class ObjectFile;
 class XCOFFObjectFile;
@@ -39,6 +40,9 @@ class ObjDumper {
 
   virtual bool canDumpContent() { return true; }
 
+  virtual void printFileSummary(StringRef FileStr, object::ObjectFile &Obj,
+                                ArrayRef<std::string> InputFilenames,
+                                const object::Archive *A);
   virtual void printFileHeaders() = 0;
   virtual void printSectionHeaders() = 0;
   virtual void printRelocations() = 0;

diff  --git a/llvm/tools/llvm-readobj/Opts.td b/llvm/tools/llvm-readobj/Opts.td
index 7723691e8225e..d0f273fa60c76 100644
--- a/llvm/tools/llvm-readobj/Opts.td
+++ b/llvm/tools/llvm-readobj/Opts.td
@@ -28,6 +28,7 @@ def expand_relocs : FF<"expand-relocs", "Expand each shown relocation to multipl
 def file_header : FF<"file-header", "Display file header">;
 def headers : FF<"headers", "Equivalent to setting: --file-header, --program-headers, --section-headers">;
 defm hex_dump : Eq<"hex-dump", "Display the specified section(s) as hexadecimal bytes">, MetaVarName<"<name or index>">;
+def pretty_print : FF<"pretty-print", "Pretty print JSON output">;
 def relocs : FF<"relocs", "Display the relocation entries in the file">;
 def section_data : FF<"section-data", "Display section data for each section shown. This option has no effect for GNU style output">;
 def section_details : FF<"section-details", "Display the section details">;
@@ -47,7 +48,7 @@ def unwind : FF<"unwind", "Display unwind information">;
 def grp_elf : OptionGroup<"kind">, HelpText<"OPTIONS (ELF specific)">;
 def dynamic_table : FF<"dynamic-table", "Display the dynamic section table">, Group<grp_elf>;
 def elf_linker_options : FF<"elf-linker-options", "Display the .linker-options section">, Group<grp_elf>;
-defm elf_output_style : Eq<"elf-output-style", "Specify ELF dump style: LLVM or GNU">, Group<grp_elf>;
+defm elf_output_style : Eq<"elf-output-style", "Specify ELF dump style: LLVM, GNU, JSON">, Group<grp_elf>;
 def histogram : FF<"histogram", "Display bucket list histogram for hash sections">, Group<grp_elf>;
 def section_groups : FF<"section-groups", "Display section groups">, Group<grp_elf>;
 def gnu_hash_table : FF<"gnu-hash-table", "Display the GNU hash table for dynamic symbols">, Group<grp_elf>;

diff  --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp
index a598e2c28832d..46862bbad7cb6 100644
--- a/llvm/tools/llvm-readobj/llvm-readobj.cpp
+++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp
@@ -99,6 +99,7 @@ static bool DynamicSymbols;
 static bool FileHeaders;
 static bool Headers;
 static std::vector<std::string> HexDump;
+static bool PrettyPrint;
 static bool PrintStackMap;
 static bool PrintStackSizes;
 static bool Relocations;
@@ -230,13 +231,17 @@ static void parseOptions(const opt::InputArgList &Args) {
   opts::DynamicTable = Args.hasArg(OPT_dynamic_table);
   opts::ELFLinkerOptions = Args.hasArg(OPT_elf_linker_options);
   if (Arg *A = Args.getLastArg(OPT_elf_output_style_EQ)) {
-    StringRef V(A->getValue());
-    if (V == "LLVM")
-      opts::Output = opts::OutputStyleTy::LLVM;
-    else if (V == "GNU")
-      opts::Output = opts::OutputStyleTy::GNU;
-    else
-      error("--elf-output-style value should be either 'LLVM' or 'GNU'");
+    std::string OutputStyleChoice = A->getValue();
+    opts::Output = StringSwitch<opts::OutputStyleTy>(OutputStyleChoice)
+                       .Case("LLVM", opts::OutputStyleTy::LLVM)
+                       .Case("GNU", opts::OutputStyleTy::GNU)
+                       .Case("JSON", opts::OutputStyleTy::JSON)
+                       .Default(opts::OutputStyleTy::UNKNOWN);
+    if (opts::Output == opts::OutputStyleTy::UNKNOWN) {
+      error("--elf-output-style value should be either 'LLVM', 'GNU', or "
+            "'JSON', but was '" +
+            OutputStyleChoice + "'");
+    }
   }
   opts::GnuHashTable = Args.hasArg(OPT_gnu_hash_table);
   opts::HashSymbols = Args.hasArg(OPT_hash_symbols);
@@ -244,6 +249,7 @@ static void parseOptions(const opt::InputArgList &Args) {
   opts::HashHistogram = Args.hasArg(OPT_histogram);
   opts::NeededLibraries = Args.hasArg(OPT_needed_libs);
   opts::Notes = Args.hasArg(OPT_notes);
+  opts::PrettyPrint = Args.hasArg(OPT_pretty_print);
   opts::ProgramHeaders = Args.hasArg(OPT_program_headers);
   opts::RawRelr = Args.hasArg(OPT_raw_relr);
   opts::SectionGroups = Args.hasArg(OPT_section_groups);
@@ -333,18 +339,7 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer,
     reportError(DumperOrErr.takeError(), FileStr);
   Dumper = (*DumperOrErr).get();
 
-  if (opts::Output == opts::LLVM || opts::InputFilenames.size() > 1 || A) {
-    Writer.startLine() << "\n";
-    Writer.printString("File", FileStr);
-  }
-  if (opts::Output == opts::LLVM) {
-    Writer.printString("Format", Obj.getFileFormatName());
-    Writer.printString("Arch", Triple::getArchTypeName(Obj.getArch()));
-    Writer.printString(
-        "AddressSize",
-        std::string(formatv("{0}bit", 8 * Obj.getBytesInAddress())));
-    Dumper->printLoadName();
-  }
+  Dumper->printFileSummary(FileStr, Obj, opts::InputFilenames, A);
 
   if (opts::FileHeaders)
     Dumper->printFileHeaders();
@@ -550,6 +545,13 @@ static void dumpInput(StringRef File, ScopedPrinter &Writer) {
       OwningBinary<Binary>(std::move(Bin), std::move(Buffer)));
 }
 
+std::unique_ptr<ScopedPrinter> createWriter() {
+  if (opts::Output == opts::JSON)
+    return std::make_unique<JSONScopedPrinter>(
+        fouts(), opts::PrettyPrint ? 2 : 0, std::make_unique<ListScope>());
+  return std::make_unique<ScopedPrinter>(fouts());
+}
+
 int main(int argc, char *argv[]) {
   InitLLVM X(argc, argv);
   BumpPtrAllocator A;
@@ -610,16 +612,17 @@ int main(int argc, char *argv[]) {
     opts::SectionHeaders = true;
   }
 
-  ScopedPrinter Writer(fouts());
+  std::unique_ptr<ScopedPrinter> Writer = createWriter();
+
   for (const std::string &I : opts::InputFilenames)
-    dumpInput(I, Writer);
+    dumpInput(I, *Writer.get());
 
   if (opts::CodeViewMergedTypes) {
     if (opts::CodeViewEnableGHash)
-      dumpCodeViewMergedTypes(Writer, CVTypes.GlobalIDTable.records(),
+      dumpCodeViewMergedTypes(*Writer.get(), CVTypes.GlobalIDTable.records(),
                               CVTypes.GlobalTypeTable.records());
     else
-      dumpCodeViewMergedTypes(Writer, CVTypes.IDTable.records(),
+      dumpCodeViewMergedTypes(*Writer.get(), CVTypes.IDTable.records(),
                               CVTypes.TypeTable.records());
   }
 

diff  --git a/llvm/tools/llvm-readobj/llvm-readobj.h b/llvm/tools/llvm-readobj/llvm-readobj.h
index 7672da5c0aae6..0ea695d1673d1 100644
--- a/llvm/tools/llvm-readobj/llvm-readobj.h
+++ b/llvm/tools/llvm-readobj/llvm-readobj.h
@@ -39,7 +39,7 @@ extern bool ExpandRelocs;
 extern bool RawRelr;
 extern bool CodeViewSubsectionBytes;
 extern bool Demangle;
-enum OutputStyleTy { LLVM, GNU };
+enum OutputStyleTy { LLVM, GNU, JSON, UNKNOWN };
 extern OutputStyleTy Output;
 } // namespace opts
 


        


More information about the llvm-commits mailing list