Merge lp:~thomas-voss/process-cpp/more_verbose_output_from_fork_and_run into lp:process-cpp
- more_verbose_output_from_fork_and_run
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Martin Pitt |
Approved revision: | 39 |
Merged at revision: | 34 |
Proposed branch: | lp:~thomas-voss/process-cpp/more_verbose_output_from_fork_and_run |
Merge into: | lp:process-cpp |
Diff against target: |
409 lines (+326/-1) 6 files modified
src/CMakeLists.txt (+3/-0) src/core/posix/backtrace.cpp (+153/-0) src/core/posix/backtrace.h (+122/-0) src/core/posix/fork.cpp (+27/-0) tests/CMakeLists.txt (+8/-1) tests/fork_and_run_test.cpp (+13/-0) |
To merge this branch: | bzr merge lp:~thomas-voss/process-cpp/more_verbose_output_from_fork_and_run |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Pitt | Approve | ||
PS Jenkins bot | continuous-integration | Approve | |
Review via email: mp+203013@code.launchpad.net |
Commit message
Print issues with the forked child to std::cerr.
Description of the change
Print issues with the forked child to std::cerr.
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:35
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:36
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 37. By Thomas Voß
-
Factor out backtrace generation to make it testable.
Martin Pitt (pitti) wrote : | # |
In debian/
If static linking doesn't work, how about dropping the .h etc. and have the test #include backtrace.cpp directly?
38 + * Copyright © 2013 Canonical Ltd.
Should be ++year by now :-)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:37
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 38. By Thomas Voß
-
Do not expose implementation details via the libraries API & ABI.
- 39. By Thomas Voß
-
Fix copyright year.
Thomas Voß (thomas-voss) wrote : | # |
> In debian/
> "0replaceme" version when autolanding? Otherwise this needs to be set to
> something sensible. That said, I'm not that happy about exposing internal
> symbols publicly; at least in a libtool world, you could statically link the
> library to the test, so that you can access its internal symbols. Is that
> possible with cmake at all?
>
It would be possible with cmake, but your suggestion of just compiling in the source file is actually better :)
> If static linking doesn't work, how about dropping the .h etc. and have the
> test #include backtrace.cpp directly?
>
See before.
> 38 + * Copyright © 2013 Canonical Ltd.
>
> Should be ++year by now :-)
Fixed :) Good catch.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:39
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Martin Pitt (pitti) wrote : | # |
Hah, very nice. Thanks!
Preview Diff
1 | === modified file 'src/CMakeLists.txt' |
2 | --- src/CMakeLists.txt 2014-01-15 15:18:53 +0000 |
3 | +++ src/CMakeLists.txt 2014-01-24 19:28:21 +0000 |
4 | @@ -17,6 +17,9 @@ |
5 | add_library( |
6 | process-cpp SHARED |
7 | |
8 | + core/posix/backtrace.h |
9 | + core/posix/backtrace.cpp |
10 | + |
11 | core/posix/child_process.cpp |
12 | core/posix/exec.cpp |
13 | core/posix/fork.cpp |
14 | |
15 | === added file 'src/core/posix/backtrace.cpp' |
16 | --- src/core/posix/backtrace.cpp 1970-01-01 00:00:00 +0000 |
17 | +++ src/core/posix/backtrace.cpp 2014-01-24 19:28:21 +0000 |
18 | @@ -0,0 +1,153 @@ |
19 | +/* |
20 | + * Copyright © 2014 Canonical Ltd. |
21 | + * |
22 | + * This program is free software: you can redistribute it and/or modify it |
23 | + * under the terms of the GNU Lesser General Public License version 3, |
24 | + * as published by the Free Software Foundation. |
25 | + * |
26 | + * This program is distributed in the hope that it will be useful, |
27 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
28 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
29 | + * GNU Lesser General Public License for more details. |
30 | + * |
31 | + * You should have received a copy of the GNU Lesser General Public License |
32 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
33 | + * |
34 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
35 | + */ |
36 | + |
37 | +#include "backtrace.h" |
38 | + |
39 | +#include <cxxabi.h> |
40 | + |
41 | +#include <execinfo.h> |
42 | + |
43 | +namespace bt = core::posix::backtrace; |
44 | + |
45 | +namespace impl |
46 | +{ |
47 | +std::tuple<std::string, bool> demangle(const std::string& symbol) |
48 | +{ |
49 | + int status = 1; |
50 | + auto result = abi::__cxa_demangle(symbol.c_str(), |
51 | + nullptr, |
52 | + nullptr, |
53 | + &status); |
54 | + |
55 | + if (!result || status != 0) |
56 | + { |
57 | + return std::make_tuple(std::string(), false); |
58 | + } |
59 | + |
60 | + std::string s{result}; |
61 | + ::free(result); |
62 | + |
63 | + return std::make_tuple(s, true); |
64 | +} |
65 | + |
66 | +struct Frame : public bt::Frame |
67 | +{ |
68 | + struct Symbol : public bt::Frame::Symbol |
69 | + { |
70 | + Symbol(const char* symbol) : raw_(symbol) |
71 | + { |
72 | + auto first = raw_.find_first_of("("); |
73 | + auto last = raw_.find_last_of(")"); |
74 | + |
75 | + if (first != std::string::npos && last != std::string::npos) |
76 | + { |
77 | + auto mangled_symbol = raw_.substr(first+1, |
78 | + (last-1) - (first+1)); |
79 | + |
80 | + auto plus = mangled_symbol.find_first_of("+"); |
81 | + if (plus != std::string::npos) |
82 | + mangled_symbol.erase(plus); |
83 | + |
84 | + std::tie(demangled_, is_cxx_) = demangle(mangled_symbol); |
85 | + if (!is_cxx_) |
86 | + demangled_ = raw_; |
87 | + } |
88 | + } |
89 | + |
90 | + bool is_cxx() const |
91 | + { |
92 | + return is_cxx_; |
93 | + } |
94 | + |
95 | + std::string demangled() const |
96 | + { |
97 | + return demangled_; |
98 | + } |
99 | + |
100 | + std::string raw() const |
101 | + { |
102 | + return raw_; |
103 | + } |
104 | + |
105 | + std::string raw_; |
106 | + std::string demangled_; |
107 | + bool is_cxx_ = false; |
108 | + }; |
109 | + |
110 | + std::size_t depth_; |
111 | + void* frame_pointer_; |
112 | + Symbol symbol_; |
113 | + |
114 | + Frame(std::size_t depth, void* frame_pointer, const char* symbol) |
115 | + : depth_(depth), |
116 | + frame_pointer_(frame_pointer), |
117 | + symbol_(symbol) |
118 | + { |
119 | + } |
120 | + |
121 | + std::size_t depth() const |
122 | + { |
123 | + return depth_; |
124 | + } |
125 | + |
126 | + virtual void* frame_pointer() const |
127 | + { |
128 | + return frame_pointer_; |
129 | + } |
130 | + |
131 | + const Symbol& symbol() const |
132 | + { |
133 | + return symbol_; |
134 | + } |
135 | +}; |
136 | +} |
137 | + |
138 | +std::shared_ptr<bt::Frame::Symbol> bt::Frame::Symbol::for_testing_from_raw_symbol(const char* symbol) |
139 | +{ |
140 | + return std::shared_ptr<bt::Frame::Symbol>(new impl::Frame::Symbol(symbol)); |
141 | +} |
142 | + |
143 | +void bt::visit_with_handler(const bt::FrameHandler& handler) |
144 | +{ |
145 | + static const unsigned int max_frames=64; |
146 | + void *frames[max_frames]; |
147 | + |
148 | + auto frame_count = ::backtrace(frames, max_frames); |
149 | + auto symbols = ::backtrace_symbols(frames, frame_count); |
150 | + |
151 | + struct Scope |
152 | + { |
153 | + Scope(char** symbols) : symbols(symbols) |
154 | + { |
155 | + } |
156 | + |
157 | + ~Scope() |
158 | + { |
159 | + ::free(symbols); |
160 | + } |
161 | + |
162 | + char** symbols = nullptr; |
163 | + } scope{symbols}; |
164 | + |
165 | + for (int i = 0; i < frame_count; i++) |
166 | + { |
167 | + impl::Frame frame(i, frames[i], symbols[i]); |
168 | + if (!handler(frame)) |
169 | + return; |
170 | + } |
171 | +} |
172 | |
173 | === added file 'src/core/posix/backtrace.h' |
174 | --- src/core/posix/backtrace.h 1970-01-01 00:00:00 +0000 |
175 | +++ src/core/posix/backtrace.h 2014-01-24 19:28:21 +0000 |
176 | @@ -0,0 +1,122 @@ |
177 | +/* |
178 | + * Copyright © 2014 Canonical Ltd. |
179 | + * |
180 | + * This program is free software: you can redistribute it and/or modify it |
181 | + * under the terms of the GNU Lesser General Public License version 3, |
182 | + * as published by the Free Software Foundation. |
183 | + * |
184 | + * This program is distributed in the hope that it will be useful, |
185 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
186 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
187 | + * GNU Lesser General Public License for more details. |
188 | + * |
189 | + * You should have received a copy of the GNU Lesser General Public License |
190 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
191 | + * |
192 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
193 | + */ |
194 | + |
195 | +#ifndef CORE_POSIX_BACKTRACE_H_ |
196 | +#define CORE_POSIX_BACKTRACE_H_ |
197 | + |
198 | +#include <core/posix/visibility.h> |
199 | + |
200 | +#include <functional> |
201 | +#include <memory> |
202 | +#include <string> |
203 | + |
204 | +namespace core |
205 | +{ |
206 | +namespace posix |
207 | +{ |
208 | +namespace backtrace |
209 | +{ |
210 | +/** |
211 | + * @brief The Frame class models an individual frame of a backtrace. |
212 | + */ |
213 | +class Frame |
214 | +{ |
215 | +public: |
216 | + /** |
217 | + * @brief The Symbol class models the symbolic representation of a frame pointer. |
218 | + */ |
219 | + class Symbol |
220 | + { |
221 | + public: |
222 | + |
223 | + static std::shared_ptr<Symbol> for_testing_from_raw_symbol(const char* symbol); |
224 | + |
225 | + Symbol(const Symbol&) = delete; |
226 | + virtual ~Symbol() = default; |
227 | + |
228 | + Symbol& operator=(const Symbol&) = delete; |
229 | + |
230 | + /** |
231 | + * @brief is_cxx checks whether the symbol refers to a mangled C++ symbol. |
232 | + * @return true iff the symbol refers to a mangled C++ symbol. |
233 | + */ |
234 | + virtual bool is_cxx() const = 0; |
235 | + |
236 | + /** |
237 | + * @brief demangled returns the demangled C++ symbol name or raw. |
238 | + */ |
239 | + virtual std::string demangled() const = 0; |
240 | + |
241 | + /** |
242 | + * @brief raw The raw symbolic representation of a frame pointer. |
243 | + * @return |
244 | + */ |
245 | + virtual std::string raw() const = 0; |
246 | + |
247 | + protected: |
248 | + Symbol() = default; |
249 | + }; |
250 | + |
251 | + Frame(const Frame&) = delete; |
252 | + virtual ~Frame() = default; |
253 | + |
254 | + Frame& operator=(const Frame&) = delete; |
255 | + |
256 | + /** |
257 | + * @brief depth returns the depth of this frame in the overall backtrace. |
258 | + */ |
259 | + virtual std::size_t depth() const = 0; |
260 | + |
261 | + /** |
262 | + * @brief frame_pointer returns the the raw frame pointer of this frame. |
263 | + * @return |
264 | + */ |
265 | + virtual void* frame_pointer() const = 0; |
266 | + |
267 | + /** |
268 | + * @brief symbol returns the symbolic representation of this frame. |
269 | + */ |
270 | + virtual const Symbol& symbol() const = 0; |
271 | + |
272 | +protected: |
273 | + Frame() = default; |
274 | +}; |
275 | + |
276 | +/** |
277 | + * @brief FrameHandler is the functor invoked for every frame of a backtrace. |
278 | + * |
279 | + * A FrameHandler should return true if it wants to continue walking the stack |
280 | + * or false otherwise. |
281 | + */ |
282 | +typedef std::function<bool(const Frame& frame)> FrameHandler; |
283 | + |
284 | +/** |
285 | + *@brief visit_with_handler iterates the backtrace of the calling program, |
286 | + *invoking the handler for every frame. |
287 | + * |
288 | + * A FrameHandler should return true if it wants to continue walking the stack |
289 | + * or false otherwise |
290 | + * |
291 | + * @param handler The handler invoked for every frame. |
292 | + */ |
293 | +void visit_with_handler(const FrameHandler& handler); |
294 | +} |
295 | +} |
296 | +} |
297 | + |
298 | +#endif // CORE_POSIX_BACKTRACE_H_ |
299 | |
300 | === modified file 'src/core/posix/fork.cpp' |
301 | --- src/core/posix/fork.cpp 2013-11-29 07:38:07 +0000 |
302 | +++ src/core/posix/fork.cpp 2014-01-24 19:28:21 +0000 |
303 | @@ -19,6 +19,9 @@ |
304 | #include <core/posix/exit.h> |
305 | #include <core/posix/fork.h> |
306 | |
307 | +#include "backtrace.h" |
308 | + |
309 | +#include <iomanip> |
310 | #include <iostream> |
311 | #include <system_error> |
312 | |
313 | @@ -32,6 +35,16 @@ |
314 | if (rc == -1) |
315 | throw std::system_error(errno, std::system_category()); |
316 | } |
317 | + |
318 | +void print_backtrace(std::ostream& out, const std::string& line_prefix) |
319 | +{ |
320 | + core::posix::backtrace::visit_with_handler([&out, line_prefix](const core::posix::backtrace::Frame& frame) |
321 | + { |
322 | + out << line_prefix << std::dec << std::setw(2) << frame.depth() << "@" << std::hex << std::setw(14) << frame.frame_pointer() << ": " |
323 | + << (frame.symbol().is_cxx() ? frame.symbol().demangled() : frame.symbol().raw()) << std::endl; |
324 | + return true; |
325 | + }); |
326 | +} |
327 | } |
328 | |
329 | namespace core |
330 | @@ -78,8 +91,15 @@ |
331 | redirect_stream_to_fd(stderr_pipe.write_fd(), STDERR_FILENO); |
332 | |
333 | result = main(); |
334 | + } catch(const std::exception& e) |
335 | + { |
336 | + std::cerr << "core::posix::fork(): An unhandled std::exception occured in the child process:" << std::endl |
337 | + << " what(): " << e.what() << std::endl; |
338 | + print_backtrace(std::cerr, " "); |
339 | } catch(...) |
340 | { |
341 | + std::cerr << "core::posix::fork(): An unhandled exception occured in the child process." << std::endl; |
342 | + print_backtrace(std::cerr, " "); |
343 | } |
344 | |
345 | // We have to ensure that we exit here. Otherwise, we run into |
346 | @@ -128,8 +148,15 @@ |
347 | redirect_stream_to_fd(stderr_pipe.write_fd(), STDERR_FILENO); |
348 | |
349 | result = main(); |
350 | + } catch(const std::exception& e) |
351 | + { |
352 | + std::cerr << "core::posix::fork(): An unhandled std::exception occured in the child process:" << std::endl |
353 | + << " what(): " << e.what() << std::endl; |
354 | + print_backtrace(std::cerr, " "); |
355 | } catch(...) |
356 | { |
357 | + std::cerr << "core::posix::fork(): An unhandled exception occured in the child process." << std::endl; |
358 | + print_backtrace(std::cerr, " "); |
359 | } |
360 | |
361 | // We have to ensure that we exit here. Otherwise, we run into |
362 | |
363 | === modified file 'tests/CMakeLists.txt' |
364 | --- tests/CMakeLists.txt 2014-01-15 15:18:53 +0000 |
365 | +++ tests/CMakeLists.txt 2014-01-24 19:28:21 +0000 |
366 | @@ -21,7 +21,10 @@ |
367 | include_directories(${GMOCK_INCLUDE_DIR} ${GTEST_INCLUDE_DIR}) |
368 | set (CMAKE_CXX_FLAGS ${OLD_CMAKE_CXX_FLAGS}) |
369 | |
370 | -include_directories(${GTEST_INCLUDE_DIR}) |
371 | +include_directories( |
372 | + ${CMAKE_SOURCE_DIR}/src |
373 | + ${GTEST_INCLUDE_DIR} |
374 | +) |
375 | |
376 | add_executable( |
377 | posix_process_test |
378 | @@ -36,6 +39,10 @@ |
379 | add_executable( |
380 | fork_and_run_test |
381 | fork_and_run_test.cpp |
382 | + |
383 | + # We include an external source file to prevent from leaking |
384 | + # symbols to the outside world |
385 | + ${CMAKE_SOURCE_DIR}/src/core/posix/backtrace.cpp |
386 | ) |
387 | |
388 | add_executable( |
389 | |
390 | === modified file 'tests/fork_and_run_test.cpp' |
391 | --- tests/fork_and_run_test.cpp 2013-12-06 12:50:07 +0000 |
392 | +++ tests/fork_and_run_test.cpp 2014-01-24 19:28:21 +0000 |
393 | @@ -145,3 +145,16 @@ |
394 | { |
395 | return core::posix::exit::Status::failure; |
396 | }) |
397 | + |
398 | +#include <core/posix/backtrace.h> |
399 | + |
400 | +TEST(BacktraceSymbolDemangling, demangling_a_cpp_symbol_works) |
401 | +{ |
402 | + const char* ref = "tests/fork_and_run_test(_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc+0x4b) [0x4591f8]"; |
403 | + const char* ref_demangled = "bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*)"; |
404 | + auto symbol = core::posix::backtrace::Frame::Symbol::for_testing_from_raw_symbol(ref); |
405 | + |
406 | + EXPECT_TRUE(symbol->is_cxx()); |
407 | + EXPECT_EQ(ref, symbol->raw()); |
408 | + EXPECT_EQ(ref_demangled, symbol->demangled()); |
409 | +} |
FAILED: Continuous integration, rev:34 jenkins. qa.ubuntu. com/job/ process- cpp-ci/ 28/ jenkins. qa.ubuntu. com/job/ process- cpp-trusty- amd64-ci/ 28/console jenkins. qa.ubuntu. com/job/ process- cpp-trusty- armhf-ci/ 28 jenkins. qa.ubuntu. com/job/ process- cpp-trusty- armhf-ci/ 28/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ process- cpp-trusty- i386-ci/ 24
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/process- cpp-ci/ 28/rebuild
http://