Merge lp:~thomas-voss/process-cpp/more_verbose_output_from_fork_and_run into lp:process-cpp

Proposed by Thomas Voß
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
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.

To post a comment you must log in.
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
Martin Pitt (pitti) wrote :

Nice!

review: Approve
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)
37. By Thomas Voß

Factor out backtrace generation to make it testable.

Revision history for this message
Martin Pitt (pitti) wrote :

In debian/libprocess-cpp0.symbols, does something automatically replace the "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?

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 :-)

review: Needs Information
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
38. By Thomas Voß

Do not expose implementation details via the libraries API & ABI.

39. By Thomas Voß

Fix copyright year.

Revision history for this message
Thomas Voß (thomas-voss) wrote :

> In debian/libprocess-cpp0.symbols, does something automatically replace the
> "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.

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

Hah, very nice. Thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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+}

Subscribers

People subscribed via source and target branches