| //===-- 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; | |
| } |