aboutsummaryrefslogtreecommitdiff
path: root/build2/diagnostics
diff options
context:
space:
mode:
Diffstat (limited to 'build2/diagnostics')
-rw-r--r--build2/diagnostics78
1 files changed, 74 insertions, 4 deletions
diff --git a/build2/diagnostics b/build2/diagnostics
index dff1bd6..b889fd4 100644
--- a/build2/diagnostics
+++ b/build2/diagnostics
@@ -113,6 +113,70 @@ namespace build2
using butl::diag_stream;
using butl::diag_epilogue;
+ // Diagnostics stack. Each frame is "applied" to the fail/error/warn/info
+ // diag record.
+ //
+ // Unfortunately most of our use-cases don't fit into the 2-pointer small
+ // object optimization of std::function. So we have to complicate things
+ // a bit here.
+ //
+ struct diag_frame
+ {
+ explicit
+ diag_frame (void (*f) (const diag_frame&, const diag_record&))
+ : func_ (f), prev_ (stack) {stack = this;}
+
+ // Start with an existing stack, for example, from another thread.
+ //
+ explicit
+ diag_frame (const diag_frame* prev)
+ : prev_ (stack) {stack = prev;} // Just a restore guard.
+
+ static void
+ apply (const diag_record& r)
+ {
+ for (const diag_frame* f (stack); f != nullptr; f = f->prev_)
+ f->func_ (*f, r);
+ }
+
+ ~diag_frame () {stack = prev_;}
+
+ static
+#ifdef __cpp_thread_local
+ thread_local
+#else
+ __thread
+#endif
+ const diag_frame* stack; // Tip of the stack.
+
+ private:
+ void (*func_) (const diag_frame&, const diag_record&);
+ const diag_frame* prev_;
+ };
+
+ template <typename F>
+ struct diag_frame_impl: diag_frame
+ {
+ explicit
+ diag_frame_impl (F f): diag_frame (&thunk), func_ (move (f)) {}
+
+ private:
+ static void
+ thunk (const diag_frame& f, const diag_record& r)
+ {
+ static_cast<const diag_frame_impl&> (f).func_ (r);
+ }
+
+ const F func_;
+ };
+
+ template <typename F>
+ inline diag_frame_impl<F>
+ make_diag_frame (F f)
+ {
+ return diag_frame_impl<F> (move (f));
+ }
+
// Diagnostic facility, project specifics.
//
struct simple_prologue_base
@@ -179,11 +243,11 @@ namespace build2
explicit
basic_mark_base (const char* type,
+ diag_epilogue* epilogue = &diag_frame::apply,
uint16_t (*sverb) () = &stream_verb_map,
const char* mod = nullptr,
const char* name = nullptr,
- const void* data = nullptr,
- diag_epilogue* epilogue = nullptr)
+ const void* data = nullptr)
: sverb_ (sverb),
type_ (type), mod_ (mod), name_ (name), data_ (data),
epilogue_ (epilogue) {}
@@ -235,6 +299,7 @@ namespace build2
const char* name,
const void* data = nullptr)
: basic_mark_base ("trace",
+ nullptr, // No diag stack.
[]() {return stream_verb_max;},
mod,
name,
@@ -251,11 +316,16 @@ namespace build2
fail_mark_base (const char* type,
const void* data = nullptr)
: basic_mark_base (type,
+ [](const diag_record& r)
+ {
+ diag_frame::apply (r);
+ r.flush ();
+ throw failed ();
+ },
&stream_verb_map,
nullptr,
nullptr,
- data,
- [](const diag_record&) {throw failed ();}) {}
+ data) {}
};
using fail_mark = butl::diag_mark<fail_mark_base>;