Mir

Merge lp:~vanvugt/mir/fatal-error into lp:mir

Proposed by Daniel van Vugt
Status: Rejected
Rejected by: Daniel van Vugt
Proposed branch: lp:~vanvugt/mir/fatal-error
Merge into: lp:mir
Diff against target: 587 lines (+256/-79)
13 files modified
include/shared/mir/fatal.h (+52/-0)
src/platform/graphics/mesa/CMakeLists.txt (+2/-2)
src/platform/graphics/mesa/display_buffer.cpp (+9/-8)
src/platform/graphics/mesa/real_kms_output.cpp (+18/-29)
src/shared/CMakeLists.txt (+1/-0)
src/shared/fatal/CMakeLists.txt (+3/-0)
src/shared/fatal/fatal.cpp (+43/-0)
tests/acceptance-tests/CMakeLists.txt (+1/-0)
tests/acceptance-tests/test_server_shutdown.cpp (+34/-0)
tests/unit-tests/CMakeLists.txt (+2/-0)
tests/unit-tests/graphics/mesa/test_display.cpp (+27/-27)
tests/unit-tests/graphics/mesa/test_real_kms_output.cpp (+15/-13)
tests/unit-tests/test_fatal.cpp (+49/-0)
To merge this branch: bzr merge lp:~vanvugt/mir/fatal-error
Reviewer Review Type Date Requested Status
Cemil Azizoglu (community) Disapprove
Robert Carr (community) Needs Fixing
Alexandros Frantzis (community) Needs Fixing
PS Jenkins bot (community) continuous-integration Approve
Alan Griffiths Needs Fixing
Chris Halse Rogers Approve
Gerry Boland (community) Needs Information
Michał Sawicz Pending
Review via email: mp+219471@code.launchpad.net

Commit message

Add support for "fatal errors". These are situations we don't expect to ever
happen in the absence of bugs and don't have logic to gracefully recover
from. To ensure a useful core file results from fatal errors, we must
implement them by raising a fatal signal (SIGABRT). Exceptions are frequently
useless in this scenario because stack unwinding clobbers all forensic
information including the stack trace that you need to debug and diagnose the
cause. (LP: #1316867)

Description of the change

The need for this feature has been a long time coming. In 2013 when we asked
for public testing of XMir we were bombarded by crash reports containing
unusable core files, the result of exceptions. And so the root causes of
those crashes mostly (and still do) remain unsolved.

Caveats:
  1. I have not converted everything that could be converted to mir::abort().
     Just enough to resolve LP: #1316867 covering the major sources of
     crashes reported by users.
  2. GoogleTest is reporting warnings in the presence of threads when calling
     EXPECT_DEATH/EXIT. These appear to be either spurious or harmless as
     the warnings still occur in simple single-threaded tests like:
        mir_unit_tests --gtest_filter="FatalTest.*"

To post a comment you must log in.
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

This negates the benefits of previous work to ensure proper shutdown in case of server problems, since many (most?) of our exceptions, especially in the platforms, are not recoverable and hence could be changed to mir::abort(). From that perspective this MP causes a regression for https://bugs.launchpad.net/mir/+bug/1189770 .

Like last time we had this discussion, it's a matter of what we deem to be more important: user experience or crash information. Ideally we want both, and there are some avenues worth exploring that may allow us to achieve that middle ground. For example we could get the core with 'gcore' and pipe that to whatever command/file is in /proc/sys/kernel/core_pattern, mimicking a crash, and then graciously shutdown mir.

On the other hand, if the init system is able to bring USC/unity8 back up properly after an abort (which is probably the case) then this is a problem mostly for developers (e.g. crashing a test server and not being able to change VTs) not for normal users. So, perhaps the best way forward is to be able to configure what mir::abort() does. For example, it could std::abort by default, or optionally throw + gcore to a file.

"Needs discussion"

review: Needs Information
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

"last time we had this discussion" I feel we made the wrong decision by allowing the offending change to land:
    https://code.launchpad.net/~afrantzis/mir/fix-1189770/+merge/217652

The reason being we have a collection of hidden critical bugs that will never be resolvable without this change. They're hiding in the duplicates list on the right-hand-side of:
    https://bugs.launchpad.net/mir/+bug/1237332

I believe the user experience of eventually not crashing at all is more important than recovering the screen but never fixing the crashes. Although we could achieve both if we utilize the existing logic that catches crashes and does lightweight recovery: server/run_mir.cpp
I think the screen/VT recovery should be there.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Alexandros, I think we should resolve your concern as bug 1320114. I now realize that's a problem which pre-dates this proposal as the fix for bug 1189770 should have included it.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

It's worth noting that you can safely do a little cleanup after a fatal signal because you're only making the stack deeper in doing so. No unwinding (loss of stack frames) occurs, so it's fine.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> This negates the benefits of previous work to ensure proper shutdown in case
> of server problems, since many (most?) of our exceptions, especially in the
> platforms, are not recoverable and hence could be changed to mir::abort().
> From that perspective this MP causes a regression for
> https://bugs.launchpad.net/mir/+bug/1189770 .

I presume that by rasing SIGABRT we'll run the cleanup handler installed by run_mir(). Why doesn't that adequately address lp:1189770?

> Like last time we had this discussion, it's a matter of what we deem to be
> more important: user experience or crash information. Ideally we want both,
> and there are some avenues worth exploring that may allow us to achieve that
> middle ground. For example we could get the core with 'gcore' and pipe that to
> whatever command/file is in /proc/sys/kernel/core_pattern, mimicking a crash,
> and then graciously shutdown mir.

User experience is always key. And anything that damages that should be optional (like the above or a debug mode that cores immediately without any cleanup).

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Alan,
Due to a small oversight, the fix for bug 1189770 is missing a fix for the signal/crash case. So I logged that as a new bug 1320114 and Alexandros is making progress toward resolving it in:
   lp:~afrantzis/mir/emergency-cleanup

So fatal signals such as SIGABRT raised by mir::abort() do still go through our emergency cleanup code. We just accidentally forgot to include VT/DRM recovery in that code path. But alf is working toward fixing that(?).

Thus when all the fixes are in place Mir will both recover the screen *and* dump clean core files. However they're not strictly dependent activities, and I'd rather keep proposals separate and smaller where possible.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

I'm as keen as anyone to get clean cores in our development. But it is not so clear this is the best option for downstream developers - who may find it "impolite" for a library they are using to kill their whole process.

Adding Gerry and Michal to reviewers list for a "downstream" viewpoint.

review: Abstain
Revision history for this message
Michał Sawicz (saviq) wrote :

If things are recoverable at all, sure it would be best to not abrt. But then there's probably only so many instances when that's the case. In theory we could try and restart Mir, but since the shell is the client, that will probably be rather difficult, so I can't see many instances where Mir can die while the shell can live on...

Revision history for this message
Gerry Boland (gerboland) wrote :

I'm all for better stack traces. But I'd like to know if Mir aborts, what happens to threads that the shell owns? Does shell have any way to clean up its own state?

review: Needs Information
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> I'm all for better stack traces. But I'd like to know if Mir aborts, what
> happens to threads that the shell owns? Does shell have any way to clean up
> its own state?

Add a handler to the_emergency_cleanup()...

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Chris Halse Rogers (raof) wrote :

> > I'm all for better stack traces. But I'd like to know if Mir aborts, what
> > happens to threads that the shell owns? Does shell have any way to clean up
> > its own state?
>
> Add a handler to the_emergency_cleanup()...

Although it's worth noting that this will be run in a signal context, so you'll be severely restricted as to what you can reliably do! (The emergency cleanup code currently looks like it allocates memory in the signal context which runs a reasonable chance of deadlocking).

Globally this seems like a reasonable idea, but is there really no way to get useful debugging information out of C++ in the presence of exceptions?

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Apparently not. C++ exceptions are mostly designed to be caught and handled (resolved). In some cases you do retain the original stack of an exception, but that only happens in bare threads where no one is catching. So that's a rather unrealistic scenario (unless you disallow all non-specific catches?).

Once you've caught (or even rethrown?) an exception, the original error details are lost. Because your thread only has one stack.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Another way to approach the problem would be to a:
   class UncatchableException
   {
   public:
      UncatchableException() { abort(); }
   }

That means you can still "throw" instead of using the new syntax "mir::abort". Although I'm not sure I like that.

Revision history for this message
Chris Halse Rogers (raof) wrote :

Thinking about this some more, I suspect that this is not actually what we really want.

Core files aren't actually very useful for diagnosing things like bug# 1237332, or the many ways unity-system-compositor can fail to start with some form of DRM error. We really want not only the current state, but also the historical state - what DRM devices have been considered, what were their statuses, etc.

For SEGV-type crashes we are forced to go poking around in a core file because we haven't considered the problem ahead of time, but as I understand the code we currently get those, and even there we'd have a better time with historical state as well.

So, while dumping a more useful core on fatal exception would be more useful than the current status, I think what we *actually* want to do is dump about 300x more information to logs by default - of unsuccessful actions, but equally importantly of successful actions. We need to understand what happened leading up to the error condition, and that's often not captured in a core, however pristine.

Dump each udev device probed, each successful modeset, successful EGL initialisation, etc. Things which happen infrequently or only at startup are good targets for logging.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Absolutely not. I've been doing core analysis from the wild for many years. And the most useless bug reports are always those from projects that try to do their own error handling/logging. Just don't do it. A developer with a debugger and correct symbols can always discover more from a clean core file. As can launchpad's automatic retracer.

With correct symbols (which is generally the hardest part, but feasible), you can analyse data structures by name and dive into all your variables in a meaningful way, post-mortem. It's just not feasible to know in advance what your crashes will be and which variables are the most relevant to debugging them. That's what core files solve.

Revision history for this message
Chris Halse Rogers (raof) wrote :

Hm.

So, I think we're actually talking about two different things here: crashes and “crashes”, which are really uncaught exceptions.

You obviously can't know what your crashes will be in advance. On the other hand, you know *exactly* what your “crashes” will be, because you deliberately put them in there.

A core file is good (or at least usually sufficient) for crashes, because the cause of the crash is the thing the program is trying to do _right now_, so it's highly likely that the state of interest is the current state.

For uncaught exceptions the current state is frequently not the interesting state - when unity-system-compositor fails to start with “could not find drm device”, the interesting data is in previous iterations of the “probe this udev device” loop. It's idiomatic to try and do a bunch of things, and if they *all* fail, throw.

Clean core files are useful, and mir::abort() looks to be useful, so I'm not disapproving.

However, most of the places you're calling mir::abort() in this branch are potentially temporary failures, and it _is_ inappropriate to abort there - failure to schedule pageflips, failure to set one crtc (out of potentially more than one active crtc), and failure in eglSwapBuffers are all potentially transitory or recoverable problems which this branch turns into a SIGABRT.

If we're raising these exceptions on a non-main thread we should stop doing that (or catch them and propagate them _to_ the main thread), though.

review: Needs Fixing
Revision history for this message
Chris Halse Rogers (raof) wrote :

Some of this is coming from Xorg experience. X writes ~500 lines of output during normal startup, and this output is extremely useful in debugging the most common failures; just a core file would be much _less_ useful for most of those.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

The "potentially temporary failures" thing is a real issue I know. Although it's not feasible to go rewriting all that logic to support recovery in the short term. It's a separate long term activity and not even clear how/if they are really recoverable at all.

Making some of these fatal errors into recoverable errors is a nice enhancement for the future but should not be considered a prerequisite for this. Or else we'll never get a fix for bug 1316867 in time for RTM/utopic.

I like the idea of more logging. Although that too is a separate activity. It can be done orthogonally to error handling. Your logging does not have to be (and shouldn't be) done just when errors occur.

Revision history for this message
Chris Halse Rogers (raof) wrote :

Under the acknowledgement that we'll need to rip this out and implement a proper solution when the time is available, I find this reasonable.

review: Approve
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Another solution, trying to combine the best of both worlds, was discussed during standup today: let the client of the library decide whether runtime errors like the ones discussed in this MP should abort, throw or do something completely different.

By default the library should be polite with client code and throw exceptions. Certain clients, which can safely handle Mir abort()s and from which we want comprehensive debugging information, can explicitly configure mir to abort(). I imagine that both unity8 and USC will want to do that.

"Needs discussion"

review: Needs Information
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> Another solution, trying to combine the best of both worlds, was discussed
> during standup today: let the client of the library decide whether runtime
> errors like the ones discussed in this MP should abort, throw or do something
> completely different.
>
> By default the library should be polite with client code and throw exceptions.
> Certain clients, which can safely handle Mir abort()s and from which we want
> comprehensive debugging information, can explicitly configure mir to abort().
> I imagine that both unity8 and USC will want to do that.
>
> "Needs discussion"

We as a library have no way of knowing what else is running in the same process nor whether it is acceptable to terminate the process at any given time. Hence, it is extremely impolite for to abort process unless the client code has caused it in some way. (I.e. either the client has requested the abort or has broken a precondition.)

I'm not saying it is a primary use case, but it is conceivable that a process with another primary purpose spins up a Mir compositor briefly and expects to continue after it closes down. With errors reported via exceptions that is possible, with errors reported by "abort()" it isn't. (FWIW I'd even like it to be fully supported to run multiple Mir servers in a process without an error in one bringing the whole lot down.)

So long as the client code uses the Mir API correctly I don't think we should ever abort. (That doesn't preclude the client installing handlers for "very bad things" into Mir that abort *in client code* with our stack intact.)

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Firstly keep in mind that the code does crash already, with unhandled (and undebuggable) exceptions. The change proposed here just changes them into being "debuggable".

Secondly, wanting to handle and recover from errors is a separate activity. The offending exceptions have been in the Mir code for years now and as yet no one has made them recoverable. So doing that is a large long term task and shouldn't block this one.

<IMHO>It's much more important that we be able to debug these problems and actually stop them from happening than the current situation where one can indeed clean up some things on an exception, but not report enough information to us to ever help to fix the underlying cause.</IMHO>

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> Firstly keep in mind that the code does crash already, with unhandled (and
> undebuggable) exceptions. The change proposed here just changes them into
> being "debuggable".

There's only a crash if the exception propagating from run_mir() isn't caught.

That is a (questionable) decision made by client code and not something we impose in the Mir library. (The only example code we provide that fails to handle the exception is minimal_server.cpp.)

Revision history for this message
Chris Halse Rogers (raof) wrote :

No, these exceptions are raised in the compositing thread, right? Client code has no opportunity to catch them.

-----Original Message-----
From: "Alan Griffiths" <email address hidden>
Sent: ‎9/‎07/‎2014 19:23
To: "Daniel van Vugt" <email address hidden>
Subject: Re: [Merge] lp:~vanvugt/mir/fatal-error into lp:mir/devel

> Firstly keep in mind that the code does crash already, with unhandled (and
> undebuggable) exceptions. The change proposed here just changes them into
> being "debuggable".

There's only a crash if the exception propagating from run_mir() isn't caught.

That is a (questionable) decision made by client code and not something we impose in the Mir library. (The only example code we provide that fails to handle the exception is minimal_server.cpp.)
--
https://code.launchpad.net/~vanvugt/mir/fatal-error/+merge/219471
You are reviewing the proposed merge of lp:~vanvugt/mir/fatal-error into lp:mir/devel.

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

> No, these exceptions are raised in the compositing thread, right? Client code
> has no opportunity to catch them.

Any exception that reaches the top of the compositing threads (i.e. it is not handled by that thread), is caught, the server is torn down, and the exception is rethrown when run_mir() finishes.

See the code in multi_threaded_compositor.cpp and the mir::terminate_with_current_exception() function for more.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> > No, these exceptions are raised in the compositing thread, right? Client
> code
> > has no opportunity to catch them.
>
> Any exception that reaches the top of the compositing threads (i.e. it is not
> handled by that thread), is caught, the server is torn down, and the exception
> is rethrown when run_mir() finishes.
>
> See the code in multi_threaded_compositor.cpp and the
> mir::terminate_with_current_exception() function for more.

Right.

What this proposal does is take code that leads to an orderly shutdown with an exception the client can (and is expected to) handle into an unconditional crash.

As I've stated before in this thread I don't think a library should behave this way.

review: Needs Fixing
Revision history for this message
Kevin DuBois (kdub) wrote :

I don't think its very well behaved for the library to abort on the user, but on the other hand do appreciate the extra debug info. Seeing as we know when that drm (or hwc for that matter) errors are not handled, maybe we could pack the exception with all the info we could glean (say via glibc's backtrace()) and hope that the client code prints it. (or we could just spew to to stderr). Obviously, this is not as nice as a coredump, but it seems to me to be an okay compromise between the concerns.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

The existing behaviour ("orderly shutdown") is in no way more useful that what is proposed here. We have now spent _years_ unable to fix our crashes because of the existing behaviour. That is unacceptable.

The assertion that "the client can handle" these exceptions is false. Clients can't handle them because even we don't know how to handle them. If we did, then we would be doing so. For clients to blindly catch those exceptions is not useful, because the system is left with a blank screen and doesn't recover.

Core files are significantly more useful to fixing bugs than exception details. Particularly since we have an automated system in place that analyses cores for us. Please let us use it so we can fix these crashes.

Simply recovering/retrying things that fail before you ever understand how they can fail is too dangerous and risks getting stuck in retry loops and/or crashing again after multiple failures in less useful locations.

Unfortunately because most developers ignore bugs the severity of the situation is not fully understood by most developers. But last time we asked for public testing of Mir we were bombarded by crash reports which to this day remain unsolved. I want to avoid that happening again.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

I don't think its very well behaved for the library to abort on the user.

I think it is important that the user (in this case U8 and/or USC) can get a core.

Alexandros has suggested an approach that meets both these concerns. (https://code.launchpad.net/~vanvugt/mir/fatal-error/+merge/219471/comments/544375)

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Absolutely crashing is not good behaviour. But if that's the fastest path to resolving the root cause then it's the best option.

I don't think making it configurable is a good idea. Because that gives shell/server developers the option of regressing on LP: #1316867 when there is no existing DRM logic in place for a clean retry/recovery to occur. I mean if a shell did catch such an error as an exception there is no code path back into the Mir DRM code to recover at all. It's not useful to have exceptions when there's no catch/recovery plan. And I think that would be irresponsible of us to allow shell developers to experience Mir bugs which don't generate meaningful crash reports we can work with and resolve.

Revision history for this message
Cemil Azizoglu (cemil-azizoglu) wrote :

I wonder if there is a way we can recover the VT/display using a separate executable tied to a watchdog, or a hot-key combination of sorts which can be triggered automatically or through a hot-key combination, to get the display back into a sane state after a fatal crash.

And perhaps a good history logging mechanism, as suggested by Chris, which we can "play back" post-mortem in a developer binary where we have aborts instead of exceptions.

I am also of the opinion that the user-experience must not be compromised just because we have bugs in the system we want to address as quickly as possible. And there will continue to be bugs even after we collect many core files and address the ones that have caused them. But we should not forget that providing a good user-experience is key - piss your users off enough many times and there won't be very many left to exercise bugs in the code and help us collect core files for us to fix.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Additional state reporting and logging is a part of the solution. But not all of it.

There are two sets of users to consider here:

1. the users of Mir - shell/server developers exemplified by U8 and U-S-C
2. the users of U8

The former are the ones that should decide whether an abort is acceptable behavior for their program. Not the developers of Mir.

It may even be argued that the latter should be offered a choice too. Then beta testers can run with one option and final users with another.

For example, an option like this:

    --on-fatal-error arg (=throw) Action to take on a fatal error [throw|abort|break]

In U8 and/or U-S-C development we can change the default to "abort" in order to ensure better diagnostics. But reconfigure in a production environment to "throw".

With "throw" there should be a bespoke a FatalError exception that is understood by mir::report_exception() and reads:

    to get additional debug information in a core:
       $ ulimit -c unlimited
    and rerun with "--on-fatal-error abort"

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

I think the current "orderly shutdown" behavior is a good option to have and if we break it (even with the intent of restoring it "eventually") it will be hard to recover.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

We already do recover the VT/display on fatal signals. That was Fix Released in Mir 0.4.0, although we failed to document it as fixed so I just updated bug 1320114 now. I was on vacation when that change landed.

mir::abort() follows an orderly shutdown procedure. It does recover the screen and provides a _more_ friendly user experience in that it allows us to finally debug the crashes and fix them once and for all. Thus, more friendly in that the number of crashes will be reduced.

I don't see what benefit exceptions buy you in this case. Our fatal signal handler is just as good at recovering the screen and providing a nice user experience. The only difference is that exceptions would prevent us from getting good crash reports and being able to fix bugs.

Revision history for this message
Chris Halse Rogers (raof) wrote :

Exceptions allow users of our library to also partake of the
clean-shutdown goodness.

They may want to do things other than SIGABRT when their Mir server
crashes.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

I thought users could utilize the emergency recovery code?... Or maybe it needs more API still?...
   https://code.launchpad.net/~afrantzis/mir/platform-emergency-cleanup/+merge/222662
Inserting new handlers with that _should_ allow you to clean up on both exceptions and fatal signals.

If you relied on exceptions alone for clean up then you're missing out, because that will never catch crashes like SIGSEGV.

Revision history for this message
Chris Halse Rogers (raof) wrote :

That assumes that terminating is the correct behavior, and that everything they want to do is safe in a signal context, which it probably isn't.

-----Original Message-----
From: "Daniel van Vugt" <email address hidden>
Sent: ‎14/‎07/‎2014 18:32
To: "Daniel van Vugt" <email address hidden>
Subject: Re: [Merge] lp:~vanvugt/mir/fatal-error into lp:mir/devel

I thought users could utilize the emergency recovery code?... Or maybe it needs more API still?...
   https://code.launchpad.net/~afrantzis/mir/platform-emergency-cleanup/+merge/222662
Inserting new handlers with that _should_ allow you to clean up on both exceptions and fatal signals.

If you relied on exceptions alone for clean up then you're missing out, because that will never catch crashes like SIGSEGV.
--
https://code.launchpad.net/~vanvugt/mir/fatal-error/+merge/219471
You are reviewing the proposed merge of lp:~vanvugt/mir/fatal-error into lp:mir/devel.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Blocked on Alan's "Needs Fixing". Not sure what needs fixing though. Looks like a general disapproval of the whole concept without any alternate solutions for bug 1316867 being proposed yet.

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

> Blocked on Alan's "Needs Fixing". Not sure what needs fixing though. Looks
> like a general disapproval of the whole concept without any alternate
> solutions for bug 1316867 being proposed yet.

What "Needs Fixing" is:

1. Aborting as the only action for fatal errors.
2. Aborting as the default action for fatal errors.

Both of would be solved by introducing a mechanism that allows the _users_ of libmirserver to decide what to do in case of fatal errors. The default implementation would be to throw an exception, which, as discussed above, is the most safe and polite reaction.

I expect unity8 and USC to override the fatal error handler and abort() on error to provide sufficient debugging information in the wild, but that's ultimately their decision to make.

review: Needs Fixing
Revision history for this message
Robert Carr (robertcarr) wrote :

It seems like exceptions are more flexible to the library user, who knows what they want to do. Installing emergency cleanup handlers isn't a sufficient substitute (signal context). I don't like abort by default because of this.

review: Approve
Revision history for this message
Robert Carr (robertcarr) wrote :

I clicked the wrong review status

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

I'm confused. I've already explained why exceptions are unacceptable here.

Giving people the option of regressing on the bug without providing them with any DRM recovery logic to use as an alternative solution in exception handlers is pointless. It fails to address bug 1316867, defeating the point of this whole exercise.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

I'm happy to say "Won't Fix" on the bug. But not happy to wear the blame for it.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

I don't understand your confusion. Ever since the first review comment it has been explained why exceptions are not only acceptable but also the desired default for a library.

The fixes outlined by Alexandros do indeed need some additional work in unity-mir and/or unity8 to resolve bug 1316867 but those changes are under our control. It would also means that users of Mir will not be swearing at us for writing a library that summarily terminates their process.

Revision history for this message
Cemil Azizoglu (cemil-azizoglu) wrote :

We're kind of going round in circles. So I thought I should clarify my position and state where the red-line lies as far as I'm concerned :

- It's important for us not to abort on the user unconditionally just to make our lives easier.

I'd be happy with any configurable way that this is handled as long as abort is not the default. In its current form, I'll go ahead and disapprove.

review: Disapprove

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'include/shared/mir/fatal.h'
--- include/shared/mir/fatal.h 1970-01-01 00:00:00 +0000
+++ include/shared/mir/fatal.h 2014-07-14 06:58:31 +0000
@@ -0,0 +1,52 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * ---
17 * Fatal error handling - Fatal errors are situations we don't expect to ever
18 * happen and don't have logic to gracefully recover from. The most useful
19 * thing you can do in that situation is abort to get a clean core file and
20 * stack trace to maximize the chances of it being readable.
21 *
22 * Author: Daniel van Vugt <daniel.van.vugt@canonical.com>
23 */
24
25#ifndef MIR_FATAL_H_
26#define MIR_FATAL_H_
27
28namespace mir
29{
30/**
31 * Kill the program and dump core as cleanly as possible. Note the reason
32 * parameter is a simple char* so its value is clearly visible in stack trace
33 * output.
34 * mir::abort() is strictly for "this should never happen" situations that
35 * you cannot recover from.
36 * \param [in] reason A printf-style format string.
37 */
38void abort(char const* reason, ...);
39
40/**
41 * Mir-specific assert() function that is NEVER optimized out, always tested
42 * even in release builds.
43 */
44#define mir_assert(expr) \
45{ \
46 if (!(expr)) ::mir::_fail_assertion(__FILE__, __LINE__, #expr); \
47}
48void _fail_assertion(char const* file, int line, char const* expr);
49
50} // namespace mir
51
52#endif // MIR_FATAL_H_
053
=== modified file 'src/platform/graphics/mesa/CMakeLists.txt'
--- src/platform/graphics/mesa/CMakeLists.txt 2014-04-22 23:15:16 +0000
+++ src/platform/graphics/mesa/CMakeLists.txt 2014-07-14 06:58:31 +0000
@@ -44,9 +44,9 @@
44 LIBRARY_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH}/mesa44 LIBRARY_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH}/mesa
45)45)
4646
47target_link_libraries(47target_link_libraries(mirplatformgraphicsmesa
48 mirplatformgraphicsmesa
49 mirplatform48 mirplatform
49 mirfatal
5050
51 ${Boost_PROGRAM_OPTIONS_LIBRARY}51 ${Boost_PROGRAM_OPTIONS_LIBRARY}
52 ${DRM_LDFLAGS} ${DRM_LIBRARIES}52 ${DRM_LDFLAGS} ${DRM_LIBRARIES}
5353
=== modified file 'src/platform/graphics/mesa/display_buffer.cpp'
--- src/platform/graphics/mesa/display_buffer.cpp 2014-07-09 08:22:57 +0000
+++ src/platform/graphics/mesa/display_buffer.cpp 2014-07-14 06:58:31 +0000
@@ -22,6 +22,7 @@
22#include "mir/graphics/display_report.h"22#include "mir/graphics/display_report.h"
23#include "bypass.h"23#include "bypass.h"
24#include "gbm_buffer.h"24#include "gbm_buffer.h"
25#include "mir/fatal.h"
2526
26#include <boost/throw_exception.hpp>27#include <boost/throw_exception.hpp>
27#include <GLES2/gl2.h>28#include <GLES2/gl2.h>
@@ -143,18 +144,18 @@
143 glClear(GL_COLOR_BUFFER_BIT);144 glClear(GL_COLOR_BUFFER_BIT);
144145
145 if (!egl.swap_buffers())146 if (!egl.swap_buffers())
146 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to perform initial surface buffer swap"));147 mir::abort("Failed to perform initial surface buffer swap");
147148
148 listener->report_successful_egl_buffer_swap_on_construction();149 listener->report_successful_egl_buffer_swap_on_construction();
149150
150 scheduled_bufobj = get_front_buffer_object();151 scheduled_bufobj = get_front_buffer_object();
151 if (!scheduled_bufobj)152 if (!scheduled_bufobj)
152 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to get frontbuffer"));153 mir::abort("Failed to get frontbuffer");
153154
154 for (auto& output : outputs)155 for (auto& output : outputs)
155 {156 {
156 if (!output->set_crtc(scheduled_bufobj->get_drm_fb_id()))157 if (!output->set_crtc(scheduled_bufobj->get_drm_fb_id()))
157 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to set DRM crtc"));158 mir::abort("Failed to set DRM crtc");
158 }159 }
159160
160 egl.release_current();161 egl.release_current();
@@ -256,7 +257,7 @@
256 * corresponding to the front buffer.257 * corresponding to the front buffer.
257 */258 */
258 if (!bypass_buf && !egl.swap_buffers())259 if (!bypass_buf && !egl.swap_buffers())
259 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to perform initial surface buffer swap"));260 mir::abort("Failed to perform initial surface buffer swap");
260261
261 mgm::BufferObject *bufobj;262 mgm::BufferObject *bufobj;
262 if (bypass_buf)263 if (bypass_buf)
@@ -271,7 +272,7 @@
271 }272 }
272273
273 if (!bufobj)274 if (!bufobj)
274 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to get front buffer object"));275 mir::abort("Failed to get front buffer object");
275276
276 /*277 /*
277 * Schedule the current front buffer object for display, and wait278 * Schedule the current front buffer object for display, and wait
@@ -284,14 +285,14 @@
284 {285 {
285 if (!bypass_buf)286 if (!bypass_buf)
286 bufobj->release();287 bufobj->release();
287 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to schedule page flip"));288 mir::abort("Failed to schedule page flip");
288 }289 }
289 else if (needs_set_crtc)290 else if (needs_set_crtc)
290 {291 {
291 for (auto& output : outputs)292 for (auto& output : outputs)
292 {293 {
293 if (!output->set_crtc(bufobj->get_drm_fb_id()))294 if (!output->set_crtc(bufobj->get_drm_fb_id()))
294 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to set DRM crtc"));295 mir::abort("Failed to set DRM crtc");
295 }296 }
296 needs_set_crtc = false;297 needs_set_crtc = false;
297 }298 }
@@ -397,7 +398,7 @@
397{398{
398 if (!egl.make_current())399 if (!egl.make_current())
399 {400 {
400 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to make EGL surface current"));401 mir::abort("Failed to make EGL surface current");
401 }402 }
402}403}
403404
404405
=== modified file 'src/platform/graphics/mesa/real_kms_output.cpp'
--- src/platform/graphics/mesa/real_kms_output.cpp 2014-07-09 10:48:47 +0000
+++ src/platform/graphics/mesa/real_kms_output.cpp 2014-07-14 06:58:31 +0000
@@ -18,11 +18,7 @@
1818
19#include "real_kms_output.h"19#include "real_kms_output.h"
20#include "page_flipper.h"20#include "page_flipper.h"
2121#include "mir/fatal.h"
22#include <boost/throw_exception.hpp>
23#include <boost/exception/info.hpp>
24
25#include <stdexcept>
26#include <vector>22#include <vector>
27#include <string.h> // strcmp23#include <string.h> // strcmp
2824
@@ -166,7 +162,7 @@
166 connector = resources.connector(connector_id);162 connector = resources.connector(connector_id);
167163
168 if (!connector)164 if (!connector)
169 BOOST_THROW_EXCEPTION(std::runtime_error("No DRM connector found\n"));165 mir::abort("No DRM connector found");
170166
171 // TODO: What if we can't locate the DPMS property?167 // TODO: What if we can't locate the DPMS property?
172 for (int i = 0; i < connector->count_props; i++)168 for (int i = 0; i < connector->count_props; i++)
@@ -202,9 +198,10 @@
202bool mgm::RealKMSOutput::set_crtc(uint32_t fb_id)198bool mgm::RealKMSOutput::set_crtc(uint32_t fb_id)
203{199{
204 if (!ensure_crtc())200 if (!ensure_crtc())
205 BOOST_THROW_EXCEPTION(std::runtime_error("Output " +201 {
206 connector_name(connector.get()) +202 mir::abort("Output %s has no associated CRTC to set a framebuffer on",
207 " has no associated CRTC to set a framebuffer on"));203 connector_name(connector.get()).c_str());
204 }
208205
209 auto ret = drmModeSetCrtc(drm_fd, current_crtc->crtc_id,206 auto ret = drmModeSetCrtc(drm_fd, current_crtc->crtc_id,
210 fb_id, fb_offset.dx.as_int(), fb_offset.dy.as_int(),207 fb_id, fb_offset.dx.as_int(), fb_offset.dy.as_int(),
@@ -235,12 +232,8 @@
235 0, 0, 0, nullptr, 0, nullptr);232 0, 0, 0, nullptr, 0, nullptr);
236 if (result)233 if (result)
237 {234 {
238 std::string const msg =235 mir::abort("Couldn't clear output %s (drmModeSetCrtc = %d)",
239 "Couldn't clear output " + connector_name(connector.get());236 connector_name(connector.get()).c_str(), result);
240
241 BOOST_THROW_EXCEPTION(
242 ::boost::enable_error_info(std::runtime_error(msg))
243 << (boost::error_info<KMSOutput, decltype(result)>(result)));
244 }237 }
245238
246 current_crtc = nullptr;239 current_crtc = nullptr;
@@ -252,10 +245,10 @@
252 if (power_mode != mir_power_mode_on)245 if (power_mode != mir_power_mode_on)
253 return true;246 return true;
254 if (!current_crtc)247 if (!current_crtc)
255 BOOST_THROW_EXCEPTION(std::runtime_error("Output " +248 {
256 connector_name(connector.get()) +249 mir::abort("Output %s has no associated CRTC to schedule page flips on",
257 " has no associated CRTC to schedule page flips on"));250 connector_name(connector.get()).c_str());
258251 }
259 return page_flipper->schedule_flip(current_crtc->crtc_id, fb_id);252 return page_flipper->schedule_flip(current_crtc->crtc_id, fb_id);
260}253}
261254
@@ -265,10 +258,10 @@
265 if (power_mode != mir_power_mode_on)258 if (power_mode != mir_power_mode_on)
266 return;259 return;
267 if (!current_crtc)260 if (!current_crtc)
268 BOOST_THROW_EXCEPTION(std::runtime_error("Output " +261 {
269 connector_name(connector.get()) +262 mir::abort("Output %s has no associated CRTC to wait on",
270 " has no associated CRTC to wait on"));263 connector_name(connector.get()).c_str());
271264 }
272 page_flipper->wait_for_flip(current_crtc->crtc_id);265 page_flipper->wait_for_flip(current_crtc->crtc_id);
273}266}
274267
@@ -283,9 +276,7 @@
283 gbm_bo_get_width(buffer),276 gbm_bo_get_width(buffer),
284 gbm_bo_get_height(buffer)))277 gbm_bo_get_height(buffer)))
285 {278 {
286 BOOST_THROW_EXCEPTION(279 mir::abort("drmModeSetCursor failed (returned %d)", result);
287 ::boost::enable_error_info(std::runtime_error("drmModeSetCursor() failed"))
288 << (boost::error_info<KMSOutput, decltype(result)>(result)));
289 }280 }
290281
291 has_cursor_ = true;282 has_cursor_ = true;
@@ -300,9 +291,7 @@
300 destination.x.as_uint32_t(),291 destination.x.as_uint32_t(),
301 destination.y.as_uint32_t()))292 destination.y.as_uint32_t()))
302 {293 {
303 BOOST_THROW_EXCEPTION(294 mir::abort("drmModeMoveCursor failed (returned %d)", result);
304 ::boost::enable_error_info(std::runtime_error("drmModeMoveCursor() failed"))
305 << (boost::error_info<KMSOutput, decltype(result)>(result)));
306 }295 }
307 }296 }
308}297}
309298
=== modified file 'src/shared/CMakeLists.txt'
--- src/shared/CMakeLists.txt 2014-07-11 03:33:03 +0000
+++ src/shared/CMakeLists.txt 2014-07-14 06:58:31 +0000
@@ -14,6 +14,7 @@
14add_subdirectory(sharedlibrary)14add_subdirectory(sharedlibrary)
15add_subdirectory(thread)15add_subdirectory(thread)
16add_subdirectory(testdraw)16add_subdirectory(testdraw)
17add_subdirectory(fatal)
1718
18set(19set(
19 MIR_COMMON_PLATFORM_LIBRARIES20 MIR_COMMON_PLATFORM_LIBRARIES
2021
=== added directory 'src/shared/fatal'
=== added file 'src/shared/fatal/CMakeLists.txt'
--- src/shared/fatal/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ src/shared/fatal/CMakeLists.txt 2014-07-14 06:58:31 +0000
@@ -0,0 +1,3 @@
1add_library(mirfatal STATIC
2 fatal.cpp
3)
04
=== added file 'src/shared/fatal/fatal.cpp'
--- src/shared/fatal/fatal.cpp 1970-01-01 00:00:00 +0000
+++ src/shared/fatal/fatal.cpp 2014-07-14 06:58:31 +0000
@@ -0,0 +1,43 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15*
16 * Author: Daniel van Vugt <daniel.van.vugt@canonical.com>
17 */
18
19#include "mir/fatal.h"
20#include <cstdlib>
21#include <cstdio>
22#include <cstdarg>
23
24void mir::abort(char const* reason, ...)
25{
26 va_list args;
27
28 // Keep this as simple as possible, avoiding any object construction and
29 // minimizing the potential for heap operations between the error location
30 // and the abort().
31 va_start(args, reason);
32 fprintf(stderr, "Mir fatal error: ");
33 vfprintf(stderr, reason, args);
34 fprintf(stderr, "\n");
35 va_end(args);
36
37 std::abort();
38}
39
40void mir::_fail_assertion(char const* file, int line, char const* expr)
41{
42 mir::abort("Assertion failed at %s:%d: %s", file, line, expr);
43}
044
=== modified file 'tests/acceptance-tests/CMakeLists.txt'
--- tests/acceptance-tests/CMakeLists.txt 2014-07-11 14:04:15 +0000
+++ tests/acceptance-tests/CMakeLists.txt 2014-07-14 06:58:31 +0000
@@ -64,6 +64,7 @@
64 mirserver64 mirserver
65 mirclient65 mirclient
66 mirplatform66 mirplatform
67 mirfatal
6768
68 mir-test69 mir-test
69 mir-test-framework70 mir-test-framework
7071
=== modified file 'tests/acceptance-tests/test_server_shutdown.cpp'
--- tests/acceptance-tests/test_server_shutdown.cpp 2014-07-09 07:54:29 +0000
+++ tests/acceptance-tests/test_server_shutdown.cpp 2014-07-14 06:58:31 +0000
@@ -23,6 +23,7 @@
23#include "mir/compositor/display_buffer_compositor_factory.h"23#include "mir/compositor/display_buffer_compositor_factory.h"
24#include "mir/input/composite_event_filter.h"24#include "mir/input/composite_event_filter.h"
25#include "mir/run_mir.h"25#include "mir/run_mir.h"
26#include "mir/fatal.h"
2627
27#include "mir_test_framework/display_server_test_fixture.h"28#include "mir_test_framework/display_server_test_fixture.h"
28#include "mir_test/fake_event_hub_input_configuration.h"29#include "mir_test/fake_event_hub_input_configuration.h"
@@ -416,6 +417,39 @@
416 });417 });
417}418}
418419
420TEST_F(ServerShutdown, server_removes_endpoint_on_mir_abort)
421{ // Even fatal errors sometimes need to be caught for critical cleanup...
422 struct ServerConfig : TestingServerConfiguration
423 {
424 void on_start() override
425 {
426 sync.wait_for_signal_ready_for();
427 mir::abort("Bang");
428 }
429
430 mtf::CrossProcessSync sync;
431 };
432
433 ServerConfig server_config;
434 launch_server_process(server_config);
435
436 run_in_test_process([&]
437 {
438 ASSERT_TRUE(file_exists(server_config.the_socket_file()));
439
440 server_config.sync.signal_ready();
441
442 auto result = wait_for_shutdown_server_process();
443 EXPECT_EQ(mtf::TerminationReason::child_terminated_by_signal, result.reason);
444 // Under valgrind the server process is reported as being terminated
445 // by SIGKILL because of multithreading madness.
446 // TODO: Investigate if we can do better than this workaround
447 EXPECT_TRUE(result.signal == SIGABRT || result.signal == SIGKILL);
448
449 EXPECT_FALSE(file_exists(server_config.the_socket_file()));
450 });
451}
452
419struct OnSignal : ServerShutdown, ::testing::WithParamInterface<int> {};453struct OnSignal : ServerShutdown, ::testing::WithParamInterface<int> {};
420454
421TEST_P(OnSignal, removes_endpoint_on_signal)455TEST_P(OnSignal, removes_endpoint_on_signal)
422456
=== modified file 'tests/unit-tests/CMakeLists.txt'
--- tests/unit-tests/CMakeLists.txt 2014-07-11 21:53:44 +0000
+++ tests/unit-tests/CMakeLists.txt 2014-07-14 06:58:31 +0000
@@ -20,6 +20,7 @@
20 test_thread_name.cpp20 test_thread_name.cpp
21 test_default_emergency_cleanup.cpp21 test_default_emergency_cleanup.cpp
22 test_basic_observers.cpp22 test_basic_observers.cpp
23 test_fatal.cpp
23)24)
2425
25add_subdirectory(options/)26add_subdirectory(options/)
@@ -50,6 +51,7 @@
50 mirdraw51 mirdraw
51 mirtestdraw52 mirtestdraw
52 mirlogging53 mirlogging
54 mirfatal
5355
54 mir-test56 mir-test
55 mir-test-doubles57 mir-test-doubles
5658
=== modified file 'tests/unit-tests/graphics/mesa/test_display.cpp'
--- tests/unit-tests/graphics/mesa/test_display.cpp 2014-06-23 16:29:17 +0000
+++ tests/unit-tests/graphics/mesa/test_display.cpp 2014-07-14 06:58:31 +0000
@@ -450,39 +450,39 @@
450{450{
451 using namespace testing;451 using namespace testing;
452452
453 auto const crtc_id = get_connected_crtc_id();453 EXPECT_DEATH(
454454 {
455 setup_post_update_expectations();455 auto const crtc_id = get_connected_crtc_id();
456456
457 {457 setup_post_update_expectations();
458 InSequence s;458
459459 {
460 /* New FB flip failure */460 InSequence s;
461 EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(),461
462 crtc_id,462 /* New FB flip failure */
463 fake.fb_id2,463 EXPECT_CALL(mock_drm, drmModePageFlip(mock_drm.fake_drm.fd(),
464 _, _))464 crtc_id,
465 .Times(Exactly(1))465 fake.fb_id2,
466 .WillOnce(Return(-1));466 _, _))
467467 .Times(Exactly(1))
468 /* Release failed bufobj */468 .WillOnce(Return(-1));
469 EXPECT_CALL(mock_gbm, gbm_surface_release_buffer(mock_gbm.fake_gbm.surface, fake.bo2))469
470 .Times(Exactly(1));470 /* Release failed bufobj */
471471 EXPECT_CALL(mock_gbm, gbm_surface_release_buffer(mock_gbm.fake_gbm.surface, fake.bo2))
472 /* Release scheduled_bufobj (at destruction time) */472 .Times(Exactly(1));
473 EXPECT_CALL(mock_gbm, gbm_surface_release_buffer(mock_gbm.fake_gbm.surface, fake.bo1))473
474 .Times(Exactly(1));474 /* Release scheduled_bufobj (at destruction time) */
475 }475 EXPECT_CALL(mock_gbm, gbm_surface_release_buffer(mock_gbm.fake_gbm.surface, fake.bo1))
476476 .Times(Exactly(1));
477 EXPECT_THROW(477 }
478 {478
479 auto display = create_display(create_platform());479 auto display = create_display(create_platform());
480480
481 display->for_each_display_buffer([](mg::DisplayBuffer& db)481 display->for_each_display_buffer([](mg::DisplayBuffer& db)
482 {482 {
483 db.post_update();483 db.post_update();
484 });484 });
485 }, std::runtime_error);485 }, "Mir fatal error: Failed to schedule page flip");
486}486}
487487
488TEST_F(MesaDisplayTest, successful_creation_of_display_reports_successful_setup_of_native_resources)488TEST_F(MesaDisplayTest, successful_creation_of_display_reports_successful_setup_of_native_resources)
489489
=== modified file 'tests/unit-tests/graphics/mesa/test_real_kms_output.cpp'
--- tests/unit-tests/graphics/mesa/test_real_kms_output.cpp 2014-03-06 06:05:17 +0000
+++ tests/unit-tests/graphics/mesa/test_real_kms_output.cpp 2014-07-14 06:58:31 +0000
@@ -23,7 +23,7 @@
2323
24#include "mir_test_doubles/mock_drm.h"24#include "mir_test_doubles/mock_drm.h"
2525
26#include <stdexcept>26#include <csignal>
2727
28#include <gtest/gtest.h>28#include <gtest/gtest.h>
29#include <gmock/gmock.h>29#include <gmock/gmock.h>
@@ -227,12 +227,15 @@
227 mt::fake_shared(mock_page_flipper)};227 mt::fake_shared(mock_page_flipper)};
228228
229 EXPECT_FALSE(output.set_crtc(fb_id));229 EXPECT_FALSE(output.set_crtc(fb_id));
230 EXPECT_THROW({230
231 output.schedule_page_flip(fb_id);231 EXPECT_EXIT({output.schedule_page_flip(fb_id);},
232 }, std::runtime_error);232 KilledBySignal(SIGABRT),
233 EXPECT_THROW({233 "Mir fatal error: Output VGA-0 has no associated CRTC to schedule page flips on");
234 output.wait_for_page_flip();234
235 }, std::runtime_error);235 // Yes, gtest is so awesome we can die multiple times...
236 EXPECT_EXIT({output.wait_for_page_flip();},
237 KilledBySignal(SIGABRT),
238 "Mir fatal error: Output VGA-0 has no associated CRTC to wait on");
236}239}
237240
238TEST_F(RealKMSOutputTest, clear_crtc_gets_crtc_if_none_is_current)241TEST_F(RealKMSOutputTest, clear_crtc_gets_crtc_if_none_is_current)
@@ -285,11 +288,10 @@
285 mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0],288 mgm::RealKMSOutput output{mock_drm.fake_drm.fd(), connector_ids[0],
286 mt::fake_shared(mock_page_flipper)};289 mt::fake_shared(mock_page_flipper)};
287290
288 EXPECT_CALL(mock_drm, drmModeSetCrtc(_, crtc_ids[0], 0, 0, 0, nullptr, 0, nullptr))291 ON_CALL(mock_drm, drmModeSetCrtc(_, crtc_ids[0], 0, 0, 0, nullptr, 0, nullptr))
289 .Times(1)292 .WillByDefault(Return(-1));
290 .WillOnce(Return(-1));
291293
292 EXPECT_THROW({294 EXPECT_EXIT({output.clear_crtc();},
293 output.clear_crtc();295 KilledBySignal(SIGABRT),
294 }, std::runtime_error);296 "Mir fatal error: Couldn't clear output VGA-0 \\(drmModeSetCrtc = -1\\)");
295}297}
296298
=== added file 'tests/unit-tests/test_fatal.cpp'
--- tests/unit-tests/test_fatal.cpp 1970-01-01 00:00:00 +0000
+++ tests/unit-tests/test_fatal.cpp 2014-07-14 06:58:31 +0000
@@ -0,0 +1,49 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Author: Daniel van Vugt <daniel.van.vugt@canonical.com>
17 */
18
19#include "mir/fatal.h"
20#include <gtest/gtest.h>
21#include <csignal>
22
23using namespace testing;
24
25TEST(FatalTest, abort_formats_message_to_stderr)
26{
27 EXPECT_DEATH({mir::abort("%s had %d %s %s", "Mary", 1, "little", "lamb");},
28 "Mary had 1 little lamb");
29}
30
31TEST(FatalTest, abort_raises_sigabrt)
32{
33 EXPECT_EXIT({mir::abort("Hello world");},
34 KilledBySignal(SIGABRT),
35 "Hello world");
36}
37
38TEST(FatalTest, true_assertion_raises_nothing)
39{
40 mir_assert(1 + 1 == 2);
41}
42
43TEST(FatalTest, false_assertion_emits_message_and_aborts)
44{
45 EXPECT_EXIT({mir_assert(2+2==5);},
46 KilledBySignal(SIGABRT),
47 "Mir fatal error: Assertion failed at .* 2\\+2==5");
48}
49

Subscribers

People subscribed via source and target branches