Simplify read templates

Clang build analyzer when run on bmcweb shows that
sdbusplus::message::read functions are one of the longer-to-compile
templates. Given that bmcweb has 400 ish method calls, and a variant
type[1] that's made grown adults cry in fear when presented with
compiler template errors, this should be something worth optimizing.

Luckily, we have c++17 these days, so we can vastly simplify our
unpacking code using variadic fold expressions[2]

As part of the simplification, this commit removes the whole grouping
class, as well as the class that creates std::tuples entirely, and
operates entirely on the variadic pack directly.  This drops the
per-template compile time down to 700ms from 1800ms (note, these exact
times fluctuate a lot run to run and machine to machine).

For the grouping entries, there were only two users that I was able to
find.  Ipmi[3] and ipmb.  Running ipmitool in a fast loop shows no
measurable change in performance.

[1] https://github.com/openbmc/bmcweb/blob/cc67d0a0fed101c930b334a583d9ca9b222ceb77/include/dbus_utility.hpp#L32
[2] https://en.cppreference.com/w/cpp/language/fold
[3] https://github.com/openbmc/phosphor-host-ipmid/blob/920602f5f89b7aeeb7fd31dc77c768f1149cc1f2/ipmid-new.cpp#L493

Results from clang build analyzer after.  Note, this is when isolated to
a single compile unit (redfish.cpp).  When run across the full build and
all the template instantiations, this template saves 3-4 seconds on the
compile time.
```
735 ms: sdbusplus::message::message::read<std::vector<std::pair<std::basic_string<char>, std::variant<std::vector<std::tuple<std::basic_string<cha... (1 times, avg 735 ms)
735 ms: sdbusplus::message::read<std::vector<std::pair<std::basic_string<char>, std::variant<std::vector<std::tuple<std::basic_string<char>, std::... (1 times, avg 735 ms)
733 ms: sdbusplus::message::details::read_single<std::vector<std::pair<std::basic_string<char>, std::variant<std::vector<std::tuple<std::basic_str... (1 times, avg 733 ms)
```

Before
```
1024 ms: sdbusplus::message::message::read<std::vector<std::pair<std::basic_string<char>, std::variant<std::vector<std::tuple<std::basic_string<cha... (1 times, avg 1024 ms)
1024 ms: sdbusplus::message::read<std::vector<std::pair<std::basic_string<char>, std::variant<std::vector<std::tuple<std::basic_string<char>, std::... (1 times, avg 1024 ms)
1022 ms: sdbusplus::message::details::read_grouping<std::tuple<>, std::vector<std::pair<std::basic_string<char>, std::variant<std::vector<std::tupl... (1 times, avg 1022 ms)
1017 ms: sdbusplus::message::details::read_tuple<std::tuple<std::vector<std::pair<std::basic_string<char>, std::variant<std::vector<std::tuple<std:... (1 times, avg 1017 ms)
1015 ms: sdbusplus::message::details::read_single<std::vector<std::pair<std::basic_string<char>, std::variant<std::vector<std::tuple<std::basic_str... (1 times, avg 1015 ms)
```

Change-Id: I6445ec448f94db39c3bc4615e57a6cd3df421ca6
Signed-off-by: Ed Tanous <ed@tanous.net>
1 file changed
tree: 4a087235a22f92e1119b261837e566a3c334b9a6
  1. docs/
  2. example/
  3. include/
  4. src/
  5. subprojects/
  6. test/
  7. tools/
  8. .clang-format
  9. .flake8
  10. .gitignore
  11. .isort.cfg
  12. .markdownlint.yaml
  13. .prettierrc.yaml
  14. .shellcheck
  15. .shellcheckrc
  16. LICENSE
  17. meson.build
  18. meson.options
  19. OWNERS
  20. pyproject.toml
  21. README.md
  22. setup.cfg
README.md

sdbusplus

sdbusplus contains two parts:

  1. A C++ library (libsdbusplus) for interacting with D-Bus, built on top of the sd-bus library from systemd.
  2. A tool (sdbus++) to generate C++ bindings to simplify the development of D-Bus-based applications.

Dependencies

The sdbusplus library requires sd-bus, which is contained in libsystemd.

The sdbus++ application requires Python 3 and the Python libraries mako and inflection.

Building

The sdbusplus library is built using meson.

meson build
cd build
ninja
ninja test
ninja install

Optionally, building the tests and examples can be disabled by passing -Dtests=disabled and -Dexamples=disabled respectively to meson.

The sdbus++ application is installed as a standard Python package using setuptools.

cd tools
./setup.py install

C++ library

The sdbusplus library builds on top of the sd-bus library to create a modern C++ API for D-Bus. The library attempts to be as lightweight as possible, usually compiling to exactly the sd-bus API calls that would have been necessary, while also providing compile-time type-safety and memory leak protection afforded by modern C++ practices.

Consider the following code:

auto b = bus::new_default_system();
auto m = b.new_method_call("org.freedesktop.login1",
                           "/org/freedesktop/login1",
                           "org.freedesktop.login1.Manager",
                           "ListUsers");
auto reply = b.call(m);

std::vector<std::tuple<uint32_t, std::string, message::object_path>> users;
reply.read(users);
    // or
auto users = reply.unpack<
    std::vector<std::tuple<uint32_t, std::string, message::object_path>>>();

In a few, relatively succinct, C++ lines this snippet will create a D-Bus connection to the system bus, and call the systemd login manager to get a list of active users. The message and bus objects are automatically freed when they leave scope and the message format strings are generated at compile time based on the types being read. Compare this to the corresponding server code within logind.

In general, the library attempts to mimic the naming conventions of the sd-bus library: ex. sd_bus_call becomes sdbusplus::bus::call, sd_bus_get_unique_name becomes sdbusplus::bus::get_unique_name, sd_bus_message_get_signature becomes sdbusplus::message::get_signature, etc. This allows a relatively straight-forward translation back to the sd-bus functions for looking up the manpage details.

Binding generation tool

sdbusplus also contains a bindings generator tool: sdbus++. The purpose of a bindings generator is to reduce the boilerplate associated with creating D-Bus server or client applications. When creating a server application, rather than creating sd-bus vtables and writing C-style functions to handle each vtable callback, you can create a small YAML file to define your D-Bus interface and the sdbus++ tool will create a C++ class that implements your D-Bus interface. This class has a set of virtual functions for each method and property, which you can overload to create your own customized behavior for the interface.

There are currently two types of YAML files: interface and error. Interfaces are used to create server and client D-Bus interfaces. Errors are used to define C++ exceptions which can be thrown and will automatically turn into D-Bus error responses.

[[D-Bus client bindings are not yet implemented. See openbmc/openbmc#851.]]

Generating bindings

How to use tools/sdbus++

The path of your file will be the interface name. For example, for an interface org.freedesktop.Example, you would create the files org/freedesktop/Example.interface.yaml and org/freedesktop/Example.errors.yaml] for interfaces and errors respectively. These can then be used to generate the server and error bindings:

sdbus++ interface server-header org.freedesktop.Example > \
    org/freedesktop/Example/server.hpp
sdbus++ interface server-cpp org.freedesktop.Example > \
    org/freedesktop/Example/server.cpp
sdbus++ error exception-header org.freedesktop.Example > \
    org/freedesktop/Example/error.hpp \
sdbus++ error exception-cpp org.freedesktop.Example > \
    org/freedesktop/Example/error.cpp

Markdown-based documentation can also be generated from the interface and exception files:

sdbus++ interface markdown org.freedesktop.Example > \
    org/freedesktop/Example.md
sdbus++ error markdown org.freedesktop.Example >> \
    org/freedesktop/Example.md

See the example/meson.build for more details.

Installing sdbusplus on custom distributions

Installation of sdbusplus bindings on a custom distribution requires a few packages to be installed prior. Although these packages are the same for several distributions the names of these packages do differ. Below are the packages needed for Ubuntu and Fedora.

Installation on Ubuntu

sudo apt install git meson libtool pkg-config g++ libsystemd-dev \
    python3 python3-pip python3-yaml python3-mako python3-inflection

Installation on Fedora

sudo dnf install git meson libtool gcc-c++ pkgconfig systemd-devel \
    python3 python3-pip python3-yaml python3-mako

Install the inflection package using the pip utility (on Fedora)

pip3 install inflection