|  | //===-- OptionGroupFormat.cpp ---------------------------------------------===// | 
|  | // | 
|  | // 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 | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "lldb/Interpreter/OptionGroupFormat.h" | 
|  |  | 
|  | #include "lldb/Host/OptionParser.h" | 
|  | #include "lldb/Interpreter/CommandInterpreter.h" | 
|  | #include "lldb/Target/ExecutionContext.h" | 
|  | #include "lldb/Target/Target.h" | 
|  |  | 
|  | using namespace lldb; | 
|  | using namespace lldb_private; | 
|  |  | 
|  | static constexpr OptionDefinition g_default_option_definitions[] = { | 
|  | {LLDB_OPT_SET_1, false, "format", 'f', OptionParser::eRequiredArgument, | 
|  | nullptr, {}, 0, eArgTypeFormat, | 
|  | "Specify a format to be used for display."}, | 
|  | {LLDB_OPT_SET_2, false, "gdb-format", 'G', OptionParser::eRequiredArgument, | 
|  | nullptr, {}, 0, eArgTypeGDBFormat, | 
|  | "Specify a format using a GDB format specifier string."}, | 
|  | {LLDB_OPT_SET_3, false, "size", 's', OptionParser::eRequiredArgument, | 
|  | nullptr, {}, 0, eArgTypeByteSize, | 
|  | "The size in bytes to use when displaying with the selected format."}, | 
|  | {LLDB_OPT_SET_4, false, "count", 'c', OptionParser::eRequiredArgument, | 
|  | nullptr, {}, 0, eArgTypeCount, | 
|  | "The number of total items to display."}, | 
|  | }; | 
|  |  | 
|  | OptionGroupFormat::OptionGroupFormat( | 
|  | lldb::Format default_format, uint64_t default_byte_size, | 
|  | uint64_t default_count, OptionGroupFormatUsageTextVector usage_text_vector) | 
|  | : m_format(default_format, default_format), | 
|  | m_byte_size(default_byte_size, default_byte_size), | 
|  | m_count(default_count, default_count), m_prev_gdb_format('x'), | 
|  | m_prev_gdb_size('w'), m_has_gdb_format(false) { | 
|  | // Copy the default option definitions. | 
|  | std::copy(std::begin(g_default_option_definitions), | 
|  | std::end(g_default_option_definitions), | 
|  | std::begin(m_option_definitions)); | 
|  |  | 
|  | for (auto usage_text_tuple : usage_text_vector) { | 
|  | switch (std::get<0>(usage_text_tuple)) { | 
|  | case eArgTypeFormat: | 
|  | m_option_definitions[0].usage_text = std::get<1>(usage_text_tuple); | 
|  | break; | 
|  | case eArgTypeByteSize: | 
|  | m_option_definitions[2].usage_text = std::get<1>(usage_text_tuple); | 
|  | break; | 
|  | default: | 
|  | llvm_unreachable("Unimplemented option"); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | llvm::ArrayRef<OptionDefinition> OptionGroupFormat::GetDefinitions() { | 
|  | auto result = llvm::ArrayRef(m_option_definitions); | 
|  | if (m_byte_size.GetDefaultValue() < UINT64_MAX) { | 
|  | if (m_count.GetDefaultValue() < UINT64_MAX) | 
|  | return result; | 
|  | else | 
|  | return result.take_front(3); | 
|  | } | 
|  | return result.take_front(2); | 
|  | } | 
|  |  | 
|  | Status OptionGroupFormat::SetOptionValue(uint32_t option_idx, | 
|  | llvm::StringRef option_arg, | 
|  | ExecutionContext *execution_context) { | 
|  | Status error; | 
|  | const int short_option = m_option_definitions[option_idx].short_option; | 
|  |  | 
|  | switch (short_option) { | 
|  | case 'f': | 
|  | error = m_format.SetValueFromString(option_arg); | 
|  | break; | 
|  |  | 
|  | case 'c': | 
|  | if (m_count.GetDefaultValue() == 0) { | 
|  | error = Status::FromErrorString("--count option is disabled"); | 
|  | } else { | 
|  | error = m_count.SetValueFromString(option_arg); | 
|  | if (m_count.GetCurrentValue() == 0) | 
|  | error = Status::FromErrorStringWithFormat( | 
|  | "invalid --count option value '%s'", option_arg.str().c_str()); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 's': | 
|  | if (m_byte_size.GetDefaultValue() == 0) { | 
|  | error = Status::FromErrorString("--size option is disabled"); | 
|  | } else { | 
|  | error = m_byte_size.SetValueFromString(option_arg); | 
|  | if (m_byte_size.GetCurrentValue() == 0) | 
|  | error = Status::FromErrorStringWithFormat( | 
|  | "invalid --size option value '%s'", option_arg.str().c_str()); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 'G': { | 
|  | uint64_t count = 0; | 
|  | llvm::StringRef gdb_format_str = option_arg; | 
|  | gdb_format_str.consumeInteger(0, count); | 
|  |  | 
|  | Format format = eFormatDefault; | 
|  | uint32_t byte_size = 0; | 
|  |  | 
|  | while (!gdb_format_str.empty() && | 
|  | ParserGDBFormatLetter(execution_context, gdb_format_str[0], format, | 
|  | byte_size)) { | 
|  | gdb_format_str = gdb_format_str.drop_front(); | 
|  | } | 
|  |  | 
|  | // We the first character of the "gdb_format_str" is not the | 
|  | // NULL terminator, we didn't consume the entire string and | 
|  | // something is wrong. Also, if none of the format, size or count was | 
|  | // specified correctly, then abort. | 
|  | if (!gdb_format_str.empty() || | 
|  | (format == eFormatInvalid && byte_size == 0 && count == 0)) { | 
|  | // Nothing got set correctly | 
|  | error = Status::FromErrorStringWithFormat( | 
|  | "invalid gdb format string '%s'", option_arg.str().c_str()); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | // At least one of the format, size or count was set correctly. Anything | 
|  | // that wasn't set correctly should be set to the previous default | 
|  | if (format == eFormatInvalid) | 
|  | ParserGDBFormatLetter(execution_context, m_prev_gdb_format, format, | 
|  | byte_size); | 
|  |  | 
|  | const bool byte_size_enabled = m_byte_size.GetDefaultValue() < UINT64_MAX; | 
|  | const bool count_enabled = m_count.GetDefaultValue() < UINT64_MAX; | 
|  | if (byte_size_enabled) { | 
|  | // Byte size is enabled | 
|  | if (byte_size == 0) | 
|  | ParserGDBFormatLetter(execution_context, m_prev_gdb_size, format, | 
|  | byte_size); | 
|  | } else { | 
|  | // Byte size is disabled, make sure it wasn't specified but if this is an | 
|  | // address, it's actually necessary to specify one so don't error out | 
|  | if (byte_size > 0 && format != lldb::eFormatAddressInfo) { | 
|  | error = Status::FromErrorString( | 
|  | "this command doesn't support specifying a byte size"); | 
|  | return error; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (count_enabled) { | 
|  | // Count is enabled and was not set, set it to the default for gdb format | 
|  | // statements (which is 1). | 
|  | if (count == 0) | 
|  | count = 1; | 
|  | } else { | 
|  | // Count is disabled, make sure it wasn't specified | 
|  | if (count > 0) { | 
|  | error = Status::FromErrorString( | 
|  | "this command doesn't support specifying a count"); | 
|  | return error; | 
|  | } | 
|  | } | 
|  |  | 
|  | m_format.SetCurrentValue(format); | 
|  | m_format.SetOptionWasSet(); | 
|  | if (byte_size_enabled) { | 
|  | m_byte_size.SetCurrentValue(byte_size); | 
|  | m_byte_size.SetOptionWasSet(); | 
|  | } | 
|  | if (count_enabled) { | 
|  | m_count.SetCurrentValue(count); | 
|  | m_count.SetOptionWasSet(); | 
|  | } | 
|  | } break; | 
|  |  | 
|  | default: | 
|  | llvm_unreachable("Unimplemented option"); | 
|  | } | 
|  |  | 
|  | return error; | 
|  | } | 
|  |  | 
|  | bool OptionGroupFormat::ParserGDBFormatLetter( | 
|  | ExecutionContext *execution_context, char format_letter, Format &format, | 
|  | uint32_t &byte_size) { | 
|  | m_has_gdb_format = true; | 
|  | switch (format_letter) { | 
|  | case 'o': | 
|  | format = eFormatOctal; | 
|  | m_prev_gdb_format = format_letter; | 
|  | return true; | 
|  | case 'x': | 
|  | format = eFormatHex; | 
|  | m_prev_gdb_format = format_letter; | 
|  | return true; | 
|  | case 'd': | 
|  | format = eFormatDecimal; | 
|  | m_prev_gdb_format = format_letter; | 
|  | return true; | 
|  | case 'u': | 
|  | format = eFormatUnsigned; | 
|  | m_prev_gdb_format = format_letter; | 
|  | return true; | 
|  | case 't': | 
|  | format = eFormatBinary; | 
|  | m_prev_gdb_format = format_letter; | 
|  | return true; | 
|  | case 'f': | 
|  | format = eFormatFloat; | 
|  | m_prev_gdb_format = format_letter; | 
|  | return true; | 
|  | case 'a': | 
|  | format = eFormatAddressInfo; | 
|  | { | 
|  | TargetSP target_sp = | 
|  | execution_context ? execution_context->GetTargetSP() : TargetSP(); | 
|  | if (target_sp) | 
|  | byte_size = target_sp->GetArchitecture().GetAddressByteSize(); | 
|  | m_prev_gdb_format = format_letter; | 
|  | return true; | 
|  | } | 
|  | case 'i': | 
|  | format = eFormatInstruction; | 
|  | m_prev_gdb_format = format_letter; | 
|  | return true; | 
|  | case 'c': | 
|  | format = eFormatChar; | 
|  | m_prev_gdb_format = format_letter; | 
|  | return true; | 
|  | case 's': | 
|  | format = eFormatCString; | 
|  | m_prev_gdb_format = format_letter; | 
|  | return true; | 
|  | case 'T': | 
|  | format = eFormatOSType; | 
|  | m_prev_gdb_format = format_letter; | 
|  | return true; | 
|  | case 'A': | 
|  | format = eFormatHexFloat; | 
|  | m_prev_gdb_format = format_letter; | 
|  | return true; | 
|  |  | 
|  | case 'b': | 
|  | case 'h': | 
|  | case 'w': | 
|  | case 'g': | 
|  | { | 
|  | // Size isn't used for printing instructions, so if a size is specified, | 
|  | // and the previous format was 'i', then we should reset it to the | 
|  | // default ('x').  Otherwise we'll continue to print as instructions, | 
|  | // which isn't expected. | 
|  | if (format_letter == 'b') | 
|  | byte_size = 1; | 
|  | else if (format_letter == 'h') | 
|  | byte_size = 2; | 
|  | else if (format_letter == 'w') | 
|  | byte_size = 4; | 
|  | else if (format_letter == 'g') | 
|  | byte_size = 8; | 
|  |  | 
|  | m_prev_gdb_size = format_letter; | 
|  | if (m_prev_gdb_format == 'i') | 
|  | m_prev_gdb_format = 'x'; | 
|  | return true; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void OptionGroupFormat::OptionParsingStarting( | 
|  | ExecutionContext *execution_context) { | 
|  | m_format.Clear(); | 
|  | m_byte_size.Clear(); | 
|  | m_count.Clear(); | 
|  | m_has_gdb_format = false; | 
|  | } |