- Now using ${var} as the summary for an aggregate type will produce "name-of-type @ object-location" instead of giving an error

e.g. you may get "foo_class @ 0x123456" when typing "type summary add -f ${var} foo_class"
- Added a new special formatting token %T for summaries. This shows the type of the object.
  Using it, the new "type @ location" summary could be manually generated by writing ${var%T} @ ${var%L}
- Bits and pieces required to support "frame variable array[n-m]"
  The feature is not enabled yet because some additional design and support code is required, but the basics
  are getting there
- Fixed a potential issue where a ValueObjectSyntheticFilter was not holding on to its SyntheticChildrenSP
  Because of the way VOSF are being built now, this has never been an actual issue, but it is still sensible for
  a VOSF to hold on to the SyntheticChildrenSP as well as to its FrontEnd

llvm-svn: 138080
This commit is contained in:
Enrico Granata 2011-08-19 21:13:46 +00:00
parent 4ad83e69a0
commit d64d0bc0ea
14 changed files with 614 additions and 30 deletions

View File

@ -384,6 +384,241 @@ public:
};
struct SyntheticArrayRange
{
private:
int m_low;
int m_high;
SyntheticArrayRange* m_next;
public:
SyntheticArrayRange () :
m_low(-1),
m_high(-2),
m_next(NULL)
{}
SyntheticArrayRange (int L) :
m_low(L),
m_high(L),
m_next(NULL)
{}
SyntheticArrayRange (int L, int H) :
m_low(L),
m_high(H),
m_next(NULL)
{}
SyntheticArrayRange (int L, int H, SyntheticArrayRange* N) :
m_low(L),
m_high(H),
m_next(N)
{}
inline int
GetLow ()
{
return m_low;
}
inline int
GetHigh ()
{
return m_high;
}
inline void
SetLow (int L)
{
m_low = L;
}
inline void
SetHigh (int H)
{
m_high = H;
}
inline int
GetSelfCount()
{
return GetHigh() - GetLow() + 1;
}
int
GetCount()
{
int count = GetSelfCount();
if (m_next)
count += m_next->GetCount();
return count;
}
inline SyntheticArrayRange*
GetNext()
{
return m_next;
}
void
SetNext(SyntheticArrayRange* N)
{
if (m_next)
delete m_next;
m_next = N;
}
void
SetNext(int L, int H)
{
if (m_next)
delete m_next;
m_next = new SyntheticArrayRange(L, H);
}
void
SetNext(int L)
{
if (m_next)
delete m_next;
m_next = new SyntheticArrayRange(L);
}
~SyntheticArrayRange()
{
delete m_next;
m_next = NULL;
}
};
class SyntheticArrayView : public SyntheticChildren
{
SyntheticArrayRange m_head;
SyntheticArrayRange *m_tail;
public:
SyntheticArrayView(bool casc = false,
bool skipptr = false,
bool skipref = false) :
SyntheticChildren(casc, skipptr, skipref),
m_head(),
m_tail(&m_head)
{
}
void
AddRange(int L, int H)
{
m_tail->SetLow(L);
m_tail->SetHigh(H);
m_tail->SetNext(new SyntheticArrayRange());
m_tail = m_tail->GetNext();
}
int
GetCount()
{
return m_head.GetCount();
}
const int
GetRealIndexForIndex(int i)
{
if (i >= GetCount())
return -1;
SyntheticArrayRange* ptr = &m_head;
int residual = i;
while(ptr && ptr != m_tail)
{
if (residual >= ptr->GetSelfCount())
{
residual -= ptr->GetSelfCount();
ptr = ptr->GetNext();
}
return ptr->GetLow() + residual;
}
return -1;
}
bool
IsScripted()
{
return false;
}
std::string
GetDescription();
class FrontEnd : public SyntheticChildrenFrontEnd
{
private:
SyntheticArrayView* filter;
public:
FrontEnd(SyntheticArrayView* flt,
lldb::ValueObjectSP be) :
SyntheticChildrenFrontEnd(be),
filter(flt)
{}
virtual
~FrontEnd()
{
}
virtual uint32_t
CalculateNumChildren()
{
return filter->GetCount();
}
virtual lldb::ValueObjectSP
GetChildAtIndex (uint32_t idx, bool can_create)
{
if (idx >= filter->GetCount())
return lldb::ValueObjectSP();
return m_backend->GetSyntheticArrayMember(filter->GetRealIndexForIndex(idx), can_create);
}
virtual void
Update() {}
virtual uint32_t
GetIndexOfChildWithName (const ConstString &name_cs)
{
const char* name_cstr = name_cs.GetCString();
if (*name_cstr != '[')
return UINT32_MAX;
std::string name(name_cstr+1);
if (name[name.size()-1] != ']')
return UINT32_MAX;
name = name.erase(name.size()-1,1);
int index = Args::StringToSInt32 (name.c_str(), -1);
if (index < 0)
return UINT32_MAX;
return index;
}
typedef lldb::SharedPtr<SyntheticChildrenFrontEnd>::Type SharedPointer;
};
virtual SyntheticChildrenFrontEnd::SharedPointer
GetFrontEnd(lldb::ValueObjectSP backend)
{
return SyntheticChildrenFrontEnd::SharedPointer(new FrontEnd(this, backend));
}
};
struct SummaryFormat
{

View File

@ -79,7 +79,8 @@ public:
eDisplaySummary,
eDisplayLanguageSpecific,
eDisplayLocation,
eDisplayChildrenCount
eDisplayChildrenCount,
eDisplayType
};
enum ExpressionPathScanEndReason
@ -657,6 +658,10 @@ public:
ValueObjectRepresentationStyle val_obj_display = eDisplaySummary,
lldb::Format custom_format = lldb::eFormatInvalid);
bool
HasSpecialCasesForPrintableRepresentation(ValueObjectRepresentationStyle val_obj_display,
lldb::Format custom_format);
bool
DumpPrintableRepresentation(Stream& s,
ValueObjectRepresentationStyle val_obj_display = eDisplaySummary,
@ -693,6 +698,9 @@ protected:
public:
lldb::ValueObjectSP
GetSyntheticChild (const ConstString &key) const;
lldb::ValueObjectSP
GetSyntheticArrayMember (int32_t index, bool can_create);
lldb::ValueObjectSP
GetSyntheticArrayMemberFromPointer (int32_t index, bool can_create);
@ -703,6 +711,9 @@ public:
lldb::ValueObjectSP
GetSyntheticBitFieldChild (uint32_t from, uint32_t to, bool can_create);
lldb::ValueObjectSP
GetSyntheticArrayRangeChild (uint32_t from, uint32_t to, bool can_create);
lldb::ValueObjectSP
GetSyntheticExpressionPathChild(const char* expression, bool can_create);

View File

@ -24,9 +24,9 @@ namespace lldb_private {
//----------------------------------------------------------------------
// A ValueObject that obtains its children from some source other than
// real information
// This is currently used to implement children filtering, where only
// a subset of the real children are shown, but it can be used for any
// source of made-up children information
// This is currently used to implement Python-based children and filters
// but you can bind it to any source of synthetic information and have
// it behave accordingly
//----------------------------------------------------------------------
class ValueObjectSynthetic : public ValueObject
{
@ -121,6 +121,7 @@ protected:
lldb::TypeSP m_type_sp;
lldb::ValueObjectSP m_owning_valobj_sp;
lldb::SyntheticValueType m_use_synthetic;
lldb::SyntheticChildrenSP m_synth_sp; // hold on to your synthetic children provider
lldb::SyntheticChildrenFrontEndSP m_synth_filter;
typedef std::map<uint32_t, lldb::ValueObjectSP> ByIndexMap;

View File

@ -32,9 +32,10 @@ class StackFrame :
public:
enum ExpressionPathOption
{
eExpressionPathOptionCheckPtrVsMember = (1u << 0),
eExpressionPathOptionsNoFragileObjcIvar = (1u << 1),
eExpressionPathOptionsNoSyntheticChildren = (1u << 2)
eExpressionPathOptionCheckPtrVsMember = (1u << 0),
eExpressionPathOptionsNoFragileObjcIvar = (1u << 1),
eExpressionPathOptionsNoSyntheticChildren = (1u << 2),
eExpressionPathOptionsNoSyntheticArrayRange = (1u << 3),
};
//------------------------------------------------------------------
// Constructors and Destructors

View File

@ -744,6 +744,8 @@ ScanFormatDescriptor(const char* var_name_begin,
*val_obj_display = ValueObject::eDisplaySummary;
else if (*format_name == '#')
*val_obj_display = ValueObject::eDisplayChildrenCount;
else if (*format_name == 'T')
*val_obj_display = ValueObject::eDisplayType;
else if (log)
log->Printf("%s is an error, leaving the previous value alone", format_name);
}
@ -1165,25 +1167,52 @@ Debugger::FormatPrompt
StreamString str_temp;
if (log)
log->Printf("I am into array || pointer && !range");
// try to use the special cases
var_success = target->DumpPrintableRepresentation(str_temp,
val_obj_display,
custom_format);
if (log)
log->Printf("special cases did%s match", var_success ? "" : "n't");
if (!var_success)
if (target->HasSpecialCasesForPrintableRepresentation(val_obj_display,
custom_format))
{
s << "<invalid usage of pointer value as object>";
// try to use the special cases
var_success = target->DumpPrintableRepresentation(str_temp,
val_obj_display,
custom_format);
if (log)
log->Printf("special cases did%s match", var_success ? "" : "n't");
// should not happen
if (!var_success)
s << "<invalid usage of pointer value as object>";
else
s << str_temp.GetData();
var_success = true;
break;
}
else
s << str_temp.GetData();
{
// if ${var}
if (was_plain_var)
{
s << target->GetTypeName() << " @ " << target->GetLocationAsCString();
}
else
{
s << "<invalid usage of pointer value as object>";
}
var_success = true;
break;
}
}
// if directly trying to print ${var}, and this is an aggregate, display a nice
// type @ location message
if (is_aggregate && was_plain_var)
{
s << target->GetTypeName() << " @ " << target->GetLocationAsCString();
var_success = true;
break;
}
// if directly trying to print ${var} using its value, and this is an aggregate, display a nice
// error message about it (and avoid recursion in DumpPrintableRepresentation)
if (is_aggregate && ((was_var_format && val_obj_display == ValueObject::eDisplayValue) || was_plain_var))
// if directly trying to print ${var%V}, and this is an aggregate, do not let the user do it
if (is_aggregate && ((was_var_format && val_obj_display == ValueObject::eDisplayValue)))
{
s << "<invalid use of aggregate type>";
var_success = true;

View File

@ -181,6 +181,31 @@ SyntheticFilter::GetDescription()
return sstr.GetString();
}
std::string
SyntheticArrayView::GetDescription()
{
StreamString sstr;
sstr.Printf("%s%s%s {\n",
m_cascades ? "" : " (not cascading)",
m_skip_pointers ? " (skip pointers)" : "",
m_skip_references ? " (skip references)" : "");
SyntheticArrayRange* ptr = &m_head;
while (ptr && ptr != m_tail)
{
if (ptr->GetLow() == ptr->GetHigh())
sstr.Printf(" [%d]\n",
ptr->GetLow());
else
sstr.Printf(" [%d-%d]\n",
ptr->GetLow(),
ptr->GetHigh());
ptr = ptr->GetNext();
}
sstr.Printf("}");
return sstr.GetString();
}
SyntheticScriptProvider::FrontEnd::FrontEnd(std::string pclass,
lldb::ValueObjectSP be) :
SyntheticChildrenFrontEnd(be),

View File

@ -989,6 +989,9 @@ ValueObject::GetPrintableRepresentation(Stream& s,
snprintf((char*)return_value, 512, "%d", count);
break;
}
case eDisplayType:
return_value = GetTypeName().AsCString();
break;
default:
break;
}
@ -1007,7 +1010,11 @@ ValueObject::GetPrintableRepresentation(Stream& s,
// this thing has no value, and it seems to have no summary
// some combination of unitialized data and other factors can also
// raise this condition, so let's print a nice generic error message
return_value = "<no available summary>";
{
alloc_mem.resize(684);
return_value = &alloc_mem[0];
snprintf((char*)return_value, 684, "%s @ %s", GetTypeName().AsCString(), GetLocationAsCString());
}
}
else
return_value = GetValueAsCString();
@ -1026,6 +1033,50 @@ ValueObject::GetPrintableRepresentation(Stream& s,
}
// if any more "special cases" are added to ValueObject::DumpPrintableRepresentation() please keep
// this call up to date by returning true for your new special cases. We will eventually move
// to checking this call result before trying to display special cases
bool
ValueObject::HasSpecialCasesForPrintableRepresentation(ValueObjectRepresentationStyle val_obj_display,
lldb::Format custom_format)
{
clang_type_t elem_or_pointee_type;
Flags flags(ClangASTContext::GetTypeInfo(GetClangType(), GetClangAST(), &elem_or_pointee_type));
if (flags.AnySet(ClangASTContext::eTypeIsArray | ClangASTContext::eTypeIsPointer)
&& val_obj_display == ValueObject::eDisplayValue)
{
if (IsCStringContainer(true) &&
(custom_format == lldb::eFormatCString ||
custom_format == lldb::eFormatCharArray ||
custom_format == lldb::eFormatChar ||
custom_format == lldb::eFormatVectorOfChar))
return true;
if (flags.Test(ClangASTContext::eTypeIsArray))
{
if ((custom_format == lldb::eFormatBytes) ||
(custom_format == lldb::eFormatBytesWithASCII))
return true;
if ((custom_format == lldb::eFormatVectorOfChar) ||
(custom_format == lldb::eFormatVectorOfFloat32) ||
(custom_format == lldb::eFormatVectorOfFloat64) ||
(custom_format == lldb::eFormatVectorOfSInt16) ||
(custom_format == lldb::eFormatVectorOfSInt32) ||
(custom_format == lldb::eFormatVectorOfSInt64) ||
(custom_format == lldb::eFormatVectorOfSInt8) ||
(custom_format == lldb::eFormatVectorOfUInt128) ||
(custom_format == lldb::eFormatVectorOfUInt16) ||
(custom_format == lldb::eFormatVectorOfUInt32) ||
(custom_format == lldb::eFormatVectorOfUInt64) ||
(custom_format == lldb::eFormatVectorOfUInt8))
return true;
}
}
return false;
}
bool
ValueObject::DumpPrintableRepresentation(Stream& s,
ValueObjectRepresentationStyle val_obj_display,
@ -1380,6 +1431,19 @@ ValueObject::IsPossibleDynamicType ()
return ClangASTContext::IsPossibleDynamicType (GetClangAST (), GetClangType());
}
lldb::ValueObjectSP
ValueObject::GetSyntheticArrayMember (int32_t index, bool can_create)
{
if (IsArrayType())
return GetSyntheticArrayMemberFromArray(index, can_create);
if (IsPointerType())
return GetSyntheticArrayMemberFromPointer(index, can_create);
return ValueObjectSP();
}
ValueObjectSP
ValueObject::GetSyntheticArrayMemberFromPointer (int32_t index, bool can_create)
{
@ -1496,6 +1560,42 @@ ValueObject::GetSyntheticBitFieldChild (uint32_t from, uint32_t to, bool can_cre
return synthetic_child_sp;
}
lldb::ValueObjectSP
ValueObject::GetSyntheticArrayRangeChild (uint32_t from, uint32_t to, bool can_create)
{
ValueObjectSP synthetic_child_sp;
if (IsArrayType () || IsPointerType ())
{
char index_str[64];
snprintf(index_str, sizeof(index_str), "[%i-%i]", from, to);
ConstString index_const_str(index_str);
// Check if we have already created a synthetic array member in this
// valid object. If we have we will re-use it.
synthetic_child_sp = GetSyntheticChild (index_const_str);
if (!synthetic_child_sp)
{
ValueObjectSynthetic *synthetic_child;
// We haven't made a synthetic array member for INDEX yet, so
// lets make one and cache it for any future reference.
SyntheticArrayView *view = new SyntheticArrayView();
view->AddRange(from,to);
SyntheticChildrenSP view_sp(view);
synthetic_child = new ValueObjectSynthetic(*this, view_sp);
// Cache the value if we got one back...
if (synthetic_child)
{
AddSyntheticChild(index_const_str, synthetic_child);
synthetic_child_sp = synthetic_child->GetSP();
synthetic_child_sp->SetName(ConstString(index_str));
synthetic_child_sp->m_is_bitfield_for_scalar = true;
}
}
}
return synthetic_child_sp;
}
lldb::ValueObjectSP
ValueObject::GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type, bool can_create)
{

View File

@ -40,6 +40,7 @@ ValueObjectSynthetic::ValueObjectSynthetic (ValueObject &parent, lldb::Synthetic
m_address (),
m_type_sp(),
m_use_synthetic (lldb::eUseSyntheticFilter),
m_synth_sp(filter),
m_synth_filter(filter->GetFrontEnd(parent.GetSP())),
m_children_byindex(),
m_name_toindex()

View File

@ -526,6 +526,7 @@ StackFrame::GetValueForVariableExpressionPath (const char *var_expr_cstr,
const bool check_ptr_vs_member = (options & eExpressionPathOptionCheckPtrVsMember) != 0;
const bool no_fragile_ivar = (options & eExpressionPathOptionsNoFragileObjcIvar) != 0;
const bool no_synth_child = (options & eExpressionPathOptionsNoSyntheticChildren) != 0;
const bool no_synth_array = (options & eExpressionPathOptionsNoSyntheticArrayRange) != 0;
error.Clear();
bool deref = false;
bool address_of = false;
@ -861,6 +862,7 @@ StackFrame::GetValueForVariableExpressionPath (const char *var_expr_cstr,
// this is most probably a BitField, let's take a look
char *real_end = NULL;
long final_index = ::strtol (end+1, &real_end, 0);
bool expand_bitfield = true;
if (real_end && *real_end == ']')
{
// if the format given is [high-low], swap range
@ -908,15 +910,31 @@ StackFrame::GetValueForVariableExpressionPath (const char *var_expr_cstr,
valobj_sp = temp;
deref = false;
}
child_valobj_sp = valobj_sp->GetSyntheticBitFieldChild(child_index, final_index, true);
if (!child_valobj_sp)
/*else if (valobj_sp->IsArrayType() || valobj_sp->IsPointerType())
{
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
error.SetErrorStringWithFormat ("bitfield range %i-%i is not valid for \"(%s) %s\"",
child_index, final_index,
valobj_sp->GetTypeName().AsCString("<invalid type>"),
var_expr_path_strm.GetString().c_str());
child_valobj_sp = valobj_sp->GetSyntheticArrayRangeChild(child_index, final_index, true);
expand_bitfield = false;
if (!child_valobj_sp)
{
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
error.SetErrorStringWithFormat ("array range %i-%i is not valid for \"(%s) %s\"",
child_index, final_index,
valobj_sp->GetTypeName().AsCString("<invalid type>"),
var_expr_path_strm.GetString().c_str());
}
}*/
if (expand_bitfield)
{
child_valobj_sp = valobj_sp->GetSyntheticBitFieldChild(child_index, final_index, true);
if (!child_valobj_sp)
{
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
error.SetErrorStringWithFormat ("bitfield range %i-%i is not valid for \"(%s) %s\"",
child_index, final_index,
valobj_sp->GetTypeName().AsCString("<invalid type>"),
var_expr_path_strm.GetString().c_str());
}
}
}

View File

@ -195,7 +195,34 @@ class DataFormatterTestCase(TestBase):
'[2] = cool object @ 0x',
'[3] = cool object @ 0x',
'[4] = cool object @ 0x'])
# test getting similar output by exploiting ${var} = 'type @ location' for aggregates
self.runCmd("type summary add -f \"${var}\" i_am_cool")
# this test might fail if the compiler tries to store
# these values into registers.. hopefully this is not
# going to be the case
self.expect("frame variable cool_array",
substrs = ['[0] = i_am_cool @ 0x',
'[1] = i_am_cool @ 0x',
'[2] = i_am_cool @ 0x',
'[3] = i_am_cool @ 0x',
'[4] = i_am_cool @ 0x'])
# test getting same output by exploiting %T and %L together for aggregates
self.runCmd("type summary add -f \"${var%T} @ ${var%L}\" i_am_cool")
# this test might fail if the compiler tries to store
# these values into registers.. hopefully this is not
# going to be the case
self.expect("frame variable cool_array",
substrs = ['[0] = i_am_cool @ 0x',
'[1] = i_am_cool @ 0x',
'[2] = i_am_cool @ 0x',
'[3] = i_am_cool @ 0x',
'[4] = i_am_cool @ 0x'])
self.runCmd("type summary add -f \"goofy\" i_am_cool")
self.runCmd("type summary add -f \"${var.second_cool%S}\" i_am_cooler")

View File

@ -56,10 +56,10 @@ class DataFormatterTestCase(TestBase):
self.runCmd("type summary add -f \"SUMMARY SUCCESS ${var}\" Summarize")
self.expect('frame variable mine_ptr',
substrs = ['SUMMARY SUCCESS <invalid usage of pointer value as object>'])
substrs = ['SUMMARY SUCCESS summarize_ptr_t @ '])
self.expect('frame variable *mine_ptr',
substrs = ['SUMMARY SUCCESS <invalid use of aggregate type>'])
substrs = ['SUMMARY SUCCESS summarize_t @'])
self.runCmd("type summary add -f \"SUMMARY SUCCESS ${var.first}\" Summarize")

View File

@ -0,0 +1,5 @@
LEVEL = ../../../make
CXX_SOURCES := main.cpp
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,90 @@
"""
Test lldb data formatter subsystem.
"""
import os, time
import unittest2
import lldb
from lldbtest import *
class DataFormatterTestCase(TestBase):
# test for rdar://problem/9973992 (What should we do for "${var}" in summaries of aggregate types?)
mydir = os.path.join("functionalities", "data-formatter", "rdar-9973992")
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
def test_with_dsym_and_run_command(self):
"""Test data formatter commands."""
self.buildDsym()
self.data_formatter_commands()
def test_with_dwarf_and_run_command(self):
"""Test data formatter commands."""
self.buildDwarf()
self.data_formatter_commands()
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
# Find the line number to break at.
self.line = line_number('main.cpp', '// Set break point at this line.')
def data_formatter_commands(self):
"""Test that that file and class static variables display correctly."""
self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
self.expect("breakpoint set -f main.cpp -l %d" % self.line,
BREAKPOINT_CREATED,
startstr = "Breakpoint created: 1: file ='main.cpp', line = %d, locations = 1" %
self.line)
self.runCmd("run", RUN_SUCCEEDED)
# The stop reason of the thread should be breakpoint.
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs = ['stopped',
'stop reason = breakpoint'])
# This is the function to remove the custom formats in order to have a
# clean slate for the next test case.
def cleanup():
self.runCmd('type summary clear', check=False)
# Execute the cleanup function during test case tear down.
self.addTearDownHook(cleanup)
self.runCmd("type summary add -f \"SUMMARY SUCCESS ${var}\" Summarize")
self.expect('frame variable mine_ptr',
substrs = ['SUMMARY SUCCESS summarize_ptr_t @ '])
self.expect('frame variable *mine_ptr',
substrs = ['SUMMARY SUCCESS summarize_t @'])
self.runCmd("type summary add -f \"SUMMARY SUCCESS ${var.first}\" Summarize")
self.expect('frame variable mine_ptr',
substrs = ['SUMMARY SUCCESS 10'])
self.expect('frame variable *mine_ptr',
substrs = ['SUMMARY SUCCESS 10'])
self.runCmd("type summary add -f \"${var}\" Summarize")
self.runCmd("type summary add -f \"${var}\" -e TwoSummarizes")
self.expect('frame variable',
substrs = ['(TwoSummarizes) twos = TwoSummarizes @ ',
'first = summarize_t @ ',
'second = summarize_t @ '])
self.runCmd("type summary add -f \"SUMMARY SUCCESS ${var.first}\" Summarize")
self.expect('frame variable',
substrs = ['(TwoSummarizes) twos = TwoSummarizes @ ',
'first = SUMMARY SUCCESS 1',
'second = SUMMARY SUCCESS 3'])
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()

View File

@ -0,0 +1,41 @@
//===-- main.cpp ------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <stdio.h>
struct Summarize
{
int first;
int second;
};
typedef struct Summarize summarize_t;
typedef summarize_t *summarize_ptr_t;
summarize_t global_mine = {30, 40};
struct TwoSummarizes
{
summarize_t first;
summarize_t second;
};
int
main()
{
summarize_t mine = {10, 20};
summarize_ptr_t mine_ptr = &mine;
TwoSummarizes twos = { {1,2}, {3,4} };
printf ("Summarize: first: %d second: %d and address: 0x%p\n", mine.first, mine.second, mine_ptr); // Set break point at this line.
printf ("Global summarize: first: %d second: %d.\n", global_mine.first, global_mine.second);
return 0;
}