some editing of data visualization error messages to make them more meaningful

debugging printfs() for data visualization turned into a meaningful log:
 - introduced a new log category `types' in channel `lldb'

llvm-svn: 135773
This commit is contained in:
Enrico Granata 2011-07-22 17:03:19 +00:00
parent 4535b9194a
commit e992a0899e
6 changed files with 152 additions and 70 deletions

View File

@ -46,6 +46,7 @@ namespace std
#include "lldb/Core/FormatClasses.h"
#include "lldb/Core/InputReaderStack.h"
#include "lldb/Core/Listener.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/RegularExpression.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/SourceManager.h"
@ -58,6 +59,8 @@ namespace std
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/TargetList.h"
using lldb::LogSP;
namespace lldb_private {
class IFormatChangeListener
@ -269,13 +272,22 @@ private:
MapValueType& entry,
uint32_t& reason)
{
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES));
if (type.isNull())
{
if (log)
log->Printf("type is NULL, returning");
return false;
}
// clang::QualType type = q_type.getUnqualifiedType();
type.removeLocalConst(); type.removeLocalVolatile(); type.removeLocalRestrict();
const clang::Type* typePtr = type.getTypePtrOrNull();
if (!typePtr)
{
if (log)
log->Printf("type is NULL, returning");
return false;
}
ConstString name(ClangASTType::GetTypeNameForQualType(type).c_str());
if (vobj.GetBitfieldBitSize() > 0)
{
@ -283,13 +295,24 @@ private:
StreamString sstring;
sstring.Printf("%s:%d",name.AsCString(),vobj.GetBitfieldBitSize());
name = ConstString(sstring.GetData());
if (log)
log->Printf("appended bitfield info, final result is %s", name.GetCString());
}
//printf("trying to get format for VO name %s of type %s\n",vobj.GetName().AsCString(),name.AsCString());
if (log)
log->Printf("trying to get format for VO name %s of type %s",vobj.GetName().AsCString(),name.AsCString());
if (Get(name.GetCString(), entry))
{
if (log)
log->Printf("direct match found, returning");
return true;
}
if (log)
log->Printf("no direct match");
// look for a "base type", whatever that means
if (typePtr->isReferenceType())
{
if (log)
log->Printf("stripping reference");
if (Get(vobj,type.getNonReferenceType(),entry, reason) && !entry->m_skip_references)
{
reason |= lldb::eFormatterStrippedPointerReference;
@ -298,6 +321,8 @@ private:
}
if (typePtr->isPointerType())
{
if (log)
log->Printf("stripping pointer");
if (Get(vobj, typePtr->getPointeeType(), entry, reason) && !entry->m_skip_pointers)
{
reason |= lldb::eFormatterStrippedPointerReference;
@ -306,6 +331,8 @@ private:
}
if (typePtr->isObjCObjectPointerType())
{
if (log)
log->Printf("stripping ObjC pointer");
/*
for some reason, C++ can quite easily obtain the type hierarchy for a ValueObject
even if the VO represent a pointer-to-class, as long as the typePtr is right
@ -325,19 +352,21 @@ private:
const clang::ObjCObjectType *objc_class_type = typePtr->getAs<clang::ObjCObjectType>();
if (objc_class_type)
{
//printf("working with ObjC\n");
if (log)
log->Printf("working with ObjC");
clang::ASTContext *ast = vobj.GetClangAST();
if (ClangASTContext::GetCompleteType(ast, vobj.GetClangType()) && !objc_class_type->isObjCId())
{
clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface();
if (class_interface_decl)
{
//printf("down here\n");
if (log)
log->Printf("got an ObjCInterfaceDecl");
clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass();
//printf("one further step and we're there...\n");
if (superclass_interface_decl)
{
//printf("the end is here\n");
if (log)
log->Printf("got a parent class for this ObjC class");
clang::QualType ivar_qual_type(ast->getObjCInterfaceType(superclass_interface_decl));
if (Get(vobj, ivar_qual_type, entry, reason) && entry->m_cascades)
{
@ -351,6 +380,8 @@ private:
// for C++ classes, navigate up the hierarchy
if (typePtr->isRecordType())
{
if (log)
log->Printf("working with C++");
clang::CXXRecordDecl* record = typePtr->getAsCXXRecordDecl();
if (record)
{
@ -361,6 +392,8 @@ private:
clang::CXXRecordDecl::base_class_iterator pos,end;
if (record->getNumBases() > 0)
{
if (log)
log->Printf("look into bases");
end = record->bases_end();
for (pos = record->bases_begin(); pos != end; pos++)
{
@ -373,6 +406,8 @@ private:
}
if (record->getNumVBases() > 0)
{
if (log)
log->Printf("look into VBases");
end = record->vbases_end();
for (pos = record->vbases_begin(); pos != end; pos++)
{
@ -390,6 +425,8 @@ private:
const clang::TypedefType* type_tdef = type->getAs<clang::TypedefType>();
if (type_tdef)
{
if (log)
log->Printf("stripping typedef");
if ((Get(vobj, type_tdef->getDecl()->getUnderlyingType(), entry, reason)) && entry->m_cascades)
{
reason |= lldb::eFormatterNavigatedTypedefs;

View File

@ -38,6 +38,7 @@
#define LIBLLDB_LOG_API (1u << 16)
#define LIBLLDB_LOG_SCRIPT (1u << 17)
#define LIBLLDB_LOG_COMMANDS (1U << 18)
#define LIBLLDB_LOG_TYPES (1u << 19)
#define LIBLLDB_LOG_ALL (UINT32_MAX)
#define LIBLLDB_LOG_DEFAULT (LIBLLDB_LOG_PROCESS |\
LIBLLDB_LOG_THREAD |\

View File

@ -694,18 +694,12 @@ TestPromptFormats (StackFrame *frame)
}
}
// FIXME this should eventually be replaced by proper use of LLDB logging facilities
//#define VERBOSE_FORMATPROMPT_OUTPUT
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
#define IFERROR_PRINT_IT if (error.Fail()) \
{ \
printf("ERROR: %s\n",error.AsCString("unknown")); \
if (log) \
log->Printf("ERROR: %s\n", error.AsCString("unknown")); \
break; \
}
#else // IFERROR_PRINT_IT
#define IFERROR_PRINT_IT if (error.Fail()) \
break;
#endif // IFERROR_PRINT_IT
static bool
ScanFormatDescriptor(const char* var_name_begin,
@ -715,36 +709,55 @@ ScanFormatDescriptor(const char* var_name_begin,
lldb::Format* custom_format,
ValueObject::ValueObjectRepresentationStyle* val_obj_display)
{
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES));
*percent_position = ::strchr(var_name_begin,'%');
if (!*percent_position || *percent_position > var_name_end)
{
if (log)
log->Printf("no format descriptor in string, skipping");
*var_name_final = var_name_end;
}
else
{
*var_name_final = *percent_position;
char* format_name = new char[var_name_end-*var_name_final]; format_name[var_name_end-*var_name_final-1] = '\0';
memcpy(format_name, *var_name_final+1, var_name_end-*var_name_final-1);
if (log)
log->Printf("parsing %s as a format descriptor", format_name);
if ( !FormatManager::GetFormatFromCString(format_name,
true,
*custom_format) )
{
if (log)
log->Printf("%s is an unknown format", format_name);
// if this is an @ sign, print ObjC description
if (*format_name == '@')
*val_obj_display = ValueObject::eDisplayLanguageSpecific;
// if this is a V, print the value using the default format
if (*format_name == 'V')
else if (*format_name == 'V')
*val_obj_display = ValueObject::eDisplayValue;
// if this is an L, print the location of the value
if (*format_name == 'L')
else if (*format_name == 'L')
*val_obj_display = ValueObject::eDisplayLocation;
// if this is an S, print the summary after all
if (*format_name == 'S')
else if (*format_name == 'S')
*val_obj_display = ValueObject::eDisplaySummary;
else if (log)
log->Printf("%s is an error, leaving the previous value alone", format_name);
}
// a good custom format tells us to print the value using it
else
{
if (log)
log->Printf("will display value for this VO");
*val_obj_display = ValueObject::eDisplayValue;
}
delete format_name;
}
if (log)
log->Printf("final format description outcome: custom_format = %d, val_obj_display = %d",
*custom_format,
*val_obj_display);
return true;
}
@ -759,6 +772,7 @@ ScanBracketedRange(const char* var_name_begin,
int64_t* index_lower,
int64_t* index_higher)
{
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES));
*open_bracket_position = ::strchr(var_name_begin,'[');
if (*open_bracket_position && *open_bracket_position < var_name_final)
{
@ -769,6 +783,8 @@ ScanBracketedRange(const char* var_name_begin,
*var_name_final_if_array_range = *open_bracket_position;
if (*close_bracket_position - *open_bracket_position == 1)
{
if (log)
log->Printf("[] detected.. going from 0 to end of data");
*index_lower = 0;
}
else if (*separator_position == NULL || *separator_position > var_name_end)
@ -776,24 +792,34 @@ ScanBracketedRange(const char* var_name_begin,
char *end = NULL;
*index_lower = ::strtoul (*open_bracket_position+1, &end, 0);
*index_higher = *index_lower;
//printf("got to read low=%d high same\n",bitfield_lower);
if (log)
log->Printf("[%d] detected, high index is same",index_lower);
}
else if (*close_bracket_position && *close_bracket_position < var_name_end)
{
char *end = NULL;
*index_lower = ::strtoul (*open_bracket_position+1, &end, 0);
*index_higher = ::strtoul (*separator_position+1, &end, 0);
//printf("got to read low=%d high=%d\n",bitfield_lower,bitfield_higher);
if (log)
log->Printf("[%d-%d] detected",index_lower,index_higher);
}
else
{
if (log)
log->Printf("expression is erroneous, cannot extract indices out of it");
return false;
}
if (*index_lower > *index_higher && *index_higher > 0)
{
if (log)
log->Printf("swapping indices");
int temp = *index_lower;
*index_lower = *index_higher;
*index_higher = temp;
}
}
else if (log)
log->Printf("no bracketed range, skipping entirely");
return true;
}
@ -806,26 +832,30 @@ ExpandExpressionPath(ValueObject* vobj,
const char* var_name_final,
Error& error)
{
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES));
StreamString sstring;
VariableSP var_sp;
if (*do_deref_pointer)
{
if (log)
log->Printf("been told to deref_pointer by caller");
sstring.PutChar('*');
}
else if (vobj->IsDereferenceOfParent() && ClangASTContext::IsPointerType(vobj->GetParent()->GetClangType()) && !vobj->IsArrayItemForPointer())
{
if (log)
log->Printf("decided to deref_pointer myself");
sstring.PutChar('*');
*do_deref_pointer = true;
}
vobj->GetExpressionPath(sstring, true, ValueObject::eHonorPointers);
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
printf("name to expand in phase 0: %s\n",sstring.GetData());
#endif //VERBOSE_FORMATPROMPT_OUTPUT
if (log)
log->Printf("expression path to expand in phase 0: %s",sstring.GetData());
sstring.PutRawBytes(var_name_begin+3, var_name_final-var_name_begin-3);
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
printf("name to expand in phase 1: %s\n",sstring.GetData());
#endif //VERBOSE_FORMATPROMPT_OUTPUT
if (log)
log->Printf("expression path to expand in phase 1: %s",sstring.GetData());
std::string name = std::string(sstring.GetData());
ValueObjectSP target = frame->GetValueForVariableExpressionPath (name.c_str(),
eNoDynamicValues,
@ -841,12 +871,12 @@ ExpandIndexedExpression(ValueObject* vobj,
StackFrame* frame,
bool deref_pointer)
{
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES));
const char* ptr_deref_format = "[%d]";
std::auto_ptr<char> ptr_deref_buffer(new char[10]);
::sprintf(ptr_deref_buffer.get(), ptr_deref_format, index);
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
printf("name to deref: %s\n",ptr_deref_buffer.get());
#endif //VERBOSE_FORMATPROMPT_OUTPUT
if (log)
log->Printf("name to deref: %s",ptr_deref_buffer.get());
const char* first_unparsed;
ValueObject::GetValueForExpressionPathOptions options;
ValueObject::ExpressionPathEndResultType final_value_type;
@ -860,20 +890,18 @@ ExpandIndexedExpression(ValueObject* vobj,
&what_next);
if (!item)
{
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
printf("ERROR: unparsed portion = %s, why stopping = %d,"
" final_value_type %d\n",
if (log)
log->Printf("ERROR: unparsed portion = %s, why stopping = %d,"
" final_value_type %d",
first_unparsed, reason_to_stop, final_value_type);
#endif //VERBOSE_FORMATPROMPT_OUTPUT
}
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
else
{
printf("ALL RIGHT: unparsed portion = %s, why stopping = %d,"
" final_value_type %d\n",
if (log)
log->Printf("ALL RIGHT: unparsed portion = %s, why stopping = %d,"
" final_value_type %d",
first_unparsed, reason_to_stop, final_value_type);
}
#endif //VERBOSE_FORMATPROMPT_OUTPUT
return item;
}
@ -892,6 +920,7 @@ Debugger::FormatPrompt
ValueObject* realvobj = NULL; // makes it super-easy to parse pointers
bool success = true;
const char *p;
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES));
for (p = format; *p != '\0'; ++p)
{
if (realvobj)
@ -967,8 +996,8 @@ Debugger::FormatPrompt
const RegisterInfo *reg_info = NULL;
RegisterContext *reg_ctx = NULL;
bool do_deref_pointer = false;
ValueObject::ExpressionPathScanEndReason reason_to_stop;
ValueObject::ExpressionPathEndResultType final_value_type;
ValueObject::ExpressionPathScanEndReason reason_to_stop = ValueObject::eEndOfString;
ValueObject::ExpressionPathEndResultType final_value_type = ValueObject::ePlain;
// Each variable must set success to true below...
bool var_success = false;
@ -1050,9 +1079,8 @@ Debugger::FormatPrompt
::memset(expr_path.get(), 0, var_name_final-var_name_begin-1);
memcpy(expr_path.get(), var_name_begin+3,var_name_final-var_name_begin-3);
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
printf("symbol to expand: %s\n",expr_path.get());
#endif //VERBOSE_FORMATPROMPT_OUTPUT
if (log)
log->Printf("symbol to expand: %s",expr_path.get());
target = vobj->GetValueForExpressionPath(expr_path.get(),
&first_unparsed,
@ -1063,21 +1091,19 @@ Debugger::FormatPrompt
if (!target)
{
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
printf("ERROR: unparsed portion = %s, why stopping = %d,"
" final_value_type %d\n",
if (log)
log->Printf("ERROR: unparsed portion = %s, why stopping = %d,"
" final_value_type %d",
first_unparsed, reason_to_stop, final_value_type);
#endif //VERBOSE_FORMATPROMPT_OUTPUT
break;
}
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
else
{
printf("ALL RIGHT: unparsed portion = %s, why stopping = %d,"
" final_value_type %d\n",
if (log)
log->Printf("ALL RIGHT: unparsed portion = %s, why stopping = %d,"
" final_value_type %d",
first_unparsed, reason_to_stop, final_value_type);
}
#endif //VERBOSE_FORMATPROMPT_OUTPUT
}
else
break;
@ -1103,26 +1129,31 @@ Debugger::FormatPrompt
if ((is_array || is_pointer) && (!is_array_range) && val_obj_display == ValueObject::eDisplayValue) // this should be wrong, but there are some exceptions
{
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
printf("I am into array || pointer && !range\n");
#endif //VERBOSE_FORMATPROMPT_OUTPUT
if (log)
log->Printf("I am into array || pointer && !range");
// try to use the special cases
var_success = target->DumpPrintableRepresentation(s,val_obj_display, custom_format);
if (!var_success)
s << "<invalid, please use [] operator>";
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
printf("outcome was : %s\n", var_success ? "good" : "bad");
#endif //VERBOSE_FORMATPROMPT_OUTPUT
if (log)
log->Printf("special cases did%s match", var_success ? "" : "n't");
break;
}
if (!is_array_range)
{
if (log)
log->Printf("dumping ordinary printable output");
var_success = target->DumpPrintableRepresentation(s,val_obj_display, custom_format);
}
else
{
{
if (log)
log->Printf("checking if I can handle as array");
if (!is_array && !is_pointer)
break;
if (log)
log->Printf("handle as array");
const char* special_directions = NULL;
StreamString special_directions_writer;
if (close_bracket_position && (var_name_end-close_bracket_position > 1))
@ -1151,16 +1182,14 @@ Debugger::FormatPrompt
if (!item)
{
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
printf("ERROR\n");
#endif //VERBOSE_FORMATPROMPT_OUTPUT
if (log)
log->Printf("ERROR in getting child item at index %d", index_lower);
}
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
else
{
printf("special_directions: %s\n",special_directions);
if (log)
log->Printf("special_directions for child item: %s",special_directions);
}
#endif //VERBOSE_FORMATPROMPT_OUTPUT
if (!special_directions)
var_success &= item->DumpPrintableRepresentation(s,val_obj_display, custom_format);

View File

@ -885,6 +885,9 @@ ValueObject::GetValueAsCString ()
return m_value_str.c_str();
}
// this call should only return pointers to data that needs no special memory management
// (either because they are hardcoded strings, or because they are backed by some other
// object); returning any new()-ed or malloc()-ed data here, will lead to leaks!
const char *
ValueObject::GetPrintableRepresentation(ValueObjectRepresentationStyle val_obj_display,
lldb::Format custom_format)
@ -921,10 +924,18 @@ ValueObject::GetPrintableRepresentation(ValueObjectRepresentationStyle val_obj_d
if (val_obj_display == eDisplayValue)
return_value = GetSummaryAsCString();
else if (val_obj_display == eDisplaySummary)
return_value = GetValueAsCString();
{
if (ClangASTContext::IsAggregateType (GetClangType()) == true)
{
// this thing has no value
return_value = "<no summary defined for this datatype>";
}
else
return_value = GetValueAsCString();
}
}
return (return_value ? return_value : "<error>");
return (return_value ? return_value : "<no printable representation>");
}
@ -980,7 +991,7 @@ ValueObject::DumpPrintableRepresentation(Stream& s,
ValueObjectSP child = GetChildAtIndex(low,true);
if (!child.get())
{
s << "<error>";
s << "<invalid child>";
continue;
}
child->DumpPrintableRepresentation(s, ValueObject::eDisplayValue, custom_format);
@ -1018,7 +1029,7 @@ ValueObject::DumpPrintableRepresentation(Stream& s,
ValueObjectSP child = GetChildAtIndex(low,true);
if (!child.get())
{
s << "<error>";
s << "<invalid child>";
continue;
}
child->DumpPrintableRepresentation(s, ValueObject::eDisplayValue, format);

View File

@ -134,6 +134,7 @@ lldb_private::DisableLog (Args &args, Stream *feedback_strm)
else if (0 == ::strncasecmp(arg, "conn", 4)) flag_bits &= ~LIBLLDB_LOG_CONNECTION;
else if (0 == ::strncasecmp(arg, "host", 4)) flag_bits &= ~LIBLLDB_LOG_HOST;
else if (0 == ::strncasecmp(arg, "unwind", 6)) flag_bits &= ~LIBLLDB_LOG_UNWIND;
else if (0 == ::strncasecmp(arg, "types", 5)) flag_bits &= ~LIBLLDB_LOG_TYPES;
else
{
feedback_strm->Printf ("error: unrecognized log category '%s'\n", arg);
@ -200,6 +201,7 @@ lldb_private::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, Args &ar
else if (0 == ::strncasecmp(arg, "conn", 4)) flag_bits |= LIBLLDB_LOG_CONNECTION;
else if (0 == ::strncasecmp(arg, "host", 4)) flag_bits |= LIBLLDB_LOG_HOST;
else if (0 == ::strncasecmp(arg, "unwind", 6)) flag_bits |= LIBLLDB_LOG_UNWIND;
else if (0 == ::strncasecmp(arg, "types", 5)) flag_bits |= LIBLLDB_LOG_TYPES;
else
{
feedback_strm->Printf("error: unrecognized log category '%s'\n", arg);
@ -238,5 +240,6 @@ lldb_private::ListLogCategories (Stream *strm)
"\tstep - log step related activities\n"
"\tunwind - log stack unwind activities\n"
"\tverbose - enable verbose logging\n"
"\twatch - log watchpoint related activities\n");
"\twatch - log watchpoint related activities\n"
"\ttypes - log type system related activities\n");
}

View File

@ -57,7 +57,7 @@ class DataFormatterTestCase(TestBase):
self.runCmd("type summary add -f \"${var%V}\" SomeData")
self.expect("frame variable data",
substrs = ['error'])
substrs = ['no printable representation'])
# ${var%s}
self.runCmd("type summary add -f \"ptr = ${var%s}\" \"char *\"")
@ -198,12 +198,13 @@ class DataFormatterTestCase(TestBase):
'{0x00000009},{0x00000008},{0x00000007},{0x00000006},{0x00000005}'])
# printing full array as an array
self.runCmd("log enable lldb types -f dummy.log")
self.runCmd("type summary add -f \"arr = ${var%uint32_t[]}\" \"int [5]\"")
self.expect("frame variable intarr",
substrs = ['intarr = arr =',
'0x00000001,0x00000001,0x00000002,0x00000003,0x00000005'])
self.runCmd("log disable lldb types")
self.expect("frame variable other.intarr",
substrs = ['intarr = arr =',
'0x00000009,0x00000008,0x00000007,0x00000006,0x00000005'])