| /* | |
| * Copyright (C) 2026 The Android Open Source Project | |
| * | |
| * Licensed 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. | |
| */ | |
| #ifndef SRC_TRACE_PROCESSOR_PLUGINS_FLAMEGRAPH_FLAMEGRAPH_H_ | |
| #define SRC_TRACE_PROCESSOR_PLUGINS_FLAMEGRAPH_FLAMEGRAPH_H_ | |
| #include <cstdint> | |
| #include <optional> | |
| #include <string> | |
| #include <vector> | |
| #include "perfetto/ext/base/regex.h" | |
| #include "perfetto/ext/base/status_or.h" | |
| #include "perfetto/ext/base/type_set.h" | |
| #include "src/trace_processor/containers/string_pool.h" | |
| #include "src/trace_processor/core/tree/tree.h" | |
| #include "src/trace_processor/core/util/slab.h" | |
| namespace perfetto::trace_processor::flamegraph { | |
| inline bool IsNumericColumn(const core::Tree::Column& column) { | |
| return column.type.Is<core::Int64>() || column.type.Is<core::Double>(); | |
| } | |
| // Fully resolved inputs to the flamegraph algorithm. The SQL layer is | |
| // responsible for resolving column names, validating column types, and | |
| // compiling patterns before calling Build. Per-row matching remains part of | |
| // the algorithm. | |
| // Column pointers refer to the Tree passed to Build and only need to remain | |
| // valid for that call. | |
| struct Config { | |
| explicit Config(StringPool& string_pool) : pool(string_pool) {} | |
| static constexpr uint32_t kMaxShowStackFilters = 61; | |
| // Structural orientations of the output. The named subsets below describe | |
| // which views share a behaviour; membership tests use View::IsAnyOf. | |
| struct TopDown {}; | |
| struct BottomUp {}; | |
| struct Pivot {}; | |
| struct FromFrame {}; | |
| using View = base::TypeSet<TopDown, BottomUp, Pivot, FromFrame>; | |
| // Views anchored on frames matching |view_pattern|. | |
| using PatternViews = base::TypeSet<Pivot, FromFrame>; | |
| // Views which build the downward (descendant) half of the output. | |
| using DownwardViews = base::TypeSet<TopDown, Pivot, FromFrame>; | |
| // Views which build the upward (ancestor) half of the output. | |
| using UpwardViews = base::TypeSet<BottomUp, Pivot>; | |
| enum class Aggregate { | |
| kSum, | |
| kOneOrSummary, | |
| kConcatWithComma, | |
| }; | |
| struct AggregateColumn { | |
| const core::Tree::Column* input; | |
| Aggregate aggregate; | |
| std::string output_name; | |
| }; | |
| StringPool& pool; | |
| // Structural orientation. Pivot builds both descendants and ancestors of | |
| // rows matching |view_pattern|. From-frame builds only descendants of the | |
| // matching rows. |view_pattern| is set exactly for those two views. | |
| View view = View(TopDown{}); | |
| std::optional<base::Regex> view_pattern; | |
| // Frame identity. Grouping columns, together with name, determine which | |
| // frames can be merged. Regex filters match against all of these columns. | |
| const core::Tree::Column* name = nullptr; | |
| std::vector<const core::Tree::Column*> grouping_columns; | |
| // Filtering, separated by operation before entering the algorithm. | |
| std::vector<base::Regex> show_stack_filters; | |
| std::vector<base::Regex> hide_stack_filters; | |
| std::vector<base::Regex> hide_frame_filters; | |
| // Numeric sample-weight columns. Each is SUM-aggregated into a | |
| // self_<name>/cumulative_<name> output pair. At least one is required. | |
| std::vector<const core::Tree::Column*> value_columns; | |
| // Additional aggregates carried through structural folds. They do not | |
| // contribute to flamegraph geometry. | |
| std::vector<AggregateColumn> aggregate_columns; | |
| // Returns true if any filters are configured, false otherwise. | |
| bool HasFilters() const { | |
| return !show_stack_filters.empty() || !hide_stack_filters.empty() || | |
| !hide_frame_filters.empty(); | |
| } | |
| }; | |
| // Builds a merged structural flamegraph from a parent-before-child tree. | |
| // Presentation layout is a separate operation: see ComputeLayout. | |
| base::StatusOr<core::Tree> Build(const core::Tree&, const Config&); | |
| // Presentation geometry for one built flamegraph, in render order: | |
| // breadth-first, siblings packed left-to-right by decreasing cumulative | |
| // value. A node's width is its cumulative value clamped to zero, so its end | |
| // position is x_start plus that width. | |
| struct Layout { | |
| core::Slab<uint32_t> node; // output row -> tree row | |
| core::Slab<uint32_t> parent_row; // output row -> parent output row | |
| core::Slab<double> x_start; | |
| }; | |
| // Computes presentation geometry for a tree returned by Build, positioning | |
| // each node by |cumulative|, one of the tree's cumulative value columns. | |
| // |depth| is the tree's depth column; its sign separates the downward and | |
| // upward halves of the flamegraph, which are laid out as independent strips. | |
| // Nodes with non-positive cumulative value take up no width but keep their | |
| // position so that consumers of secondary value columns still see them. | |
| Layout ComputeLayout(const core::Tree&, | |
| const core::Tree::Column& cumulative, | |
| const core::Tree::Column& depth); | |
| } // namespace perfetto::trace_processor::flamegraph | |
| #endif // SRC_TRACE_PROCESSOR_PLUGINS_FLAMEGRAPH_FLAMEGRAPH_H_ |