Merge lp:~vanvugt/mir/fix-1527449-Plugin into lp:mir
- fix-1527449-Plugin
- Merge into development-branch
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 |
Related bugs: |
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_
Description of the change
This is the Plugin version of the fix, which works reliably but is less popular.
Alternative:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
Kevin DuBois (kdub) wrote : | # |
LGTM, nonblocking:
+ // Must never be inlined!
+ Plugin();
could __attribute__ (noinline) be used?
+ auto modules = mir::libraries_
+ *shared_
could be one line.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:3262
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 3263. By Daniel van Vugt
-
Merge latest trunk
- 3264. By Daniel van Vugt
-
Merge latest trunk and fix a conflict.
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:3264
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:3264
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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
1 | === added file 'include/common/mir/plugin.h' | |||
2 | --- include/common/mir/plugin.h 1970-01-01 00:00:00 +0000 | |||
3 | +++ include/common/mir/plugin.h 2016-01-20 16:21:34 +0000 | |||
4 | @@ -0,0 +1,48 @@ | |||
5 | 1 | /* | ||
6 | 2 | * Copyright © 2015 Canonical Ltd. | ||
7 | 3 | * | ||
8 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
9 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
10 | 6 | * as published by the Free Software Foundation. | ||
11 | 7 | * | ||
12 | 8 | * This program is distributed in the hope that it will be useful, | ||
13 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | 11 | * GNU Lesser General Public License for more details. | ||
16 | 12 | * | ||
17 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
18 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | 15 | * | ||
20 | 16 | * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com> | ||
21 | 17 | */ | ||
22 | 18 | |||
23 | 19 | #ifndef MIR_PLUGIN_H_ | ||
24 | 20 | #define MIR_PLUGIN_H_ | ||
25 | 21 | |||
26 | 22 | #include <memory> | ||
27 | 23 | |||
28 | 24 | namespace mir | ||
29 | 25 | { | ||
30 | 26 | |||
31 | 27 | /** | ||
32 | 28 | * Plugin is a base class designed to underpin any class whose implementation | ||
33 | 29 | * exists in a dynamically run-time loaded library. | ||
34 | 30 | * Plugin provides a foolproof check that you don't accidentally unload the | ||
35 | 31 | * library whose code you're still executing. | ||
36 | 32 | */ | ||
37 | 33 | class Plugin | ||
38 | 34 | { | ||
39 | 35 | public: | ||
40 | 36 | // Must never be inlined! | ||
41 | 37 | Plugin(); | ||
42 | 38 | virtual ~Plugin() noexcept; | ||
43 | 39 | |||
44 | 40 | void keep_library_loaded(std::shared_ptr<void> const&); | ||
45 | 41 | std::shared_ptr<void> keep_library_loaded() const; | ||
46 | 42 | private: | ||
47 | 43 | std::shared_ptr<void> library; | ||
48 | 44 | }; | ||
49 | 45 | |||
50 | 46 | } // namespace mir | ||
51 | 47 | |||
52 | 48 | #endif // MIR_PLUGIN_H_ | ||
53 | 0 | 49 | ||
54 | === modified file 'src/client/default_connection_configuration.cpp' | |||
55 | --- src/client/default_connection_configuration.cpp 2015-12-23 08:13:34 +0000 | |||
56 | +++ src/client/default_connection_configuration.cpp 2016-01-20 16:21:34 +0000 | |||
57 | @@ -34,11 +34,9 @@ | |||
58 | 34 | #include "lttng/shared_library_prober_report.h" | 34 | #include "lttng/shared_library_prober_report.h" |
59 | 35 | #include "connection_surface_map.h" | 35 | #include "connection_surface_map.h" |
60 | 36 | #include "lifecycle_control.h" | 36 | #include "lifecycle_control.h" |
61 | 37 | #include "mir/shared_library.h" | ||
62 | 38 | #include "mir/client_platform_factory.h" | 37 | #include "mir/client_platform_factory.h" |
63 | 39 | #include "probing_client_platform_factory.h" | 38 | #include "probing_client_platform_factory.h" |
64 | 40 | #include "mir_event_distributor.h" | 39 | #include "mir_event_distributor.h" |
65 | 41 | #include "mir/shared_library_prober.h" | ||
66 | 42 | 40 | ||
67 | 43 | namespace mcl = mir::client; | 41 | namespace mcl = mir::client; |
68 | 44 | 42 | ||
69 | @@ -47,10 +45,6 @@ | |||
70 | 47 | std::string const off_opt_val{"off"}; | 45 | std::string const off_opt_val{"off"}; |
71 | 48 | std::string const log_opt_val{"log"}; | 46 | std::string const log_opt_val{"log"}; |
72 | 49 | std::string const lttng_opt_val{"lttng"}; | 47 | std::string const lttng_opt_val{"lttng"}; |
73 | 50 | |||
74 | 51 | // Shove this here until we properly manage the lifetime of our | ||
75 | 52 | // loadable modules | ||
76 | 53 | std::shared_ptr<mcl::ProbingClientPlatformFactory> the_platform_prober; | ||
77 | 54 | } | 48 | } |
78 | 55 | 49 | ||
79 | 56 | mcl::DefaultConnectionConfiguration::DefaultConnectionConfiguration( | 50 | mcl::DefaultConnectionConfiguration::DefaultConnectionConfiguration( |
80 | @@ -95,22 +89,20 @@ | |||
81 | 95 | return client_platform_factory( | 89 | return client_platform_factory( |
82 | 96 | [this] | 90 | [this] |
83 | 97 | { | 91 | { |
90 | 98 | auto const platform_override = getenv("MIR_CLIENT_PLATFORM_LIB"); | 92 | std::vector<std::string> libs, paths; |
91 | 99 | std::vector<std::shared_ptr<mir::SharedLibrary>> platform_plugins; | 93 | if (auto const lib = getenv("MIR_CLIENT_PLATFORM_LIB")) |
92 | 100 | if (platform_override) | 94 | libs.push_back(lib); |
93 | 101 | { | 95 | |
94 | 102 | platform_plugins.push_back(std::make_shared<mir::SharedLibrary>(platform_override)); | 96 | if (auto path = getenv("MIR_CLIENT_PLATFORM_PATH")) |
95 | 103 | } | 97 | paths.push_back(path); |
96 | 104 | else | 98 | else |
106 | 105 | { | 99 | paths.push_back(MIR_CLIENT_PLATFORM_PATH); |
107 | 106 | auto const platform_path_override = getenv("MIR_CLIENT_PLATFORM_PATH"); | 100 | |
108 | 107 | auto const platform_path = platform_path_override ? platform_path_override : MIR_CLIENT_PLATFORM_PATH; | 101 | return std::make_shared<mcl::ProbingClientPlatformFactory>( |
109 | 108 | platform_plugins = mir::libraries_for_path(platform_path, *the_shared_library_prober_report()); | 102 | the_shared_library_prober_report(), |
110 | 109 | } | 103 | libs, |
111 | 110 | 104 | paths | |
112 | 111 | the_platform_prober = std::make_shared<mcl::ProbingClientPlatformFactory>(platform_plugins); | 105 | ); |
104 | 112 | |||
105 | 113 | return the_platform_prober; | ||
113 | 114 | }); | 106 | }); |
114 | 115 | } | 107 | } |
115 | 116 | 108 | ||
116 | 117 | 109 | ||
117 | === modified file 'src/client/mir_connection.cpp' | |||
118 | --- src/client/mir_connection.cpp 2016-01-20 00:44:40 +0000 | |||
119 | +++ src/client/mir_connection.cpp 2016-01-20 16:21:34 +0000 | |||
120 | @@ -521,6 +521,7 @@ | |||
121 | 521 | }; | 521 | }; |
122 | 522 | 522 | ||
123 | 523 | platform = client_platform_factory->create_client_platform(this); | 523 | platform = client_platform_factory->create_client_platform(this); |
124 | 524 | platform_library = platform->keep_library_loaded(); | ||
125 | 524 | native_display = platform->create_egl_native_display(); | 525 | native_display = platform->create_egl_native_display(); |
126 | 525 | display_configuration->set_configuration(connect_result->display_configuration()); | 526 | display_configuration->set_configuration(connect_result->display_configuration()); |
127 | 526 | lifecycle_control->set_callback(default_lifecycle_event_handler); | 527 | lifecycle_control->set_callback(default_lifecycle_event_handler); |
128 | 527 | 528 | ||
129 | === modified file 'src/client/mir_connection.h' | |||
130 | --- src/client/mir_connection.h 2016-01-18 15:54:40 +0000 | |||
131 | +++ src/client/mir_connection.h 2016-01-20 16:21:34 +0000 | |||
132 | @@ -216,6 +216,9 @@ | |||
133 | 216 | 216 | ||
134 | 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) |
135 | 218 | 218 | ||
136 | 219 | // Destruct this last as it will unmap the platform library from memory. | ||
137 | 220 | std::shared_ptr<void> platform_library; | ||
138 | 221 | |||
139 | 219 | std::shared_ptr<mir::client::ConnectionSurfaceMap> surface_map; | 222 | std::shared_ptr<mir::client::ConnectionSurfaceMap> surface_map; |
140 | 220 | std::shared_ptr<mir::client::rpc::MirBasicRpcChannel> const channel; | 223 | std::shared_ptr<mir::client::rpc::MirBasicRpcChannel> const channel; |
141 | 221 | mir::client::rpc::DisplayServer server; | 224 | mir::client::rpc::DisplayServer server; |
142 | 222 | 225 | ||
143 | === modified file 'src/client/probing_client_platform_factory.cpp' | |||
144 | --- src/client/probing_client_platform_factory.cpp 2016-01-05 10:23:12 +0000 | |||
145 | +++ src/client/probing_client_platform_factory.cpp 2016-01-20 16:21:34 +0000 | |||
146 | @@ -1,23 +1,60 @@ | |||
147 | 1 | #include "probing_client_platform_factory.h" | 1 | #include "probing_client_platform_factory.h" |
148 | 2 | #include "mir/client_platform.h" | ||
149 | 3 | #include "mir/shared_library.h" | ||
150 | 4 | #include "mir/shared_library_prober.h" | ||
151 | 5 | #include "mir/shared_library_prober_report.h" | ||
152 | 2 | 6 | ||
153 | 3 | #include <boost/exception/all.hpp> | 7 | #include <boost/exception/all.hpp> |
154 | 4 | #include <stdexcept> | 8 | #include <stdexcept> |
155 | 5 | 9 | ||
156 | 6 | namespace mcl = mir::client; | 10 | namespace mcl = mir::client; |
157 | 7 | 11 | ||
160 | 8 | mcl::ProbingClientPlatformFactory::ProbingClientPlatformFactory(std::vector<std::shared_ptr<mir::SharedLibrary>> const& modules) | 12 | mcl::ProbingClientPlatformFactory::ProbingClientPlatformFactory( |
161 | 9 | : platform_modules{modules} | 13 | std::shared_ptr<mir::SharedLibraryProberReport> const& rep, |
162 | 14 | StringList const& force_libs, | ||
163 | 15 | StringList const& lib_paths) | ||
164 | 16 | : shared_library_prober_report{rep}, | ||
165 | 17 | platform_overrides{force_libs}, | ||
166 | 18 | platform_paths{lib_paths} | ||
167 | 10 | { | 19 | { |
169 | 11 | if (modules.empty()) | 20 | if (platform_overrides.empty() && platform_paths.empty()) |
170 | 12 | { | 21 | { |
171 | 13 | BOOST_THROW_EXCEPTION( | 22 | BOOST_THROW_EXCEPTION( |
173 | 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"}); |
174 | 15 | } | 24 | } |
175 | 16 | } | 25 | } |
176 | 17 | 26 | ||
177 | 18 | std::shared_ptr<mcl::ClientPlatform> | 27 | std::shared_ptr<mcl::ClientPlatform> |
178 | 19 | mcl::ProbingClientPlatformFactory::create_client_platform(mcl::ClientContext* context) | 28 | mcl::ProbingClientPlatformFactory::create_client_platform(mcl::ClientContext* context) |
179 | 20 | { | 29 | { |
180 | 30 | // Note we don't want to keep unused platform modules loaded any longer | ||
181 | 31 | // than it takes to choose the right one. So this list is local: | ||
182 | 32 | std::vector<std::shared_ptr<mir::SharedLibrary>> platform_modules; | ||
183 | 33 | |||
184 | 34 | if (!platform_overrides.empty()) | ||
185 | 35 | { | ||
186 | 36 | // Even forcing a choice, platform is only loaded on demand. It's good | ||
187 | 37 | // to not need to hold the module open when there are no connections. | ||
188 | 38 | // Also, this allows you to swap driver binaries on disk at run-time, | ||
189 | 39 | // if you really wanted to. | ||
190 | 40 | |||
191 | 41 | for (auto const& platform : platform_overrides) | ||
192 | 42 | platform_modules.push_back( | ||
193 | 43 | std::make_shared<mir::SharedLibrary>(platform)); | ||
194 | 44 | } | ||
195 | 45 | else | ||
196 | 46 | { | ||
197 | 47 | for (auto const& path : platform_paths) | ||
198 | 48 | { | ||
199 | 49 | // This is sub-optimal. We shouldn't need to have all drivers | ||
200 | 50 | // loaded simultaneously, but we do for now due to this API: | ||
201 | 51 | auto modules = mir::libraries_for_path(path, | ||
202 | 52 | *shared_library_prober_report); | ||
203 | 53 | for (auto const& module : modules) | ||
204 | 54 | platform_modules.push_back(module); | ||
205 | 55 | } | ||
206 | 56 | } | ||
207 | 57 | |||
208 | 21 | for (auto& module : platform_modules) | 58 | for (auto& module : platform_modules) |
209 | 22 | { | 59 | { |
210 | 23 | try | 60 | try |
211 | @@ -26,7 +63,9 @@ | |||
212 | 26 | if (probe(context)) | 63 | if (probe(context)) |
213 | 27 | { | 64 | { |
214 | 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); |
216 | 29 | return factory(context); | 66 | auto platform = factory(context); |
217 | 67 | platform->keep_library_loaded(module); | ||
218 | 68 | return platform; | ||
219 | 30 | } | 69 | } |
220 | 31 | } | 70 | } |
221 | 32 | catch(std::runtime_error const&) | 71 | catch(std::runtime_error const&) |
222 | 33 | 72 | ||
223 | === modified file 'src/client/probing_client_platform_factory.h' | |||
224 | --- src/client/probing_client_platform_factory.h 2015-01-21 07:34:50 +0000 | |||
225 | +++ src/client/probing_client_platform_factory.h 2016-01-20 16:21:34 +0000 | |||
226 | @@ -8,16 +8,25 @@ | |||
227 | 8 | 8 | ||
228 | 9 | namespace mir | 9 | namespace mir |
229 | 10 | { | 10 | { |
230 | 11 | class SharedLibraryProberReport; | ||
231 | 12 | |||
232 | 11 | namespace client | 13 | namespace client |
233 | 12 | { | 14 | { |
234 | 13 | class ProbingClientPlatformFactory : public ClientPlatformFactory | 15 | class ProbingClientPlatformFactory : public ClientPlatformFactory |
235 | 14 | { | 16 | { |
236 | 15 | public: | 17 | public: |
238 | 16 | ProbingClientPlatformFactory(std::vector<std::shared_ptr<SharedLibrary>> const& modules); | 18 | using StringList = std::vector<std::string>; |
239 | 19 | ProbingClientPlatformFactory( | ||
240 | 20 | std::shared_ptr<mir::SharedLibraryProberReport> const& rep, | ||
241 | 21 | StringList const& force_libs, | ||
242 | 22 | StringList const& lib_paths); | ||
243 | 17 | 23 | ||
244 | 18 | std::shared_ptr<ClientPlatform> create_client_platform(ClientContext *context) override; | 24 | std::shared_ptr<ClientPlatform> create_client_platform(ClientContext *context) override; |
245 | 25 | |||
246 | 19 | private: | 26 | private: |
248 | 20 | std::vector<std::shared_ptr<SharedLibrary>> platform_modules; | 27 | std::shared_ptr<mir::SharedLibraryProberReport> const shared_library_prober_report; |
249 | 28 | StringList const platform_overrides; | ||
250 | 29 | StringList const platform_paths; | ||
251 | 21 | }; | 30 | }; |
252 | 22 | 31 | ||
253 | 23 | } | 32 | } |
254 | 24 | 33 | ||
255 | === modified file 'src/common/sharedlibrary/CMakeLists.txt' | |||
256 | --- src/common/sharedlibrary/CMakeLists.txt 2015-06-17 05:20:42 +0000 | |||
257 | +++ src/common/sharedlibrary/CMakeLists.txt 2016-01-20 16:21:34 +0000 | |||
258 | @@ -18,6 +18,7 @@ | |||
259 | 18 | module_deleter.cpp | 18 | module_deleter.cpp |
260 | 19 | shared_library.cpp | 19 | shared_library.cpp |
261 | 20 | shared_library_prober.cpp | 20 | shared_library_prober.cpp |
262 | 21 | plugin.cpp | ||
263 | 21 | ) | 22 | ) |
264 | 22 | 23 | ||
265 | 23 | list(APPEND MIR_COMMON_SOURCES | 24 | list(APPEND MIR_COMMON_SOURCES |
266 | 24 | 25 | ||
267 | === added file 'src/common/sharedlibrary/plugin.cpp' | |||
268 | --- src/common/sharedlibrary/plugin.cpp 1970-01-01 00:00:00 +0000 | |||
269 | +++ src/common/sharedlibrary/plugin.cpp 2016-01-20 16:21:34 +0000 | |||
270 | @@ -0,0 +1,50 @@ | |||
271 | 1 | /* | ||
272 | 2 | * Copyright © 2015 Canonical Ltd. | ||
273 | 3 | * | ||
274 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
275 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
276 | 6 | * as published by the Free Software Foundation. | ||
277 | 7 | * | ||
278 | 8 | * This program is distributed in the hope that it will be useful, | ||
279 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
280 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
281 | 11 | * GNU Lesser General Public License for more details. | ||
282 | 12 | * | ||
283 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
284 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
285 | 15 | * | ||
286 | 16 | * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com> | ||
287 | 17 | */ | ||
288 | 18 | |||
289 | 19 | #include "mir/plugin.h" | ||
290 | 20 | #include "mir/fatal.h" | ||
291 | 21 | |||
292 | 22 | namespace mir | ||
293 | 23 | { | ||
294 | 24 | |||
295 | 25 | Plugin::Plugin() | ||
296 | 26 | { | ||
297 | 27 | } | ||
298 | 28 | |||
299 | 29 | Plugin::~Plugin() noexcept | ||
300 | 30 | { | ||
301 | 31 | // This is not a recoverable error. If we don't exit now, we'll just | ||
302 | 32 | // end up with a worse crash where we have unloaded the library we're | ||
303 | 33 | // executing in, and so get no stack trace at all... | ||
304 | 34 | if (library.unique()) | ||
305 | 35 | mir::fatal_error_abort("Attempted to unload a plugin library " | ||
306 | 36 | "while it's still in use (~Plugin not " | ||
307 | 37 | "completed yet)"); | ||
308 | 38 | } | ||
309 | 39 | |||
310 | 40 | void Plugin::keep_library_loaded(std::shared_ptr<void> const& lib) | ||
311 | 41 | { | ||
312 | 42 | library = lib; | ||
313 | 43 | } | ||
314 | 44 | |||
315 | 45 | std::shared_ptr<void> Plugin::keep_library_loaded() const | ||
316 | 46 | { | ||
317 | 47 | return library; | ||
318 | 48 | } | ||
319 | 49 | |||
320 | 50 | } // namespace mir | ||
321 | 0 | 51 | ||
322 | === modified file 'src/common/symbols.map' | |||
323 | --- src/common/symbols.map 2016-01-08 09:50:51 +0000 | |||
324 | +++ src/common/symbols.map 2016-01-20 16:21:34 +0000 | |||
325 | @@ -208,6 +208,12 @@ | |||
326 | 208 | typeinfo?for?mir::time::SteadyClock; | 208 | typeinfo?for?mir::time::SteadyClock; |
327 | 209 | typeinfo?for?android::InputChannel; | 209 | typeinfo?for?android::InputChannel; |
328 | 210 | typeinfo?for?mir::logging::DumbConsoleLogger; | 210 | typeinfo?for?mir::logging::DumbConsoleLogger; |
329 | 211 | |||
330 | 211 | mir::detail::libname_impl*; | 212 | mir::detail::libname_impl*; |
331 | 213 | |||
332 | 214 | typeinfo?for?mir::Plugin; | ||
333 | 215 | mir::Plugin::Plugin*; | ||
334 | 216 | mir::Plugin::?Plugin*; | ||
335 | 217 | mir::Plugin::keep_library_loaded*; | ||
336 | 212 | }; | 218 | }; |
337 | 213 | } MIR_COMMON_5.1; | 219 | } MIR_COMMON_5.1; |
338 | 214 | 220 | ||
339 | === modified file 'src/include/client/mir/client_platform.h' | |||
340 | --- src/include/client/mir/client_platform.h 2015-12-23 06:41:56 +0000 | |||
341 | +++ src/include/client/mir/client_platform.h 2016-01-20 16:21:34 +0000 | |||
342 | @@ -21,6 +21,7 @@ | |||
343 | 21 | #include "mir/graphics/native_buffer.h" | 21 | #include "mir/graphics/native_buffer.h" |
344 | 22 | #include "mir_toolkit/client_types.h" | 22 | #include "mir_toolkit/client_types.h" |
345 | 23 | #include "mir_toolkit/mir_native_buffer.h" | 23 | #include "mir_toolkit/mir_native_buffer.h" |
346 | 24 | #include "mir/plugin.h" | ||
347 | 24 | 25 | ||
348 | 25 | #include <EGL/eglplatform.h> | 26 | #include <EGL/eglplatform.h> |
349 | 26 | #include <EGL/egl.h> // for EGLConfig | 27 | #include <EGL/egl.h> // for EGLConfig |
350 | @@ -38,7 +39,7 @@ | |||
351 | 38 | * Interface to client-side platform specific support for graphics operations. | 39 | * Interface to client-side platform specific support for graphics operations. |
352 | 39 | * \ingroup platform_enablement | 40 | * \ingroup platform_enablement |
353 | 40 | */ | 41 | */ |
355 | 41 | class ClientPlatform | 42 | class ClientPlatform : public mir::Plugin |
356 | 42 | { | 43 | { |
357 | 43 | public: | 44 | public: |
358 | 44 | ClientPlatform() = default; | 45 | ClientPlatform() = default; |
359 | 45 | 46 | ||
360 | === modified file 'src/platforms/mesa/client/client_platform_factory.cpp' | |||
361 | --- src/platforms/mesa/client/client_platform_factory.cpp 2015-11-25 20:26:59 +0000 | |||
362 | +++ src/platforms/mesa/client/client_platform_factory.cpp 2016-01-20 16:21:34 +0000 | |||
363 | @@ -35,14 +35,18 @@ | |||
364 | 35 | 35 | ||
365 | 36 | namespace | 36 | namespace |
366 | 37 | { | 37 | { |
369 | 38 | // Hack around the way mesa loads mir: This hack makes the | 38 | // Re-export our own symbols from mesa.so.N globally so that Mesa itself can |
370 | 39 | // necessary symbols global. | 39 | // find them with a simple dlsym(NULL,) |
371 | 40 | void ensure_loaded_with_rtld_global_mesa_client() | 40 | void ensure_loaded_with_rtld_global_mesa_client() |
372 | 41 | { | 41 | { |
373 | 42 | Dl_info info; | 42 | Dl_info info; |
374 | 43 | 43 | ||
375 | 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); |
377 | 45 | dlopen(info.dli_fname, RTLD_NOW | RTLD_NOLOAD | RTLD_GLOBAL); | 45 | void* reexport_self_global = |
378 | 46 | dlopen(info.dli_fname, RTLD_NOW | RTLD_NOLOAD | RTLD_GLOBAL); | ||
379 | 47 | // Yes, RTLD_NOLOAD does increase the ref count. So dlclose... | ||
380 | 48 | if (reexport_self_global) | ||
381 | 49 | dlclose(reexport_self_global); | ||
382 | 46 | } | 50 | } |
383 | 47 | 51 | ||
384 | 48 | struct RealBufferFileOps : public mclm::BufferFileOps | 52 | struct RealBufferFileOps : public mclm::BufferFileOps |
385 | 49 | 53 | ||
386 | === modified file 'tests/mir_test_framework/CMakeLists.txt' | |||
387 | --- tests/mir_test_framework/CMakeLists.txt 2016-01-05 19:14:36 +0000 | |||
388 | +++ tests/mir_test_framework/CMakeLists.txt 2016-01-20 16:21:34 +0000 | |||
389 | @@ -93,6 +93,7 @@ | |||
390 | 93 | mirclientplatformstub | 93 | mirclientplatformstub |
391 | 94 | 94 | ||
392 | 95 | mir-test-framework-static | 95 | mir-test-framework-static |
393 | 96 | mircommon | ||
394 | 96 | ${UMOCKDEV_LDFLAGS} ${UMOCKDEV_LIBRARIES} | 97 | ${UMOCKDEV_LDFLAGS} ${UMOCKDEV_LIBRARIES} |
395 | 97 | ) | 98 | ) |
396 | 98 | 99 | ||
397 | 99 | 100 | ||
398 | === modified file 'tests/unit-tests/client/test_probing_client_platform_factory.cpp' | |||
399 | --- tests/unit-tests/client/test_probing_client_platform_factory.cpp 2015-09-30 19:34:21 +0000 | |||
400 | +++ tests/unit-tests/client/test_probing_client_platform_factory.cpp 2016-01-20 16:21:34 +0000 | |||
401 | @@ -18,6 +18,7 @@ | |||
402 | 18 | 18 | ||
403 | 19 | #include "mir/client_platform.h" | 19 | #include "mir/client_platform.h" |
404 | 20 | #include "src/client/probing_client_platform_factory.h" | 20 | #include "src/client/probing_client_platform_factory.h" |
405 | 21 | #include "src/server/report/null_report_factory.h" | ||
406 | 21 | 22 | ||
407 | 22 | #include "mir/test/doubles/mock_client_context.h" | 23 | #include "mir/test/doubles/mock_client_context.h" |
408 | 23 | #include "mir_test_framework/executable_path.h" | 24 | #include "mir_test_framework/executable_path.h" |
409 | @@ -25,30 +26,57 @@ | |||
410 | 25 | 26 | ||
411 | 26 | #include <gmock/gmock.h> | 27 | #include <gmock/gmock.h> |
412 | 27 | #include <gtest/gtest.h> | 28 | #include <gtest/gtest.h> |
413 | 29 | #include <dlfcn.h> | ||
414 | 28 | 30 | ||
415 | 29 | namespace mtf = mir_test_framework; | 31 | namespace mtf = mir_test_framework; |
416 | 30 | namespace mtd = mir::test::doubles; | 32 | namespace mtd = mir::test::doubles; |
417 | 31 | 33 | ||
418 | 32 | namespace | 34 | namespace |
419 | 33 | { | 35 | { |
421 | 34 | std::vector<std::shared_ptr<mir::SharedLibrary>> | 36 | std::vector<std::string> |
422 | 35 | all_available_modules() | 37 | all_available_modules() |
423 | 36 | { | 38 | { |
425 | 37 | std::vector<std::shared_ptr<mir::SharedLibrary>> modules; | 39 | std::vector<std::string> modules; |
426 | 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) |
428 | 39 | modules.push_back(std::make_shared<mir::SharedLibrary>(mtf::client_platform("mesa"))); | 41 | modules.push_back(mtf::client_platform("mesa")); |
429 | 40 | #endif | 42 | #endif |
430 | 41 | #ifdef MIR_BUILD_PLATFORM_ANDROID | 43 | #ifdef MIR_BUILD_PLATFORM_ANDROID |
432 | 42 | modules.push_back(std::make_shared<mir::SharedLibrary>(mtf::client_platform("android"))); | 44 | modules.push_back(mtf::client_platform("android")); |
433 | 43 | #endif | 45 | #endif |
434 | 44 | return modules; | 46 | return modules; |
435 | 45 | } | 47 | } |
436 | 48 | |||
437 | 49 | bool loaded(std::string const& path) | ||
438 | 50 | { | ||
439 | 51 | void* x = dlopen(path.c_str(), RTLD_LAZY | RTLD_NOLOAD); | ||
440 | 52 | if (x) | ||
441 | 53 | dlclose(x); | ||
442 | 54 | return !!x; | ||
443 | 55 | } | ||
444 | 56 | |||
445 | 57 | void populate_valid(MirPlatformPackage& pkg) | ||
446 | 58 | { | ||
447 | 59 | memset(&pkg, 0, sizeof(MirPlatformPackage)); | ||
448 | 60 | pkg.fd_items = 1; | ||
449 | 61 | pkg.fd[0] = 23; | ||
450 | 62 | } | ||
451 | 63 | |||
452 | 64 | void safely_unload(std::shared_ptr<mir::client::ClientPlatform>& platform) | ||
453 | 65 | { | ||
454 | 66 | ASSERT_TRUE(platform.unique()); | ||
455 | 67 | auto library = platform->keep_library_loaded(); | ||
456 | 68 | platform.reset(); | ||
457 | 69 | library.reset(); | ||
458 | 70 | } | ||
459 | 71 | |||
460 | 46 | } | 72 | } |
461 | 47 | 73 | ||
462 | 48 | TEST(ProbingClientPlatformFactory, ThrowsErrorWhenConstructedWithNoPlatforms) | 74 | TEST(ProbingClientPlatformFactory, ThrowsErrorWhenConstructedWithNoPlatforms) |
463 | 49 | { | 75 | { |
464 | 50 | std::vector<std::shared_ptr<mir::SharedLibrary>> empty_modules; | 76 | std::vector<std::shared_ptr<mir::SharedLibrary>> empty_modules; |
466 | 51 | EXPECT_THROW(mir::client::ProbingClientPlatformFactory{empty_modules}, | 77 | EXPECT_THROW(mir::client::ProbingClientPlatformFactory( |
467 | 78 | mir::report::null_shared_library_prober_report(), | ||
468 | 79 | {}, {}), | ||
469 | 52 | std::runtime_error); | 80 | std::runtime_error); |
470 | 53 | } | 81 | } |
471 | 54 | 82 | ||
472 | @@ -56,7 +84,10 @@ | |||
473 | 56 | { | 84 | { |
474 | 57 | using namespace testing; | 85 | using namespace testing; |
475 | 58 | 86 | ||
477 | 59 | mir::client::ProbingClientPlatformFactory factory{all_available_modules()}; | 87 | mir::client::ProbingClientPlatformFactory factory( |
478 | 88 | mir::report::null_shared_library_prober_report(), | ||
479 | 89 | all_available_modules(), | ||
480 | 90 | {}); | ||
481 | 60 | 91 | ||
482 | 61 | mtd::MockClientContext context; | 92 | mtd::MockClientContext context; |
483 | 62 | ON_CALL(context, populate_server_package(_)) | 93 | ON_CALL(context, populate_server_package(_)) |
484 | @@ -73,6 +104,98 @@ | |||
485 | 73 | std::runtime_error); | 104 | std::runtime_error); |
486 | 74 | } | 105 | } |
487 | 75 | 106 | ||
488 | 107 | TEST(ProbingClientPlatformFactory, DoesNotLeakTheUsedDriverModuleOnShutdown) | ||
489 | 108 | { // Regression test for LP: #1527449 | ||
490 | 109 | using namespace testing; | ||
491 | 110 | auto const modules = all_available_modules(); | ||
492 | 111 | ASSERT_FALSE(modules.empty()); | ||
493 | 112 | std::string const preferred_module = modules.front(); | ||
494 | 113 | |||
495 | 114 | mir::client::ProbingClientPlatformFactory factory( | ||
496 | 115 | mir::report::null_shared_library_prober_report(), | ||
497 | 116 | {preferred_module}, | ||
498 | 117 | {}); | ||
499 | 118 | |||
500 | 119 | std::shared_ptr<mir::client::ClientPlatform> platform; | ||
501 | 120 | mtd::MockClientContext context; | ||
502 | 121 | ON_CALL(context, populate_server_package(_)) | ||
503 | 122 | .WillByDefault(Invoke(populate_valid)); | ||
504 | 123 | |||
505 | 124 | ASSERT_FALSE(loaded(preferred_module)); | ||
506 | 125 | platform = factory.create_client_platform(&context); | ||
507 | 126 | ASSERT_TRUE(loaded(preferred_module)); | ||
508 | 127 | safely_unload(platform); | ||
509 | 128 | EXPECT_FALSE(loaded(preferred_module)); | ||
510 | 129 | } | ||
511 | 130 | |||
512 | 131 | TEST(ProbingClientPlatformFactory, DoesNotLeakUnusedDriverModulesOnStartup) | ||
513 | 132 | { // Regression test for LP: #1527449 and LP: #1526658 | ||
514 | 133 | using namespace testing; | ||
515 | 134 | auto const modules = all_available_modules(); | ||
516 | 135 | ASSERT_FALSE(modules.empty()); | ||
517 | 136 | |||
518 | 137 | // Note: This test is only really effective with nmodules>1, which many of | ||
519 | 138 | // our builds will have. But nmodules==1 is harmless. | ||
520 | 139 | |||
521 | 140 | mir::client::ProbingClientPlatformFactory factory( | ||
522 | 141 | mir::report::null_shared_library_prober_report(), | ||
523 | 142 | modules, | ||
524 | 143 | {}); | ||
525 | 144 | |||
526 | 145 | std::shared_ptr<mir::client::ClientPlatform> platform; | ||
527 | 146 | mtd::MockClientContext context; | ||
528 | 147 | ON_CALL(context, populate_server_package(_)) | ||
529 | 148 | .WillByDefault(Invoke(populate_valid)); | ||
530 | 149 | |||
531 | 150 | int nloaded = 0; | ||
532 | 151 | for (auto const& m : modules) | ||
533 | 152 | if (loaded(m)) ++nloaded; | ||
534 | 153 | ASSERT_EQ(0, nloaded); | ||
535 | 154 | |||
536 | 155 | platform = factory.create_client_platform(&context); | ||
537 | 156 | |||
538 | 157 | nloaded = 0; | ||
539 | 158 | for (auto const& m : modules) | ||
540 | 159 | if (loaded(m)) ++nloaded; | ||
541 | 160 | EXPECT_EQ(1, nloaded); // expect not assert, because we need safely_unload | ||
542 | 161 | |||
543 | 162 | safely_unload(platform); | ||
544 | 163 | |||
545 | 164 | nloaded = 0; | ||
546 | 165 | for (auto const& m : modules) | ||
547 | 166 | if (loaded(m)) ++nloaded; | ||
548 | 167 | ASSERT_EQ(0, nloaded); | ||
549 | 168 | } | ||
550 | 169 | |||
551 | 170 | // Note "DeathTest" informs our scripts that we expect a leak from the | ||
552 | 171 | // child process that died, and to ignore it. | ||
553 | 172 | TEST(ProbingClientPlatformFactoryDeathTest, DiesOnUnsafeRelease) | ||
554 | 173 | { | ||
555 | 174 | using namespace testing; | ||
556 | 175 | auto const modules = all_available_modules(); | ||
557 | 176 | ASSERT_FALSE(modules.empty()); | ||
558 | 177 | std::string const preferred_module = modules.front(); | ||
559 | 178 | |||
560 | 179 | mir::client::ProbingClientPlatformFactory factory( | ||
561 | 180 | mir::report::null_shared_library_prober_report(), | ||
562 | 181 | {preferred_module}, | ||
563 | 182 | {}); | ||
564 | 183 | |||
565 | 184 | std::shared_ptr<mir::client::ClientPlatform> platform; | ||
566 | 185 | mtd::MockClientContext context; | ||
567 | 186 | ON_CALL(context, populate_server_package(_)) | ||
568 | 187 | .WillByDefault(Invoke(populate_valid)); | ||
569 | 188 | |||
570 | 189 | platform = factory.create_client_platform(&context); | ||
571 | 190 | |||
572 | 191 | // Google Test creates a child process to verify this: | ||
573 | 192 | ASSERT_DEATH({platform.reset();}, ".*still in use.*"); | ||
574 | 193 | |||
575 | 194 | // ... but here in the parent process we need to actively avoid the | ||
576 | 195 | // death happening, so that our test completes: | ||
577 | 196 | safely_unload(platform); | ||
578 | 197 | } | ||
579 | 198 | |||
580 | 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) |
581 | 77 | TEST(ProbingClientPlatformFactory, CreatesMesaPlatformWhenAppropriate) | 200 | TEST(ProbingClientPlatformFactory, CreatesMesaPlatformWhenAppropriate) |
582 | 78 | #else | 201 | #else |
583 | @@ -81,7 +204,10 @@ | |||
584 | 81 | { | 204 | { |
585 | 82 | using namespace testing; | 205 | using namespace testing; |
586 | 83 | 206 | ||
588 | 84 | mir::client::ProbingClientPlatformFactory factory{all_available_modules()}; | 207 | mir::client::ProbingClientPlatformFactory factory( |
589 | 208 | mir::report::null_shared_library_prober_report(), | ||
590 | 209 | all_available_modules(), | ||
591 | 210 | {}); | ||
592 | 85 | 211 | ||
593 | 86 | mtd::MockClientContext context; | 212 | mtd::MockClientContext context; |
594 | 87 | ON_CALL(context, populate_server_package(_)) | 213 | ON_CALL(context, populate_server_package(_)) |
595 | @@ -95,6 +221,7 @@ | |||
596 | 95 | })); | 221 | })); |
597 | 96 | auto platform = factory.create_client_platform(&context); | 222 | auto platform = factory.create_client_platform(&context); |
598 | 97 | EXPECT_EQ(mir_platform_type_gbm, platform->platform_type()); | 223 | EXPECT_EQ(mir_platform_type_gbm, platform->platform_type()); |
599 | 224 | safely_unload(platform); | ||
600 | 98 | } | 225 | } |
601 | 99 | 226 | ||
602 | 100 | #ifdef MIR_BUILD_PLATFORM_ANDROID | 227 | #ifdef MIR_BUILD_PLATFORM_ANDROID |
603 | @@ -105,7 +232,10 @@ | |||
604 | 105 | { | 232 | { |
605 | 106 | using namespace testing; | 233 | using namespace testing; |
606 | 107 | 234 | ||
608 | 108 | mir::client::ProbingClientPlatformFactory factory{all_available_modules()}; | 235 | mir::client::ProbingClientPlatformFactory factory( |
609 | 236 | mir::report::null_shared_library_prober_report(), | ||
610 | 237 | all_available_modules(), | ||
611 | 238 | {}); | ||
612 | 109 | 239 | ||
613 | 110 | mtd::MockClientContext context; | 240 | mtd::MockClientContext context; |
614 | 111 | ON_CALL(context, populate_server_package(_)) | 241 | ON_CALL(context, populate_server_package(_)) |
615 | @@ -118,6 +248,7 @@ | |||
616 | 118 | 248 | ||
617 | 119 | auto platform = factory.create_client_platform(&context); | 249 | auto platform = factory.create_client_platform(&context); |
618 | 120 | EXPECT_EQ(mir_platform_type_android, platform->platform_type()); | 250 | EXPECT_EQ(mir_platform_type_android, platform->platform_type()); |
619 | 251 | safely_unload(platform); | ||
620 | 121 | } | 252 | } |
621 | 122 | 253 | ||
622 | 123 | TEST(ProbingClientPlatformFactory, IgnoresNonClientPlatformModules) | 254 | TEST(ProbingClientPlatformFactory, IgnoresNonClientPlatformModules) |
623 | @@ -126,10 +257,13 @@ | |||
624 | 126 | 257 | ||
625 | 127 | auto modules = all_available_modules(); | 258 | auto modules = all_available_modules(); |
626 | 128 | // NOTE: For minimum fuss, load something that has minimal side-effects... | 259 | // NOTE: For minimum fuss, load something that has minimal side-effects... |
629 | 129 | modules.push_back(std::make_shared<mir::SharedLibrary>("libc.so.6")); | 260 | modules.push_back("libc.so.6"); |
630 | 130 | modules.push_back(std::make_shared<mir::SharedLibrary>(mtf::client_platform("dummy.so"))); | 261 | modules.push_back(mtf::client_platform("dummy.so")); |
631 | 131 | 262 | ||
633 | 132 | mir::client::ProbingClientPlatformFactory factory{modules}; | 263 | mir::client::ProbingClientPlatformFactory factory( |
634 | 264 | mir::report::null_shared_library_prober_report(), | ||
635 | 265 | modules, | ||
636 | 266 | {}); | ||
637 | 133 | 267 | ||
638 | 134 | mtd::MockClientContext context; | 268 | mtd::MockClientContext context; |
639 | 135 | ON_CALL(context, populate_server_package(_)) | 269 | ON_CALL(context, populate_server_package(_)) |
640 | @@ -139,4 +273,5 @@ | |||
641 | 139 | })); | 273 | })); |
642 | 140 | 274 | ||
643 | 141 | auto platform = factory.create_client_platform(&context); | 275 | auto platform = factory.create_client_platform(&context); |
644 | 276 | safely_unload(platform); | ||
645 | 142 | } | 277 | } |
PASSED: Continuous integration, rev:3262 /mir-jenkins. ubuntu. com/job/ mir-ci/ 85/ /mir-jenkins. ubuntu. com/job/ generic- update- mp/85/console
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild: /mir-jenkins. ubuntu. com/job/ mir-ci/ 85/rebuild
https:/