Mir

Merge lp:~vanvugt/mir/fix-1527449-Plugin into lp:mir

Proposed by Daniel van Vugt
Status: Rejected
Rejected by: Alan Griffiths
Proposed branch: lp:~vanvugt/mir/fix-1527449-Plugin
Merge into: lp:mir
Diff against target: 645 lines (+333/-43)
13 files modified
include/common/mir/plugin.h (+48/-0)
src/client/default_connection_configuration.cpp (+13/-21)
src/client/mir_connection.cpp (+1/-0)
src/client/mir_connection.h (+3/-0)
src/client/probing_client_platform_factory.cpp (+44/-5)
src/client/probing_client_platform_factory.h (+11/-2)
src/common/sharedlibrary/CMakeLists.txt (+1/-0)
src/common/sharedlibrary/plugin.cpp (+50/-0)
src/common/symbols.map (+6/-0)
src/include/client/mir/client_platform.h (+2/-1)
src/platforms/mesa/client/client_platform_factory.cpp (+7/-3)
tests/mir_test_framework/CMakeLists.txt (+1/-0)
tests/unit-tests/client/test_probing_client_platform_factory.cpp (+146/-11)
To merge this branch: bzr merge lp:~vanvugt/mir/fix-1527449-Plugin
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Mir CI Bot continuous-integration Approve
Kevin DuBois (community) Approve
Review via email: mp+283168@code.launchpad.net

Commit message

Avoid leaking client platform modules, and particularly ensure unused
modules are not kept resident while the client is running (LP: #1527449)

This also fixes crashing clients (LP: #1526658) due to Mesa's dlsym calls landing on the wrong symbol versions. However that fix only works for future Mir releases and not retrospectively because of the dlopen leak in client_platform_factory.cpp, which will forever exist in mesa.so.[23]

Description of the change

This is the Plugin version of the fix, which works reliably but is less popular.

Alternative:
https://code.launchpad.net/~vanvugt/mir/fix-1527449-make_module_ptr-2/+merge/283378

To post a comment you must log in.
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3262
https://mir-jenkins.ubuntu.com/job/mir-ci/85/
Executed test runs:
    None: https://mir-jenkins.ubuntu.com/job/generic-update-mp/85/console

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/85/rebuild

review: Approve (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

LGTM, nonblocking:

+ // Must never be inlined!
+ Plugin();

could __attribute__ (noinline) be used?

+ auto modules = mir::libraries_for_path(path,
+ *shared_library_prober_report);
could be one line.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:3262
http://jenkins.qa.ubuntu.com/job/mir-ci/6056/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-android-vivid-i386-build/5588
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-clang-vivid-amd64-build/4495
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-vivid-touch/5544
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-xenial-touch/298
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-xenial-amd64-ci/381
        deb: http://jenkins.qa.ubuntu.com/job/mir-xenial-amd64-ci/381/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-xenial-i386-ci/381
        deb: http://jenkins.qa.ubuntu.com/job/mir-xenial-i386-ci/381/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-vivid-armhf/5541
        deb: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-vivid-armhf/5541/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-runner-touch/8000
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/26785
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-xenial-armhf/294
        deb: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-xenial-armhf/294/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-runner-xenial-touch/150
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/26795

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/mir-ci/6056/rebuild

review: Approve (continuous-integration)
lp:~vanvugt/mir/fix-1527449-Plugin updated
3263. By Daniel van Vugt

Merge latest trunk

3264. By Daniel van Vugt

Merge latest trunk and fix a conflict.

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3264
https://mir-jenkins.ubuntu.com/job/mir-ci/99/
Executed test runs:
    None: https://mir-jenkins.ubuntu.com/job/generic-update-mp/99/console

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/99/rebuild

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

PASSED: Continuous integration, rev:3264
http://jenkins.qa.ubuntu.com/job/mir-ci/6073/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-android-vivid-i386-build/5610
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-clang-vivid-amd64-build/4517
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-vivid-touch/5566
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-xenial-touch/305
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-xenial-amd64-ci/397
        deb: http://jenkins.qa.ubuntu.com/job/mir-xenial-amd64-ci/397/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-xenial-i386-ci/397
        deb: http://jenkins.qa.ubuntu.com/job/mir-xenial-i386-ci/397/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-vivid-armhf/5563
        deb: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-vivid-armhf/5563/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-runner-touch/8013
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/26833
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-xenial-armhf/301
        deb: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-xenial-armhf/301/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-runner-xenial-touch/157
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/26844

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/mir-ci/6073/rebuild

review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

We seem to be landing the alternate

Unmerged revisions

3264. By Daniel van Vugt

Merge latest trunk and fix a conflict.

3263. By Daniel van Vugt

Merge latest trunk

3262. By Daniel van Vugt

Remove redundant platform initialization added recently that was tripping
the destruction check.

3261. By Daniel van Vugt

Continue reverting back to Plugin

3260. By Daniel van Vugt

Start reverting back to the Plugin approach

3259. By Daniel van Vugt

Merge latest trunk

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'include/common/mir/plugin.h'
--- include/common/mir/plugin.h 1970-01-01 00:00:00 +0000
+++ include/common/mir/plugin.h 2016-01-20 16:21:34 +0000
@@ -0,0 +1,48 @@
1/*
2 * Copyright © 2015 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 * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
17 */
18
19#ifndef MIR_PLUGIN_H_
20#define MIR_PLUGIN_H_
21
22#include <memory>
23
24namespace mir
25{
26
27/**
28 * Plugin is a base class designed to underpin any class whose implementation
29 * exists in a dynamically run-time loaded library.
30 * Plugin provides a foolproof check that you don't accidentally unload the
31 * library whose code you're still executing.
32 */
33class Plugin
34{
35public:
36 // Must never be inlined!
37 Plugin();
38 virtual ~Plugin() noexcept;
39
40 void keep_library_loaded(std::shared_ptr<void> const&);
41 std::shared_ptr<void> keep_library_loaded() const;
42private:
43 std::shared_ptr<void> library;
44};
45
46} // namespace mir
47
48#endif // MIR_PLUGIN_H_
049
=== modified file 'src/client/default_connection_configuration.cpp'
--- src/client/default_connection_configuration.cpp 2015-12-23 08:13:34 +0000
+++ src/client/default_connection_configuration.cpp 2016-01-20 16:21:34 +0000
@@ -34,11 +34,9 @@
34#include "lttng/shared_library_prober_report.h"34#include "lttng/shared_library_prober_report.h"
35#include "connection_surface_map.h"35#include "connection_surface_map.h"
36#include "lifecycle_control.h"36#include "lifecycle_control.h"
37#include "mir/shared_library.h"
38#include "mir/client_platform_factory.h"37#include "mir/client_platform_factory.h"
39#include "probing_client_platform_factory.h"38#include "probing_client_platform_factory.h"
40#include "mir_event_distributor.h"39#include "mir_event_distributor.h"
41#include "mir/shared_library_prober.h"
4240
43namespace mcl = mir::client;41namespace mcl = mir::client;
4442
@@ -47,10 +45,6 @@
47std::string const off_opt_val{"off"};45std::string const off_opt_val{"off"};
48std::string const log_opt_val{"log"};46std::string const log_opt_val{"log"};
49std::string const lttng_opt_val{"lttng"};47std::string const lttng_opt_val{"lttng"};
50
51// Shove this here until we properly manage the lifetime of our
52// loadable modules
53std::shared_ptr<mcl::ProbingClientPlatformFactory> the_platform_prober;
54}48}
5549
56mcl::DefaultConnectionConfiguration::DefaultConnectionConfiguration(50mcl::DefaultConnectionConfiguration::DefaultConnectionConfiguration(
@@ -95,22 +89,20 @@
95 return client_platform_factory(89 return client_platform_factory(
96 [this]90 [this]
97 {91 {
98 auto const platform_override = getenv("MIR_CLIENT_PLATFORM_LIB");92 std::vector<std::string> libs, paths;
99 std::vector<std::shared_ptr<mir::SharedLibrary>> platform_plugins;93 if (auto const lib = getenv("MIR_CLIENT_PLATFORM_LIB"))
100 if (platform_override)94 libs.push_back(lib);
101 {95
102 platform_plugins.push_back(std::make_shared<mir::SharedLibrary>(platform_override));96 if (auto path = getenv("MIR_CLIENT_PLATFORM_PATH"))
103 }97 paths.push_back(path);
104 else98 else
105 {99 paths.push_back(MIR_CLIENT_PLATFORM_PATH);
106 auto const platform_path_override = getenv("MIR_CLIENT_PLATFORM_PATH");100
107 auto const platform_path = platform_path_override ? platform_path_override : MIR_CLIENT_PLATFORM_PATH;101 return std::make_shared<mcl::ProbingClientPlatformFactory>(
108 platform_plugins = mir::libraries_for_path(platform_path, *the_shared_library_prober_report());102 the_shared_library_prober_report(),
109 }103 libs,
110104 paths
111 the_platform_prober = std::make_shared<mcl::ProbingClientPlatformFactory>(platform_plugins);105 );
112
113 return the_platform_prober;
114 });106 });
115}107}
116108
117109
=== modified file 'src/client/mir_connection.cpp'
--- src/client/mir_connection.cpp 2016-01-20 00:44:40 +0000
+++ src/client/mir_connection.cpp 2016-01-20 16:21:34 +0000
@@ -521,6 +521,7 @@
521 };521 };
522522
523 platform = client_platform_factory->create_client_platform(this);523 platform = client_platform_factory->create_client_platform(this);
524 platform_library = platform->keep_library_loaded();
524 native_display = platform->create_egl_native_display();525 native_display = platform->create_egl_native_display();
525 display_configuration->set_configuration(connect_result->display_configuration());526 display_configuration->set_configuration(connect_result->display_configuration());
526 lifecycle_control->set_callback(default_lifecycle_event_handler);527 lifecycle_control->set_callback(default_lifecycle_event_handler);
527528
=== modified file 'src/client/mir_connection.h'
--- src/client/mir_connection.h 2016-01-18 15:54:40 +0000
+++ src/client/mir_connection.h 2016-01-20 16:21:34 +0000
@@ -216,6 +216,9 @@
216216
217 mutable std::mutex mutex; // Protects all members of *this (except release_wait_handles)217 mutable std::mutex mutex; // Protects all members of *this (except release_wait_handles)
218218
219 // Destruct this last as it will unmap the platform library from memory.
220 std::shared_ptr<void> platform_library;
221
219 std::shared_ptr<mir::client::ConnectionSurfaceMap> surface_map;222 std::shared_ptr<mir::client::ConnectionSurfaceMap> surface_map;
220 std::shared_ptr<mir::client::rpc::MirBasicRpcChannel> const channel;223 std::shared_ptr<mir::client::rpc::MirBasicRpcChannel> const channel;
221 mir::client::rpc::DisplayServer server;224 mir::client::rpc::DisplayServer server;
222225
=== modified file 'src/client/probing_client_platform_factory.cpp'
--- src/client/probing_client_platform_factory.cpp 2016-01-05 10:23:12 +0000
+++ src/client/probing_client_platform_factory.cpp 2016-01-20 16:21:34 +0000
@@ -1,23 +1,60 @@
1#include "probing_client_platform_factory.h"1#include "probing_client_platform_factory.h"
2#include "mir/client_platform.h"
3#include "mir/shared_library.h"
4#include "mir/shared_library_prober.h"
5#include "mir/shared_library_prober_report.h"
26
3#include <boost/exception/all.hpp>7#include <boost/exception/all.hpp>
4#include <stdexcept>8#include <stdexcept>
59
6namespace mcl = mir::client;10namespace mcl = mir::client;
711
8mcl::ProbingClientPlatformFactory::ProbingClientPlatformFactory(std::vector<std::shared_ptr<mir::SharedLibrary>> const& modules)12mcl::ProbingClientPlatformFactory::ProbingClientPlatformFactory(
9 : platform_modules{modules}13 std::shared_ptr<mir::SharedLibraryProberReport> const& rep,
14 StringList const& force_libs,
15 StringList const& lib_paths)
16 : shared_library_prober_report{rep},
17 platform_overrides{force_libs},
18 platform_paths{lib_paths}
10{19{
11 if (modules.empty())20 if (platform_overrides.empty() && platform_paths.empty())
12 {21 {
13 BOOST_THROW_EXCEPTION(22 BOOST_THROW_EXCEPTION(
14 std::runtime_error{"Attempted to create a ClientPlatformFactory with no platform modules"});23 std::runtime_error{"Attempted to create a ClientPlatformFactory with no platform paths or overrides"});
15 }24 }
16}25}
1726
18std::shared_ptr<mcl::ClientPlatform>27std::shared_ptr<mcl::ClientPlatform>
19mcl::ProbingClientPlatformFactory::create_client_platform(mcl::ClientContext* context)28mcl::ProbingClientPlatformFactory::create_client_platform(mcl::ClientContext* context)
20{29{
30 // Note we don't want to keep unused platform modules loaded any longer
31 // than it takes to choose the right one. So this list is local:
32 std::vector<std::shared_ptr<mir::SharedLibrary>> platform_modules;
33
34 if (!platform_overrides.empty())
35 {
36 // Even forcing a choice, platform is only loaded on demand. It's good
37 // to not need to hold the module open when there are no connections.
38 // Also, this allows you to swap driver binaries on disk at run-time,
39 // if you really wanted to.
40
41 for (auto const& platform : platform_overrides)
42 platform_modules.push_back(
43 std::make_shared<mir::SharedLibrary>(platform));
44 }
45 else
46 {
47 for (auto const& path : platform_paths)
48 {
49 // This is sub-optimal. We shouldn't need to have all drivers
50 // loaded simultaneously, but we do for now due to this API:
51 auto modules = mir::libraries_for_path(path,
52 *shared_library_prober_report);
53 for (auto const& module : modules)
54 platform_modules.push_back(module);
55 }
56 }
57
21 for (auto& module : platform_modules)58 for (auto& module : platform_modules)
22 {59 {
23 try60 try
@@ -26,7 +63,9 @@
26 if (probe(context))63 if (probe(context))
27 {64 {
28 auto factory = module->load_function<mir::client::CreateClientPlatform>("create_client_platform", CLIENT_PLATFORM_VERSION);65 auto factory = module->load_function<mir::client::CreateClientPlatform>("create_client_platform", CLIENT_PLATFORM_VERSION);
29 return factory(context);66 auto platform = factory(context);
67 platform->keep_library_loaded(module);
68 return platform;
30 }69 }
31 }70 }
32 catch(std::runtime_error const&)71 catch(std::runtime_error const&)
3372
=== modified file 'src/client/probing_client_platform_factory.h'
--- src/client/probing_client_platform_factory.h 2015-01-21 07:34:50 +0000
+++ src/client/probing_client_platform_factory.h 2016-01-20 16:21:34 +0000
@@ -8,16 +8,25 @@
88
9namespace mir9namespace mir
10{10{
11class SharedLibraryProberReport;
12
11namespace client13namespace client
12{14{
13class ProbingClientPlatformFactory : public ClientPlatformFactory15class ProbingClientPlatformFactory : public ClientPlatformFactory
14{16{
15public:17public:
16 ProbingClientPlatformFactory(std::vector<std::shared_ptr<SharedLibrary>> const& modules);18 using StringList = std::vector<std::string>;
19 ProbingClientPlatformFactory(
20 std::shared_ptr<mir::SharedLibraryProberReport> const& rep,
21 StringList const& force_libs,
22 StringList const& lib_paths);
1723
18 std::shared_ptr<ClientPlatform> create_client_platform(ClientContext *context) override;24 std::shared_ptr<ClientPlatform> create_client_platform(ClientContext *context) override;
25
19private:26private:
20 std::vector<std::shared_ptr<SharedLibrary>> platform_modules;27 std::shared_ptr<mir::SharedLibraryProberReport> const shared_library_prober_report;
28 StringList const platform_overrides;
29 StringList const platform_paths;
21};30};
2231
23}32}
2433
=== modified file 'src/common/sharedlibrary/CMakeLists.txt'
--- src/common/sharedlibrary/CMakeLists.txt 2015-06-17 05:20:42 +0000
+++ src/common/sharedlibrary/CMakeLists.txt 2016-01-20 16:21:34 +0000
@@ -18,6 +18,7 @@
18 module_deleter.cpp18 module_deleter.cpp
19 shared_library.cpp19 shared_library.cpp
20 shared_library_prober.cpp20 shared_library_prober.cpp
21 plugin.cpp
21)22)
2223
23list(APPEND MIR_COMMON_SOURCES24list(APPEND MIR_COMMON_SOURCES
2425
=== added file 'src/common/sharedlibrary/plugin.cpp'
--- src/common/sharedlibrary/plugin.cpp 1970-01-01 00:00:00 +0000
+++ src/common/sharedlibrary/plugin.cpp 2016-01-20 16:21:34 +0000
@@ -0,0 +1,50 @@
1/*
2 * Copyright © 2015 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 * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
17 */
18
19#include "mir/plugin.h"
20#include "mir/fatal.h"
21
22namespace mir
23{
24
25Plugin::Plugin()
26{
27}
28
29Plugin::~Plugin() noexcept
30{
31 // This is not a recoverable error. If we don't exit now, we'll just
32 // end up with a worse crash where we have unloaded the library we're
33 // executing in, and so get no stack trace at all...
34 if (library.unique())
35 mir::fatal_error_abort("Attempted to unload a plugin library "
36 "while it's still in use (~Plugin not "
37 "completed yet)");
38}
39
40void Plugin::keep_library_loaded(std::shared_ptr<void> const& lib)
41{
42 library = lib;
43}
44
45std::shared_ptr<void> Plugin::keep_library_loaded() const
46{
47 return library;
48}
49
50} // namespace mir
051
=== modified file 'src/common/symbols.map'
--- src/common/symbols.map 2016-01-08 09:50:51 +0000
+++ src/common/symbols.map 2016-01-20 16:21:34 +0000
@@ -208,6 +208,12 @@
208 typeinfo?for?mir::time::SteadyClock;208 typeinfo?for?mir::time::SteadyClock;
209 typeinfo?for?android::InputChannel;209 typeinfo?for?android::InputChannel;
210 typeinfo?for?mir::logging::DumbConsoleLogger;210 typeinfo?for?mir::logging::DumbConsoleLogger;
211
211 mir::detail::libname_impl*;212 mir::detail::libname_impl*;
213
214 typeinfo?for?mir::Plugin;
215 mir::Plugin::Plugin*;
216 mir::Plugin::?Plugin*;
217 mir::Plugin::keep_library_loaded*;
212 };218 };
213} MIR_COMMON_5.1;219} MIR_COMMON_5.1;
214220
=== modified file 'src/include/client/mir/client_platform.h'
--- src/include/client/mir/client_platform.h 2015-12-23 06:41:56 +0000
+++ src/include/client/mir/client_platform.h 2016-01-20 16:21:34 +0000
@@ -21,6 +21,7 @@
21#include "mir/graphics/native_buffer.h"21#include "mir/graphics/native_buffer.h"
22#include "mir_toolkit/client_types.h"22#include "mir_toolkit/client_types.h"
23#include "mir_toolkit/mir_native_buffer.h"23#include "mir_toolkit/mir_native_buffer.h"
24#include "mir/plugin.h"
2425
25#include <EGL/eglplatform.h>26#include <EGL/eglplatform.h>
26#include <EGL/egl.h> // for EGLConfig27#include <EGL/egl.h> // for EGLConfig
@@ -38,7 +39,7 @@
38 * Interface to client-side platform specific support for graphics operations.39 * Interface to client-side platform specific support for graphics operations.
39 * \ingroup platform_enablement40 * \ingroup platform_enablement
40 */41 */
41class ClientPlatform42class ClientPlatform : public mir::Plugin
42{43{
43public:44public:
44 ClientPlatform() = default;45 ClientPlatform() = default;
4546
=== modified file 'src/platforms/mesa/client/client_platform_factory.cpp'
--- src/platforms/mesa/client/client_platform_factory.cpp 2015-11-25 20:26:59 +0000
+++ src/platforms/mesa/client/client_platform_factory.cpp 2016-01-20 16:21:34 +0000
@@ -35,14 +35,18 @@
3535
36namespace36namespace
37{37{
38// Hack around the way mesa loads mir: This hack makes the38// Re-export our own symbols from mesa.so.N globally so that Mesa itself can
39// necessary symbols global.39// find them with a simple dlsym(NULL,)
40void ensure_loaded_with_rtld_global_mesa_client()40void ensure_loaded_with_rtld_global_mesa_client()
41{41{
42 Dl_info info;42 Dl_info info;
4343
44 dladdr(reinterpret_cast<void*>(&ensure_loaded_with_rtld_global_mesa_client), &info);44 dladdr(reinterpret_cast<void*>(&ensure_loaded_with_rtld_global_mesa_client), &info);
45 dlopen(info.dli_fname, RTLD_NOW | RTLD_NOLOAD | RTLD_GLOBAL);45 void* reexport_self_global =
46 dlopen(info.dli_fname, RTLD_NOW | RTLD_NOLOAD | RTLD_GLOBAL);
47 // Yes, RTLD_NOLOAD does increase the ref count. So dlclose...
48 if (reexport_self_global)
49 dlclose(reexport_self_global);
46}50}
4751
48struct RealBufferFileOps : public mclm::BufferFileOps52struct RealBufferFileOps : public mclm::BufferFileOps
4953
=== modified file 'tests/mir_test_framework/CMakeLists.txt'
--- tests/mir_test_framework/CMakeLists.txt 2016-01-05 19:14:36 +0000
+++ tests/mir_test_framework/CMakeLists.txt 2016-01-20 16:21:34 +0000
@@ -93,6 +93,7 @@
93 mirclientplatformstub93 mirclientplatformstub
9494
95 mir-test-framework-static95 mir-test-framework-static
96 mircommon
96 ${UMOCKDEV_LDFLAGS} ${UMOCKDEV_LIBRARIES}97 ${UMOCKDEV_LDFLAGS} ${UMOCKDEV_LIBRARIES}
97)98)
9899
99100
=== modified file 'tests/unit-tests/client/test_probing_client_platform_factory.cpp'
--- tests/unit-tests/client/test_probing_client_platform_factory.cpp 2015-09-30 19:34:21 +0000
+++ tests/unit-tests/client/test_probing_client_platform_factory.cpp 2016-01-20 16:21:34 +0000
@@ -18,6 +18,7 @@
1818
19#include "mir/client_platform.h"19#include "mir/client_platform.h"
20#include "src/client/probing_client_platform_factory.h"20#include "src/client/probing_client_platform_factory.h"
21#include "src/server/report/null_report_factory.h"
2122
22#include "mir/test/doubles/mock_client_context.h"23#include "mir/test/doubles/mock_client_context.h"
23#include "mir_test_framework/executable_path.h"24#include "mir_test_framework/executable_path.h"
@@ -25,30 +26,57 @@
2526
26#include <gmock/gmock.h>27#include <gmock/gmock.h>
27#include <gtest/gtest.h>28#include <gtest/gtest.h>
29#include <dlfcn.h>
2830
29namespace mtf = mir_test_framework;31namespace mtf = mir_test_framework;
30namespace mtd = mir::test::doubles;32namespace mtd = mir::test::doubles;
3133
32namespace34namespace
33{35{
34std::vector<std::shared_ptr<mir::SharedLibrary>>36std::vector<std::string>
35all_available_modules()37all_available_modules()
36{38{
37 std::vector<std::shared_ptr<mir::SharedLibrary>> modules;39 std::vector<std::string> modules;
38#if defined(MIR_BUILD_PLATFORM_MESA_KMS) || defined(MIR_BUILD_PLATFORM_MESA_X11)40#if defined(MIR_BUILD_PLATFORM_MESA_KMS) || defined(MIR_BUILD_PLATFORM_MESA_X11)
39 modules.push_back(std::make_shared<mir::SharedLibrary>(mtf::client_platform("mesa")));41 modules.push_back(mtf::client_platform("mesa"));
40#endif42#endif
41#ifdef MIR_BUILD_PLATFORM_ANDROID43#ifdef MIR_BUILD_PLATFORM_ANDROID
42 modules.push_back(std::make_shared<mir::SharedLibrary>(mtf::client_platform("android")));44 modules.push_back(mtf::client_platform("android"));
43#endif45#endif
44 return modules;46 return modules;
45}47}
48
49bool loaded(std::string const& path)
50{
51 void* x = dlopen(path.c_str(), RTLD_LAZY | RTLD_NOLOAD);
52 if (x)
53 dlclose(x);
54 return !!x;
55}
56
57void populate_valid(MirPlatformPackage& pkg)
58{
59 memset(&pkg, 0, sizeof(MirPlatformPackage));
60 pkg.fd_items = 1;
61 pkg.fd[0] = 23;
62}
63
64void safely_unload(std::shared_ptr<mir::client::ClientPlatform>& platform)
65{
66 ASSERT_TRUE(platform.unique());
67 auto library = platform->keep_library_loaded();
68 platform.reset();
69 library.reset();
70}
71
46}72}
4773
48TEST(ProbingClientPlatformFactory, ThrowsErrorWhenConstructedWithNoPlatforms)74TEST(ProbingClientPlatformFactory, ThrowsErrorWhenConstructedWithNoPlatforms)
49{75{
50 std::vector<std::shared_ptr<mir::SharedLibrary>> empty_modules;76 std::vector<std::shared_ptr<mir::SharedLibrary>> empty_modules;
51 EXPECT_THROW(mir::client::ProbingClientPlatformFactory{empty_modules},77 EXPECT_THROW(mir::client::ProbingClientPlatformFactory(
78 mir::report::null_shared_library_prober_report(),
79 {}, {}),
52 std::runtime_error);80 std::runtime_error);
53}81}
5482
@@ -56,7 +84,10 @@
56{84{
57 using namespace testing;85 using namespace testing;
5886
59 mir::client::ProbingClientPlatformFactory factory{all_available_modules()};87 mir::client::ProbingClientPlatformFactory factory(
88 mir::report::null_shared_library_prober_report(),
89 all_available_modules(),
90 {});
6091
61 mtd::MockClientContext context;92 mtd::MockClientContext context;
62 ON_CALL(context, populate_server_package(_))93 ON_CALL(context, populate_server_package(_))
@@ -73,6 +104,98 @@
73 std::runtime_error);104 std::runtime_error);
74}105}
75106
107TEST(ProbingClientPlatformFactory, DoesNotLeakTheUsedDriverModuleOnShutdown)
108{ // Regression test for LP: #1527449
109 using namespace testing;
110 auto const modules = all_available_modules();
111 ASSERT_FALSE(modules.empty());
112 std::string const preferred_module = modules.front();
113
114 mir::client::ProbingClientPlatformFactory factory(
115 mir::report::null_shared_library_prober_report(),
116 {preferred_module},
117 {});
118
119 std::shared_ptr<mir::client::ClientPlatform> platform;
120 mtd::MockClientContext context;
121 ON_CALL(context, populate_server_package(_))
122 .WillByDefault(Invoke(populate_valid));
123
124 ASSERT_FALSE(loaded(preferred_module));
125 platform = factory.create_client_platform(&context);
126 ASSERT_TRUE(loaded(preferred_module));
127 safely_unload(platform);
128 EXPECT_FALSE(loaded(preferred_module));
129}
130
131TEST(ProbingClientPlatformFactory, DoesNotLeakUnusedDriverModulesOnStartup)
132{ // Regression test for LP: #1527449 and LP: #1526658
133 using namespace testing;
134 auto const modules = all_available_modules();
135 ASSERT_FALSE(modules.empty());
136
137 // Note: This test is only really effective with nmodules>1, which many of
138 // our builds will have. But nmodules==1 is harmless.
139
140 mir::client::ProbingClientPlatformFactory factory(
141 mir::report::null_shared_library_prober_report(),
142 modules,
143 {});
144
145 std::shared_ptr<mir::client::ClientPlatform> platform;
146 mtd::MockClientContext context;
147 ON_CALL(context, populate_server_package(_))
148 .WillByDefault(Invoke(populate_valid));
149
150 int nloaded = 0;
151 for (auto const& m : modules)
152 if (loaded(m)) ++nloaded;
153 ASSERT_EQ(0, nloaded);
154
155 platform = factory.create_client_platform(&context);
156
157 nloaded = 0;
158 for (auto const& m : modules)
159 if (loaded(m)) ++nloaded;
160 EXPECT_EQ(1, nloaded); // expect not assert, because we need safely_unload
161
162 safely_unload(platform);
163
164 nloaded = 0;
165 for (auto const& m : modules)
166 if (loaded(m)) ++nloaded;
167 ASSERT_EQ(0, nloaded);
168}
169
170// Note "DeathTest" informs our scripts that we expect a leak from the
171// child process that died, and to ignore it.
172TEST(ProbingClientPlatformFactoryDeathTest, DiesOnUnsafeRelease)
173{
174 using namespace testing;
175 auto const modules = all_available_modules();
176 ASSERT_FALSE(modules.empty());
177 std::string const preferred_module = modules.front();
178
179 mir::client::ProbingClientPlatformFactory factory(
180 mir::report::null_shared_library_prober_report(),
181 {preferred_module},
182 {});
183
184 std::shared_ptr<mir::client::ClientPlatform> platform;
185 mtd::MockClientContext context;
186 ON_CALL(context, populate_server_package(_))
187 .WillByDefault(Invoke(populate_valid));
188
189 platform = factory.create_client_platform(&context);
190
191 // Google Test creates a child process to verify this:
192 ASSERT_DEATH({platform.reset();}, ".*still in use.*");
193
194 // ... but here in the parent process we need to actively avoid the
195 // death happening, so that our test completes:
196 safely_unload(platform);
197}
198
76#if defined(MIR_BUILD_PLATFORM_MESA_KMS) || defined(MIR_BUILD_PLATFORM_MESA_X11)199#if defined(MIR_BUILD_PLATFORM_MESA_KMS) || defined(MIR_BUILD_PLATFORM_MESA_X11)
77TEST(ProbingClientPlatformFactory, CreatesMesaPlatformWhenAppropriate)200TEST(ProbingClientPlatformFactory, CreatesMesaPlatformWhenAppropriate)
78#else201#else
@@ -81,7 +204,10 @@
81{204{
82 using namespace testing;205 using namespace testing;
83206
84 mir::client::ProbingClientPlatformFactory factory{all_available_modules()};207 mir::client::ProbingClientPlatformFactory factory(
208 mir::report::null_shared_library_prober_report(),
209 all_available_modules(),
210 {});
85211
86 mtd::MockClientContext context;212 mtd::MockClientContext context;
87 ON_CALL(context, populate_server_package(_))213 ON_CALL(context, populate_server_package(_))
@@ -95,6 +221,7 @@
95 }));221 }));
96 auto platform = factory.create_client_platform(&context);222 auto platform = factory.create_client_platform(&context);
97 EXPECT_EQ(mir_platform_type_gbm, platform->platform_type());223 EXPECT_EQ(mir_platform_type_gbm, platform->platform_type());
224 safely_unload(platform);
98}225}
99226
100#ifdef MIR_BUILD_PLATFORM_ANDROID227#ifdef MIR_BUILD_PLATFORM_ANDROID
@@ -105,7 +232,10 @@
105{232{
106 using namespace testing;233 using namespace testing;
107234
108 mir::client::ProbingClientPlatformFactory factory{all_available_modules()};235 mir::client::ProbingClientPlatformFactory factory(
236 mir::report::null_shared_library_prober_report(),
237 all_available_modules(),
238 {});
109239
110 mtd::MockClientContext context;240 mtd::MockClientContext context;
111 ON_CALL(context, populate_server_package(_))241 ON_CALL(context, populate_server_package(_))
@@ -118,6 +248,7 @@
118248
119 auto platform = factory.create_client_platform(&context);249 auto platform = factory.create_client_platform(&context);
120 EXPECT_EQ(mir_platform_type_android, platform->platform_type());250 EXPECT_EQ(mir_platform_type_android, platform->platform_type());
251 safely_unload(platform);
121}252}
122253
123TEST(ProbingClientPlatformFactory, IgnoresNonClientPlatformModules)254TEST(ProbingClientPlatformFactory, IgnoresNonClientPlatformModules)
@@ -126,10 +257,13 @@
126257
127 auto modules = all_available_modules();258 auto modules = all_available_modules();
128 // NOTE: For minimum fuss, load something that has minimal side-effects...259 // NOTE: For minimum fuss, load something that has minimal side-effects...
129 modules.push_back(std::make_shared<mir::SharedLibrary>("libc.so.6"));260 modules.push_back("libc.so.6");
130 modules.push_back(std::make_shared<mir::SharedLibrary>(mtf::client_platform("dummy.so")));261 modules.push_back(mtf::client_platform("dummy.so"));
131262
132 mir::client::ProbingClientPlatformFactory factory{modules};263 mir::client::ProbingClientPlatformFactory factory(
264 mir::report::null_shared_library_prober_report(),
265 modules,
266 {});
133267
134 mtd::MockClientContext context;268 mtd::MockClientContext context;
135 ON_CALL(context, populate_server_package(_))269 ON_CALL(context, populate_server_package(_))
@@ -139,4 +273,5 @@
139 }));273 }));
140274
141 auto platform = factory.create_client_platform(&context);275 auto platform = factory.create_client_platform(&context);
276 safely_unload(platform);
142}277}

Subscribers

People subscribed via source and target branches