Skip to content

Latest commit

 

History

History
218 lines (176 loc) · 8.76 KB

dev_info.md

File metadata and controls

218 lines (176 loc) · 8.76 KB

Modifying Bruce's Implementation

Before making code changes, it is helpful to become familiar with Bruce's build system, which is based on SCons.

Build System

Files SConstruct and src/SConscript contain the build configuration. As shown here, to build something, first source the file bash_defs in the root of Bruce's Git repository. Then cd into the src directory or any directory beneath src and use the build command to build a particular target. In general, to build an executable you simply specify its name when invoking the build command. For instance:

source bash_defs
cd src/bruce
build bruce  # builds bruce executable
build client/to_bruce  # builds command line client
build msg.o  # compile source file msg.cc
build conf/conf.o  # compile source file conf/conf.cc

All build results are in the out directory (relative to the root of the Git repository). For instance, the built bruce executable will be out/debug/bruce/bruce. To build a release version of a target, use the --release option with the build command. For instance, build --release bruce will build a release version of bruce, which will be out/release/bruce/bruce.

Source files ending in .test.cc are unit tests, which can be executed as standalone executables. For instance, in the above example if we typed build unix_dg_input_agent.test, this would build the unit test for Bruce's UNIX datagram input agent, which would then appear as executable file out/debug/bruce/unix_dg_input_agent.test. If you type build --test unix_dg_input_agent.test, that will build the unit test and then immediately execute it.

If you type build -c, that will remove all build artifacts by deleting the out directory. For make users, this is the equivalent of make clean. Alternatively, you can just type rm -fr out from the root of the Git repository. If you wish to change any compiler or linker flags, look for the section of code in the SConstruct file that looks roughly like this:

# Environment.
prog_libs = {'pthread', 'dl', 'rt'}
env = Environment(CFLAGS=['-Wwrite-strings'],
                  CCFLAGS=['-Wall', '-Wextra', '-Werror', '-Wformat=2',
                          '-Winit-self', '-Wunused-parameter', '-Wshadow',
                          '-Wpointer-arith', '-Wcast-align', '-Wlogical-op'],
                  CPPDEFINES=[('SRC_ROOT', '\'"' + src.abspath + '"\'')],
                  CPPPATH=[src, tclap, gtestincdir],
                  CXXFLAGS=['-std=c++11', '-Wold-style-cast'],
                  DEP_SUFFIXES=['.cc', '.cpp', '.c', '.cxx', '.c++', '.C'],
                  TESTSUFFIX='.test',
                  PROG_LIBS=[lib for lib in prog_libs],
                  GENERATED_SOURCE_MAP={},
                  LIB_HEADER_MAP={})

if GetOption('import_path'):
    env['ENV']['PATH'] = os.environ['PATH']


def set_debug_options():
    # Note: If you specify -fsanitize=address, you must also specify
    # -fno-omit-frame-pointer and be sure libasan is installed (RPM package
    # libasan on RHEL, Fedora, and CentOS).
    env.AppendUnique(CCFLAGS=['-g', '-fno-omit-frame-pointer',
                              '-fvisibility=hidden'])
    env.AppendUnique(CXXFLAGS=['-D_GLIBCXX_DEBUG',
                               '-D_GLIBCXX_DEBUG_PEDANTIC'])
    env.AppendUnique(LINKFLAGS=['-rdynamic'])

    if GetOption('asan') == 'yes':
        env.AppendUnique(CCFLAGS=['-fsanitize=address'])
        env.AppendUnique(LINKFLAGS=['-fsanitize=address'])


def set_release_options():
    env.AppendUnique(CCFLAGS=['-O2', '-DNDEBUG', '-Wno-unused',
                              '-Wno-unused-parameter', '-flto',
                              '-fvisibility=hidden'])
    env.AppendUnique(LINKFLAGS=['-flto', '-rdynamic'])

Note that SCons build files are actually Python scripts, so you can add arbitrary Python code. Adding, removing or renaming source files does not require any changes to the build scripts, since they are written to figure out the dependencies on their own. If you want to build all targets (or a substantial subset of all targets) with a single command, you can execute the build_all script in the root of the Git repository. For instance, build_all run_tests will build and run all unit tests. Type build_all --help for a full description of the command line options. Eventually it would be nice to eliminate the build_all script and integrate its functionality directly into the SCons configuration.

Debug Builds

As shown above, if you use the build command to build an individual binary, it will create a debug version by default if --release is not specified. Likewise, if you do not specify --mode=release when running the build_all script, debug versions of binaries will be built by default. As documented here, the pkg command may be used for building an RPM package. By default, pkg builds a release version of Bruce. To build a debug version, specify --debug.

In GCC 4.8, support was added for [AddressSanitizer] (http://code.google.com/p/address-sanitizer/), a useful debugging tool. This is enabled by default in debug builds. When running Bruce with the address sanitizer, you may notice that it uses a large amount of virtual memory (often multiple terabytes). This is expected behavior. After running Bruce for a while with the address sanitizer, it may exit with the following error message:

ERROR: Failed to mmap

If this causes problems, you can build Bruce with the address sanitizer disabled. When building Bruce directly, as described [here] (build_install.md#building-bruce-directly), you can disable the address sanitizer as follows:

build --asan=no bruce

Likewise, you can invoke build_all as follows:

./build_all --asan=no

When building an RPM package (as described [here] (build_install.md#building-an-rpm-package)), a debug version with the address sanitizer disabled may be created as follows:

./pkg --debug --asan no rpm

or

./pkg --debug --asan no rpm_noconfig

Another thing to keep in mind about the address sanitizer's behavior is that it causes operator new to return nullptr on allocation failure in cases where the C++ standard dictates that std::bad_alloc should be thrown. This can cause out of memory conditions to result in segmentation faults due to null pointer dereferences. See [this issue] (google/sanitizers#295) for details.

Regardless of how you build bruce (via the build command, the build_all script, or the pkg script), the address sanitizer is always disabled in release builds, regardless of any command line options that disable or enable the address sanitizer.

The GNU C++ library provides a [debug mode] (https://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode.html) which implements various assertion checks for STL containers. Bruce makes use of this in its debug build. A word of caution is therefore necessary. Suppose you have the following piece of code:

/* Do something interesting to an array of int values.  'begin' points to the
   beginning of the array and 'end' points one position past the last element.
   'begin' and 'end' will be equal in the case of an empty input. */
void DoSomethingToIntArray(int *begin, int *end) {
  assert(begin || (end == begin));
  assert(end >= begin);
  // do something interesting ...
}

void foo(std::vector<int> &v) {
  DoSomethingToIntArray(&v[0], &v[v.size()]);
}

The above code is totally legitimate C++. However, the expression &v[v.size()] will cause an out of range vector index to be reported when running with debug mode enabled. In fact, the expression &v[0] is enough to cause an error to be reported in the case where v is empty. Therefore the code needs to be written a bit differently to avoid spurious errors in debug builds. For instance, one might instead implement foo() like this:

void foo(std::vector<int> &v) {
  if (!v.empty()) {
    DoSomethingToIntArray(&v[0], &v[0] + v.size());
  }
}

Although this is a bit less elegant than the previous implementation, the benefits of tools such as debug mode can be great when tracking down problems. Therefore please avoid code such as the first version of foo() when making changes to Bruce.

Contributing Code

Information on contributing to Bruce is provided here.

Information on getting help with Bruce is provided here.


dev_info.md: Copyright 2014 if(we), Inc.

dev_info.md is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

You should have received a copy of the license along with this work. If not, see http://creativecommons.org/licenses/by-sa/4.0/.