blob: a2ca9ff398189d76eaaa0c0a00a65f4e572efca7 [file] [log] [blame]
//===-- 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') {
// 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::makeArrayRef(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.SetErrorString("--count option is disabled");
} else {
error = m_count.SetValueFromString(option_arg);
if (m_count.GetCurrentValue() == 0)
error.SetErrorStringWithFormat("invalid --count option value '%s'",
option_arg.str().c_str());
}
break;
case 's':
if (m_byte_size.GetDefaultValue() == 0) {
error.SetErrorString("--size option is disabled");
} else {
error = m_byte_size.SetValueFromString(option_arg);
if (m_byte_size.GetCurrentValue() == 0)
error.SetErrorStringWithFormat("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.SetErrorStringWithFormat("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.SetErrorString(
"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.SetErrorString("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;
}