/* Copyright (c) 2015 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Author: Leonardo de Moura */ #include #include #include "util/option_declarations.h" #include "kernel/environment.h" #include "library/type_context.h" #include "library/io_state.h" #include "library/trace.h" #include "library/messages.h" namespace lean { static name_set * g_trace_classes = nullptr; static name_map * g_trace_aliases = nullptr; static name * g_trace_as_messages = nullptr; MK_THREAD_LOCAL_GET_DEF(std::vector, get_enabled_trace_classes); MK_THREAD_LOCAL_GET_DEF(std::vector, get_disabled_trace_classes); MK_THREAD_LOCAL_GET_DEF(environment, get_dummy_env); MK_THREAD_LOCAL_GET_DEF(options, get_dummy_options); LEAN_THREAD_VALUE(bool, g_silent, false); LEAN_THREAD_PTR(environment, g_env); LEAN_THREAD_PTR(options, g_opts); LEAN_THREAD_PTR(abstract_type_context, g_ctx); LEAN_THREAD_VALUE(unsigned, g_depth, 0); void register_trace_class(name const & n) { register_option(name("trace") + n, data_value_kind::Bool, "false", "(trace) enable/disable tracing for the given module and submodules"); g_trace_classes->insert(n); } void register_trace_class_alias(name const & n, name const & alias) { name_set new_s; if (auto s = g_trace_aliases->find(n)) new_s = *s; new_s.insert(alias); g_trace_aliases->insert(n, new_s); } bool is_trace_enabled() { return !get_enabled_trace_classes().empty(); } static void update_class(std::vector & cs, name const & c) { if (std::find(cs.begin(), cs.end(), c) == cs.end()) { cs.push_back(c); } } static void enable_trace_class(name const & c) { update_class(get_enabled_trace_classes(), c); } static void disable_trace_class(name const & c) { update_class(get_disabled_trace_classes(), c); } static bool is_trace_class_set_core(std::vector const & cs, name const & n) { for (name const & p : cs) { if (is_prefix_of(p, n)) { return true; } } return false; } static bool is_trace_class_set(std::vector const & cs, name const & n) { if (is_trace_class_set_core(cs, n)) return true; auto it = n; while (true) { if (auto s = g_trace_aliases->find(it)) { bool found = false; s->for_each([&](name const & alias) { if (!found && is_trace_class_set_core(cs, alias)) found = true; }); if (found) return true; } if (it.is_atomic()) return false; it = it.get_prefix(); } } bool is_trace_class_enabled(name const & n) { if (!is_trace_enabled()) return false; if (is_trace_class_set(get_disabled_trace_classes(), n)) return false; // it was explicitly disabled return is_trace_class_set(get_enabled_trace_classes(), n); } void scope_trace_env::init(environment * env, options * opts, abstract_type_context * ctx) { m_enable_sz = get_enabled_trace_classes().size(); m_disable_sz = get_disabled_trace_classes().size(); m_old_env = g_env; m_old_opts = g_opts; m_old_ctx = g_ctx; g_env = env; g_ctx = ctx; name trace("trace"); if (opts && g_opts != opts) { opts->for_each([&](name const & n) { if (is_prefix_of(trace, n)) { name cls = n.replace_prefix(trace, name()); if (opts->get_bool(n, false)) enable_trace_class(cls); else disable_trace_class(cls); } }); } g_opts = opts; } scope_trace_env::scope_trace_env(environment const & env, options const & o, abstract_type_context & ctx) { init(const_cast(&env), const_cast(&o), &ctx); } scope_trace_env::scope_trace_env(environment const & env, abstract_type_context & ctx) { init(const_cast(&env), g_opts, &ctx); } scope_trace_env::scope_trace_env(options const & o) { init(g_env, const_cast(&o), g_ctx); } scope_trace_env::~scope_trace_env() { g_env = const_cast(m_old_env); g_opts = const_cast(m_old_opts); g_ctx = m_old_ctx; get_enabled_trace_classes().resize(m_enable_sz); get_disabled_trace_classes().resize(m_disable_sz); } void scope_trace_inc_depth::activate() { lean_assert(!m_active); m_active = true; g_depth++; } scope_trace_inc_depth::~scope_trace_inc_depth() { if (m_active) g_depth--; } void scope_trace_init_bool_option::init(name const & n, bool v) { m_initialized = true; m_old_opts = g_opts; if (g_opts) m_opts = *g_opts; m_opts = m_opts.update_if_undef(n, v); g_opts = &m_opts; } scope_trace_init_bool_option::~scope_trace_init_bool_option() { if (m_initialized) { g_opts = m_old_opts; } } struct silent_ios_helper { std::shared_ptr m_out; io_state m_ios; silent_ios_helper(): m_out(new null_output_channel()), m_ios(get_dummy_ios(), m_out, m_out) {} }; MK_THREAD_LOCAL_GET_DEF(silent_ios_helper, get_silent_ios_helper); MK_THREAD_LOCAL_GET(type_context_old, get_dummy_tc, get_dummy_env()); scope_trace_silent::scope_trace_silent(bool flag) { m_old_value = g_silent; g_silent = flag; } scope_trace_silent::~scope_trace_silent() { g_silent = m_old_value; } io_state_stream tout() { if (g_env && !g_silent) { options opts = g_opts ? *g_opts : get_dummy_options(); io_state ios(get_global_ios(), opts); return diagnostic(*g_env, ios, *g_ctx); } else { return diagnostic(get_dummy_env(), get_silent_ios_helper().m_ios, get_dummy_tc()); } } io_state_stream const & operator<<(io_state_stream const & ios, tdepth const &) { ios << g_depth << ". "; return ios; } io_state_stream const & operator<<(io_state_stream const & ios, tclass const & c) { ios << "[" << c.m_cls << "] "; return ios; } void initialize_trace() { g_trace_classes = new name_set(); g_trace_aliases = new name_map(); g_trace_as_messages = new name {"trace", "as_messages"}; mark_persistent(g_trace_as_messages->raw()); register_trace_class(name{"debug"}); } void finalize_trace() { delete g_trace_classes; delete g_trace_aliases; delete g_trace_as_messages; } scope_traces_as_messages::scope_traces_as_messages(std::string const & stream_name, pos_info const & pos) : m_stream_name(stream_name), m_pos(pos) { if (get_global_ios().get_options().get_bool(*g_trace_as_messages, false)) { m_redirected_ios = std::unique_ptr(new io_state(get_global_ios())); m_buffer = std::make_shared(); m_redirected_ios->set_regular_channel(m_buffer); m_redirected_ios->set_diagnostic_channel(m_buffer); m_scoped_ios = std::unique_ptr(new scope_global_ios(*m_redirected_ios)); } } scope_traces_as_messages::scope_traces_as_messages(pos_info_provider const *provider, expr const &ref) : scope_traces_as_messages(provider ? provider->get_file_name() : "", provider ? provider->get_pos_info_or_some(ref) : pos_info(1, 0)) {} scope_traces_as_messages::~scope_traces_as_messages() { if (enabled()) { auto msg = m_buffer->str(); if (!msg.empty()) { auto redirected_output = m_buffer->str(); if (!redirected_output.empty()) report_message(message( m_stream_name, m_pos, INFORMATION, "trace output", redirected_output)); } } } scope_traces_as_string::scope_traces_as_string() { m_redirected_ios = std::unique_ptr(new io_state(get_global_ios())); m_buffer = std::make_shared(); m_redirected_ios->set_regular_channel(m_buffer); m_redirected_ios->set_diagnostic_channel(m_buffer); m_scoped_ios = std::unique_ptr(new scope_global_ios(*m_redirected_ios)); } scope_traces_as_string::~scope_traces_as_string() { } }