github-actions[bot] commented on code in PR #16466: URL: https://github.com/apache/doris/pull/16466#discussion_r1098272444
########## be/src/runtime/thread_context.h: ########## @@ -182,15 +191,24 @@ // to nullptr, but the object it points to is not initialized. At this time, when the memory // is released somewhere, the TCMalloc hook is triggered to cause the crash. std::unique_ptr<ThreadMemTrackerMgr> thread_mem_tracker_mgr; - MemTrackerLimiter* thread_mem_tracker() { + MemTrackerLimiter* thread_mem_tracker() const { return thread_mem_tracker_mgr->limiter_mem_tracker_raw(); } + // any time we need the trace context of current query/fragment/task..., tls_trace_ctx is it. +#ifdef ENABLE_QUERY_DEBUG_TRACE + debug::QueryTraceContext tls_trace_ctx; +#endif + private: - std::string _task_id = ""; + std::string _task_id; TUniqueId _fragment_instance_id; }; +debug::QueryTraceContext& get_tls_trace_ctx(){ + return bthread_context->tls_trace_ctx; Review Comment: warning: no member named 'tls_trace_ctx' in 'doris::ThreadContext' [clang-diagnostic-error] ```cpp return bthread_context->tls_trace_ctx; ^ ``` ########## be/src/pipeline/pipeline_task.h: ########## @@ -171,7 +176,9 @@ class PipelineTask { bool has_dependency(); - uint32_t index() const { return _index; } + uint32_t index() const { return _index; } // TODO: is it useful? + + auto get_id() const { return _pipeline->get_id(); } OperatorPtr get_root() { return _root; } Review Comment: warning: unknown type name 'OperatorPtr' [clang-diagnostic-error] ```cpp OperatorPtr get_root() { return _root; } ^ ``` ########## be/src/util/debug/tracing.cpp: ########## @@ -0,0 +1,207 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include <gen_cpp/Opcodes_types.h> +#include <filesystem> +#include <fstream> +#include <mutex> +#include <utility> + +#include "common/config.h" +#include "pipeline/pipeline_task.h" +#include "fmt/printf.h" +#include "util/debug/tracing.h" +#include "util/time.h" + +namespace doris::debug { + +QueryTraceEvent QueryTraceEvent::create(const std::string& name, const std::string& category, int64_t id, char phase, Review Comment: warning: out-of-line definition of 'create' does not match any declaration in 'doris::debug::QueryTraceEvent' [clang-diagnostic-error] ```cpp QueryTraceEvent QueryTraceEvent::create(const std::string& name, const std::string& category, int64_t id, char phase, ^ ``` ########## be/src/util/debug/tracing.cpp: ########## @@ -0,0 +1,207 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include <gen_cpp/Opcodes_types.h> +#include <filesystem> +#include <fstream> +#include <mutex> +#include <utility> + +#include "common/config.h" +#include "pipeline/pipeline_task.h" +#include "fmt/printf.h" +#include "util/debug/tracing.h" +#include "util/time.h" + +namespace doris::debug { + +QueryTraceEvent QueryTraceEvent::create(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, + std::vector<std::pair<std::string, std::string>>&& args) { + QueryTraceEvent event; + event.name = name; + event.category = category; + event.id = id; + event.phase = phase; + event.start_time = timestamp; + event.duration = duration; + event.instance_id = instance_id; + event.task = task; + event.args = std::move(args); + event.thread_id = std::this_thread::get_id(); + return event; +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, const QueryTraceContext& ctx) { + return create(name, category, id, phase, MonotonicMicros() - ctx.start_ts, QueryTraceContext::DEFAULT_EVENT_ID, + ctx.fragment_instance_id, ctx.task, {}); +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, int64_t start_ts, int64_t duration, + const QueryTraceContext& ctx) { + return create(name, category, id, phase, start_ts, duration, ctx.fragment_instance_id, ctx.task, {}); +} + +static const char* kSimpleEventFormat = + R"({"cat":"%s","name":"%s","pid":"0x%x","tid":"0x%x","id":"0x%x","ts":%ld,"ph":"%c","args":%s,"tidx":"0x%x"})"; +static const char* kCompleteEventFormat = + R"({"cat":"%s","name":"%s","pid":"0x%x","tid":"0x%x","id":"0x%x","ts":%ld,"dur":%ld,"ph":"%c","args":%s,"tidx":"0x%x"})"; + +std::string QueryTraceEvent::to_string() { + std::string args_str = args_to_string(); + size_t tidx = std::hash<std::thread::id>{}(thread_id); + + // transform a raw pointer to uint64_t by re_cast is maybe not the best way, but at least unambiguous & safe. + if (phase == 'X') { + return fmt::sprintf(kCompleteEventFormat, category.c_str(), name.c_str(), (uint64_t)instance_id, + reinterpret_cast<uint64_t>(task), id, start_time, duration, phase, args_str.c_str(), tidx); + } else { + return fmt::sprintf(kSimpleEventFormat, category.c_str(), name.c_str(), (uint64_t)instance_id, + reinterpret_cast<uint64_t>(task), id, start_time, phase, args_str.c_str(), tidx); + } +} + +std::string QueryTraceEvent::args_to_string() { + if (args.empty()) { + return "{}"; + } + std::ostringstream oss; + oss << "{"; + oss << fmt::sprintf(R"("%s":"%s")", args[0].first.c_str(), args[0].second.c_str()); + for (size_t i = 1; i < args.size(); i++) { + oss << fmt::sprintf(R"(,"%s":"%s")", args[i].first.c_str(), args[i].second.c_str()); + } + oss << "}"; + return oss.str(); +} + +void EventBuffer::add(QueryTraceEvent&& event) { + std::lock_guard<std::mutex> l(_mutex); + _buffer.emplace_back(std::move(event)); +} + +#ifdef ENABLE_QUERY_DEBUG_TRACE +QueryTrace::QueryTrace(const TUniqueId& query_id, bool is_enable) : _query_id(query_id), _is_enable(is_enable) { + if (_is_enable) { + _start_ts = MonotonicMicros(); + } +} +#else +QueryTrace::QueryTrace(const TUniqueId& query_id, bool is_enable) {} +#endif + +void QueryTrace::register_tasks(const TUniqueId& fragment_instance_id, pipeline::PipelineTasks& tasks) { +#ifdef ENABLE_QUERY_DEBUG_TRACE + if (!_is_enable) { + return; + } + std::unique_lock l(_mutex); + auto iter = _fragment_tasks.find(fragment_instance_id); + if (iter == _fragment_tasks.end()) { + _fragment_tasks.emplace(fragment_instance_id, + std::make_shared<std::unordered_set<pipeline::PipelineTaskRawPtr>>()); + iter = _fragment_tasks.find(fragment_instance_id); + } + for (auto& task : tasks) { + iter->second->insert(task); // into this fragment's task map. + _buffers.emplace(task, std::make_unique<EventBuffer>()); Review Comment: warning: use of undeclared identifier '_buffers' [clang-diagnostic-error] ```cpp _buffers.emplace(task, std::make_unique<EventBuffer>()); ^ ``` ########## be/src/util/debug/tracing.cpp: ########## @@ -0,0 +1,207 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include <gen_cpp/Opcodes_types.h> +#include <filesystem> +#include <fstream> +#include <mutex> +#include <utility> + +#include "common/config.h" +#include "pipeline/pipeline_task.h" +#include "fmt/printf.h" +#include "util/debug/tracing.h" +#include "util/time.h" + +namespace doris::debug { + +QueryTraceEvent QueryTraceEvent::create(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, + std::vector<std::pair<std::string, std::string>>&& args) { + QueryTraceEvent event; + event.name = name; + event.category = category; + event.id = id; + event.phase = phase; + event.start_time = timestamp; + event.duration = duration; + event.instance_id = instance_id; + event.task = task; + event.args = std::move(args); + event.thread_id = std::this_thread::get_id(); + return event; +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, const QueryTraceContext& ctx) { + return create(name, category, id, phase, MonotonicMicros() - ctx.start_ts, QueryTraceContext::DEFAULT_EVENT_ID, + ctx.fragment_instance_id, ctx.task, {}); +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, int64_t start_ts, int64_t duration, + const QueryTraceContext& ctx) { + return create(name, category, id, phase, start_ts, duration, ctx.fragment_instance_id, ctx.task, {}); +} + +static const char* kSimpleEventFormat = + R"({"cat":"%s","name":"%s","pid":"0x%x","tid":"0x%x","id":"0x%x","ts":%ld,"ph":"%c","args":%s,"tidx":"0x%x"})"; +static const char* kCompleteEventFormat = + R"({"cat":"%s","name":"%s","pid":"0x%x","tid":"0x%x","id":"0x%x","ts":%ld,"dur":%ld,"ph":"%c","args":%s,"tidx":"0x%x"})"; + +std::string QueryTraceEvent::to_string() { + std::string args_str = args_to_string(); + size_t tidx = std::hash<std::thread::id>{}(thread_id); + + // transform a raw pointer to uint64_t by re_cast is maybe not the best way, but at least unambiguous & safe. + if (phase == 'X') { + return fmt::sprintf(kCompleteEventFormat, category.c_str(), name.c_str(), (uint64_t)instance_id, + reinterpret_cast<uint64_t>(task), id, start_time, duration, phase, args_str.c_str(), tidx); + } else { + return fmt::sprintf(kSimpleEventFormat, category.c_str(), name.c_str(), (uint64_t)instance_id, + reinterpret_cast<uint64_t>(task), id, start_time, phase, args_str.c_str(), tidx); + } +} + +std::string QueryTraceEvent::args_to_string() { + if (args.empty()) { + return "{}"; + } + std::ostringstream oss; + oss << "{"; + oss << fmt::sprintf(R"("%s":"%s")", args[0].first.c_str(), args[0].second.c_str()); + for (size_t i = 1; i < args.size(); i++) { + oss << fmt::sprintf(R"(,"%s":"%s")", args[i].first.c_str(), args[i].second.c_str()); + } + oss << "}"; + return oss.str(); +} + +void EventBuffer::add(QueryTraceEvent&& event) { + std::lock_guard<std::mutex> l(_mutex); + _buffer.emplace_back(std::move(event)); +} + +#ifdef ENABLE_QUERY_DEBUG_TRACE +QueryTrace::QueryTrace(const TUniqueId& query_id, bool is_enable) : _query_id(query_id), _is_enable(is_enable) { + if (_is_enable) { + _start_ts = MonotonicMicros(); + } +} +#else +QueryTrace::QueryTrace(const TUniqueId& query_id, bool is_enable) {} +#endif + +void QueryTrace::register_tasks(const TUniqueId& fragment_instance_id, pipeline::PipelineTasks& tasks) { Review Comment: warning: out-of-line definition of 'register_tasks' does not match any declaration in 'doris::debug::QueryTrace' [clang-diagnostic-error] ```cpp void QueryTrace::register_tasks(const TUniqueId& fragment_instance_id, pipeline::PipelineTasks& tasks) { ^ ``` ########## be/src/util/debug/tracing.cpp: ########## @@ -0,0 +1,207 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include <gen_cpp/Opcodes_types.h> +#include <filesystem> +#include <fstream> +#include <mutex> +#include <utility> + +#include "common/config.h" +#include "pipeline/pipeline_task.h" +#include "fmt/printf.h" +#include "util/debug/tracing.h" +#include "util/time.h" + +namespace doris::debug { + +QueryTraceEvent QueryTraceEvent::create(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, + std::vector<std::pair<std::string, std::string>>&& args) { + QueryTraceEvent event; + event.name = name; + event.category = category; + event.id = id; + event.phase = phase; + event.start_time = timestamp; + event.duration = duration; + event.instance_id = instance_id; + event.task = task; + event.args = std::move(args); + event.thread_id = std::this_thread::get_id(); + return event; +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, const QueryTraceContext& ctx) { + return create(name, category, id, phase, MonotonicMicros() - ctx.start_ts, QueryTraceContext::DEFAULT_EVENT_ID, + ctx.fragment_instance_id, ctx.task, {}); +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, int64_t start_ts, int64_t duration, + const QueryTraceContext& ctx) { + return create(name, category, id, phase, start_ts, duration, ctx.fragment_instance_id, ctx.task, {}); +} + +static const char* kSimpleEventFormat = + R"({"cat":"%s","name":"%s","pid":"0x%x","tid":"0x%x","id":"0x%x","ts":%ld,"ph":"%c","args":%s,"tidx":"0x%x"})"; +static const char* kCompleteEventFormat = + R"({"cat":"%s","name":"%s","pid":"0x%x","tid":"0x%x","id":"0x%x","ts":%ld,"dur":%ld,"ph":"%c","args":%s,"tidx":"0x%x"})"; + +std::string QueryTraceEvent::to_string() { + std::string args_str = args_to_string(); + size_t tidx = std::hash<std::thread::id>{}(thread_id); + + // transform a raw pointer to uint64_t by re_cast is maybe not the best way, but at least unambiguous & safe. + if (phase == 'X') { + return fmt::sprintf(kCompleteEventFormat, category.c_str(), name.c_str(), (uint64_t)instance_id, + reinterpret_cast<uint64_t>(task), id, start_time, duration, phase, args_str.c_str(), tidx); + } else { + return fmt::sprintf(kSimpleEventFormat, category.c_str(), name.c_str(), (uint64_t)instance_id, + reinterpret_cast<uint64_t>(task), id, start_time, phase, args_str.c_str(), tidx); + } +} + +std::string QueryTraceEvent::args_to_string() { + if (args.empty()) { + return "{}"; + } + std::ostringstream oss; + oss << "{"; + oss << fmt::sprintf(R"("%s":"%s")", args[0].first.c_str(), args[0].second.c_str()); + for (size_t i = 1; i < args.size(); i++) { + oss << fmt::sprintf(R"(,"%s":"%s")", args[i].first.c_str(), args[i].second.c_str()); + } + oss << "}"; + return oss.str(); +} + +void EventBuffer::add(QueryTraceEvent&& event) { + std::lock_guard<std::mutex> l(_mutex); + _buffer.emplace_back(std::move(event)); +} + +#ifdef ENABLE_QUERY_DEBUG_TRACE +QueryTrace::QueryTrace(const TUniqueId& query_id, bool is_enable) : _query_id(query_id), _is_enable(is_enable) { + if (_is_enable) { + _start_ts = MonotonicMicros(); + } +} +#else +QueryTrace::QueryTrace(const TUniqueId& query_id, bool is_enable) {} +#endif + +void QueryTrace::register_tasks(const TUniqueId& fragment_instance_id, pipeline::PipelineTasks& tasks) { +#ifdef ENABLE_QUERY_DEBUG_TRACE + if (!_is_enable) { + return; + } + std::unique_lock l(_mutex); + auto iter = _fragment_tasks.find(fragment_instance_id); + if (iter == _fragment_tasks.end()) { + _fragment_tasks.emplace(fragment_instance_id, + std::make_shared<std::unordered_set<pipeline::PipelineTaskRawPtr>>()); + iter = _fragment_tasks.find(fragment_instance_id); + } + for (auto& task : tasks) { + iter->second->insert(task); // into this fragment's task map. + _buffers.emplace(task, std::make_unique<EventBuffer>()); + } +#endif +} + +Status QueryTrace::dump() { +#ifdef ENABLE_QUERY_DEBUG_TRACE + if (!_is_enable) { + return Status::OK(); + } + static const char* kProcessNameMetaEventFormat = + "{\"name\":\"process_name\",\"ph\":\"M\",\"pid\":\"0x%x\",\"args\":{\"name\":\"%s\"}}"; + static const char* kThreadNameMetaEventFormat = + "{\"name\":\"thread_name\",\"ph\":\"M\",\"pid\":\"0x%x\",\"tid\":\"0x%x\",\"args\":{\"name\":\"%s\"}}"; + try { + std::filesystem::create_directory(doris::config::tracing_dir); + std::string file_name = + fmt::format("{}/{}.json", doris::config::tracing_dir, print_id(_query_id)); + std::ofstream oss(file_name.c_str(), std::ios::out | std::ios::binary); + oss << "{\"traceEvents\":[\n"; + bool is_first = true; + for (auto& [fragment_id, task_set] : _fragment_tasks) { + std::string fragment_id_str = print_id(fragment_id); + oss << (is_first ? "" : ",\n"); + oss << fmt::sprintf(kProcessNameMetaEventFormat, (uint64_t)fragment_id.lo, fragment_id_str.c_str()); + is_first = false; + for (auto& task : *task_set) { + pipeline::PipelineTaskRawPtr ptr = reinterpret_cast<pipeline::PipelineTaskRawPtr>(task); + oss << (is_first ? "" : ",\n"); + oss << fmt::sprintf(kThreadNameMetaEventFormat, (uint64_t)fragment_id.lo, reinterpret_cast<uint64_t>(task.get()), + std::to_string(ptr->get_id())); + } + } + + for (auto& [_, buffer_ptr] : _buffers) { + auto& buffer = buffer_ptr->_buffer; + for (auto iter : buffer) { + oss << (is_first ? "" : ",\n"); + oss << iter.to_string(); + } + } + oss << "\n]}"; + + oss.close(); + } catch (std::exception& e) { + return Status::IOError(fmt::format("dump trace log error {}", e.what())); + } +#endif + return Status::OK(); +} + +void QueryTrace::set_tls_trace_context(QueryTrace* query_trace, const TUniqueId& fragment_instance_id, + pipeline::PipelineTaskRawPtr task) { +#ifdef ENABLE_QUERY_DEBUG_TRACE + if (!query_trace->_is_enable) { + tls_trace_ctx.reset(); + return; + } + { + std::shared_lock l(query_trace->_mutex); + auto iter = query_trace->_buffers.find(task); + DCHECK(iter != query_trace->_buffers.end()); Review Comment: warning: no member named '_buffers' in 'doris::debug::QueryTrace' [clang-diagnostic-error] ```cpp DCHECK(iter != query_trace->_buffers.end()); ^ ``` **thirdparty/installed/include/glog/logging.h:1043:** expanded from macro 'DCHECK' ```cpp GLOG_MSVC_POP_WARNING() CHECK(condition) ^ ``` **thirdparty/installed/include/glog/logging.h:592:** expanded from macro 'CHECK' ```cpp LOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \ ^ ``` **thirdparty/installed/include/glog/logging.h:131:** expanded from macro 'GOOGLE_PREDICT_BRANCH_NOT_TAKEN' ```cpp #define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) (__builtin_expect(x, 0)) ^ ``` **thirdparty/installed/include/glog/logging.h:577:** expanded from macro 'LOG_IF' ```cpp !(condition) ? (void) 0 : google::LogMessageVoidify() & LOG(severity) ^ ``` ########## be/src/util/debug/tracing.h: ########## @@ -0,0 +1,166 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once + +#include <deque> +#include <list> +#include <map> +#include <memory> +#include <mutex> +#include <shared_mutex> +#include <string> +#include <unordered_set> +#include <thread> + +#include "common/status.h" +#include "gen_cpp/Types_types.h" +#include "pipeline/pipeline_task.h" + +#define ENABLE_QUERY_DEBUG_TRACE + +// The whole function is control by marco in building. if the function is turned off, no entity will be construct. +// In another word, it's zero-overhead. + +namespace doris::debug { + +class QueryTraceContext; + +/// QueryTraceEvent saves a specific event info. +class QueryTraceEvent { +public: + std::string name; + std::string category; + int64_t id; // used for async event + char phase; // type of event + int64_t start_time; + int64_t duration = -1; // used only in compelete event + decltype(TUniqueId::lo) instance_id; + pipeline::PipelineTaskRawPtr task; // task pointer + std::thread::id thread_id; + std::vector<std::pair<std::string, std::string>> args; + + std::string to_string(); + + static QueryTraceEvent create(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, + std::vector<std::pair<std::string, std::string>>&& args); + + static QueryTraceEvent create_with_ctx(const std::string& name, const std::string& category, int64_t id, char phase, + const QueryTraceContext& ctx); + + static QueryTraceEvent create_with_ctx(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t start_ts, int64_t duration, const QueryTraceContext& ctx); + +private: + std::string args_to_string(); +}; + +/// Event buffer for a single PipelineTask. +/// didn't store in threads' own trace_context, but in QueryTrace. +class EventBuffer { +public: + EventBuffer() = default; + ~EventBuffer() = default; + + void add(QueryTraceEvent&& event); + +private: + friend class QueryTrace; + std::mutex _mutex; + std::deque<QueryTraceEvent> _buffer; +}; + +class QueryTrace { +public: + QueryTrace(const TUniqueId& query_id, bool is_enable); + ~QueryTrace() = default; + + // init event buffer for all tasks in a single fragment instance + void register_tasks(const TUniqueId& fragment_instance_id, pipeline::PipelineTasks& tasks); + + Status dump(); + + static void set_tls_trace_context(QueryTrace* query_trace, const TUniqueId& fragment_instance_id, + pipeline::PipelineTaskRawPtr task); Review Comment: warning: no type named 'PipelineTaskRawPtr' in namespace 'doris::pipeline' [clang-diagnostic-error] ```cpp pipeline::PipelineTaskRawPtr task); ^ ``` ########## be/src/util/debug/tracing.cpp: ########## @@ -0,0 +1,207 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include <gen_cpp/Opcodes_types.h> +#include <filesystem> +#include <fstream> +#include <mutex> +#include <utility> + +#include "common/config.h" +#include "pipeline/pipeline_task.h" +#include "fmt/printf.h" +#include "util/debug/tracing.h" +#include "util/time.h" + +namespace doris::debug { + +QueryTraceEvent QueryTraceEvent::create(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, + std::vector<std::pair<std::string, std::string>>&& args) { + QueryTraceEvent event; + event.name = name; + event.category = category; + event.id = id; + event.phase = phase; + event.start_time = timestamp; + event.duration = duration; + event.instance_id = instance_id; + event.task = task; + event.args = std::move(args); + event.thread_id = std::this_thread::get_id(); + return event; +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, const QueryTraceContext& ctx) { + return create(name, category, id, phase, MonotonicMicros() - ctx.start_ts, QueryTraceContext::DEFAULT_EVENT_ID, + ctx.fragment_instance_id, ctx.task, {}); +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, int64_t start_ts, int64_t duration, + const QueryTraceContext& ctx) { + return create(name, category, id, phase, start_ts, duration, ctx.fragment_instance_id, ctx.task, {}); +} + +static const char* kSimpleEventFormat = + R"({"cat":"%s","name":"%s","pid":"0x%x","tid":"0x%x","id":"0x%x","ts":%ld,"ph":"%c","args":%s,"tidx":"0x%x"})"; +static const char* kCompleteEventFormat = + R"({"cat":"%s","name":"%s","pid":"0x%x","tid":"0x%x","id":"0x%x","ts":%ld,"dur":%ld,"ph":"%c","args":%s,"tidx":"0x%x"})"; + +std::string QueryTraceEvent::to_string() { + std::string args_str = args_to_string(); + size_t tidx = std::hash<std::thread::id>{}(thread_id); + + // transform a raw pointer to uint64_t by re_cast is maybe not the best way, but at least unambiguous & safe. + if (phase == 'X') { + return fmt::sprintf(kCompleteEventFormat, category.c_str(), name.c_str(), (uint64_t)instance_id, + reinterpret_cast<uint64_t>(task), id, start_time, duration, phase, args_str.c_str(), tidx); + } else { + return fmt::sprintf(kSimpleEventFormat, category.c_str(), name.c_str(), (uint64_t)instance_id, + reinterpret_cast<uint64_t>(task), id, start_time, phase, args_str.c_str(), tidx); + } +} + +std::string QueryTraceEvent::args_to_string() { + if (args.empty()) { + return "{}"; + } + std::ostringstream oss; + oss << "{"; + oss << fmt::sprintf(R"("%s":"%s")", args[0].first.c_str(), args[0].second.c_str()); + for (size_t i = 1; i < args.size(); i++) { + oss << fmt::sprintf(R"(,"%s":"%s")", args[i].first.c_str(), args[i].second.c_str()); + } + oss << "}"; + return oss.str(); +} + +void EventBuffer::add(QueryTraceEvent&& event) { + std::lock_guard<std::mutex> l(_mutex); + _buffer.emplace_back(std::move(event)); +} + +#ifdef ENABLE_QUERY_DEBUG_TRACE +QueryTrace::QueryTrace(const TUniqueId& query_id, bool is_enable) : _query_id(query_id), _is_enable(is_enable) { + if (_is_enable) { + _start_ts = MonotonicMicros(); + } +} +#else +QueryTrace::QueryTrace(const TUniqueId& query_id, bool is_enable) {} +#endif + +void QueryTrace::register_tasks(const TUniqueId& fragment_instance_id, pipeline::PipelineTasks& tasks) { +#ifdef ENABLE_QUERY_DEBUG_TRACE + if (!_is_enable) { + return; + } + std::unique_lock l(_mutex); + auto iter = _fragment_tasks.find(fragment_instance_id); + if (iter == _fragment_tasks.end()) { + _fragment_tasks.emplace(fragment_instance_id, + std::make_shared<std::unordered_set<pipeline::PipelineTaskRawPtr>>()); + iter = _fragment_tasks.find(fragment_instance_id); + } + for (auto& task : tasks) { + iter->second->insert(task); // into this fragment's task map. + _buffers.emplace(task, std::make_unique<EventBuffer>()); + } +#endif +} + +Status QueryTrace::dump() { +#ifdef ENABLE_QUERY_DEBUG_TRACE + if (!_is_enable) { + return Status::OK(); + } + static const char* kProcessNameMetaEventFormat = + "{\"name\":\"process_name\",\"ph\":\"M\",\"pid\":\"0x%x\",\"args\":{\"name\":\"%s\"}}"; + static const char* kThreadNameMetaEventFormat = + "{\"name\":\"thread_name\",\"ph\":\"M\",\"pid\":\"0x%x\",\"tid\":\"0x%x\",\"args\":{\"name\":\"%s\"}}"; + try { + std::filesystem::create_directory(doris::config::tracing_dir); + std::string file_name = + fmt::format("{}/{}.json", doris::config::tracing_dir, print_id(_query_id)); + std::ofstream oss(file_name.c_str(), std::ios::out | std::ios::binary); + oss << "{\"traceEvents\":[\n"; + bool is_first = true; + for (auto& [fragment_id, task_set] : _fragment_tasks) { + std::string fragment_id_str = print_id(fragment_id); + oss << (is_first ? "" : ",\n"); + oss << fmt::sprintf(kProcessNameMetaEventFormat, (uint64_t)fragment_id.lo, fragment_id_str.c_str()); + is_first = false; + for (auto& task : *task_set) { + pipeline::PipelineTaskRawPtr ptr = reinterpret_cast<pipeline::PipelineTaskRawPtr>(task); + oss << (is_first ? "" : ",\n"); + oss << fmt::sprintf(kThreadNameMetaEventFormat, (uint64_t)fragment_id.lo, reinterpret_cast<uint64_t>(task.get()), + std::to_string(ptr->get_id())); + } + } + + for (auto& [_, buffer_ptr] : _buffers) { + auto& buffer = buffer_ptr->_buffer; + for (auto iter : buffer) { + oss << (is_first ? "" : ",\n"); + oss << iter.to_string(); + } + } + oss << "\n]}"; + + oss.close(); + } catch (std::exception& e) { + return Status::IOError(fmt::format("dump trace log error {}", e.what())); + } +#endif + return Status::OK(); +} + +void QueryTrace::set_tls_trace_context(QueryTrace* query_trace, const TUniqueId& fragment_instance_id, + pipeline::PipelineTaskRawPtr task) { +#ifdef ENABLE_QUERY_DEBUG_TRACE + if (!query_trace->_is_enable) { + tls_trace_ctx.reset(); Review Comment: warning: member reference base type 'debug::QueryTraceContext &()' is not a structure or union [clang-diagnostic-error] ```cpp tls_trace_ctx.reset(); ^ ``` ########## be/src/runtime/thread_context.h: ########## @@ -182,15 +191,24 @@ class ThreadContext { // to nullptr, but the object it points to is not initialized. At this time, when the memory // is released somewhere, the TCMalloc hook is triggered to cause the crash. std::unique_ptr<ThreadMemTrackerMgr> thread_mem_tracker_mgr; - MemTrackerLimiter* thread_mem_tracker() { + MemTrackerLimiter* thread_mem_tracker() const { return thread_mem_tracker_mgr->limiter_mem_tracker_raw(); } + // any time we need the trace context of current query/fragment/task..., tls_trace_ctx is it. +#ifdef ENABLE_QUERY_DEBUG_TRACE + debug::QueryTraceContext tls_trace_ctx; +#endif + private: - std::string _task_id = ""; + std::string _task_id; TUniqueId _fragment_instance_id; }; +debug::QueryTraceContext& get_tls_trace_ctx(){ Review Comment: warning: no type named 'QueryTraceContext' in namespace 'doris::debug' [clang-diagnostic-error] ```cpp debug::QueryTraceContext& get_tls_trace_ctx(){ ^ ``` ########## be/src/util/debug/tracing.cpp: ########## @@ -0,0 +1,207 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include <gen_cpp/Opcodes_types.h> +#include <filesystem> +#include <fstream> +#include <mutex> +#include <utility> + +#include "common/config.h" +#include "pipeline/pipeline_task.h" +#include "fmt/printf.h" +#include "util/debug/tracing.h" +#include "util/time.h" + +namespace doris::debug { + +QueryTraceEvent QueryTraceEvent::create(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, + std::vector<std::pair<std::string, std::string>>&& args) { + QueryTraceEvent event; + event.name = name; + event.category = category; + event.id = id; + event.phase = phase; + event.start_time = timestamp; + event.duration = duration; + event.instance_id = instance_id; + event.task = task; + event.args = std::move(args); + event.thread_id = std::this_thread::get_id(); + return event; +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, const QueryTraceContext& ctx) { + return create(name, category, id, phase, MonotonicMicros() - ctx.start_ts, QueryTraceContext::DEFAULT_EVENT_ID, Review Comment: warning: call to non-static member function without an object argument [clang-diagnostic-error] ```cpp return create(name, category, id, phase, MonotonicMicros() - ctx.start_ts, QueryTraceContext::DEFAULT_EVENT_ID, ^ ``` ########## be/src/util/debug/tracing.cpp: ########## @@ -0,0 +1,207 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include <gen_cpp/Opcodes_types.h> +#include <filesystem> +#include <fstream> +#include <mutex> +#include <utility> + +#include "common/config.h" +#include "pipeline/pipeline_task.h" +#include "fmt/printf.h" +#include "util/debug/tracing.h" +#include "util/time.h" + +namespace doris::debug { + +QueryTraceEvent QueryTraceEvent::create(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, + std::vector<std::pair<std::string, std::string>>&& args) { + QueryTraceEvent event; + event.name = name; + event.category = category; + event.id = id; + event.phase = phase; + event.start_time = timestamp; + event.duration = duration; + event.instance_id = instance_id; + event.task = task; + event.args = std::move(args); + event.thread_id = std::this_thread::get_id(); + return event; +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, const QueryTraceContext& ctx) { + return create(name, category, id, phase, MonotonicMicros() - ctx.start_ts, QueryTraceContext::DEFAULT_EVENT_ID, + ctx.fragment_instance_id, ctx.task, {}); +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, int64_t start_ts, int64_t duration, + const QueryTraceContext& ctx) { + return create(name, category, id, phase, start_ts, duration, ctx.fragment_instance_id, ctx.task, {}); Review Comment: warning: call to non-static member function without an object argument [clang-diagnostic-error] ```cpp return create(name, category, id, phase, start_ts, duration, ctx.fragment_instance_id, ctx.task, {}); ^ ``` ########## be/src/util/debug/tracing.cpp: ########## @@ -0,0 +1,207 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include <gen_cpp/Opcodes_types.h> +#include <filesystem> +#include <fstream> +#include <mutex> +#include <utility> + +#include "common/config.h" +#include "pipeline/pipeline_task.h" +#include "fmt/printf.h" +#include "util/debug/tracing.h" +#include "util/time.h" + +namespace doris::debug { + +QueryTraceEvent QueryTraceEvent::create(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, + std::vector<std::pair<std::string, std::string>>&& args) { + QueryTraceEvent event; + event.name = name; + event.category = category; + event.id = id; + event.phase = phase; + event.start_time = timestamp; + event.duration = duration; + event.instance_id = instance_id; + event.task = task; + event.args = std::move(args); + event.thread_id = std::this_thread::get_id(); + return event; +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, const QueryTraceContext& ctx) { + return create(name, category, id, phase, MonotonicMicros() - ctx.start_ts, QueryTraceContext::DEFAULT_EVENT_ID, + ctx.fragment_instance_id, ctx.task, {}); +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, int64_t start_ts, int64_t duration, + const QueryTraceContext& ctx) { + return create(name, category, id, phase, start_ts, duration, ctx.fragment_instance_id, ctx.task, {}); +} + +static const char* kSimpleEventFormat = + R"({"cat":"%s","name":"%s","pid":"0x%x","tid":"0x%x","id":"0x%x","ts":%ld,"ph":"%c","args":%s,"tidx":"0x%x"})"; +static const char* kCompleteEventFormat = + R"({"cat":"%s","name":"%s","pid":"0x%x","tid":"0x%x","id":"0x%x","ts":%ld,"dur":%ld,"ph":"%c","args":%s,"tidx":"0x%x"})"; + +std::string QueryTraceEvent::to_string() { + std::string args_str = args_to_string(); + size_t tidx = std::hash<std::thread::id>{}(thread_id); + + // transform a raw pointer to uint64_t by re_cast is maybe not the best way, but at least unambiguous & safe. + if (phase == 'X') { + return fmt::sprintf(kCompleteEventFormat, category.c_str(), name.c_str(), (uint64_t)instance_id, + reinterpret_cast<uint64_t>(task), id, start_time, duration, phase, args_str.c_str(), tidx); + } else { + return fmt::sprintf(kSimpleEventFormat, category.c_str(), name.c_str(), (uint64_t)instance_id, + reinterpret_cast<uint64_t>(task), id, start_time, phase, args_str.c_str(), tidx); + } +} + +std::string QueryTraceEvent::args_to_string() { + if (args.empty()) { + return "{}"; + } + std::ostringstream oss; + oss << "{"; + oss << fmt::sprintf(R"("%s":"%s")", args[0].first.c_str(), args[0].second.c_str()); + for (size_t i = 1; i < args.size(); i++) { + oss << fmt::sprintf(R"(,"%s":"%s")", args[i].first.c_str(), args[i].second.c_str()); + } + oss << "}"; + return oss.str(); +} + +void EventBuffer::add(QueryTraceEvent&& event) { + std::lock_guard<std::mutex> l(_mutex); + _buffer.emplace_back(std::move(event)); +} + +#ifdef ENABLE_QUERY_DEBUG_TRACE +QueryTrace::QueryTrace(const TUniqueId& query_id, bool is_enable) : _query_id(query_id), _is_enable(is_enable) { + if (_is_enable) { + _start_ts = MonotonicMicros(); + } +} +#else +QueryTrace::QueryTrace(const TUniqueId& query_id, bool is_enable) {} +#endif + +void QueryTrace::register_tasks(const TUniqueId& fragment_instance_id, pipeline::PipelineTasks& tasks) { +#ifdef ENABLE_QUERY_DEBUG_TRACE + if (!_is_enable) { + return; + } + std::unique_lock l(_mutex); + auto iter = _fragment_tasks.find(fragment_instance_id); + if (iter == _fragment_tasks.end()) { + _fragment_tasks.emplace(fragment_instance_id, + std::make_shared<std::unordered_set<pipeline::PipelineTaskRawPtr>>()); + iter = _fragment_tasks.find(fragment_instance_id); + } + for (auto& task : tasks) { + iter->second->insert(task); // into this fragment's task map. + _buffers.emplace(task, std::make_unique<EventBuffer>()); + } +#endif +} + +Status QueryTrace::dump() { +#ifdef ENABLE_QUERY_DEBUG_TRACE + if (!_is_enable) { + return Status::OK(); + } + static const char* kProcessNameMetaEventFormat = + "{\"name\":\"process_name\",\"ph\":\"M\",\"pid\":\"0x%x\",\"args\":{\"name\":\"%s\"}}"; + static const char* kThreadNameMetaEventFormat = + "{\"name\":\"thread_name\",\"ph\":\"M\",\"pid\":\"0x%x\",\"tid\":\"0x%x\",\"args\":{\"name\":\"%s\"}}"; + try { + std::filesystem::create_directory(doris::config::tracing_dir); + std::string file_name = + fmt::format("{}/{}.json", doris::config::tracing_dir, print_id(_query_id)); + std::ofstream oss(file_name.c_str(), std::ios::out | std::ios::binary); + oss << "{\"traceEvents\":[\n"; + bool is_first = true; + for (auto& [fragment_id, task_set] : _fragment_tasks) { + std::string fragment_id_str = print_id(fragment_id); + oss << (is_first ? "" : ",\n"); + oss << fmt::sprintf(kProcessNameMetaEventFormat, (uint64_t)fragment_id.lo, fragment_id_str.c_str()); + is_first = false; + for (auto& task : *task_set) { + pipeline::PipelineTaskRawPtr ptr = reinterpret_cast<pipeline::PipelineTaskRawPtr>(task); + oss << (is_first ? "" : ",\n"); + oss << fmt::sprintf(kThreadNameMetaEventFormat, (uint64_t)fragment_id.lo, reinterpret_cast<uint64_t>(task.get()), + std::to_string(ptr->get_id())); + } + } + + for (auto& [_, buffer_ptr] : _buffers) { Review Comment: warning: use of undeclared identifier '_buffers' [clang-diagnostic-error] ```cpp for (auto& [_, buffer_ptr] : _buffers) { ^ ``` ########## be/src/util/debug/tracing.h: ########## @@ -0,0 +1,166 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once + +#include <deque> +#include <list> +#include <map> +#include <memory> +#include <mutex> +#include <shared_mutex> +#include <string> +#include <unordered_set> +#include <thread> + +#include "common/status.h" +#include "gen_cpp/Types_types.h" +#include "pipeline/pipeline_task.h" + +#define ENABLE_QUERY_DEBUG_TRACE + +// The whole function is control by marco in building. if the function is turned off, no entity will be construct. +// In another word, it's zero-overhead. + +namespace doris::debug { + +class QueryTraceContext; + +/// QueryTraceEvent saves a specific event info. +class QueryTraceEvent { +public: + std::string name; + std::string category; + int64_t id; // used for async event + char phase; // type of event + int64_t start_time; + int64_t duration = -1; // used only in compelete event + decltype(TUniqueId::lo) instance_id; + pipeline::PipelineTaskRawPtr task; // task pointer + std::thread::id thread_id; + std::vector<std::pair<std::string, std::string>> args; + + std::string to_string(); + + static QueryTraceEvent create(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, + std::vector<std::pair<std::string, std::string>>&& args); + + static QueryTraceEvent create_with_ctx(const std::string& name, const std::string& category, int64_t id, char phase, + const QueryTraceContext& ctx); + + static QueryTraceEvent create_with_ctx(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t start_ts, int64_t duration, const QueryTraceContext& ctx); + +private: + std::string args_to_string(); +}; + +/// Event buffer for a single PipelineTask. +/// didn't store in threads' own trace_context, but in QueryTrace. +class EventBuffer { +public: + EventBuffer() = default; + ~EventBuffer() = default; + + void add(QueryTraceEvent&& event); + +private: + friend class QueryTrace; + std::mutex _mutex; + std::deque<QueryTraceEvent> _buffer; +}; + +class QueryTrace { +public: + QueryTrace(const TUniqueId& query_id, bool is_enable); + ~QueryTrace() = default; + + // init event buffer for all tasks in a single fragment instance + void register_tasks(const TUniqueId& fragment_instance_id, pipeline::PipelineTasks& tasks); Review Comment: warning: no type named 'PipelineTasks' in namespace 'doris::pipeline' [clang-diagnostic-error] ```cpp void register_tasks(const TUniqueId& fragment_instance_id, pipeline::PipelineTasks& tasks); ^ ``` ########## be/src/util/debug/tracing.cpp: ########## @@ -0,0 +1,207 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include <gen_cpp/Opcodes_types.h> +#include <filesystem> +#include <fstream> +#include <mutex> +#include <utility> + +#include "common/config.h" +#include "pipeline/pipeline_task.h" +#include "fmt/printf.h" +#include "util/debug/tracing.h" +#include "util/time.h" + +namespace doris::debug { + +QueryTraceEvent QueryTraceEvent::create(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, + std::vector<std::pair<std::string, std::string>>&& args) { + QueryTraceEvent event; + event.name = name; + event.category = category; + event.id = id; + event.phase = phase; + event.start_time = timestamp; + event.duration = duration; + event.instance_id = instance_id; + event.task = task; + event.args = std::move(args); + event.thread_id = std::this_thread::get_id(); + return event; +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, const QueryTraceContext& ctx) { + return create(name, category, id, phase, MonotonicMicros() - ctx.start_ts, QueryTraceContext::DEFAULT_EVENT_ID, + ctx.fragment_instance_id, ctx.task, {}); +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, int64_t start_ts, int64_t duration, + const QueryTraceContext& ctx) { + return create(name, category, id, phase, start_ts, duration, ctx.fragment_instance_id, ctx.task, {}); +} + +static const char* kSimpleEventFormat = + R"({"cat":"%s","name":"%s","pid":"0x%x","tid":"0x%x","id":"0x%x","ts":%ld,"ph":"%c","args":%s,"tidx":"0x%x"})"; +static const char* kCompleteEventFormat = + R"({"cat":"%s","name":"%s","pid":"0x%x","tid":"0x%x","id":"0x%x","ts":%ld,"dur":%ld,"ph":"%c","args":%s,"tidx":"0x%x"})"; + +std::string QueryTraceEvent::to_string() { + std::string args_str = args_to_string(); + size_t tidx = std::hash<std::thread::id>{}(thread_id); + + // transform a raw pointer to uint64_t by re_cast is maybe not the best way, but at least unambiguous & safe. + if (phase == 'X') { + return fmt::sprintf(kCompleteEventFormat, category.c_str(), name.c_str(), (uint64_t)instance_id, + reinterpret_cast<uint64_t>(task), id, start_time, duration, phase, args_str.c_str(), tidx); + } else { + return fmt::sprintf(kSimpleEventFormat, category.c_str(), name.c_str(), (uint64_t)instance_id, + reinterpret_cast<uint64_t>(task), id, start_time, phase, args_str.c_str(), tidx); + } +} + +std::string QueryTraceEvent::args_to_string() { + if (args.empty()) { + return "{}"; + } + std::ostringstream oss; + oss << "{"; + oss << fmt::sprintf(R"("%s":"%s")", args[0].first.c_str(), args[0].second.c_str()); + for (size_t i = 1; i < args.size(); i++) { + oss << fmt::sprintf(R"(,"%s":"%s")", args[i].first.c_str(), args[i].second.c_str()); + } + oss << "}"; + return oss.str(); +} + +void EventBuffer::add(QueryTraceEvent&& event) { + std::lock_guard<std::mutex> l(_mutex); + _buffer.emplace_back(std::move(event)); +} + +#ifdef ENABLE_QUERY_DEBUG_TRACE +QueryTrace::QueryTrace(const TUniqueId& query_id, bool is_enable) : _query_id(query_id), _is_enable(is_enable) { + if (_is_enable) { + _start_ts = MonotonicMicros(); + } +} +#else +QueryTrace::QueryTrace(const TUniqueId& query_id, bool is_enable) {} +#endif + +void QueryTrace::register_tasks(const TUniqueId& fragment_instance_id, pipeline::PipelineTasks& tasks) { +#ifdef ENABLE_QUERY_DEBUG_TRACE + if (!_is_enable) { + return; + } + std::unique_lock l(_mutex); + auto iter = _fragment_tasks.find(fragment_instance_id); + if (iter == _fragment_tasks.end()) { + _fragment_tasks.emplace(fragment_instance_id, + std::make_shared<std::unordered_set<pipeline::PipelineTaskRawPtr>>()); + iter = _fragment_tasks.find(fragment_instance_id); + } + for (auto& task : tasks) { + iter->second->insert(task); // into this fragment's task map. + _buffers.emplace(task, std::make_unique<EventBuffer>()); + } +#endif +} + +Status QueryTrace::dump() { +#ifdef ENABLE_QUERY_DEBUG_TRACE + if (!_is_enable) { + return Status::OK(); + } + static const char* kProcessNameMetaEventFormat = + "{\"name\":\"process_name\",\"ph\":\"M\",\"pid\":\"0x%x\",\"args\":{\"name\":\"%s\"}}"; + static const char* kThreadNameMetaEventFormat = + "{\"name\":\"thread_name\",\"ph\":\"M\",\"pid\":\"0x%x\",\"tid\":\"0x%x\",\"args\":{\"name\":\"%s\"}}"; + try { + std::filesystem::create_directory(doris::config::tracing_dir); + std::string file_name = + fmt::format("{}/{}.json", doris::config::tracing_dir, print_id(_query_id)); + std::ofstream oss(file_name.c_str(), std::ios::out | std::ios::binary); + oss << "{\"traceEvents\":[\n"; + bool is_first = true; + for (auto& [fragment_id, task_set] : _fragment_tasks) { + std::string fragment_id_str = print_id(fragment_id); + oss << (is_first ? "" : ",\n"); + oss << fmt::sprintf(kProcessNameMetaEventFormat, (uint64_t)fragment_id.lo, fragment_id_str.c_str()); + is_first = false; + for (auto& task : *task_set) { + pipeline::PipelineTaskRawPtr ptr = reinterpret_cast<pipeline::PipelineTaskRawPtr>(task); + oss << (is_first ? "" : ",\n"); + oss << fmt::sprintf(kThreadNameMetaEventFormat, (uint64_t)fragment_id.lo, reinterpret_cast<uint64_t>(task.get()), + std::to_string(ptr->get_id())); + } + } + + for (auto& [_, buffer_ptr] : _buffers) { + auto& buffer = buffer_ptr->_buffer; + for (auto iter : buffer) { + oss << (is_first ? "" : ",\n"); + oss << iter.to_string(); + } + } + oss << "\n]}"; + + oss.close(); + } catch (std::exception& e) { + return Status::IOError(fmt::format("dump trace log error {}", e.what())); + } +#endif + return Status::OK(); +} + +void QueryTrace::set_tls_trace_context(QueryTrace* query_trace, const TUniqueId& fragment_instance_id, + pipeline::PipelineTaskRawPtr task) { +#ifdef ENABLE_QUERY_DEBUG_TRACE + if (!query_trace->_is_enable) { + tls_trace_ctx.reset(); Review Comment: warning: use of undeclared identifier 'tls_trace_ctx'; did you mean 'get_tls_trace_ctx'? [clang-diagnostic-error] ```suggestion get_tls_trace_ctx.reset(); ``` **be/src/runtime/thread_context.h:207:** 'get_tls_trace_ctx' declared here ```cpp debug::QueryTraceContext& get_tls_trace_ctx(){ ^ ``` ########## be/src/util/debug/tracing.cpp: ########## @@ -0,0 +1,207 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include <gen_cpp/Opcodes_types.h> +#include <filesystem> +#include <fstream> +#include <mutex> +#include <utility> + +#include "common/config.h" +#include "pipeline/pipeline_task.h" +#include "fmt/printf.h" +#include "util/debug/tracing.h" +#include "util/time.h" + +namespace doris::debug { + +QueryTraceEvent QueryTraceEvent::create(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, + std::vector<std::pair<std::string, std::string>>&& args) { + QueryTraceEvent event; + event.name = name; + event.category = category; + event.id = id; + event.phase = phase; + event.start_time = timestamp; + event.duration = duration; + event.instance_id = instance_id; + event.task = task; + event.args = std::move(args); + event.thread_id = std::this_thread::get_id(); + return event; +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, const QueryTraceContext& ctx) { + return create(name, category, id, phase, MonotonicMicros() - ctx.start_ts, QueryTraceContext::DEFAULT_EVENT_ID, + ctx.fragment_instance_id, ctx.task, {}); +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, int64_t start_ts, int64_t duration, + const QueryTraceContext& ctx) { + return create(name, category, id, phase, start_ts, duration, ctx.fragment_instance_id, ctx.task, {}); +} + +static const char* kSimpleEventFormat = + R"({"cat":"%s","name":"%s","pid":"0x%x","tid":"0x%x","id":"0x%x","ts":%ld,"ph":"%c","args":%s,"tidx":"0x%x"})"; +static const char* kCompleteEventFormat = + R"({"cat":"%s","name":"%s","pid":"0x%x","tid":"0x%x","id":"0x%x","ts":%ld,"dur":%ld,"ph":"%c","args":%s,"tidx":"0x%x"})"; + +std::string QueryTraceEvent::to_string() { + std::string args_str = args_to_string(); + size_t tidx = std::hash<std::thread::id>{}(thread_id); + + // transform a raw pointer to uint64_t by re_cast is maybe not the best way, but at least unambiguous & safe. + if (phase == 'X') { + return fmt::sprintf(kCompleteEventFormat, category.c_str(), name.c_str(), (uint64_t)instance_id, + reinterpret_cast<uint64_t>(task), id, start_time, duration, phase, args_str.c_str(), tidx); + } else { + return fmt::sprintf(kSimpleEventFormat, category.c_str(), name.c_str(), (uint64_t)instance_id, + reinterpret_cast<uint64_t>(task), id, start_time, phase, args_str.c_str(), tidx); + } +} + +std::string QueryTraceEvent::args_to_string() { + if (args.empty()) { + return "{}"; + } + std::ostringstream oss; + oss << "{"; + oss << fmt::sprintf(R"("%s":"%s")", args[0].first.c_str(), args[0].second.c_str()); + for (size_t i = 1; i < args.size(); i++) { + oss << fmt::sprintf(R"(,"%s":"%s")", args[i].first.c_str(), args[i].second.c_str()); + } + oss << "}"; + return oss.str(); +} + +void EventBuffer::add(QueryTraceEvent&& event) { + std::lock_guard<std::mutex> l(_mutex); + _buffer.emplace_back(std::move(event)); +} + +#ifdef ENABLE_QUERY_DEBUG_TRACE +QueryTrace::QueryTrace(const TUniqueId& query_id, bool is_enable) : _query_id(query_id), _is_enable(is_enable) { + if (_is_enable) { + _start_ts = MonotonicMicros(); + } +} +#else +QueryTrace::QueryTrace(const TUniqueId& query_id, bool is_enable) {} +#endif + +void QueryTrace::register_tasks(const TUniqueId& fragment_instance_id, pipeline::PipelineTasks& tasks) { +#ifdef ENABLE_QUERY_DEBUG_TRACE + if (!_is_enable) { + return; + } + std::unique_lock l(_mutex); + auto iter = _fragment_tasks.find(fragment_instance_id); + if (iter == _fragment_tasks.end()) { + _fragment_tasks.emplace(fragment_instance_id, + std::make_shared<std::unordered_set<pipeline::PipelineTaskRawPtr>>()); + iter = _fragment_tasks.find(fragment_instance_id); + } + for (auto& task : tasks) { + iter->second->insert(task); // into this fragment's task map. + _buffers.emplace(task, std::make_unique<EventBuffer>()); + } +#endif +} + +Status QueryTrace::dump() { +#ifdef ENABLE_QUERY_DEBUG_TRACE + if (!_is_enable) { + return Status::OK(); + } + static const char* kProcessNameMetaEventFormat = + "{\"name\":\"process_name\",\"ph\":\"M\",\"pid\":\"0x%x\",\"args\":{\"name\":\"%s\"}}"; + static const char* kThreadNameMetaEventFormat = + "{\"name\":\"thread_name\",\"ph\":\"M\",\"pid\":\"0x%x\",\"tid\":\"0x%x\",\"args\":{\"name\":\"%s\"}}"; + try { + std::filesystem::create_directory(doris::config::tracing_dir); + std::string file_name = + fmt::format("{}/{}.json", doris::config::tracing_dir, print_id(_query_id)); + std::ofstream oss(file_name.c_str(), std::ios::out | std::ios::binary); + oss << "{\"traceEvents\":[\n"; + bool is_first = true; + for (auto& [fragment_id, task_set] : _fragment_tasks) { + std::string fragment_id_str = print_id(fragment_id); + oss << (is_first ? "" : ",\n"); + oss << fmt::sprintf(kProcessNameMetaEventFormat, (uint64_t)fragment_id.lo, fragment_id_str.c_str()); + is_first = false; + for (auto& task : *task_set) { + pipeline::PipelineTaskRawPtr ptr = reinterpret_cast<pipeline::PipelineTaskRawPtr>(task); + oss << (is_first ? "" : ",\n"); + oss << fmt::sprintf(kThreadNameMetaEventFormat, (uint64_t)fragment_id.lo, reinterpret_cast<uint64_t>(task.get()), + std::to_string(ptr->get_id())); + } + } + + for (auto& [_, buffer_ptr] : _buffers) { + auto& buffer = buffer_ptr->_buffer; + for (auto iter : buffer) { + oss << (is_first ? "" : ",\n"); + oss << iter.to_string(); + } + } + oss << "\n]}"; + + oss.close(); + } catch (std::exception& e) { + return Status::IOError(fmt::format("dump trace log error {}", e.what())); + } +#endif + return Status::OK(); +} + +void QueryTrace::set_tls_trace_context(QueryTrace* query_trace, const TUniqueId& fragment_instance_id, Review Comment: warning: out-of-line definition of 'set_tls_trace_context' does not match any declaration in 'doris::debug::QueryTrace' [clang-diagnostic-error] ```cpp void QueryTrace::set_tls_trace_context(QueryTrace* query_trace, const TUniqueId& fragment_instance_id, ^ ``` ########## be/src/util/debug/tracing.cpp: ########## @@ -0,0 +1,207 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include <gen_cpp/Opcodes_types.h> +#include <filesystem> +#include <fstream> +#include <mutex> +#include <utility> + +#include "common/config.h" +#include "pipeline/pipeline_task.h" +#include "fmt/printf.h" +#include "util/debug/tracing.h" +#include "util/time.h" + +namespace doris::debug { + +QueryTraceEvent QueryTraceEvent::create(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, + std::vector<std::pair<std::string, std::string>>&& args) { + QueryTraceEvent event; + event.name = name; + event.category = category; + event.id = id; + event.phase = phase; + event.start_time = timestamp; + event.duration = duration; + event.instance_id = instance_id; + event.task = task; + event.args = std::move(args); + event.thread_id = std::this_thread::get_id(); + return event; +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, const QueryTraceContext& ctx) { + return create(name, category, id, phase, MonotonicMicros() - ctx.start_ts, QueryTraceContext::DEFAULT_EVENT_ID, + ctx.fragment_instance_id, ctx.task, {}); +} + +QueryTraceEvent QueryTraceEvent::create_with_ctx(const std::string& name, const std::string& category, int64_t id, + char phase, int64_t start_ts, int64_t duration, + const QueryTraceContext& ctx) { + return create(name, category, id, phase, start_ts, duration, ctx.fragment_instance_id, ctx.task, {}); +} + +static const char* kSimpleEventFormat = + R"({"cat":"%s","name":"%s","pid":"0x%x","tid":"0x%x","id":"0x%x","ts":%ld,"ph":"%c","args":%s,"tidx":"0x%x"})"; +static const char* kCompleteEventFormat = + R"({"cat":"%s","name":"%s","pid":"0x%x","tid":"0x%x","id":"0x%x","ts":%ld,"dur":%ld,"ph":"%c","args":%s,"tidx":"0x%x"})"; + +std::string QueryTraceEvent::to_string() { + std::string args_str = args_to_string(); + size_t tidx = std::hash<std::thread::id>{}(thread_id); + + // transform a raw pointer to uint64_t by re_cast is maybe not the best way, but at least unambiguous & safe. + if (phase == 'X') { + return fmt::sprintf(kCompleteEventFormat, category.c_str(), name.c_str(), (uint64_t)instance_id, + reinterpret_cast<uint64_t>(task), id, start_time, duration, phase, args_str.c_str(), tidx); + } else { + return fmt::sprintf(kSimpleEventFormat, category.c_str(), name.c_str(), (uint64_t)instance_id, + reinterpret_cast<uint64_t>(task), id, start_time, phase, args_str.c_str(), tidx); + } +} + +std::string QueryTraceEvent::args_to_string() { + if (args.empty()) { + return "{}"; + } + std::ostringstream oss; + oss << "{"; + oss << fmt::sprintf(R"("%s":"%s")", args[0].first.c_str(), args[0].second.c_str()); + for (size_t i = 1; i < args.size(); i++) { + oss << fmt::sprintf(R"(,"%s":"%s")", args[i].first.c_str(), args[i].second.c_str()); + } + oss << "}"; + return oss.str(); +} + +void EventBuffer::add(QueryTraceEvent&& event) { + std::lock_guard<std::mutex> l(_mutex); + _buffer.emplace_back(std::move(event)); +} + +#ifdef ENABLE_QUERY_DEBUG_TRACE +QueryTrace::QueryTrace(const TUniqueId& query_id, bool is_enable) : _query_id(query_id), _is_enable(is_enable) { + if (_is_enable) { + _start_ts = MonotonicMicros(); + } +} +#else +QueryTrace::QueryTrace(const TUniqueId& query_id, bool is_enable) {} +#endif + +void QueryTrace::register_tasks(const TUniqueId& fragment_instance_id, pipeline::PipelineTasks& tasks) { +#ifdef ENABLE_QUERY_DEBUG_TRACE + if (!_is_enable) { + return; + } + std::unique_lock l(_mutex); + auto iter = _fragment_tasks.find(fragment_instance_id); + if (iter == _fragment_tasks.end()) { + _fragment_tasks.emplace(fragment_instance_id, + std::make_shared<std::unordered_set<pipeline::PipelineTaskRawPtr>>()); + iter = _fragment_tasks.find(fragment_instance_id); + } + for (auto& task : tasks) { + iter->second->insert(task); // into this fragment's task map. + _buffers.emplace(task, std::make_unique<EventBuffer>()); + } +#endif +} + +Status QueryTrace::dump() { +#ifdef ENABLE_QUERY_DEBUG_TRACE + if (!_is_enable) { + return Status::OK(); + } + static const char* kProcessNameMetaEventFormat = + "{\"name\":\"process_name\",\"ph\":\"M\",\"pid\":\"0x%x\",\"args\":{\"name\":\"%s\"}}"; + static const char* kThreadNameMetaEventFormat = + "{\"name\":\"thread_name\",\"ph\":\"M\",\"pid\":\"0x%x\",\"tid\":\"0x%x\",\"args\":{\"name\":\"%s\"}}"; + try { + std::filesystem::create_directory(doris::config::tracing_dir); + std::string file_name = + fmt::format("{}/{}.json", doris::config::tracing_dir, print_id(_query_id)); + std::ofstream oss(file_name.c_str(), std::ios::out | std::ios::binary); + oss << "{\"traceEvents\":[\n"; + bool is_first = true; + for (auto& [fragment_id, task_set] : _fragment_tasks) { + std::string fragment_id_str = print_id(fragment_id); + oss << (is_first ? "" : ",\n"); + oss << fmt::sprintf(kProcessNameMetaEventFormat, (uint64_t)fragment_id.lo, fragment_id_str.c_str()); + is_first = false; + for (auto& task : *task_set) { + pipeline::PipelineTaskRawPtr ptr = reinterpret_cast<pipeline::PipelineTaskRawPtr>(task); + oss << (is_first ? "" : ",\n"); + oss << fmt::sprintf(kThreadNameMetaEventFormat, (uint64_t)fragment_id.lo, reinterpret_cast<uint64_t>(task.get()), + std::to_string(ptr->get_id())); + } + } + + for (auto& [_, buffer_ptr] : _buffers) { + auto& buffer = buffer_ptr->_buffer; + for (auto iter : buffer) { + oss << (is_first ? "" : ",\n"); + oss << iter.to_string(); + } + } + oss << "\n]}"; + + oss.close(); + } catch (std::exception& e) { + return Status::IOError(fmt::format("dump trace log error {}", e.what())); + } +#endif + return Status::OK(); +} + +void QueryTrace::set_tls_trace_context(QueryTrace* query_trace, const TUniqueId& fragment_instance_id, + pipeline::PipelineTaskRawPtr task) { +#ifdef ENABLE_QUERY_DEBUG_TRACE + if (!query_trace->_is_enable) { + tls_trace_ctx.reset(); + return; + } + { + std::shared_lock l(query_trace->_mutex); + auto iter = query_trace->_buffers.find(task); Review Comment: warning: no member named '_buffers' in 'doris::debug::QueryTrace' [clang-diagnostic-error] ```cpp auto iter = query_trace->_buffers.find(task); ^ ``` ########## be/src/util/debug/tracing.h: ########## @@ -0,0 +1,166 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once + +#include <deque> +#include <list> +#include <map> +#include <memory> +#include <mutex> +#include <shared_mutex> +#include <string> +#include <unordered_set> +#include <thread> + +#include "common/status.h" +#include "gen_cpp/Types_types.h" +#include "pipeline/pipeline_task.h" + +#define ENABLE_QUERY_DEBUG_TRACE + +// The whole function is control by marco in building. if the function is turned off, no entity will be construct. +// In another word, it's zero-overhead. + +namespace doris::debug { + +class QueryTraceContext; + +/// QueryTraceEvent saves a specific event info. +class QueryTraceEvent { +public: + std::string name; + std::string category; + int64_t id; // used for async event + char phase; // type of event + int64_t start_time; + int64_t duration = -1; // used only in compelete event + decltype(TUniqueId::lo) instance_id; + pipeline::PipelineTaskRawPtr task; // task pointer Review Comment: warning: no type named 'PipelineTaskRawPtr' in namespace 'doris::pipeline' [clang-diagnostic-error] ```cpp pipeline::PipelineTaskRawPtr task; // task pointer ^ ``` ########## be/src/util/debug/tracing.h: ########## @@ -0,0 +1,166 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once + +#include <deque> +#include <list> +#include <map> +#include <memory> +#include <mutex> +#include <shared_mutex> +#include <string> +#include <unordered_set> +#include <thread> + +#include "common/status.h" +#include "gen_cpp/Types_types.h" +#include "pipeline/pipeline_task.h" + +#define ENABLE_QUERY_DEBUG_TRACE + +// The whole function is control by marco in building. if the function is turned off, no entity will be construct. +// In another word, it's zero-overhead. + +namespace doris::debug { + +class QueryTraceContext; + +/// QueryTraceEvent saves a specific event info. +class QueryTraceEvent { +public: + std::string name; + std::string category; + int64_t id; // used for async event + char phase; // type of event + int64_t start_time; + int64_t duration = -1; // used only in compelete event + decltype(TUniqueId::lo) instance_id; + pipeline::PipelineTaskRawPtr task; // task pointer + std::thread::id thread_id; + std::vector<std::pair<std::string, std::string>> args; + + std::string to_string(); + + static QueryTraceEvent create(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, Review Comment: warning: no type named 'PipelineTaskRawPtr' in namespace 'doris::pipeline' [clang-diagnostic-error] ```cpp int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, ^ ``` ########## be/src/util/debug/tracing.h: ########## @@ -0,0 +1,166 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once + +#include <deque> +#include <list> +#include <map> +#include <memory> +#include <mutex> +#include <shared_mutex> +#include <string> +#include <unordered_set> +#include <thread> + +#include "common/status.h" +#include "gen_cpp/Types_types.h" +#include "pipeline/pipeline_task.h" + +#define ENABLE_QUERY_DEBUG_TRACE + +// The whole function is control by marco in building. if the function is turned off, no entity will be construct. +// In another word, it's zero-overhead. + +namespace doris::debug { + +class QueryTraceContext; + +/// QueryTraceEvent saves a specific event info. +class QueryTraceEvent { +public: + std::string name; + std::string category; + int64_t id; // used for async event + char phase; // type of event + int64_t start_time; + int64_t duration = -1; // used only in compelete event + decltype(TUniqueId::lo) instance_id; + pipeline::PipelineTaskRawPtr task; // task pointer + std::thread::id thread_id; + std::vector<std::pair<std::string, std::string>> args; + + std::string to_string(); + + static QueryTraceEvent create(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, + std::vector<std::pair<std::string, std::string>>&& args); + + static QueryTraceEvent create_with_ctx(const std::string& name, const std::string& category, int64_t id, char phase, + const QueryTraceContext& ctx); + + static QueryTraceEvent create_with_ctx(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t start_ts, int64_t duration, const QueryTraceContext& ctx); + +private: + std::string args_to_string(); +}; + +/// Event buffer for a single PipelineTask. +/// didn't store in threads' own trace_context, but in QueryTrace. +class EventBuffer { +public: + EventBuffer() = default; + ~EventBuffer() = default; + + void add(QueryTraceEvent&& event); + +private: + friend class QueryTrace; + std::mutex _mutex; + std::deque<QueryTraceEvent> _buffer; +}; + +class QueryTrace { +public: + QueryTrace(const TUniqueId& query_id, bool is_enable); + ~QueryTrace() = default; + + // init event buffer for all tasks in a single fragment instance + void register_tasks(const TUniqueId& fragment_instance_id, pipeline::PipelineTasks& tasks); + + Status dump(); + + static void set_tls_trace_context(QueryTrace* query_trace, const TUniqueId& fragment_instance_id, + pipeline::PipelineTaskRawPtr task); + +private: +#ifdef ENABLE_QUERY_DEBUG_TRACE + TUniqueId _query_id; + [[maybe_unused]] bool _is_enable = false; + [[maybe_unused]] int64_t _start_ts = -1; + + std::shared_mutex _mutex; + std::unordered_map<pipeline::PipelineTaskRawPtr, std::unique_ptr<EventBuffer>> _buffers; Review Comment: warning: no member named 'PipelineTaskRawPtr' in namespace 'doris::pipeline' [clang-diagnostic-error] ```cpp std::unordered_map<pipeline::PipelineTaskRawPtr, std::unique_ptr<EventBuffer>> _buffers; ^ ``` ########## be/src/util/debug/tracing.h: ########## @@ -0,0 +1,166 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once + +#include <deque> +#include <list> +#include <map> +#include <memory> +#include <mutex> +#include <shared_mutex> +#include <string> +#include <unordered_set> +#include <thread> + +#include "common/status.h" +#include "gen_cpp/Types_types.h" +#include "pipeline/pipeline_task.h" + +#define ENABLE_QUERY_DEBUG_TRACE + +// The whole function is control by marco in building. if the function is turned off, no entity will be construct. +// In another word, it's zero-overhead. + +namespace doris::debug { + +class QueryTraceContext; + +/// QueryTraceEvent saves a specific event info. +class QueryTraceEvent { +public: + std::string name; + std::string category; + int64_t id; // used for async event + char phase; // type of event + int64_t start_time; + int64_t duration = -1; // used only in compelete event + decltype(TUniqueId::lo) instance_id; + pipeline::PipelineTaskRawPtr task; // task pointer + std::thread::id thread_id; + std::vector<std::pair<std::string, std::string>> args; + + std::string to_string(); + + static QueryTraceEvent create(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, + std::vector<std::pair<std::string, std::string>>&& args); + + static QueryTraceEvent create_with_ctx(const std::string& name, const std::string& category, int64_t id, char phase, + const QueryTraceContext& ctx); + + static QueryTraceEvent create_with_ctx(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t start_ts, int64_t duration, const QueryTraceContext& ctx); + +private: + std::string args_to_string(); +}; + +/// Event buffer for a single PipelineTask. +/// didn't store in threads' own trace_context, but in QueryTrace. +class EventBuffer { +public: + EventBuffer() = default; + ~EventBuffer() = default; + + void add(QueryTraceEvent&& event); + +private: + friend class QueryTrace; + std::mutex _mutex; + std::deque<QueryTraceEvent> _buffer; +}; + +class QueryTrace { +public: + QueryTrace(const TUniqueId& query_id, bool is_enable); + ~QueryTrace() = default; + + // init event buffer for all tasks in a single fragment instance + void register_tasks(const TUniqueId& fragment_instance_id, pipeline::PipelineTasks& tasks); + + Status dump(); + + static void set_tls_trace_context(QueryTrace* query_trace, const TUniqueId& fragment_instance_id, + pipeline::PipelineTaskRawPtr task); + +private: +#ifdef ENABLE_QUERY_DEBUG_TRACE + TUniqueId _query_id; + [[maybe_unused]] bool _is_enable = false; + [[maybe_unused]] int64_t _start_ts = -1; + + std::shared_mutex _mutex; + std::unordered_map<pipeline::PipelineTaskRawPtr, std::unique_ptr<EventBuffer>> _buffers; + + // fragment_instance_id => task list, it will be used to generate meta event + // instance_id is in PipelineFragmentContext + std::unordered_map<TUniqueId, std::shared_ptr<std::unordered_set<pipeline::PipelineTaskRawPtr>>> _fragment_tasks; Review Comment: warning: no member named 'PipelineTaskRawPtr' in namespace 'doris::pipeline' [clang-diagnostic-error] ```cpp std::unordered_map<TUniqueId, std::shared_ptr<std::unordered_set<pipeline::PipelineTaskRawPtr>>> _fragment_tasks; ^ ``` ########## be/src/util/debug/tracing.h: ########## @@ -0,0 +1,166 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once + +#include <deque> +#include <list> +#include <map> +#include <memory> +#include <mutex> +#include <shared_mutex> +#include <string> +#include <unordered_set> +#include <thread> + +#include "common/status.h" +#include "gen_cpp/Types_types.h" +#include "pipeline/pipeline_task.h" + +#define ENABLE_QUERY_DEBUG_TRACE + +// The whole function is control by marco in building. if the function is turned off, no entity will be construct. +// In another word, it's zero-overhead. + +namespace doris::debug { + +class QueryTraceContext; + +/// QueryTraceEvent saves a specific event info. +class QueryTraceEvent { +public: + std::string name; + std::string category; + int64_t id; // used for async event + char phase; // type of event + int64_t start_time; + int64_t duration = -1; // used only in compelete event + decltype(TUniqueId::lo) instance_id; + pipeline::PipelineTaskRawPtr task; // task pointer + std::thread::id thread_id; + std::vector<std::pair<std::string, std::string>> args; + + std::string to_string(); + + static QueryTraceEvent create(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, + std::vector<std::pair<std::string, std::string>>&& args); + + static QueryTraceEvent create_with_ctx(const std::string& name, const std::string& category, int64_t id, char phase, + const QueryTraceContext& ctx); + + static QueryTraceEvent create_with_ctx(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t start_ts, int64_t duration, const QueryTraceContext& ctx); + +private: + std::string args_to_string(); +}; + +/// Event buffer for a single PipelineTask. +/// didn't store in threads' own trace_context, but in QueryTrace. +class EventBuffer { +public: + EventBuffer() = default; + ~EventBuffer() = default; + + void add(QueryTraceEvent&& event); + +private: + friend class QueryTrace; + std::mutex _mutex; + std::deque<QueryTraceEvent> _buffer; +}; + +class QueryTrace { +public: + QueryTrace(const TUniqueId& query_id, bool is_enable); + ~QueryTrace() = default; + + // init event buffer for all tasks in a single fragment instance + void register_tasks(const TUniqueId& fragment_instance_id, pipeline::PipelineTasks& tasks); + + Status dump(); + + static void set_tls_trace_context(QueryTrace* query_trace, const TUniqueId& fragment_instance_id, + pipeline::PipelineTaskRawPtr task); + +private: +#ifdef ENABLE_QUERY_DEBUG_TRACE + TUniqueId _query_id; + [[maybe_unused]] bool _is_enable = false; + [[maybe_unused]] int64_t _start_ts = -1; + + std::shared_mutex _mutex; + std::unordered_map<pipeline::PipelineTaskRawPtr, std::unique_ptr<EventBuffer>> _buffers; + + // fragment_instance_id => task list, it will be used to generate meta event + // instance_id is in PipelineFragmentContext + std::unordered_map<TUniqueId, std::shared_ptr<std::unordered_set<pipeline::PipelineTaskRawPtr>>> _fragment_tasks; +#endif +}; + +class ScopedTracer { +public: + ScopedTracer(std::string name, std::string category); + ~ScopedTracer() noexcept; + +private: + std::string _name; + std::string _category; + int64_t _start_ts; + int64_t _duration = -1; +}; + +// the real object is saved in bthread_context, a thread_local ThreadContext object. +class QueryTraceContext { +public: + static constexpr int64_t DEFAULT_EVENT_ID = 0; + + int64_t start_ts = -1; + int64_t fragment_instance_id = -1; + pipeline::PipelineTaskRawPtr task = nullptr; Review Comment: warning: no type named 'PipelineTaskRawPtr' in namespace 'doris::pipeline' [clang-diagnostic-error] ```cpp pipeline::PipelineTaskRawPtr task = nullptr; ^ ``` ########## be/src/util/debug/tracing.h: ########## @@ -0,0 +1,166 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once + +#include <deque> +#include <list> +#include <map> +#include <memory> +#include <mutex> +#include <shared_mutex> +#include <string> +#include <unordered_set> +#include <thread> + +#include "common/status.h" +#include "gen_cpp/Types_types.h" +#include "pipeline/pipeline_task.h" + +#define ENABLE_QUERY_DEBUG_TRACE + +// The whole function is control by marco in building. if the function is turned off, no entity will be construct. +// In another word, it's zero-overhead. + +namespace doris::debug { + +class QueryTraceContext; + +/// QueryTraceEvent saves a specific event info. +class QueryTraceEvent { +public: + std::string name; + std::string category; + int64_t id; // used for async event + char phase; // type of event + int64_t start_time; + int64_t duration = -1; // used only in compelete event + decltype(TUniqueId::lo) instance_id; + pipeline::PipelineTaskRawPtr task; // task pointer + std::thread::id thread_id; + std::vector<std::pair<std::string, std::string>> args; + + std::string to_string(); + + static QueryTraceEvent create(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t timestamp, int64_t duration, int64_t instance_id, pipeline::PipelineTaskRawPtr task, + std::vector<std::pair<std::string, std::string>>&& args); + + static QueryTraceEvent create_with_ctx(const std::string& name, const std::string& category, int64_t id, char phase, + const QueryTraceContext& ctx); + + static QueryTraceEvent create_with_ctx(const std::string& name, const std::string& category, int64_t id, char phase, + int64_t start_ts, int64_t duration, const QueryTraceContext& ctx); + +private: + std::string args_to_string(); +}; + +/// Event buffer for a single PipelineTask. +/// didn't store in threads' own trace_context, but in QueryTrace. +class EventBuffer { +public: + EventBuffer() = default; + ~EventBuffer() = default; + + void add(QueryTraceEvent&& event); + +private: + friend class QueryTrace; + std::mutex _mutex; + std::deque<QueryTraceEvent> _buffer; +}; + +class QueryTrace { +public: + QueryTrace(const TUniqueId& query_id, bool is_enable); + ~QueryTrace() = default; + + // init event buffer for all tasks in a single fragment instance + void register_tasks(const TUniqueId& fragment_instance_id, pipeline::PipelineTasks& tasks); + + Status dump(); + + static void set_tls_trace_context(QueryTrace* query_trace, const TUniqueId& fragment_instance_id, + pipeline::PipelineTaskRawPtr task); + +private: +#ifdef ENABLE_QUERY_DEBUG_TRACE + TUniqueId _query_id; + [[maybe_unused]] bool _is_enable = false; + [[maybe_unused]] int64_t _start_ts = -1; + + std::shared_mutex _mutex; + std::unordered_map<pipeline::PipelineTaskRawPtr, std::unique_ptr<EventBuffer>> _buffers; Review Comment: warning: expected member name or ';' after declaration specifiers [clang-diagnostic-error] ```cpp std::unordered_map<pipeline::PipelineTaskRawPtr, std::unique_ptr<EventBuffer>> _buffers; ^ ``` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org