scons: Use unions to prevent debug flag destruction.
When an object is a field in a union, it's the programmer's resposibility to destroy it from the union's destructor. We can simply neglect to do that and avoid having to use new to create the flags. Also, we can define the flags as inline variables (a c++17 feature), and then create a constexpr references to them. This lets us refer to debug flags in constexpr objects, although we can't interact with them at, for instance, construciton time or we'd lose our own constexpr-ness since the actual object is not constexpr. In c++20 we would hypothetically be able to use constexpr with new and delete, but there may be additional restrictions that would make this particular use impossible. Also this avoids leaking memory, which, even though it's intentional, may confuse tools like valgrind. Also, we need to ensure that all headers are included in some source file so that they exist in the final executable, so that they show up in the help, can be enabled/disabled, etc. Change-Id: Ia43111d938e7af7140b1c17dd68135f426d0a1e9 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/49783 Maintainer: Gabe Black <gabe.black@gmail.com> Reviewed-by: Jui-min Lee <fcrh@google.com> Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
@@ -35,89 +35,16 @@
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import collections
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
from code_formatter import code_formatter
|
||||
|
||||
def usage():
|
||||
print(f"Usage: {sys.argv[0]} CC [NAME DESC FMT COMPONENTS]...",
|
||||
file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
usage()
|
||||
|
||||
cc = sys.argv[1]
|
||||
|
||||
|
||||
FlagInfo = collections.namedtuple('FlagInfo',
|
||||
['name', 'desc', 'fmt', 'components'])
|
||||
|
||||
flags = []
|
||||
|
||||
pos = 2
|
||||
# Extract groups of arguments for each flag.
|
||||
while pos < len(sys.argv):
|
||||
if len(sys.argv) < pos + 4:
|
||||
usage()
|
||||
|
||||
name, desc, fmt, components = sys.argv[pos:pos+4]
|
||||
pos += 4
|
||||
fmt = fmt.lower()
|
||||
if fmt == 'true':
|
||||
fmt = True
|
||||
elif fmt == 'false':
|
||||
fmt = False
|
||||
else:
|
||||
print(f'Unrecognized "FMT" value {fmt}', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
components = components.split(':') if components else []
|
||||
flags.append(FlagInfo(name, desc, fmt, components))
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("cc", help="the path of the debug flag cc file")
|
||||
parser.add_argument("name", help="the name of the debug flag")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
code = code_formatter()
|
||||
|
||||
# File header.
|
||||
code('''
|
||||
#include "base/compiler.hh" // For namespace deprecation
|
||||
#include "base/debug.hh"
|
||||
|
||||
namespace gem5
|
||||
{
|
||||
|
||||
GEM5_DEPRECATED_NAMESPACE(Debug, debug);
|
||||
namespace debug
|
||||
{
|
||||
''')
|
||||
|
||||
# Group the flags into either simple flags or compound flags.
|
||||
simple_flags = sorted(filter(lambda f: not f.components, flags))
|
||||
compound_flags = sorted(filter(lambda f: f.components, flags))
|
||||
|
||||
# We intentionally make flag a reference to a heap allocated object so
|
||||
# (1) It has a similar interface to a global object like before
|
||||
# (2) It does not get destructed at the end of simulation
|
||||
# The second property is desirable as global objects from different
|
||||
# translation units do not have a defined destruction order, so it'll
|
||||
# be unsafe to access debug flags in their destructor in such cases.
|
||||
for flag in simple_flags:
|
||||
name, desc, components, fmt = \
|
||||
flag.name, flag.desc, flag.components, flag.fmt
|
||||
fmt = 'true' if fmt else 'false'
|
||||
code('''
|
||||
SimpleFlag& $name = *(
|
||||
new SimpleFlag("$name", "$desc", $fmt));''')
|
||||
|
||||
for flag in compound_flags:
|
||||
name, desc, components = flag.name, flag.desc, flag.components
|
||||
code('''
|
||||
CompoundFlag& $name = *(new CompoundFlag("$name", "$desc", {
|
||||
${{', '.join('&' + simple for simple in components)}}
|
||||
}));''')
|
||||
|
||||
code('''
|
||||
} // namespace debug
|
||||
} // namespace gem5''')
|
||||
|
||||
code.write(cc)
|
||||
code('#include "debug/${{args.name}}.hh"')
|
||||
code.write(args.cc)
|
||||
|
||||
@@ -43,6 +43,7 @@ from code_formatter import code_formatter
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("hh", help="the path of the debug flag header file")
|
||||
parser.add_argument("name", help="the name of the debug flag")
|
||||
parser.add_argument("desc", help="a description of the debug flag")
|
||||
parser.add_argument("fmt",
|
||||
help="whether the flag is a format flag (True or False)")
|
||||
parser.add_argument("components",
|
||||
@@ -50,21 +51,28 @@ parser.add_argument("components",
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
fmt = eval(args.fmt)
|
||||
fmt = args.fmt.lower()
|
||||
if fmt == 'true':
|
||||
fmt = True
|
||||
elif fmt == 'false':
|
||||
fmt = False
|
||||
else:
|
||||
print(f'Unrecognized "FMT" value {fmt}', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
components = args.components.split(':') if args.components else []
|
||||
|
||||
code = code_formatter()
|
||||
|
||||
typename = "CompoundFlag" if components else "SimpleFlag"
|
||||
component_flag_decls = ''.join(f'extern SimpleFlag &{simple};\n' for
|
||||
simple in components)
|
||||
|
||||
code('''\
|
||||
code('''
|
||||
#ifndef __DEBUG_${{args.name}}_HH__
|
||||
#define __DEBUG_${{args.name}}_HH__
|
||||
|
||||
#include "base/compiler.hh" // For namespace deprecation
|
||||
|
||||
#include "base/debug.hh"
|
||||
''')
|
||||
for flag in components:
|
||||
code('#include "debug/${flag}.hh"')
|
||||
code('''
|
||||
namespace gem5
|
||||
{
|
||||
|
||||
@@ -72,10 +80,43 @@ GEM5_DEPRECATED_NAMESPACE(Debug, debug);
|
||||
namespace debug
|
||||
{
|
||||
|
||||
class SimpleFlag;
|
||||
class CompoundFlag;
|
||||
extern ${typename}& ${{args.name}};
|
||||
${component_flag_decls}
|
||||
namespace unions
|
||||
{
|
||||
''')
|
||||
|
||||
# Use unions to prevent debug flags from being destructed. It's the
|
||||
# responsibility of the programmer to handle object destruction for members
|
||||
# of the union. We purposefully leave that destructor empty so that we can
|
||||
# use debug flags even in the destructors of other objects.
|
||||
if components:
|
||||
code('''
|
||||
inline union ${{args.name}}
|
||||
{
|
||||
~${{args.name}}() {}
|
||||
CompoundFlag ${{args.name}} = {
|
||||
"${{args.name}}", "${{args.desc}}", {
|
||||
${{",\\n ".join(
|
||||
f"(Flag *)&::gem5::debug::{flag}" for flag in components)}}
|
||||
}
|
||||
};
|
||||
} ${{args.name}};
|
||||
''')
|
||||
else:
|
||||
code('''
|
||||
inline union ${{args.name}}
|
||||
{
|
||||
~${{args.name}}() {}
|
||||
SimpleFlag ${{args.name}} = {
|
||||
"${{args.name}}", "${{args.desc}}", ${{"true" if fmt else "false"}}
|
||||
};
|
||||
} ${{args.name}};
|
||||
''')
|
||||
|
||||
code('''
|
||||
} // namespace unions
|
||||
|
||||
inline constexpr const auto& ${{args.name}} =
|
||||
::gem5::debug::unions::${{args.name}}.${{args.name}};
|
||||
|
||||
} // namespace debug
|
||||
} // namespace gem5
|
||||
|
||||
@@ -373,34 +373,36 @@ Export('GTest')
|
||||
# Debug Flags
|
||||
#
|
||||
|
||||
DebugFlagInfo = collections.namedtuple('DebugFlag',
|
||||
['name', 'desc', 'components', 'fmt'])
|
||||
debug_flags = set()
|
||||
def DebugFlagCommon(name, flags, desc, fmt):
|
||||
if name == "All":
|
||||
raise AttributeError('The "All" flag name is reserved')
|
||||
debug_flags = env.get('DEBUG_FLAGS', [])
|
||||
if any(name == flag.name for flag in debug_flags):
|
||||
if name in debug_flags:
|
||||
raise AttributeError(f'Flag {name} already specified')
|
||||
|
||||
flag = DebugFlagInfo(name, desc, flags, fmt)
|
||||
env.Append(DEBUG_FLAGS=[flag])
|
||||
debug_flags.add(name)
|
||||
|
||||
hh_file = Dir(env['BUILDDIR']).Dir('debug').File(f'{name}.hh')
|
||||
gem5py_env.Command(hh_file,
|
||||
[ '${GEM5PY}', '${DEBUGFLAGHH_PY}' ],
|
||||
MakeAction('"${GEM5PY}" "${DEBUGFLAGHH_PY}" "${TARGET}" "${NAME}" ' \
|
||||
'"${FMT}" "${COMPONENTS}"',
|
||||
'"${DESC}" "${FMT}" "${COMPONENTS}"',
|
||||
Transform("TRACING", 0)),
|
||||
DEBUGFLAGHH_PY=build_tools.File('debugflaghh.py'),
|
||||
NAME=name, FMT=('True' if fmt else 'False'),
|
||||
NAME=name, DESC=desc, FMT=('True' if fmt else 'False'),
|
||||
COMPONENTS=':'.join(flags))
|
||||
cc_file = Dir(env['BUILDDIR']).Dir('debug').File('%s.cc' % name)
|
||||
gem5py_env.Command(cc_file,
|
||||
[ "${GEM5PY}", "${DEBUGFLAGCC_PY}" ],
|
||||
MakeAction('"${GEM5PY}" "${DEBUGFLAGCC_PY}" "${TARGET}" "${NAME}"',
|
||||
Transform("TRACING", 0)),
|
||||
DEBUGFLAGCC_PY=build_tools.File('debugflagcc.py'), NAME=name)
|
||||
Source(cc_file, add_tags='gem5 trace')
|
||||
|
||||
def DebugFlag(name, desc=None, fmt=False):
|
||||
DebugFlagCommon(name, (), desc, fmt)
|
||||
|
||||
def CompoundFlag(name, flags, desc=None):
|
||||
DebugFlagCommon(name, flags, desc, False)
|
||||
|
||||
def DebugFormatFlag(name, desc=None):
|
||||
DebugFlag(name, desc, True)
|
||||
|
||||
@@ -800,25 +802,6 @@ for name,simobj in sorted(sim_objects.items()):
|
||||
env.Depends(cc_file, depends + extra_deps)
|
||||
Source(cc_file, add_tags='python')
|
||||
|
||||
#
|
||||
# Handle debug flags
|
||||
#
|
||||
|
||||
# Generate the files for the debug and debug-format flags
|
||||
flag_args = []
|
||||
for flag in env.get('DEBUG_FLAGS', []):
|
||||
components = ":".join(c for c in flag.components)
|
||||
arg = f'"{flag.name}" "{flag.desc}" "{bool(flag.fmt)}" ' \
|
||||
f'"{components}"'
|
||||
flag_args.append(arg)
|
||||
gem5py_env.Command('debug/flags.cc',
|
||||
[ "${GEM5PY}", "${DEBUGFLAGCC_PY}" ],
|
||||
MakeAction('"${GEM5PY}" "${DEBUGFLAGCC_PY}" "${TARGET}" ${FLAGS}',
|
||||
Transform("TRACING", 0)),
|
||||
DEBUGFLAGCC_PY=build_tools.File('debugflagcc.py'),
|
||||
FLAGS=' '.join(flag_args))
|
||||
Source('debug/flags.cc', add_tags='gem5 trace')
|
||||
|
||||
# version tags
|
||||
tags = \
|
||||
env.Command('sim/tags.cc', None,
|
||||
|
||||
@@ -45,7 +45,7 @@ if env['TARGET_ISA'] == 'arm':
|
||||
# Scons bug id: 2006 M5 Bug id: 308
|
||||
Dir('isa/formats')
|
||||
|
||||
GTest('aapcs64.test', 'aapcs64.test.cc')
|
||||
GTest('aapcs64.test', 'aapcs64.test.cc', '../../base/debug.cc')
|
||||
Source('decoder.cc')
|
||||
Source('faults.cc')
|
||||
Source('htm.cc')
|
||||
|
||||
Reference in New Issue
Block a user