diff --git a/lldb/CMakeLists.txt b/lldb/CMakeLists.txt index 47bd854c7444a4..3217cb14384c27 100644 --- a/lldb/CMakeLists.txt +++ b/lldb/CMakeLists.txt @@ -33,6 +33,8 @@ endif() if (NOT LLDB_DISABLE_PYTHON) add_subdirectory(scripts) endif () + +add_subdirectory(utils/TableGen) add_subdirectory(source) add_subdirectory(tools) add_subdirectory(docs) diff --git a/lldb/cmake/modules/AddLLDB.cmake b/lldb/cmake/modules/AddLLDB.cmake index 9859fe7b68a599..e5882329867380 100644 --- a/lldb/cmake/modules/AddLLDB.cmake +++ b/lldb/cmake/modules/AddLLDB.cmake @@ -1,4 +1,37 @@ +function(lldb_tablegen) + # Syntax: + # lldb_tablegen output-file [tablegen-arg ...] SOURCE source-file + # [[TARGET cmake-target-name] [DEPENDS extra-dependency ...]] + # + # Generates a custom command for invoking tblgen as + # + # tblgen source-file -o=output-file tablegen-arg ... + # + # and, if cmake-target-name is provided, creates a custom target for + # executing the custom command depending on output-file. It is + # possible to list more files to depend after DEPENDS. + + cmake_parse_arguments(LTG "" "SOURCE;TARGET" "" ${ARGN}) + + if(NOT LTG_SOURCE) + message(FATAL_ERROR "SOURCE source-file required by lldb_tablegen") + endif() + + set(LLVM_TARGET_DEFINITIONS ${LTG_SOURCE}) + tablegen(LLDB ${LTG_UNPARSED_ARGUMENTS}) + + if(LTG_TARGET) + add_public_tablegen_target(${LTG_TARGET}) + set_target_properties( ${LTG_TARGET} PROPERTIES FOLDER "LLDB tablegenning") + set_property(GLOBAL APPEND PROPERTY LLDB_TABLEGEN_TARGETS ${LTG_TARGET}) + endif() +endfunction(lldb_tablegen) + function(add_lldb_library name) + include_directories(BEFORE + ${CMAKE_CURRENT_BINARY_DIR} +) + # only supported parameters to this macro are the optional # MODULE;SHARED;STATIC library type and source files cmake_parse_arguments(PARAM @@ -241,4 +274,4 @@ function(lldb_setup_rpaths name) BUILD_RPATH "${LIST_BUILD_RPATH}" INSTALL_RPATH "${LIST_INSTALL_RPATH}" ) -endfunction() \ No newline at end of file +endfunction() diff --git a/lldb/source/Commands/CMakeLists.txt b/lldb/source/Commands/CMakeLists.txt index d7fd47af026ad2..f3f96af7dab02d 100644 --- a/lldb/source/Commands/CMakeLists.txt +++ b/lldb/source/Commands/CMakeLists.txt @@ -1,3 +1,7 @@ +lldb_tablegen(Options.inc -gen-lldb-option-defs + SOURCE Options.td + TARGET LLDBOptionsGen) + add_lldb_library(lldbCommands CommandCompletions.cpp CommandObjectApropos.cpp @@ -45,3 +49,5 @@ add_lldb_library(lldbCommands LINK_COMPONENTS Support ) + +add_dependencies(lldbCommands LLDBOptionsGen) diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp index a4f558e7f81ba5..a661ffc62512cd 100644 --- a/lldb/source/Commands/CommandObjectBreakpoint.cpp +++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp @@ -1246,15 +1246,10 @@ the second re-enables the first location."); #pragma mark List::CommandOptions static constexpr OptionDefinition g_breakpoint_list_options[] = { - // clang-format off - { LLDB_OPT_SET_ALL, false, "internal", 'i', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Show debugger internal breakpoints" }, - { LLDB_OPT_SET_1, false, "brief", 'b', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Give a brief description of the breakpoint (no location info)." }, - // FIXME: We need to add an "internal" command, and then add this sort of thing to it. - // But I need to see it for now, and don't want to wait. - { LLDB_OPT_SET_2, false, "full", 'f', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Give a full description of the breakpoint and its locations." }, - { LLDB_OPT_SET_3, false, "verbose", 'v', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Explain everything we know about the breakpoint (for debugging debugger bugs)." }, - { LLDB_OPT_SET_ALL, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "List Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets." }, - // clang-format on + // FIXME: We need to add an "internal" command, and then add this sort of + // thing to it. But I need to see it for now, and don't want to wait. +#define LLDB_OPTIONS_breakpoint_list +#include "Options.inc" }; #pragma mark List diff --git a/lldb/source/Commands/CommandObjectHelp.cpp b/lldb/source/Commands/CommandObjectHelp.cpp index 4e663c797ebb8d..ad53e03121f0d3 100644 --- a/lldb/source/Commands/CommandObjectHelp.cpp +++ b/lldb/source/Commands/CommandObjectHelp.cpp @@ -66,11 +66,8 @@ CommandObjectHelp::CommandObjectHelp(CommandInterpreter &interpreter) CommandObjectHelp::~CommandObjectHelp() = default; static constexpr OptionDefinition g_help_options[] = { - // clang-format off - {LLDB_OPT_SET_ALL, false, "hide-aliases", 'a', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Hide aliases in the command list."}, - {LLDB_OPT_SET_ALL, false, "hide-user-commands", 'u', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Hide user-defined commands from the list."}, - {LLDB_OPT_SET_ALL, false, "show-hidden-commands", 'h', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Include commands prefixed with an underscore."}, - // clang-format on +#define LLDB_OPTIONS_help +#include "Options.inc" }; llvm::ArrayRef diff --git a/lldb/source/Commands/CommandObjectSettings.cpp b/lldb/source/Commands/CommandObjectSettings.cpp index e7e72de5a7eefb..057c5de619cfae 100644 --- a/lldb/source/Commands/CommandObjectSettings.cpp +++ b/lldb/source/Commands/CommandObjectSettings.cpp @@ -22,10 +22,8 @@ using namespace lldb_private; // CommandObjectSettingsSet static constexpr OptionDefinition g_settings_set_options[] = { - // clang-format off - { LLDB_OPT_SET_2, false, "global", 'g', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Apply the new value to the global default value." }, - { LLDB_OPT_SET_2, false, "force", 'f', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Force an empty value to be accepted as the default." } - // clang-format on +#define LLDB_OPTIONS_settings_set +#include "Options.inc" }; class CommandObjectSettingsSet : public CommandObjectRaw { @@ -313,10 +311,8 @@ class CommandObjectSettingsShow : public CommandObjectParsed { // CommandObjectSettingsWrite -- Write settings to file static constexpr OptionDefinition g_settings_write_options[] = { - // clang-format off - { LLDB_OPT_SET_ALL, true, "file", 'f', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eDiskFileCompletion, eArgTypeFilename, "The file into which to write the settings." }, - { LLDB_OPT_SET_ALL, false, "append",'a', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Append to saved settings file if it exists."}, - // clang-format on +#define LLDB_OPTIONS_settings_write +#include "Options.inc" }; class CommandObjectSettingsWrite : public CommandObjectParsed { @@ -438,9 +434,8 @@ class CommandObjectSettingsWrite : public CommandObjectParsed { // CommandObjectSettingsRead -- Read settings from file static constexpr OptionDefinition g_settings_read_options[] = { - // clang-format off - {LLDB_OPT_SET_ALL, true, "file",'f', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eDiskFileCompletion, eArgTypeFilename, "The file from which to read the breakpoints." }, - // clang-format on +#define LLDB_OPTIONS_settings_read +#include "Options.inc" }; class CommandObjectSettingsRead : public CommandObjectParsed { diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp index 764461ee92b841..e8720157ab4cbc 100644 --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -1967,9 +1967,8 @@ static constexpr OptionEnumValueElement g_sort_option_enumeration[] = { {eSortOrderByName, "name", "Sort output by symbol name."} }; static constexpr OptionDefinition g_target_modules_dump_symtab_options[] = { - // clang-format off - { LLDB_OPT_SET_1, false, "sort", 's', OptionParser::eRequiredArgument, nullptr, OptionEnumValues(g_sort_option_enumeration), 0, eArgTypeSortOrder, "Supply a sort order when dumping the symbol table." } - // clang-format on +#define LLDB_OPTIONS_target_modules_dump_symtab +#include "Options.inc" }; class CommandObjectTargetModulesDumpSymtab diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td new file mode 100644 index 00000000000000..1f4c09c3860967 --- /dev/null +++ b/lldb/source/Commands/Options.td @@ -0,0 +1,53 @@ +include "OptionsBase.td" + +let Command = "target modules dump symtab" in { + def tm_sort : Option<"sort", "s">, Group<1>, + Desc<"Supply a sort order when dumping the symbol table.">, + EnumArg<"SortOrder", "OptionEnumValues(g_sort_option_enumeration)">; +} + +let Command = "help" in { + def help_hide_aliases : Option<"hide-aliases", "a">, + Desc<"Hide aliases in the command list.">; + def help_hide_user : Option<"hide-user-commands", "u">, + Desc<"Hide user-defined commands from the list.">; + def help_show_hidden : Option<"show-hidden-commands", "h">, + Desc<"Include commands prefixed with an underscore.">; +} + +let Command = "settings set" in { + def setset_global : Option<"global", "g">, Arg<"Filename">, + Completion<"DiskFile">, + Desc<"Apply the new value to the global default value.">; + def setset_force : Option<"force", "f">, + Desc<"Force an empty value to be accepted as the default.">; +} + +let Command = "settings write" in { + def setwrite_file : Option<"file", "f">, Required, Arg<"Filename">, + Completion<"DiskFile">, + Desc<"The file into which to write the settings.">; + def setwrite_append : Option<"append", "a">, + Desc<"Append to saved settings file if it exists.">; +} + +let Command = "settings read" in { + def setread_file : Option<"file", "f">, Required, Arg<"Filename">, + Completion<"DiskFile">, + Desc<"The file from which to read the settings.">; +} + +let Command = "breakpoint list" in { + def blist_internal : Option<"internal", "i">, + Desc<"Show debugger internal breakpoints">; + def blist_brief : Option<"brief", "b">, Group<1>, + Desc<"Give a brief description of the breakpoint (no location info).">; + def blist_full : Option<"full", "f">, Group<2>, + Desc<"Give a full description of the breakpoint and its locations.">; + def blist_verbose : Option<"verbose", "v">, Group<3>, + Desc<"Explain everything we know about the breakpoint (for debugging " + "debugger bugs).">; + def blist_dummy_bp : Option<"dummy-breakpoints", "D">, + Desc<"List Dummy breakpoints - i.e. breakpoints set before a file is " + "provided, which prime new targets.">; +} diff --git a/lldb/source/Commands/OptionsBase.td b/lldb/source/Commands/OptionsBase.td new file mode 100644 index 00000000000000..c4a326fbaf5f7b --- /dev/null +++ b/lldb/source/Commands/OptionsBase.td @@ -0,0 +1,62 @@ +// Base class for all options. +class Option { + string FullName = fullname; + string ShortName = shortname; + // The full associated command/subcommand such as "settings set". + string Command; +} + +// Moves the option into a list of option groups. +class Groups groups> { + list Groups = groups; +} + +// Moves the option in all option groups in a range. +// Start and end values are inclusive. +class GroupRange { + int GroupStart = start; + int GroupEnd = end; +} +// Moves the option in a single option group. +class Group { + int GroupStart = group; + int GroupEnd = group; +} + +// Sets the description for the option that should be +// displayed to the user. +class Desc { + string Description = description; +} + +// Marks the option as required when calling the +// associated command. +class Required { + bit Required = 1; +} + +// Gives the option an optional argument. +class OptionalArg { + string ArgType = type; + bit OptionalArg = 1; +} + +// Gives the option an required argument. +class Arg { + string ArgType = type; +} + +// Gives the option an required argument. +class EnumArg { + string ArgType = type; + string ArgEnum = enum; +} + +// Sets the available completions for the given option. +class Completions completions> { + list Completions = completions; +} +// Sets a single completion for the given option. +class Completion { + list Completions = [completion]; +} diff --git a/lldb/utils/TableGen/CMakeLists.txt b/lldb/utils/TableGen/CMakeLists.txt new file mode 100644 index 00000000000000..76e819160ea6e4 --- /dev/null +++ b/lldb/utils/TableGen/CMakeLists.txt @@ -0,0 +1,8 @@ +set(LLVM_LINK_COMPONENTS Support) + +add_tablegen(lldb-tblgen LLDB + LLDBOptionDefEmitter.cpp + LLDBTableGen.cpp + ) +set_target_properties(lldb-tblgen PROPERTIES FOLDER "LLDB tablegenning") + diff --git a/lldb/utils/TableGen/LLDBOptionDefEmitter.cpp b/lldb/utils/TableGen/LLDBOptionDefEmitter.cpp new file mode 100644 index 00000000000000..00b44c020a2012 --- /dev/null +++ b/lldb/utils/TableGen/LLDBOptionDefEmitter.cpp @@ -0,0 +1,152 @@ +//===- TableGen.cpp - Top-Level TableGen implementation for Clang ---------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// These tablegen backends emits LLDB's OptionDefinition values for different +// LLDB commands. +// +//===----------------------------------------------------------------------===// + +#include "LLDBTableGenBackends.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/StringMatcher.h" +#include "llvm/TableGen/TableGenBackend.h" +#include +#include + +using namespace llvm; + +/// Map of command names to their associated records. Also makes sure our +/// commands are sorted in a deterministic way. +typedef std::map> RecordsByCommand; + +/// Groups all records by their command. +static RecordsByCommand getCommandList(std::vector Options) { + RecordsByCommand result; + for (Record *Option : Options) + result[Option->getValueAsString("Command").str()].push_back(Option); + return result; +} + +static void emitOption(Record *Option, raw_ostream &OS) { + OS << " {"; + + // List of option groups this option is in. + std::vector GroupsArg; + + if (Option->getValue("Groups")) { + // The user specified a list of groups. + auto Groups = Option->getValueAsListOfInts("Groups"); + for (int Group : Groups) + GroupsArg.push_back("LLDB_OPT_SET_" + std::to_string(Group)); + OS << llvm::join(GroupsArg.begin(), GroupsArg.end(), " | "); + } else if (Option->getValue("GroupStart")) { + // The user specified a range of groups (with potentially only one element). + int GroupStart = Option->getValueAsInt("GroupStart"); + int GroupEnd = Option->getValueAsInt("GroupEnd"); + for (int i = GroupStart; i <= GroupEnd; ++i) + GroupsArg.push_back("LLDB_OPT_SET_" + std::to_string(i)); + } + + // If we have any groups, we merge them. Otherwise we move this option into + // the all group. + if (GroupsArg.empty()) + OS << "LLDB_OPT_SET_ALL"; + else + OS << llvm::join(GroupsArg.begin(), GroupsArg.end(), " | "); + + OS << ", "; + + // Check if this option is required. + OS << (Option->getValue("Required") ? "true" : "false"); + + // Add the full and short name for this option. + OS << ", \"" << Option->getValueAsString("FullName") << "\", "; + OS << '\'' << Option->getValueAsString("ShortName") << "'"; + + auto ArgType = Option->getValue("ArgType"); + bool IsOptionalArg = Option->getValue("OptionalArg") != nullptr; + + // Decide if we have either an option, required or no argument for this + // option. + OS << ", OptionParser::"; + if (ArgType) { + if (IsOptionalArg) + OS << "eOptionalArgument"; + else + OS << "eRequiredArgument"; + } else + OS << "eNoArgument"; + OS << ", nullptr, "; + + if (Option->getValue("ArgEnum")) + OS << Option->getValueAsString("ArgEnum"); + else + OS << "{}"; + OS << ", "; + + // Read the tab completions we offer for this option (if there are any) + if (Option->getValue("Completions")) { + auto Completions = Option->getValueAsListOfStrings("Completions"); + std::vector CompletionArgs; + for (llvm::StringRef Completion : Completions) + CompletionArgs.push_back("CommandCompletions::e" + Completion.str() + + "Completion"); + + OS << llvm::join(CompletionArgs.begin(), CompletionArgs.end(), " | "); + } else { + OS << "CommandCompletions::eNoCompletion"; + } + + // Add the argument type. + OS << ", eArgType"; + if (ArgType) { + OS << ArgType->getValue()->getAsUnquotedString(); + } else + OS << "None"; + OS << ", "; + + // Add the description if there is any. + if (auto D = Option->getValue("Description")) + OS << D->getValue()->getAsString(); + else + OS << "\"\""; + OS << "},\n"; +} + +/// Emits all option initializers to the raw_ostream. +static void emitOptions(std::string Command, std::vector Option, + raw_ostream &OS) { + // Generate the macro that the user needs to define before including the + // *.inc file. + std::string NeededMacro = "LLDB_OPTIONS_" + Command; + std::replace(NeededMacro.begin(), NeededMacro.end(), ' ', '_'); + + // All options are in one file, so we need put them behind macros and ask the + // user to define the macro for the options that are needed. + OS << "// Options for " << Command << "\n"; + OS << "#ifdef " << NeededMacro << "\n"; + for (Record *R : Option) + emitOption(R, OS); + // We undefine the macro for the user like Clang's include files are doing it. + OS << "#undef " << NeededMacro << "\n"; + OS << "#endif // " << Command << " command\n\n"; +} + +void lldb_private::EmitOptionDefs(RecordKeeper &Records, raw_ostream &OS) { + + std::vector Options = Records.getAllDerivedDefinitions("Option"); + + emitSourceFileHeader("Options for LLDB command line commands.", OS); + + RecordsByCommand ByCommand = getCommandList(Options); + + for (auto &CommandRecordPair : ByCommand) { + emitOptions(CommandRecordPair.first, CommandRecordPair.second, OS); + } +} diff --git a/lldb/utils/TableGen/LLDBTableGen.cpp b/lldb/utils/TableGen/LLDBTableGen.cpp new file mode 100644 index 00000000000000..9325fe03856153 --- /dev/null +++ b/lldb/utils/TableGen/LLDBTableGen.cpp @@ -0,0 +1,71 @@ +//===- TableGen.cpp - Top-Level TableGen implementation for Clang ---------===// +// +// 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 contains the main function for Clang's TableGen. +// +//===----------------------------------------------------------------------===// + +#include "LLDBTableGenBackends.h" // Declares all backends. +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Main.h" +#include "llvm/TableGen/Record.h" + +using namespace llvm; +using namespace lldb_private; + +enum ActionType { + PrintRecords, + DumpJSON, + GenOptionDefs, +}; + +static cl::opt + Action(cl::desc("Action to perform:"), + cl::values(clEnumValN(PrintRecords, "print-records", + "Print all records to stdout (default)"), + clEnumValN(DumpJSON, "dump-json", + "Dump all records as machine-readable JSON"), + clEnumValN(GenOptionDefs, "gen-lldb-option-defs", + "Generate clang attribute clases"))); + +static bool LLDBTableGenMain(raw_ostream &OS, RecordKeeper &Records) { + switch (Action) { + case PrintRecords: + OS << Records; // No argument, dump all contents + break; + case DumpJSON: + EmitJSON(Records, OS); + break; + case GenOptionDefs: + EmitOptionDefs(Records, OS); + break; + } + return false; +} + +int main(int argc, char **argv) { + sys::PrintStackTraceOnErrorSignal(argv[0]); + PrettyStackTraceProgram X(argc, argv); + cl::ParseCommandLineOptions(argc, argv); + + llvm_shutdown_obj Y; + + return TableGenMain(argv[0], &LLDBTableGenMain); +} + +#ifdef __has_feature +#if __has_feature(address_sanitizer) +#include +// Disable LeakSanitizer for this binary as it has too many leaks that are not +// very interesting to fix. See compiler-rt/include/sanitizer/lsan_interface.h . +int __lsan_is_turned_off() { return 1; } +#endif // __has_feature(address_sanitizer) +#endif // defined(__has_feature) diff --git a/lldb/utils/TableGen/LLDBTableGenBackends.h b/lldb/utils/TableGen/LLDBTableGenBackends.h new file mode 100644 index 00000000000000..eb14d80c5627ef --- /dev/null +++ b/lldb/utils/TableGen/LLDBTableGenBackends.h @@ -0,0 +1,34 @@ +//===- TableGen.cpp - Top-Level TableGen implementation for Clang ---------===// +// +// 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 contains the declarations for all of the LLDB TableGen +// backends. A "TableGen backend" is just a function. See +// "$LLVM_ROOT/utils/TableGen/TableGenBackends.h" for more info. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LLDB_UTILS_TABLEGEN_TABLEGENBACKENDS_H +#define LLVM_LLDB_UTILS_TABLEGEN_TABLEGENBACKENDS_H + +#include + +namespace llvm { +class raw_ostream; +class RecordKeeper; +} // namespace llvm + +using llvm::raw_ostream; +using llvm::RecordKeeper; + +namespace lldb_private { + +void EmitOptionDefs(RecordKeeper &RK, raw_ostream &OS); + +} // namespace lldb_private + +#endif