Skip to content

Dogen v1.0.24, "Imbondeiro no Iona"

Compare
Choose a tag to compare
@mcraveiro mcraveiro released this 03 May 21:19
· 2483 commits to master since this release
v1.0.24
bddc911

Imbondeiro no Iona
A baobab tree in Iona national park, Namib, Angola. (C) 2011 Alfred Weidinger

Introduction

Welcome to the second release of Dogen under quarantine. As with most people, we have now converged to the new normal - or, at least, adjusted best one can to these sorts of world-changing circumstances. Development continued to proceed at a steady clip, if somewhat slower than the previous sprint's, and delivered a fair bit of internal changes. Most significantly, with this release we may have finally broken the back of the fabled generation model refactor - though, to be fair, we'll only know for sure next sprint. We've also used some of our copious free time to make key improvements to infrastructure, fixing a number of long-standing annoyances. So, grab yourself a hot ${beverage_of_choice} and get ready for yet another exciting Dogen sprint review!

User visible changes

This section covers stories that affect end users, with the video providing a quick demonstration of the new features, and the sections below describing them in more detail. As there have only been a small number of user facing changes, we've also used the video to discuss the internal work.

Sprint 1.0.24 Demo
Video 1: Sprint 24 Demo.

Add model name to tracing dumps

Though mainly useful for Dogen developers, the tracing subsystem can be used by end users as well. As before, it can be enabled via the usual flags:

Tracing:
  --tracing-enabled              Generate metrics about executed transforms.
  --tracing-level arg            Level at which to trace.Valid values: detail, 
                                 summary. Defaults to summary.
  --tracing-guids-enabled        Use guids in tracing metrics, Not  recommended
                                 when making comparisons between runs.
  --tracing-format arg           Format to use for tracing metrics. Valid 
                                 values: plain, org-mode, graphviz. Defaults to
                                 org-mode.
  --tracing-backend arg          Backend to use for tracing. Valid values: 
                                 file, relational.
  --tracing-run-id arg           Run ID to use to identify the tracing session.

With this release, we fixed a long standing annoyance with the file backend, which is to name the trace files according to the model the transform is operating on. This is best demonstrated by means of an example. Say we take an arbitrary file from a tracing dump of the injection subsystem. Previously, files were named like so:

000-injection.dia.decoding_transform-c040099b-858a-4a3d-af5b-df74f1c7f52c-input.json
...

This made it quite difficult to find out which model was being processed with this transform, particularly when there are large numbers of similarly named files. With this release we've added the model name to the tracing file name for the transform (e.g., dogen.logical):

000-injection.dia.decoding_transform-dogen.logical-c040099b-858a-4a3d-af5b-df74f1c7f52c-input.json
...

This makes locating the tracing files much easier, and we've already made extensive use of this feature whilst troubleshooting during development.

Primitives use compiler generated default constructors

Up to now our valgrind output had been so noisy that we weren't really paying too much attention to it. However, with this release we finally tidied it up - as we shall see later on in these release notes - and, would you believe it, obvious bugs started to get uncovered almost immediately. This particular one was detected with the help of two sharp-eyed individuals - Indranil and Ian - as well as valgrind. So, it turns out we were generating primitives that used the compiler provided default constructor even when the underlying type was a built-in type. Taking an example for the C++ reference model:

class bool_primitive final {
public:
    bool_primitive() = default;
...
private:
    bool value_;

This of course resulted in uninitialised member variables. With this release the generated code now creates a manual default constructor:

class bool_primitive final {
...
public:
    bool_primitive();
...

Which does the appropriate initialisation (do forgive the static_cast, these will be cleaned up at some point in the future):

bool_primitive::bool_primitive()
    : value_(static_cast<bool>(0)) { }

This fix illustrates the importance of static and dynamic analysis tools, forcing us to refresh the story on the missing LLVM/Clang tools. Sadly there aren't enough hours of the day to tackle all of these but we must get to them sooner rather than later.

Circular references with boost::shared_ptr

Another valgrind catch was the detection of a circular reference when using boost::shared_ptr. We did the classic school-boy error of having a data structure with a child pointing to its parent, and the parent pointing to the child. This is all fine and dandy but we did so using boost::shared_ptr for both pointers (in node.hpp):

    boost::shared_ptr<dogen::logical::helpers::node> parent_;
    ...
    std::list<boost::shared_ptr<dogen::logical::helpers::node> > children_;

In these cases, the literature advises one to use weak_ptr, so that's what we did:

    boost::weak_ptr<dogen::logical::helpers::node> parent_;
    ...
    std::list<boost::shared_ptr<dogen::logical::helpers::node> > children_;

With this the valgrind warning went away. Of course, the alert reader will point out that we probably should be using pointer containers for the children but I'm afraid that's one for another story.

Allow creating models with no decorations

While we're on the subject of brown-paper-bag bugs, another interesting one was fixed this sprint: our "sanity check model", which we use to make sure our packages produce a minimally usable Dogen binary, was causing Dogen to segfault (oh, the irony, the irony). This is, in truth, a veritable comedy of errors, so its worth recapping the series of events that led to its discovery. It all started with our test packaging script, who needs to know the version of the compiler for which the package was built, so that it can look for the binaries in the filesystem. Of course, this is less than ideal, but it is what it is and sadly we have other more pressing matters to look at so it will remain this way for some time.

The code in question is like so:

#
# Compiler
#
compiler="$1"
shift
if [[ "x${compiler}" = "x" ]]; then
    compiler="gcc8";
    echo "* Compiler: ${compiler} (default)"
...
elif [ "${compiler}" = "clang8" ]; then
    echo "* Compiler: ${compiler}"
else
    echo "* Unrecognised compiler: ${compiler}"
    exit
fi

However, we forgot to update the script when we moved to clang-9. Now, normally this would have been picked up by travis as a red build, except we decided to return a non-error-error-code (see above). This meant that packages had not been tested for quite a while. To make matters interesting, we did introduce a bad bug over time; we changed the handling of default decorations. The problem is that all test models use the test profile, and the test profile contains decorations. The only model that did not contain any decorations was - you guessed it - the hello world model that is used in the package sanity tests. So once we fixed the package testing script we then had to fix the code that handles default decorations.

Development Matters

In this section we cover topics that are mainly of interest if you follow Dogen development, such as details on internal stories that consumed significant resources, important events, etc. As usual, for all the gory details of the work carried out this sprint, see the sprint log.

Ephemerides

The 11,111th commit was reached during this release.

11111th commit
Figure 1: 11,111th commit in the Dogen git repository.

Milestones

The first set of completely green builds have been obtained for Dogen - both nightlies and continuous builds. This includes tests, dynamic analysis and code coverage.

Dogen CDash
Figure 2: Builds for Dogen in CDash's dashboard.

The first set of completely green nightly builds have been obtained for the C++ Reference Model. Work still remains on continuous builds for OSX and Windows, with 4 and 2 test failures respectively.

C++ Reference Implementation CDash
Figure 3: Builds for C++ reference model in CDash's dashboard.

Significant Internal Stories

There were several stories connected to the generation model refactor, which we have aggregated under one sundry umbrella to make our life easier. The remaining stories are all connected to infrastructure and the like.

Generation model refactor

We probably should start by admitting that we did not do a particularly brilliant job of sizing tasks this sprint. Instead, we ended up with a couple of gigantic, epic-like stories - XXXL? - rather than a number of small, focused and roughly equally sized stories that we prefer - L and X, in t-shirt sizes. Yet another great opportunity for improvement is clearly presenting itself here. To make things more understandable for this post-mortem, we decided to paper over the cracks and provide a slightly more granular view - rather than the coarse-grained way in which it was originally recorded on the sprint backlog.

The core of the work was divided as follows:

  • Adding physical entities to the logical model: this story was continued from the previous sprint. The entities themselves had already been added to the logical model, so the work consisted mainly on creating the required transforms to ensure they had the right data by the time we hit the M2T (Model-to-Text) transforms.
  • Generating physical model entities from m2t classes: we finally go to the point where the top-level M2T transforms are generating the physical archetypes, which means the complete generation of the physical meta-model is not far now. The remaining physical meta-model entities (backend, facet, parts) are not quite as fiddly, hopefully.
  • Bootstrapping of physical entities: we continued the work on generation of physical entities via the logical model elements that represent them. This is very fiddly work because we are trying to bootstrap the existing templates - that is, generate code that resembles the existing generators - and therefore requires a great deal of concentration; its very easy to lose track of where we are and break everything, and we done so a few times this sprint, costing us a fair bit of time in tracking back the errors. There is hope that this work is almost complete though.
  • Add T2T (Text-to-Text) transforms: As usual, a great deal of effort was spent on making sure that the code is consistent with the current understanding of the conceptual model. One aspect that had been rather illusive is the handling of templates; these are in effect not M2T transforms, because we've already discarded the model representation. With this sprint we arrived at T2T (Text-to-Text) transforms, which are a surprisingly good fit for both types of logic-less templates we have in Dogen (stitch and wale) but also have the potential to model cartridges such as ODB, XSD tool and many other types of code generators. More work on this remains next sprint, but the direction of travel is very promising.
  • Rename the m2t model to text: following on from the previous entry, given that we now had two different types of transforms in this model (e.g., M2T and T2T) we could not longer call it the m2t model, and thus decided to rename it to just text. As it turns out, this is a much better fit for the conceptual model and prepares ourselves for the coming work on cartridges, which now have a very suitable location in which to be placed.

As you can probably gather from what is written on these topics in the sprint backlog, these few bullet points do little justice to the immense amount of mental effort that was spent on them. Sadly, we do not have the time - and I dare say, the inclination - to explain in the required detail how all of these issues contribute to the overall picture we are trying to form. Hopefully when the generation refactor is completed and all the fuzziness is taken away, a blog post can be produced summarising all of the moving parts in a concise narrative.

Code Coverage

Code coverage is important to us, for very much the same reason it is important to any software project: you want to make sure your unit tests are exercising as much of the code as possible. However, in addition to this, we also need to make sure the generated code is being adequately tested by the generated tests, both for Dogen as well as the Reference Implementation models. Historically, C++ has had good code coverage tools and services but they haven't been the most... user friendly, shall we say, pieces of software ever made. So, since Dogen's early days, I've been very eager to experiment the new wave of code coverage cloud services such as Coverals and Codecov and tools such as kcov to track code coverage. The experiment was long running but has now run its course, I am sorry to report, as we just faced too many problems for my liking. Now, in the interest of fairness, its not entirely clear if some of the problems we experienced are related to kcov rather than the cloud services; but other issues such as troubles with API keys and so forth were definitely related to the services themselves. Given we don't have the time to troubleshoot every problem, and we must be able to rely on the code coverage numbers to make important decisions, I had no option but to move back to good old CDash - a tool that had proven reliable in the past for this.

CDash continuous coverage
Figure 4: Code coverage for Dogen, continuous builds, after moving back to CDash.

I must confess that it was with a heavy heart that I even begun to contemplate moving away from kcov, as I quite like the tool; compared to the pain of setting up gcov or even llvm-cov, I think kcov is a work of art and a master of delightful user experience. Also, the maintainer is very friendly and responsive, as previous communications attest. Alas, as far as I could see, there was no easy way to connect the output of kcov with CDash, so back to the drawing board we went. I shan't bother you with graphic descriptions of the trials and tribulations of setting up gcov and llvm-cov - I presume any Linux C/C++ developer is far too battle-scarred to find any such tales interesting - but it suffices to say that, after a great deal of pain and many, many failed builds later we eventually managed to get gcov to produce the desired information.

CDash nightly coverage
Figure 5: Code coverage for Dogen, nightly builds, after moving back to CDash.

Figure 4 illustrates the progress of code coverage on Dogen's continuous builds over time, whereas Figure 5 looks at coverage in nightlies. As we explained previously, we have different uses for coverage depending on which build we use. Nightly builds run all generated tests, and as such they produce code coverage that takes into account the generated tests. This is useful, but its important not to confuse it with manually generated tests, which provide us with "real" coverage; that is, coverage that emerged as a result of "real" - i.e., domain - use of the types. We need both of these measurements in order to make sense of what areas are lacking. With CDash we now seem to have a reliable source of information for both of these measurements. As you can see from these charts, the coverage is not oscillating through time as it did previously when we used the coverage services (possibly due to kcov problems, but I personally doubt it). As an added bonus, we no longer have red builds due to "failed checks" in GitHub due to stochastic decreases in coverage, as we had far too many times in the past.

Nightly build duration
Figure 6: Dogen nightly build duration over time.

A very important aspect when adding code coverage to already busy nightlies was the impact on build duration. We first started by trying to use clang and llvm-cov but we found that the nightlies started to take far too long to complete. This is possibly something to do with our settings - perhaps valgrind was not happy with the new coverage profiling parameters? - but given we didn't have a lot of time to experiment, we decided instead to move over to gcov and gcc debug builds. Figures 6 and 7 show the impact to the build time to both Dogen and the C++ Reference Model. These were deemed acceptable.

Nightly build duration
Figure 7: C++ reference model build duration over time.

Dynamic Analysis

As with code coverage, we've been making use of CDash to keep track of data produced by valgrind. However, we let the reports bit-rot somewhat, with lots of false positives clouding the view (or at least we hope they are false positives). With this release we took the time to update our suppression files, removing the majority of false positives. We then immediately located a couple of issues in the code, as explained above.

Valgrind errors over time
Figure 8: Valgrind errors over time in CDash.

I don't think we need any additional incentives to keep the board nice and clean as far as dynamic analysis is concerned. Figure 8 shows the current state of zero warnings, which is a joy to behold.

MDE Paper of the Week (PofW)

This sprint we started another experiment with YouTube and video recording: a sort of "self-journal club". For those not from a research background, many research labs organise a weekly (insert your frequency here, I guess) meeting where the participants discuss a scientific paper. The idea is that everyone reads the paper, but the chosen presenter will go through it in depth, and the audience can ask questions and so forth. Normally, this is a great forum to discuss papers that you are reading as part of your research and get some help to understand more difficult parts. Its also a place where you can see what everybody else is up to across your lab. At any rate, with the move back to gainful employment I no longer get the chance to participate in my lab's journal club. In addition, I found that many of the papers I had read over the years had lots of useful information that makes a lot more sense now than it did when i first read them. Thus, a re-read was required.

So I combined these two ideas and come up with the somewhat sad idea of a "self-journal club", the "MDE Paper of the Week (PofW)", where I read and discuss the papers of interest . These are available in YouTube, should you, for whatever unfathomable reason, find them interesting. Four papers have been read thus far:

The last paper was more experimental than usual, what with it being in Spanish, but it worked better than we expected, so from now on we shall consider papers on other languages we can parse.

As with coding videos, the most significant advantage of this approach is motivational; I now find that I must re-read a paper a week even when I don't feel like it, purely because of the fact that I publish them online. Lets see how long the YouTube effect will last though...

Resourcing

Weighing in just short of 280 commits and with over 83 hours of work, this sprint was, by traditional measurements, a success. To be fair, we did return to the more regular duration of around four weeks rather than the three of the previous sprint, resulting in a utilisation rate of precisely 50% -a decrease of 16% from the previous sprint. On the other hand, this slower velocity seems far more sustainable than the break neck pace we attempted previously; our aim will continue to be around 50%, which effectively means part-time work.

Story Pie Chart
Figure 9: Cost of stories for sprint 24.

Where the waters become a bit murkier is when we break down the stories by "type". We spent around 56% of the overall ask on stories directly connected to the sprint goal, which may appear to be a bit low. The bulk of the remaining 44% were spent largely on process (24.5%), and infrastructure (11.5%) with a notable mention for the almost 6% spent moving code coverage into CDash. Another 6.6% was spent on reading MDE papers, which is of course time well spent from a strategic perspective but it does eat into the coding time. Of the 24.5% spent on process, a notable mention is the 11.3% spent editing the release notes. These are becoming a bit too expensive for our liking so next sprint we need to speed these along.

Roadmap

The roadmap remains more or less unchanged, other than the fact that it was projected forward by one sprint; much like Pinky and the Brain, our proximal goal remains the same: to finish the generation refactor. Its not entirely clear whether we're Pinky or the Brain, but we do feel that the problem is understood a bit better, so there is some faint hope that next sprint could bring it to a close.

Project Plan

Resource Allocation Graph

Binaries

You can download binaries from either Bintray or GitHub, as per Table 1. All binaries are 64-bit. For all other architectures and/or operative systems, you will need to build Dogen from source. Source downloads are available in zip or tar.gz format.

Operative System Format BinTray GitHub
Linux Debian/Ubuntu Deb dogen_1.0.24_amd64-applications.deb dogen_1.0.24_amd64-applications.deb
OSX DMG DOGEN-1.0.24-Darwin-x86_64.dmg DOGEN-1.0.24-Darwin-x86_64.dmg
Windows MSI DOGEN-1.0.24-Windows-AMD64.msi DOGEN-1.0.24-Windows-AMD64.msi

Table 1: Binary packages for Dogen.

Note: The OSX and Linux binaries are not stripped at present and so are larger than they should be. We have an outstanding story to address this issue, but sadly CMake does not make this a trivial undertaking.

Next Sprint

The goal for the next sprint is to complete most of the work on the generation refactor. It is unlikely we shall finish it in its entirety as they are quite a few fiddly bits, but we shall aim to get most of it out of the way.

That's all for this release. Happy Modeling!